Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6bdec7b
fix(broker): verify PTY turn submission (#1891)
kjgbot Oct 8, 2026
de91fc8
style: prettier-format merge-train files (unblocks trunk → main lint)
khaliqgant Oct 9, 2026
65bf077
ci: route Linux CI jobs through vars.CI_LINUX_RUNNER
Oct 9, 2026
ccd18c3
Merge pull request #1946 from AgentWorkforce/ci/starsling-runners
kjgbot Oct 9, 2026
4faa95a
ci: install Rust toolchain in jobs that build the broker on StarSling
Oct 9, 2026
da7d54d
Merge pull request #1947 from AgentWorkforce/ci/starsling-rust-toolchain
kjgbot Oct 9, 2026
e1287f9
fix(broker): address #1939 must-fix PTY delivery review findings (WIP)
Oct 9, 2026
b8fb668
fix: address #1939 P2/P3 review items (changelog, docs, real-codex gu…
miyaontherelay Oct 9, 2026
ca627bf
test(broker): M1/M6/OpenCode regression tests; fmt
Oct 9, 2026
a80f920
fix(broker): keep parked drafts with prompt-like rows detectable
miyaontherelay Oct 9, 2026
7c6bfa3
fix(broker): fail closed on long or off-screen failed drafts; unbound…
Oct 9, 2026
a3cbca9
fix(broker): satisfy clippy contains_key on the failed-delivery fence
Oct 9, 2026
09d1f99
fix(broker): latch partial chunked drafts; publish non-stuck state; i…
Oct 9, 2026
faba2e7
test(broker): off-screen draft tail keeps generic acceptance inconclu…
Oct 9, 2026
5447d54
fix: address #1955 review (option guard, changelog split, ack comment)
miyaontherelay Oct 10, 2026
c749f74
fix(broker): ignore prompt and border chrome when probing body text a…
Oct 10, 2026
0202e9f
fix(broker): strip only prompt and border glyphs from the cursor-row …
Oct 10, 2026
d153837
Merge origin/trunk (#1939 promotion: #1891, #1946, #1947, prettier fi…
miyaontherelay Oct 10, 2026
cb3670e
Merge remote-tracking branch 'origin/fix/1939-review-feedback' into p…
miyaontherelay Oct 10, 2026
b926c65
Merge remote-tracking branch 'origin/fix/1939-p2p3-review-items' into…
miyaontherelay Oct 10, 2026
2354c37
fix(broker): failed draft ending in a bare prompt row stays latched
Oct 10, 2026
39a80d0
fix(broker): partial chunked initial write latches until operator flu…
Oct 10, 2026
c9f1718
Merge origin/main (#1945 file attachments) into promote/1939-pty-turn…
khaliqgant Oct 10, 2026
0789356
docs(changelog): list delivery_unconfirmed / delivery_resubmitted bro…
miyaontherelay Oct 10, 2026
2c187fd
fix(broker): wrap failed-draft latch, Devin parked deferral, report l…
khaliqgant Oct 10, 2026
ce7dca5
test(broker): read-only attachments root test checks the mode is enfo…
khaliqgant Oct 10, 2026
4766a19
Merge origin/main (#1930 bundle) into promote/1939-pty-turn-submission
khaliqgant Oct 10, 2026
485084e
Merge origin/main (v13.3.0, #1942) into promote/1939-pty-turn-submission
khaliqgant Oct 10, 2026
1055cb4
fix(broker): three-outcome PTY acceptance per the #1959 policy (P2-P5)
khaliqgant Oct 10, 2026
54fbd3a
test(broker): opaque and stuck harness integration tests; fleet stub …
khaliqgant Oct 10, 2026
98b7dd0
Merge origin/main (#1940, #1924, #1944, #1918) into promote/1939-pty-…
khaliqgant Oct 10, 2026
ebe5e4d
docs(changelog): describe the approved #1959 acceptance policy agains…
miyaontherelay Oct 10, 2026
4cb0e54
fix(broker): visible drafts are stuck, not unconfirmed; only a lone E…
khaliqgant Oct 11, 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
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ jobs:
# Build all packages once, version them, and upload
build:
name: Build & Version
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_LINUX_RUNNER || 'ubuntu-latest' }}
outputs:
new_version: ${{ steps.bump.outputs.new_version }}
is_prerelease: ${{ steps.bump.outputs.is_prerelease }}
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/rust-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
if: >-
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) &&
(needs.changes.outputs.rust_changed == 'true')
runs-on: ${{ matrix.os }}
runs-on: ${{ matrix.os == 'ubuntu-latest' && (vars.CI_LINUX_RUNNER || 'ubuntu-latest') || matrix.os }}
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -70,7 +70,7 @@ jobs:
if: >-
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) &&
(needs.changes.outputs.rust_changed == 'true')
runs-on: ${{ matrix.os }}
runs-on: ${{ matrix.os == 'ubuntu-latest' && (vars.CI_LINUX_RUNNER || 'ubuntu-latest') || matrix.os }}
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -106,7 +106,7 @@ jobs:
if: >-
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) &&
(needs.changes.outputs.rust_changed == 'true')
runs-on: ${{ matrix.os }}
runs-on: ${{ matrix.os == 'ubuntu-latest' && (vars.CI_LINUX_RUNNER || 'ubuntu-latest') || matrix.os }}
strategy:
fail-fast: false
matrix:
Expand All @@ -127,7 +127,7 @@ jobs:
if: >-
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) &&
(needs.changes.outputs.rust_changed == 'true')
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_LINUX_RUNNER || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
Expand Down
10 changes: 8 additions & 2 deletions .github/workflows/test-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
test-build-script:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
name: Test build-bun.sh
runs-on: ${{ matrix.os }}
runs-on: ${{ matrix.os == 'ubuntu-latest' && (vars.CI_LINUX_RUNNER || 'ubuntu-latest') || matrix.os }}
strategy:
fail-fast: false
matrix:
Expand All @@ -33,6 +33,12 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

