Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
127298f
feat(acp): implement permission policy (#4938)
Aug 6, 2026
d8be338
feat(desktop): permission policy config + actionable Allow/Deny card …
Aug 6, 2026
90e3292
fix(desktop): control_result is delivery confirmation, not terminal o…
Aug 6, 2026
4f72c3c
Merge remote-tracking branch 'origin/hayt/permission-policy' into dun…
Aug 6, 2026
6dbbc67
feat(acp): add PermissionMode::Auto + contradiction matrix rows (#4938)
Aug 6, 2026
b3b1b3b
fix(acp): address Thufir pass-1 CRITICAL and IMPORTANT findings (#4938)
Aug 6, 2026
96538e9
fix(acp): address Thufir pass-2 review findings (#4938)
Aug 6, 2026
6607eaf
fix(acp): structural round — one nonce-keyed permission record, finis…
Aug 7, 2026
7d277da
fix(acp): address Thufir pass-4 review findings
Aug 7, 2026
73f2ffe
fix(buzz-acp): thread single nonce through sync denial paths; upgrade…
Aug 7, 2026
fcd33ff
test(buzz-acp): tighten equality-deadline test to pinned future + exa…
Aug 7, 2026
bcd9bac
refactor(desktop): extract permission/transcript types to fix file-si…
Aug 7, 2026
fe901d8
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Aug 7, 2026
2dd0706
refactor(desktop): extract RawManagedAgent/fromRawManagedAgent to man…
Aug 7, 2026
e87f265
fix(desktop): use struct literal init to satisfy clippy::field_reassi…
Aug 7, 2026
cef78d7
feat(desktop): render permission-request sentinel card in thread time…
Aug 8, 2026
d808c4b
feat(acp): publish kind-9/40003 permission sentinel cards into channe…
Aug 8, 2026
74c9200
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Aug 8, 2026
1e0c5ba
fix(acp): restore correct merge resolution for buzz-acp post-main-revert
Aug 8, 2026
a16b059
fix(desktop): parse bare-JSON sentinel, unify expiry clock, tighten g…
Aug 8, 2026
fe7efd8
fix(acp): ACK-gated sentinel lifecycle, D7 admission, NIP-AO docs
Aug 8, 2026
3129900
test(desktop): add harness integration fixture test for kind-9 parser
Aug 8, 2026
ef67355
fix(acp): background task owns ACK-waiter expiry, add 3 missing named…
Aug 8, 2026
aeca230
refactor(acp): delete publish_event_acked, only register_publish_ack …
Aug 8, 2026
f858cab
fix(desktop): sentinel-only edit gate in formatTimelineMessages; rewo…
Aug 8, 2026
60c247e
test(e2e): align permission outcome assertion with label-based rendering
Aug 8, 2026
ff5b21f
chore: merge origin/main into duncan/permission-policy
Aug 10, 2026
3ad999a
fix(desktop): fail-closed PermissionDecisionButtons for allow_always …
Aug 10, 2026
bac3264
fix(acp): add permission_decision_tx: None to main's new test TaskMet…
Aug 10, 2026
00456e4
fix(desktop): persist applied permission policy for remote deploys
Aug 10, 2026
ca9acfa
fix(desktop): exact-allowlist permission kinds, disclose persistent deny
Aug 10, 2026
0bc5683
fix(desktop): enforce deploy-receipt invariant for applied permission…
Aug 10, 2026
f4efd99
feat(desktop): relocate permission-policy default to the agent defini…
Aug 12, 2026
f1ab873
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Aug 12, 2026
3c0dfd7
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Aug 25, 2026
1725f16
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Aug 25, 2026
bf411ef
chore(desktop): reclaim file-size headroom via comment trims
Aug 25, 2026
9d42119
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Aug 25, 2026
25ddb2f
refactor(desktop): extract spawn helpers and reshape permission card …
Aug 25, 2026
d9bed35
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Aug 25, 2026
6aa3315
chore(desktop): reclaim file-size headroom for permission-policy fields
Aug 25, 2026
8672302
test(desktop): keep pending policy true in failed-deploy setup
Aug 25, 2026
d4417a9
test(desktop): expect seeded permissionPolicy in access-override edit…
Aug 25, 2026
e6014be
fix(acp): freeze permission card to a two-action contract
Aug 25, 2026
30bb1f8
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Aug 25, 2026
6e7c697
docs(nip-ao): align sentinel spec with two-action card contract
Aug 25, 2026
926a021
fix(acp): unify sentinel byte bounds across producer and parser
Aug 25, 2026
a183f25
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Aug 25, 2026
05566b9
fix(acp): gate sentinel total-content bound on raw content before trim
Aug 25, 2026
4a8305c
docs(acp): correct total-content gate rationale for control-char infl…
Aug 25, 2026
422e336
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Aug 26, 2026
0b967b5
fix(acp): bound Publishing entries and require edit provenance for re…
Aug 26, 2026
8538058
test(acp): make publish-in-flight regression production-shaped
Aug 26, 2026
7bb2982
refactor(acp): bundle ask-only preflight gates into AskGates
Aug 26, 2026
1a19de3
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Aug 27, 2026
018095e
fix(acp): retransmit resolved permission edit until relay accepts it
Aug 27, 2026
37103a6
fix(acp): bound resolved-edit retransmit per attempt, not per card
Aug 27, 2026
5916529
fix(acp): close inbound permission-decision lifecycle gap
Aug 28, 2026
111bc91
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Aug 28, 2026
247f897
fix(desktop): survive rejected send in permission retransmit loop
Aug 28, 2026
77f7440
fix(desktop): propagate control_result status; failure re-enables car…
Aug 28, 2026
ecd441b
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Aug 28, 2026
9301abf
test(desktop): add delivery-seam + component regression test for fail…
Aug 28, 2026
2787c55
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Aug 28, 2026
2f95fca
fix(acp): correct verb derivation, add description field, fix forged-…
Aug 29, 2026
8101c31
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Aug 29, 2026
05246f6
fix(acp): extract description from real producer shapes; delegate pro…
Aug 29, 2026
05b496e
fix(acp): extend description extraction to codex v1.1.7; protect pros…
Aug 29, 2026
255ed62
fix(acp): clippy borrow and MessageRow memo gaps for permission gate
Aug 30, 2026
ed3a25c
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Sep 1, 2026
aa30ce6
fix(acp): implement Carl review findings F1–F4 with full behavioral t…
Sep 1, 2026
d70558f
fix(acp): implement expanded findings F1-F4 + routing hazard
Sep 1, 2026
242dbf2
fix(acp): close Thufir addendum gaps — unconditional first attempt, i…
Sep 1, 2026
3d925ef
chore(desktop): reclaim ratchet headroom — extract duplicate types, c…
Sep 1, 2026
d5343ef
fix(desktop): restore extract_applied_permission_policy lost in merge…
Sep 1, 2026
00a04c1
fix(desktop): correct display_name in content_matches_nip_ap_vector c…
Sep 1, 2026
e5fb508
test(acp): fix bounded-exit test — small-step advance for cross-task …
Sep 1, 2026
b144047
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Sep 1, 2026
c23daf8
fix(acp): correct wrapper-overhead and test structure for F1/F3 regre…
Sep 1, 2026
9628c1e
test(buzz-acp): strengthen pass-2 acceptance proofs for F1–F4 and rou…
Sep 1, 2026
96c9b7f
test(buzz-acp): close pass-3 gaps for F1/F3/F4 and MINOR desktop wording
Sep 1, 2026
fbeb9fa
test(buzz-acp): assert reject option ID in reject_once applied write
Sep 1, 2026
5c8ec7a
fix(acp): scope permission-card retirement to terminating turn; fix f…
Sep 1, 2026
a0309cd
fix(acp): treat channel_full as transient; fix legacy-key retirement …
Sep 2, 2026
9659800
fix(acp): classify channel_full as transient in transcript reducer
Sep 2, 2026
51486b2
Merge remote-tracking branch 'origin/main' into duncan/permission-policy
Sep 2, 2026
2e2f4df
docs(agents): enumerate authoritative delivery-failure statuses in st…
Sep 2, 2026
61d47b7
Merge origin/main into duncan/permission-policy
Sep 21, 2026
8c65bc4
chore(desktop): trim discovery/tests.rs to ratchet ceiling
Sep 21, 2026
ce2c1b8
fix(desktop): submit draft session policy on full clear, not seed value
Sep 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8,429 changes: 8,291 additions & 138 deletions crates/buzz-acp/src/acp.rs

Large diffs are not rendered by default.

293 changes: 252 additions & 41 deletions crates/buzz-acp/src/config.rs

Large diffs are not rendered by default.

789 changes: 785 additions & 4 deletions crates/buzz-acp/src/lib.rs

Large diffs are not rendered by default.

61 changes: 60 additions & 1 deletion crates/buzz-acp/src/observer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,33 @@ pub struct ObserverContext {
pub started_at: Option<String>,
}

/// Authorization envelope attached to permission-related observer events.
///
/// Present on the single `acp_read` emitted after a permission request passes
/// the admission preflight, and on the corresponding `acp_write` after the
/// response is confirmed written.
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthorizationEnvelope {
/// Single-use nonce bound to this request — delivered to the desktop and
/// consumed exactly once when the owner makes a decision.
pub request_nonce: String,
/// `true` when the owner can take action (policy=ask, preflight passed,
/// owner/observer available). `false` for auto-deny / fail-closed paths.
pub actionable: bool,
/// Human-readable reason when `actionable` is `false`.
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
/// Wire card-expiry (unix seconds) for an actionable card — the same value
/// stored in the kind-9 sentinel. The desktop bounds its
/// retransmit-until-acked loop by this deadline, so a decision published
/// while the harness socket is down keeps being resent until the card
/// expires (never past it). `None` on non-actionable / already-resolved
/// frames, where no owner decision is awaited.
#[serde(skip_serializing_if = "Option::is_none")]
pub expires_at: Option<u64>,
}

/// Handle used by the harness to publish local observer events.
#[derive(Clone)]
pub struct ObserverHandle {
Expand All @@ -54,7 +81,7 @@ fn new_observer_handle() -> ObserverHandle {
}

/// Event delivered through the in-process observer bus.
#[derive(Clone, Serialize)]
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ObserverEvent {
/// Monotonic process-local sequence number.
Expand All @@ -74,6 +101,12 @@ pub struct ObserverEvent {
/// RFC3339 timestamp at which the current turn began, when known.
#[serde(skip_serializing_if = "Option::is_none")]
pub started_at: Option<String>,
/// Authorization envelope — present only on permission `acp_read` /
/// `acp_write` frames, and on the observer-only `permission_terminal` frame
/// (which carries `reason = "uncertain"` and is never sent on the ACP wire).
/// `None` on all other event kinds.
#[serde(skip_serializing_if = "Option::is_none")]
pub authorization: Option<AuthorizationEnvelope>,
/// Raw or semantic event payload.
pub payload: serde_json::Value,
}
Expand Down Expand Up @@ -107,6 +140,31 @@ impl ObserverHandle {
agent_index: Option<usize>,
context: &ObserverContext,
payload: serde_json::Value,
) {
self.emit_inner(kind, agent_index, context, None, payload);
}

/// Emit a local observer event with an authorization envelope.
///
/// Used for permission `acp_read` and `acp_write` frames.
pub fn emit_authorized(
&self,
kind: impl Into<String>,
agent_index: Option<usize>,
context: &ObserverContext,
authorization: AuthorizationEnvelope,
payload: serde_json::Value,
) {
self.emit_inner(kind, agent_index, context, Some(authorization), payload);
}

fn emit_inner(
&self,
kind: impl Into<String>,
agent_index: Option<usize>,
context: &ObserverContext,
authorization: Option<AuthorizationEnvelope>,
payload: serde_json::Value,
) {
let event = ObserverEvent {
seq: self.inner.seq.fetch_add(1, Ordering::Relaxed),
Expand All @@ -117,6 +175,7 @@ impl ObserverHandle {
session_id: context.session_id.clone(),
turn_id: context.turn_id.clone(),
started_at: context.started_at.clone(),
authorization,
payload,
};

Expand Down
Loading
Loading