native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
4.8 kB · 125 lines
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126import Foundation
/// How far Prowl trusts a producer's OSC 7501 reports (docs-ai 079). A static table,/// not a setting: a producer either reports or it does not, and a verified one is/// trusted whenever it reports. The producer's own variables are the escape hatch.nonisolated enum ProgramStatusSupport: Equatable, Sendable { /// Reports are stored, fenced, and resolved, but only compared with the live /// decision; disagreements are logged. The legacy decision is published. case unverified /// An eligible root record drives the decision and the schedule is delegated. case verified
/// Slice 2 ships every agent unverified; slices 3 and 4 flip Claude and Pi after /// their baselines replay inside Prowl. static func level(for agent: DetectedAgent) -> ProgramStatusSupport { switch agent { default: .unverified } }}
extension DetectedAgent { /// The `app` value the agent writes into its own records, or `nil` for agents /// without a known producer. `app` decides whose state a record is, never whether /// it is authentic. nonisolated var programStatusApp: String? { switch self { case .claude: "claude-code" case .pi: "pi" default: nil } }}
/// The reason a decision is OSC-driven; `identifier` is the public `detection_reason`.nonisolated enum ProgramStatusReason: Equatable, Sendable { case working case blocked(GhosttyProgramStatusReport.Kind?) case idle case done case error case childBlocked(GhosttyProgramStatusReport.Kind?)
var identifier: String { switch self { case .working: "osc.working" case .blocked(let kind): "osc.blocked.\(kind?.rawValue ?? "unspecified")" case .idle: "osc.idle" case .done: "osc.done" case .error: "osc.error" case .childBlocked(let kind): "osc.childBlocked.\(kind?.rawValue ?? "unspecified")" } }
/// `done` and `error` leave a result to look at; `idle` is sent at mount, on /// interrupt, and on a session reset, none of which does. var earnsDoneBadge: Bool { switch self { case .done, .error: true case .working, .blocked, .idle, .childBlocked: false } }}
/// What the eligible records of one surface say about its agent: the root's state and/// whether an eligible child is blocked. Child `working` is deliberately absent: the/// pane follows the root only (decision of 2026-10-10).nonisolated struct ProgramStatusEvidence: Equatable, Sendable { struct BlockedChild: Equatable, Sendable { let kind: GhosttyProgramStatusReport.Kind? }
let root: ProgramStatusRecord.State let rootKind: GhosttyProgramStatusReport.Kind? let blockedChild: BlockedChild?
init( root: ProgramStatusRecord.State, rootKind: GhosttyProgramStatusReport.Kind? = nil, blockedChild: BlockedChild? = nil ) { self.root = root self.rootKind = rootKind self.blockedChild = blockedChild }
/// The attribution fence. A record is eligible when its `app` (after inheritance) /// equals the agent's `programStatusApp` and it arrived no earlier than the detected /// process started; a root record is required. The fence is attribution, not trust. init?(store: ProgramStatusRecordStore, app: String, notBefore: Date) { func eligible(_ record: ProgramStatusRecord) -> Bool { store.app(of: record) == app && record.arrivedAt >= notBefore } guard let root = store.root, eligible(root) else { return nil } let blocked = store.children.first { $0.state == .blocked && eligible($0) } self.init( root: root.state, rootKind: root.state == .blocked ? root.kind : nil, blockedChild: blocked.map { BlockedChild(kind: $0.kind) }) }
/// The pure mapping from evidence to decision; shared by the state machine and the /// shadow comparison of unverified producers. `screenReason` is left to the caller. var decision: AgentStateDecision { switch root { case .blocked: return AgentStateDecision(state: .blocked, reason: .programStatus(.blocked(rootKind)), hasOutstandingWork: true) case .working, .idle, .done, .error: if let blockedChild { return AgentStateDecision( state: .blocked, reason: .programStatus(.childBlocked(blockedChild.kind)), hasOutstandingWork: true) } } switch root { case .working: return AgentStateDecision(state: .working, reason: .programStatus(.working), hasOutstandingWork: true) case .idle: return AgentStateDecision(state: .idle, reason: .programStatus(.idle)) case .done: return AgentStateDecision(state: .idle, reason: .programStatus(.done)) case .error: return AgentStateDecision(state: .idle, reason: .programStatus(.error)) case .blocked: return AgentStateDecision(state: .blocked, reason: .programStatus(.blocked(rootKind)), hasOutstandingWork: true) } }}