# StarSling Linux runners (vars.CI_LINUX_RUNNER) preinstall rustc 1.92, older
# than GitHub's ubuntu-24.04 image (1.99); 1.92 cannot compile the
# broker (stable-only atomic try_update). Install the toolchain explicitly.
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
Expand Down Expand Up @@ -111,7 +117,7 @@ jobs:
test-build-edge-cases:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
name: Test edge cases
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_LINUX_RUNNER || 'ubuntu-latest' }}

steps:
- name: Checkout code
Expand Down
61 changes: 58 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
if: >-
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) &&
(needs.changes.outputs.node_changed == 'true')
runs-on: ${{ matrix.os }}
runs-on: ${{ matrix.os == 'ubuntu-latest' && (vars.CI_LINUX_RUNNER || 'ubuntu-latest') || matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
Expand All @@ -40,6 +40,12 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4

# StarSling Linux runners (vars.CI_LINUX_RUNNER) preinstall rustc 1.92, older
# than GitHub's ubuntu-24.04 image (1.99); 1.92 cannot compile the
# broker (stable-only atomic try_update). Install the toolchain explicitly.
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable

- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
Expand All @@ -61,6 +67,49 @@ jobs:
- name: Run tests
run: npm test

# The PR-proof Landlock / devpts / no_new_privs sandbox tests skip themselves
# (it.skipIf(!HAS_TRUSTED_LANDLOCK_RUNTIME)) on kernels that cannot host the
# sandbox, which includes the StarSling runners that `test` above may run on
# via vars.CI_LINUX_RUNNER. This job stays pinned to GitHub's ubuntu-latest
# (never the runner variable) and fails if any test in the file is skipped,
# so those tests can never silently stop running.
sandbox-proof:
name: PR-proof sandbox tests (Landlock, ubuntu-latest)
needs: changes
if: >-
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) &&
(needs.changes.outputs.node_changed == 'true')
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22.14.0'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Ensure rollup optional dependencies are installed
run: npm install --no-save rollup || true

- name: Run PR-proof sandbox tests
run: npx vitest run tests/fixtures/pr-proof-contract.test.ts --reporter=default --reporter=json --outputFile.json=pr-proof-results.json

- name: Require every PR-proof test to have run
run: |
node -e '
const r = require("./pr-proof-results.json");
const all = r.testResults.flatMap((f) => f.assertionResults);
const notRun = all.filter((t) => t.status !== "passed" && t.status !== "failed");
console.log("pr-proof-contract: " + all.length + " tests, " + notRun.length + " not run");
for (const t of notRun) console.log("::error::not run (" + t.status + "): " + t.fullName);
if (all.length === 0 || notRun.length > 0) process.exit(1);
'

windows-credential-acl:
name: Windows credential ACL validation
needs: changes
Expand Down Expand Up @@ -100,11 +149,17 @@ jobs:
if: >-
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) &&
(needs.changes.outputs.node_changed == 'true')
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_LINUX_RUNNER || 'ubuntu-latest' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4

# StarSling Linux runners (vars.CI_LINUX_RUNNER) preinstall rustc 1.92, older
# than GitHub's ubuntu-24.04 image (1.99); 1.92 cannot compile the
# broker (stable-only atomic try_update). Install the toolchain explicitly.
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable

- name: Setup Node.js
uses: actions/setup-node@v4
with:
Expand All @@ -130,7 +185,7 @@ jobs:
if: >-
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) &&
(needs.changes.outputs.node_changed == 'true')
runs-on: ubuntu-latest
runs-on: ${{ vars.CI_LINUX_RUNNER || 'ubuntu-latest' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- `agent-relay integration subscribe --to self` routes provider events to the broker worker's registered identity, allowing fleet workers to subscribe without desktop session detection.
- Broker events report PTY submit recovery: `delivery_unconfirmed` (no acceptance evidence for a write, with `attempts` / `max_attempts`; `terminal: true` when the delivery ends unconfirmed) and `delivery_resubmitted` (a submit-only retry, with `attempt` and `strategy`); `delivery_verified` adds `evidence` and `attempts`. `@agent-relay/harness-driver` and the Python and Swift SDK protocol types include both new events.

### Changed

- A PTY delivery the broker can neither confirm nor see still parked in the agent's input box is now delivered as unconfirmed (a terminal `delivery_unconfirmed` with `outcome: "unconfirmed"`) instead of failing, and later messages to that agent are no longer held behind it. Agents whose terminal shows no acceptance signal keep receiving messages.
- A confirmed `fleet spawn --task` whose task was written but never visibly accepted now succeeds (`spawned: true`) with `warning.code: "spawn_task_unconfirmed"`, instead of failing with `spawn_task_unconfirmed` and exit 8. Only a task that could not be delivered fails the spawn, with `spawn_task_failed`.

### Fixed

- `agent-relay fleet spawn` explains how to reuse a retained agent name with `fleet release <name> --delete-agent --wait` when registration fails with `agent_already_exists`.
- The installer's Quick Start now prints `agent-relay node up --background` and `agent-relay node down`, which exist, instead of the removed `agent-relay up` form that failed with "unknown command 'up'". An install pinned with `AGENT_RELAY_VERSION` to a release older than 9.2.2 still prints the top-level `up`/`down` forms that release has.
- A PTY message still visible in the agent's input box after submit is retried by pressing submit only, never by resending the body, for up to three total attempts. Later messages queue behind it instead of being typed on top of it, and if it is still stuck after 60 seconds the broker reports a terminal `agent_blocked_on_send` with reason `failed_draft_latched`.
- Typing into an agent's terminal while a delivery is being retried takes over the terminal and cancels the retries.

## [13.3.0] - 2026-10-10

Expand Down
16 changes: 15 additions & 1 deletion crates/broker/src/attachments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1347,9 +1347,23 @@ mod tests {
std::fs::create_dir_all(&primary).unwrap();
std::fs::set_permissions(&primary, std::fs::Permissions::from_mode(0o555)).unwrap();
let fallback = temp.path().join("fallback");
// Some CI runners bypass directory permission bits (root, a granted
// CAP_DAC_OVERRIDE, or a filesystem that ignores modes). There the
// 0o555 directory really is writable, so accepting it is correct and
// refusal must be exercised with a root that cannot be written at all.
let mode_enforced = std::fs::write(primary.join(".mode-probe"), b"").is_err();
let refused = prepare_root(&primary).await.is_err();
std::fs::set_permissions(&primary, std::fs::Permissions::from_mode(0o755)).unwrap();
assert!(refused, "a read-only attachments root must be refused");
if mode_enforced {
assert!(refused, "a read-only attachments root must be refused");
} else {
let not_a_directory = temp.path().join("not-a-directory");
std::fs::write(&not_a_directory, b"").unwrap();
assert!(
prepare_root(&not_a_directory).await.is_err(),
"an unwritable attachments root must be refused"
);
}
assert!(prepare_root(&fallback).await.is_ok());
}

Expand Down
Loading
Loading