Commit Graph

506 Commits

Author SHA1 Message Date
Peter Steinberger e7cfff2167 feat(control-ui): stream live draft previews in the typing indicator (#126994)
* feat(control-ui): stream live draft previews in the typing indicator

Multi-identity sessions now show what a teammate is typing, not just that
they are typing: the composer's per-keystroke session.typing sends carry a
bounded tail of the draft (optional preview field, 400 code points max),
the gateway throttle re-emits on changed payloads at 250ms (boolean-only
stays at 1s, trailing edge keeps the latest draft), and the transcript
renders a per-actor bubble with the live text plus a blinking caret.
Actors without preview data keep the three-dot bubble.

Previews are ephemeral presence: never persisted, never part of the
session transcript or model context, excluded from aria-live regions, and
gated by the existing >=2-live-viewers, sharing-role, and incognito
checks. No new config surface.

* chore(protocol): regenerate Swift gateway models for typing preview

* fix(gateway): aggregate typing previews across same-actor connections

A boolean-only session.typing update from a second connection of the same
actor (another tab or device) erased their live draft preview, because
typing liveness aggregated per actor while the broadcast preview came only
from the latest request. Preview aggregation now lives with the connection
aggregation owner: updateTypingConnections tracks per-connection previews
and returns the newest non-empty preview among live connections, so the
broadcast keeps the active draft until its connection stops or expires.

Regression fails pre-fix (event lost its preview field).
2026-08-20 22:30:41 -07:00
Peter Steinberger e22774127a test: remove core test scaffolding (#126926) 2026-08-20 18:34:15 -07:00
Peter Steinberger bbbd70542b feat(sessions): recover offline device placements (#126284)
* feat(sessions): recover offline device placements

* chore(protocol): refresh session placement models

* perf(ui): lazy-load session placement recovery

* test(ui): remove dropdown timing assertion

* test(ui): await committed cloud recovery route

* fix(sessions): complete explicit abandonment locally

* fix(sessions): fence lists by runner availability

* fix(ui): preserve canonical session freshness

* fix(sessions): preserve recovery contracts after rebase

* test(ui): await durable cloud recovery entry

* test(gateway): complete current runner fixtures

* fix(gateway): publish runner availability edges

* fix(ui): preserve shared session freshness

* fix(ui): preserve canonical sidebar session state

* fix(sessions): preserve abandoned partials and run-owned replies

* fix(sessions): resume durable abandonment retries

* test(gateway): compose provisioning replay with runner availability

* fix(sessions): publish recovered move transitions
2026-08-20 09:59:59 -07:00
ClawSweeper 267ffc4754 feat(ui): manage automation condition triggers (#126534)
* feat(ui): manage cron condition triggers

* test(models): isolate ambient CLI availability

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

---------

Co-authored-by: RoboClaw <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-20 07:20:01 -07:00
Peter Steinberger 1d2986b06f fix: prevent incompatible cloud runtime dispatches (#126585)
* fix(ui): gate cloud profiles by runtime mode

* test(gateway): expect cloud placement mode

* test(gateway): keep route suite within lint budget
2026-08-20 01:31:27 -07:00
Peter Steinberger c6ecfd26e5 fix(outbound): preserve first-reply behavior through durable delivery (#126205)
* fix(outbound): preserve reply facts through durable delivery

* fix(protocol): regenerate message action models

* fix(outbound): complete reply fact custody
2026-08-19 17:52:12 -07:00
Peter Steinberger 0a86702241 feat(github): authorize agent identities from Settings (#126474)
* feat(github): add device authorization lifecycle

* fix(github): harden device authorization lifecycle

* fix(github): refresh native tool display snapshot

* fix(github): refresh config and UI baselines

* refactor(github): break OAuth identity import cycle

* test: make gateway retry deadline assertion scheduler-safe
2026-08-19 17:41:58 -07:00
Peter Steinberger 0606e31d0e feat(gateway): broker GitHub publication (#126306)
* feat(gateway): broker GitHub publication

* refactor(gateway): split publication owners

* fix(gateway): enforce publication branch authority

* fix(gateway): bind publication to remote identity

* fix(gateway): bind publication recovery to remote state

* fix(gateway): preserve publication git state

* fix(gateway): retain publication recovery authority

* fix(gateway): commit publication index atomically

* fix(gateway): narrow publication index errors

* refactor(gateway): keep publication CAS errors private

* fix(gateway): recover publication index transactions

* fix(agents): describe GitHub publication tool

* fix(gateway): harden publication base fetch

* fix(gateway): reject publication filter semantics

* fix(gateway): verify publication creation base

* refactor(agents): align publication tool options

* fix(gateway): isolate publication object lineage

* refactor(gateway): use shared table probe

* test(gateway): keep publication helpers in routed suite

* perf(ui): lazy-load GitHub publication request

* fix(gateway): preserve publication support contracts

* fix(gateway): recover publication before authority checks

* fix(gateway): fence local publication snapshots

* fix(android): format generated protocol models

* fix(gateway): harden publication recovery

* fix(gateway): fence publication recovery

* fix(ui): reset completed publication cycles
2026-08-19 11:05:12 -07:00
Peter Steinberger 51599041bc fix: explain preserved session worktrees accurately (#126347)
* fix(sessions): report worktree preservation reasons

* fix(sessions): align preservation checks with current main

* perf(ui): keep preservation copy within startup budget

* perf(ui): reuse localized preservation copy

* test(ui): match preserved worktree confirmation copy

* test(ui): await committed raw config state
2026-08-19 10:59:42 -07:00
Peter Steinberger 3e0c980aaf fix(models): honor per-agent model metadata (#126194)
* fix(models): honor per-agent model metadata

Resolve per-agent aliases, bare providers, fallbacks, and catalog tags consistently across runtime, CLI, Gateway, sessions, and the Control UI.

* fix(models): preserve projection ownership

* fix(models): carry agent scope through fallbacks

* test(models): complete compaction fallback mock

* test(models): complete startup fallback mock

* test(agents): isolate recovery id expectations

* refactor(protocol): split public schema barrel

* test(cron): await child readiness events

* fix(models): scope native catalogs to session agent

* perf(ui): tighten agent model option projection

* perf(ui): reduce agent model projection overhead
2026-08-19 06:04:24 -07:00
Peter Steinberger 46ef757a29 feat: add per-project cloud worker profile defaults (#126238)
* feat(config): add cloud worker project profiles

* feat(gateway): resolve project cloud worker profiles

* docs(gateway): document project profile defaults

* fix(gateway): require admin for project profile dispatch
2026-08-19 03:20:39 -07:00
Peter Steinberger 9436ad57b0 fix(ci): restore protocol lint and Codex shard ownership (#126275)
* fix(protocol): route patch result through type barrel

* fix(protocol): keep schema type barrel registry-free

* fix(test): restore Codex attempt tools shard owner
2026-08-19 03:01:46 -07:00
Peter Steinberger 4bf8d54945 fix(update): validate target config before managed handoff (#126270)
* fix(update): validate target config before handoff

Run the existing dev candidate preflight from both managed update entry points while the serving Gateway remains alive. Reject target-incompatible config before starting a handoff or scheduling restart. Closes #126269

* fix(protocol): restore gateway barrel line budget

Route SessionsPatchResult through the existing type-only schema barrel so the public export stays intact without exceeding the core max-lines gate.

* fix(ci): restore required PR gates

Keep schema-types as the registry-free wrapper, drop a redundant project schema re-export to restore the line budget, claim run-attempt-tools in one canonical shard, and align campaign tests with the new preflight boundary.
2026-08-19 02:53:33 -07:00
Peter Steinberger 94eb34fa78 fix(skills): require re-review when proposals change (#126156)
* fix(skills): bind workshop decisions to reviewed revisions

* chore(i18n): refresh native source inventory

* test(skills): align revision proof with inspect projection

* test: align skill workshop regression fixtures

* fix(ui): align workshop revision admission proof

* fix(ui): keep revision errors out of startup
2026-08-19 01:52:11 -07:00
Peter Steinberger 67750753a2 fix: capture GitHub identity from authenticated sign-in (#126114)
* fix: capture GitHub identity from authenticated sign-in

Automatically persist verified GitHub identities from Cloudflare Access and Tailscale Serve while keeping public Git co-author credit as a separate opt-in.

* test: stabilize cleanup and activity capture

* fix(security): bind GitHub profiles by account id

* test: scope activity capture to route

* fix(security): gate profile requests on identity sync

* fix(security): close pending profile authorization gaps

* test(ui): stabilize terminal continuation menu

* test: stabilize startup recovery timing

* test: keep one Codex attempt tools owner

* fix(plugins): allow profile-independent gateway reads
2026-08-19 01:35:52 -07:00
Peter Steinberger 80934e5639 feat(ui): dispatch sessions to paired devices (#126187) 2026-08-19 01:11:49 -07:00
Peter Steinberger 2bcc06cc22 fix(cron): required delivery failures no longer report success (#126164)
* fix(cron): preserve required delivery completion

Record durable completion independently from payload execution so required delivery failure cannot delete one-shots or report successful waits.\n\nCloses #126163

* fix(cron): keep completion contracts acyclic

* fix(cron): keep delivery predicate private
2026-08-18 22:44:33 -07:00
Peter Steinberger b4a720ff1e fix(worktrees): preserve active removal authority (#126174) 2026-08-18 21:21:25 -07:00
Vyctor H. Brzezowski 2a469c1cb2 fix(ui): dismiss completed progress cards (#126102)
* test(ui): cover completed progress card dismissal

* test(ui): capture both completed card themes

* fix(ui): dismiss completed progress cards

* style: format progress card dismissal

* test(ui): assert visible dismissal lifecycle

* test(ui): wait for reloaded composer proof

* refactor(ui): reuse progress card action styles

* chore(protocol): refresh Swift progress card params

* style(ui): format progress card test

* fix(ui): hide progress dismissal from viewers

* test(ui): use Vitest viewer assertion
2026-08-18 23:39:31 -03:00
Peter Steinberger 15fb00eb6b fix: surface paired-device runtime requirements (#126067)
* fix: surface paired-device runtime requirements

* test: cover session runtime placement metadata

* test(gateway): stabilize runtime catalog fixtures

* fix(gateway): reject incompatible device moves
2026-08-18 19:10:41 -07:00
Peter Steinberger 1d9cda833f feat(ui): show machine CPU and RAM in the cloud picker (#125696)
The cloud machine picker described each class in prose ("Cheap smoke checks
and small repos"), which was the widest thing in the row, got ellipsized, and
did not say what the operator actually picks on. Machine options now carry the
class shape and the picker renders "32 vCPU · 64 GB" instead.

Crabbox reports per-provider class shapes from `providers --json`; the plugin
reads that catalog once per lifecycle and attaches cpu/memoryGb to each option.
A missing binary, failed command, unparseable output, absent classes, or an
unmatched provider all degrade to label-only rows, so an older Crabbox keeps
working.

`description` was never released, so it is removed rather than deprecated, and
listMachineOptions becomes async for the catalog read. Deletes the duplicate
machine-option projector in the environments server method.
2026-08-18 15:38:15 -07:00
Peter Steinberger ef22410985 refactor(protocol): remove beta-only expectedRunId from chat.send (#125921)
The Gateway owns start-or-steer at admission (6515f6a255) and no
client produces expectedRunId anymore (d84a910fc8). The field shipped
only in v2026.8.1-beta.2 - never a stable tag - so it is removed rather
than deprecated. Steer sends resolve the selected session's current
operation; the exact-match branch, the operation|run target identity
discriminator, run_mismatch rejection, and the suggestion producers'
active-run-id selection (with its ambiguity failure) are deleted.
Provider-native turn fencing (Codex expectedTurnId) is unchanged:
the backend-captured runId on the injection target remains.
2026-08-18 11:28:36 -07:00
Peter Steinberger 2bed8caf9e feat(ui): channel conversation avatars in the sidebar (Discord + Slack) (#125668)
* feat(gateway): proxy channel conversation avatars

* feat(discord): capture conversation avatars

* feat(slack): capture DM sender avatars

* test(discord): bind guild avatar mock

* feat(ui): render channel conversation avatars

* fix(ui): align sidebar owner fixtures

* fix(gateway): version channel-avatar routes by media revision

A stable per-session URL let AuthenticatedAvatarRouteLoader's blob and
sticky-404 caches pin a mounted row to a stale or blank avatar after the
backing media changed. Append an opaque digest of the media reference so
replacement and 404-recovery change the route identity.

* test(ui): align sidebar owner facet

* fix(ui): keep owner chip until channel avatar loads

A session with a channelAvatarUrl suppressed its owner chip even while the
blob was loading, auth was not ready, or the route 404ed, leaving an empty
lead slot. The chip now rides as fallback content inside the avatar element
and yields only to a usable image. Covers 404 and auth-pending states;
avatar rows keep renderedOwnerId unset so an owner-viewer stays visible in
the facepile.

* perf(ui): keep channel avatar fallback within budget

* perf(ui): lazy-load the channel avatar element

The avatar element and its authenticated blob loader rode the startup
bundle through session-leading-indicator, pushing startup JS 51 B over the
CI gzip budget. Channel avatars are not startup-critical: register the
element on the first avatar row; the owner-chip fallback covers the
one-time upgrade window. Startup JS returns ~1 KiB under the ceiling.

* build(ui): raise startup baseline for channel avatars

CI-measured startup JS is 344379 B against a 343289 B baseline (+1090 B).
The avatar element and blob loader are code-split out of startup (previous
commit); the residual is the sidebar lead-slot render branch and row
plumbing, which cannot be deferred. Baseline updated via
check-control-ui-performance --update-baseline with CI bytes per the
script's contract; well inside the 4096 B ratchet step and 358400 B
ceiling.
2026-08-18 09:55:50 -07:00
Peter Steinberger 5f65ef8138 feat: add A2UI widgets to session boards (#125803)
* feat: add A2UI board widgets

* fix: harden A2UI board widget integration

* fix: repair A2UI CI contracts

* fix(canvas): keep native A2UI pushes on v0.8

* fix(canvas): support board-only widgets without file host
2026-08-18 09:08:45 -07:00
Peter Steinberger bdbb0444ee fix: share Control UI terminals with chat agents (#125758)
* fix: share Control UI terminals with chat agents

* fix: terminate canceled shared terminal opens

* perf(ui): keep terminal binding behind lazy boundary

* perf(ui): reuse terminal session scope for bottom dock

* perf(ui): reuse session route decision

* fix: keep shared terminal cleanup server-owned

* fix: preserve shared terminal session identity
2026-08-18 07:51:48 -07:00
Peter Steinberger 6515f6a255 refactor(steering): gateway-owned atomic start-or-steer (#125808)
* refactor(steering): gateway-owned start-or-steer via one captured-operation lifecycle

chat.send with queue mode steer now atomically captures the selected
session's current direct reply operation under the writer barrier and
injects into exactly that operation; with no direct owner it starts a
new run instead of failing with active-leaf-changed. Client-supplied
expectedRunId keeps exact-match semantics; the transcript-branch CAS
(expectedLeafEntryId) now guards only non-steer sends.

All three steering paths (gateway chat.send, channel queue steering,
server /steer) share one begin/finalize lifecycle on the reply-run
registry, including the captured-instance abort from 5a15e1a39c.
Deleted: the duplicate accepted/rejected/unconfirmed policy machine in
steer adoption, the raw embedded /steer runtime path, the leaf-bound
injection identity and reject-before-ack compat, and the dead target
tool-authority fingerprint. messageInjectionAttempted becomes a typed
messageInjectionDisposition (none | accepted | rejected); rejected
injections take one visible followup fallback, so non-injectable
runtimes queue instead of silently dropping.

Deliberate semantics: a registry-less active embedded run is no longer
steered by raw session id (correlation-only authority) - the message
becomes a visible followup; /steer injects under its own command
authorization instead of the inbound tool-authority gate it could
never satisfy.

* docs(protocol): describe gateway start-or-steer contract for chat.send

Targetless steer is no longer a leaf-bound compatibility path that can
reject with active-leaf-changed; it targets the selected session's
current state (inject into the direct run, else start a turn).
expectedRunId stays an exact-run fence; expectedLeafEntryId is the
non-steer transcript-branch CAS.

* fix(steering): require matching tool authority for /steer injection

The authorized-sender command gate is weaker than tool-authority equality. Make /steer and gateway injection present the same projected evidence as channel steering so mismatches fall back to a normal prompt under the sender’s own authority.
2026-08-18 07:33:46 -07:00
Peter Steinberger 4048ae26e0 feat: credit linked session participants as Git co-authors (#125827)
* feat: credit linked session participants as co-authors

Authenticated profiles can link GitHub and receive automatic co-author credit in shared coding sessions.

* style: format rebased co-author registries

* fix: mark profile schema DDL boundary
2026-08-18 07:15:01 -07:00
Peter Steinberger efaa867d93 feat(nodes): publish exact runner slots (#125708)
* feat(nodes): publish exact runner slots

* docs(runners): link slot inventory PR

* fix(ui): keep session menus clickable under previews

* perf(protocol): keep runner slot schema off broad barrel

* test(codex): tolerate elapsed media startup budget

* test(codex): type media client factory mocks

* fix(protocol): reject impossible runner slot counts

* test(ci): stabilize loaded lifecycle checks

* test(ui): allow loaded bulk-delete scheduling

* test(ui): stabilize loaded interaction checks

* test(ui): isolate loaded update notice checks

* test(ui): colocate bulk-delete lifecycle coverage

* test(gateway): publish session config before async setup
2026-08-18 07:08:06 -07:00
Peter Steinberger d92ebbaf72 feat(gateway): reach a Gateway behind an identity-aware proxy from the CLI (#125700)
* feat(gateway): reach a Gateway behind an identity-aware proxy from the CLI

Operator CLI surfaces (tui, attach, call, probe, onboarding, and configure) can present configured gateway.remote.edgeAuth headers to an identity-aware proxy. Headers are origin-bound, WSS-only, never follow redirects, cannot set transport-owned headers, and are redacted. Identity-proxy upgrade rejections are classified and remediated instead of being reported as an unreachable gateway.

* test(gateway): cover config-aware probe calls

Keep status probe expectations aligned with the resolved configuration forwarded for origin-bound edge-auth resolution.

* fix(gateway): preserve edge auth across wizard saves and enforce wss before secret resolution

Preserve gateway.remote.edgeAuth only when the configured Gateway scope is unchanged, and reject non-WSS targets before resolving any edge-auth SecretInput.
2026-08-18 03:29:56 -07:00
Peter Steinberger 01811fb107 fix: show effective exec approval policy in Devices (#125756) 2026-08-18 03:22:31 -07:00
Peter Steinberger f886ab181e fix(ui): align session owner filtering terminology (#125645)
* fix(ui): align session owner filtering terminology

Owner filters now use owner terminology consistently across the Gateway, Control UI, and generated native protocol clients. The old terms were prerelease-only, so no migration or compatibility aliases are needed.

* fix(gateway): preserve creator provenance filtering

Keep creatorId scoped to immutable createdActor provenance while ownerId queries the current projected assignable owner. This preserves independent creator and responsibility filtering after reassignment.

* test(ui): wait for remembered draft before reload

CI exposed an IndexedDB draft-persistence race: the test reloaded before the write completed and then re-filled the message while restoration arrived. Wait on the canonical durable-draft helper before reload so the reload cannot race the write.

* fix(ui): keep session menus clear of hovercards

Horizontal sidebar previews were positioned from the inner link rect instead of the complete session row, allowing the hovercard to overlap trailing actions.

Anchor preview placement to the full row while preserving the link trigger and ownership flow so the session menu remains clickable.

* perf(ui): keep hovercard fix within startup budget

Use the sidebar direct-row invariant to anchor the hovercard outside trailing actions without a selector lookup. This preserves the fix without increasing the startup budget.

* fix(qa): preserve Matrix CLI timeout precedence

Keep the primary timeout operator-visible when descendant settlement also reports secondary cleanup trouble. Preserve the complete AggregateError as the public wrapper cause so settlement evidence remains inspectable.

* test(ui): isolate Markdown table clipboard boundary

With isolate:false, the shared module registry can retain clipboard imports before this test installs its module mock, leaving the spy disconnected from the production call.

Stub navigator.clipboard.writeText instead so the test exercises the real copyToClipboard boundary regardless of module load order, then restore the original descriptor.
2026-08-18 03:07:21 -07:00
Peter Steinberger b8ff4fe3d7 fix: show sessions waiting for concurrency slots (#125654)
* fix: show sessions waiting for concurrency slots

* test: align queued session integration fixtures

* test: distinguish queued and reactivated followups

* fix: preserve queued state in workboard and android

* fix: project queued status through chat history

* test(ui): keep queued sidebar case under line cap
2026-08-18 02:31:39 -07:00
Peter Steinberger 079a874159 feat: revalidate cached chat history with cursor deltas (#125606)
* feat(gateway): add cursor catch-up to chat history

* feat(ui): revalidate cached transcripts via history cursor

* fix(ui): stop append-miss stubs clobbering persisted snapshots

* fix(gateway): fence history delta cursor to projected events

* fix: repair cursor delta landing gates
2026-08-18 00:54:58 -07:00
Peter Steinberger 0d74bc21b0 fix: keep non-person principals out of session owners (#125579)
* fix: keep non-person principals out of session owners

* fix(sessions): preserve creator filter semantics
2026-08-17 22:55:01 -07:00
Peter Steinberger b5b17c654b fix: keep Workboard visible in the sidebar (#125473)
* fix(ui): keep workboard in sidebar navigation

* test(ui): align workboard navigation ownership

* fix(ui): preserve saved workboard sidebar slots

* test(ui): shrink assertion safety baseline

* fix(plugins): bind native routes to bundled owners

* refactor(plugins): isolate native route policy
2026-08-17 18:44:39 -07:00
Peter Steinberger 6e458d84bf feat(agents): configure per-agent GitHub identities (#125199)
* feat(agents): add managed GitHub identities

* fix(agents): use opaque GitHub setup handles

* style(codex): format managed shell environment call

* refactor(agents): own managed GitHub process identity

* chore(config): refresh baseline after rebase

* fix(ci): satisfy managed GitHub identity gates

* fix(ci): repair managed GitHub identity checks

* test(agents): align GitHub identity CI coverage

* fix(codex): scope login shell isolation

* fix(agents): let managed gh profiles authenticate

* fix(agents): harden GitHub identity setup

* test(gateway): align method suffix counts

* fix(ui): serialize GitHub identity mutations

* fix(protocol): generate GitHub configure requests

* test(ui): restore timers after identity tests

* fix(ui): preserve GitHub identity mutation ownership

* fix(agents): preserve native GitHub CLI auth
2026-08-17 15:54:03 -07:00
Peter Steinberger 57e862980b feat(gateway): carry machine class through session moves (#125292)
* feat(gateway): carry machine class through session moves

Profile move targets accept an optional machineClass with new-session
parity: validated in the protocol, persisted on the durable move intent
(bare nullable column, lazy same-version ALTER), replayed exactly by
restart recovery, and selectable from the Control UI move dialog.
Moving to the current profile with a different class resizes the
session's worker.

* fix(gateway): project placement facts on chat startup

chat.startup and chat.history built sessionInfo without the placement
projection sessions.list applies, so clients merging that row erased a
live worker placement and its move intent — the Runs on Cloud chip
disappeared after any turn until a full reload.

Reuse the canonical ensureColumn helper for the move table's additive
column instead of a hand-rolled PRAGMA read.
2026-08-17 14:25:13 -07:00
Peter Steinberger 7170a6231a feat(agents): unify agent status into a durable progress_card (#125125)
* feat(agents): unify agent status into a durable progress_card

Replace the write-only update_plan to-do tool and the fragmented plan
rendering with one durable status artifact per session: progress_card
({plan?, markdown?}, replace-on-write, 8 KiB markdown / 50-step caps).
Cards persist in a lazy-additive session_progress_cards table in the
per-agent DB (no schema-version bump), broadcast progressCard.changed,
and render from the store with exactly one live placement per view
(session rail when visible, else the composer-adjacent bar); transcripts
collapse to one-line receipts, and the sidebar hovercard shows other
sessions' cards inline (markdown + <progress>, DOMPurify allowlist, no
iframes). The three stream-derived plan renderers and their dedup
heuristics are deleted.

Codex runs disable the native plan tool per thread
(tools.update_plan.enabled=false) and receive progress_card via the
dynamic-tool bridge; compaction restore now reinjects the card (steps +
bounded markdown). Card writes still emit the legacy plan stream event so
native apps and channels keep working until their per-platform
migrations. Policy names map update_plan -> progress_card; the shipped
tools.updatePlan=false kill switch is honored.

Net -277 production LOC; -480 test LOC.

* test(agents): regenerate Codex prompt snapshots for update_plan thread-config disable

* chore(protocol): allowlist progressCard.changed for native apps pending card migration

* fix(ci): repair progress card integration checks

* fix(codex): canonicalize native progress cards

* test(gateway): reconcile progress card method order

* test(codex): stabilize native approval fixture
2026-08-17 09:44:04 -07:00
Peter Steinberger 821f41e1cd fix(workers): propagate session permission context to worker placements (#125326)
* fix(workers): propagate session permission context

* test(workers): expect launch descriptor v4
2026-08-17 09:34:56 -07:00
Peter Steinberger e6616fdc4c fix(sessions): surface cleanup failures and purge owned artifacts (#125217) 2026-08-17 04:43:33 -07:00
Peter Steinberger 4b0d573437 feat: session permission modes with worktree-scoped defaults (#124909)
* feat(gateway): add session permission modes

* feat(agents): enforce session permission policies

* test: cover session permission modes

* docs: explain session permission modes

* fix(agents): preserve apply patch default root

* feat(codex): map session permission modes

* feat(ui): add session permission controls

* test: cover session permission surfaces

* fix(agents): enforce session permissions from entry

* fix(codex): pass params object to requirements reader

* fix(codex): honor session permission modes

* fix(codex): preserve exec approval floors

* chore: revert changelog edit (release-owned)

* test: align full-mode promotion with upstream semantics

* chore(protocol): regenerate session permission models

* docs: register session permission modes

* fix(ui): use shared permission dropdown

* fix(sessions): restore permission mode contracts

* fix(ci): record session permission startup JS growth in the budget baseline

* refactor(agents): prepare session exec mode at dispatch

Host dispatch owns permissionMode→exec override preparation; the codex
plugin consumes prepared facts and the SDK barrel export is removed,
keeping the plugin-sdk surface at its recorded budget.

* test: type exec mode fixtures as ExecMode

* fix(codex): require admin scope for yolo permissions

* test: isolate codex permission command state

* ci: refresh PR checks against current main

* ci: refresh checks after gateway test repair

* test: fix main-fallout CI suites

* chore: refresh CI merge ref

* fix(worker): fence permission-mode cloud placements

* fix(sessions): clear worktree policy on detach

* chore(protocol): generate session permission mode enum

* test(gateway): type dispatch fixtures from session entries

* fix(ui): allow clearing session permission mode

* chore: refresh CI merge ref

* fix(ci): record permission picker startup growth in the budget baseline

* fix(gateway): gate session root projections
2026-08-17 04:37:35 -07:00
Peter Steinberger 55ce95fac8 feat(sessions): three-layer session ownership — agent attribution, assignable owner, participants (#125057)
* feat(sessions): stamp agent identity on spawned sessions and return spawn receipts

Agent-spawned sessions recorded the requesting session key as createdActor.id,
so the Control UI creator chip rendered an opaque key. Spawn producers now stamp
the canonical requester agent id; parent-authority validation moves to a new
trusted requesterSessionKey field. projectSessionActor enriches agent actors
with configured identity name/avatar at read time, and visible sessions_spawn
returns a sessionUrl + owner receipt with URL-first acknowledgement guidance.

* feat(sessions): assignable session ownership with owner facet and menus

GitHub-assignee-style ownership: sessions get a mutable owner (defaulting to
the immutable createdActor) stored in additive bare-nullable SQLite columns
with first-use lazy ensure. New operator.write sessions.assignOwner validates
targets, requires an identified caller, authorizes by session visibility, and
records assignedBy/assignedAt inside the write transaction. The sessions agent
tool gains assign_owner; the Control UI adds Assign-to-me/Assign-to menus in
sidebar rows and chat headers, renders the effective owner chip, and the
creator facet/filter now keys on effective owner. Sharing authority stays
anchored on createdActor.

* feat(sessions): record session participants and stack them in the owner chip

Records every distinct external prompter (human profile/channel sender, or a
requesting agent) per session in an additive session_participants table at the
turn-admission boundary — best-effort, deferred, never blocking the turn; the
session's own agent and viewers are never recorded, capped at 32 per session.
The session row projects a bounded participants list (owner excluded) plus a
total count with the same actor enrichment as owner/createdActor. The sidebar
chip becomes a pair-stack when others have prompted (owner front, one peeking
participant or +N behind), the chat header shows the full facepile, and an
authenticated involvingMe list filter adds an Involving-me sidebar predicate.
Participant projection is excluded from logical-session CAS equality so display
history never invalidates session writes.

* fix(sessions): identify built-in agent tool callers for owner assignment

The sessions tool's assign_owner dispatched through the in-process synthetic
client, which carries neither a signed agent-runtime identity nor a human
profile, so agent-initiated reassignment always failed with FORBIDDEN. The
tool now captures its trusted requester agent identity and carries it across
in-process dispatch as internal client state (never wire params); the handler
derives assignedBy as signed runtime identity, then trusted agent-tool caller,
then authenticated human. Live-verified end-to-end on a dev gateway.

* fix(ci): split oversized session modules and refresh prompt snapshots

Split the max-lines offenders at concept boundaries for session equality, tool overrides, and protocol owner schemas. Remove the redundant Number conversion from the node:sqlite participant count. Refresh prompt snapshots after drift from the sessions and sessions_spawn tool description updates.

* fix(ci): restore solo-mode chip suppression and conform new method descriptors

Solo-mode root cause: owner-assignment submenu options reused the permanent owner-chip custom element, so hidden menu avatars were counted as attribution chrome. Menus now use viewer avatars while gateway-gated owner chips remain exclusive to collaborative sessions.

Conform sessions.assignOwner to the 2026.8 descriptor and append-only advertised-method inventories, and regenerate the Swift and Kotlin protocol surfaces.

Keep historical v15/v14 fixtures frozen by stripping the new owner columns; the existing range already excludes the participant table. Replace the new raw SQLite schema probes with synchronous Kysely queries.

Clear max-lines by splitting the organizer host contract, pure agent-navigation projections, and ownership/filtering sidebar cases at their concept boundaries.

* fix(ci): integrate ownership series with latest main surfaces

Wire the sessions-page assign-owner action, merge capability imports, narrow the navigation export scope, and apply sessions-create formatting.

The owner-presence regression came from hidden assign-owner menu avatars emitting data-viewer-id, so owner and menu chrome now opt out of presence markers while real facepiles retain them.

* fix(sessions): scope the involving-me filter to profile-backed participants

Session participant history mixed channel-native sender ids with authenticated Gateway profile ids, so involving-me missed real sessions and could accept numeric collisions.

Record the actor_source namespace at each producer, carry it through the internal SQLite projection, and match authenticated viewers only against profile-backed human participants. Legacy NULL sources fail closed for filtering, while channel ids remain available for display.

* build(ui): raise startup budget baseline for session ownership surfaces

Ownership chips, assignment menus, and the participant stack add ~0.7 KiB
gzip to the startup path; CI compression landed just over the previous
baseline+tolerance. Hard cap (350 KiB) unchanged.

* refactor(sessions): drop raw NULL projection for the lazy actor_source column

The Kysely guardrail rejects typed raw sql snippets outside allowlisted
boundaries; select the lazily-ensured column only when present and let the
row projection treat its absence as unknown/legacy.

* build(ui): refresh combined startup baseline
2026-08-17 02:35:20 -07:00
Peter Steinberger 2b55b55b72 feat(workboard): run attached automations after linked sessions finish (#125170)
* feat(workboard): nudge attached automations on session events

* fix(workboard): share automation nudge lifecycle state

* fix(workboard): satisfy nudge type guards

* fix(cron): add enabled-only immediate runs
2026-08-17 02:29:26 -07:00
Peter Steinberger 8f145789b5 fix: diagnose gateways that cannot host nodes (#125132)
* fix(doctor): expose node hosting preconditions

* fix(doctor): use canonical string coercion

* fix(auth): narrow identity header rejection
2026-08-17 00:30:21 -07:00
Peter Steinberger f59e945013 feat(gateway): move sessions between Gateway and runners (#125036)
* chore: preserve session placement move work

* feat(gateway): move sessions between runners

* test(gateway): align recovery fixture with move barriers

* fix(protocol): format literal-only Swift initializers

* test(ui): prove session move terminal state

* refactor(gateway): narrow session move target fence

* fix(gateway): preserve pending result recovery ownership

* test(workers): migrate reclaim lifecycle fixtures

* refactor(gateway): keep move internals private

* test(gateway): align split bootstrap fixture with moves

* fix(gateway): recover session moves through exact intent

* refactor(gateway): derive move placement subtypes locally
2026-08-17 00:23:13 -07:00
Peter Steinberger 4e1f26dc18 refactor(gateway,ui): one bounded display projection; delete marker strip sites (#124997)
* refactor(gateway,ui): one bounded display projection; delete marker strip sites

Persisted transcripts are marker-free since the write-boundary projection
(#124793), the historical migration (#124888), and TTS facts (#124913), so
display surfaces stop compensating. sessions.list.lastMessagePreview and its
siblings (sessions.preview/describe, TUI picker, sessions_list tool, MCP) now
share one bounded role-aware projection (240 chars, tool/system/thinking and
suppressed control replies excluded, directive-only rows fall through). The
web reply chip reads the typed openclawDelivery fact instead of parsing text;
chat.history preserves the field to the UI. Post-hoc display strips are
deleted across web/TUI/MCP/sessions-list; live streaming cleaners stay.
Stale gateway-protocol preview comments corrected; no schema change.
Assertion-safety baseline pruned for shrunk files (sanctioned direction).

Production net -173, tests net -137. Fixes the sidebar [[reply_to_current]]
preview leak and the empty-code-pill overstrip of quoted markers.

* fix(agents): preserve restart recovery transcript reads

* refactor(gateway): remove obsolete transcript exports

* fix(gateway): normalize injected delivery directives

* fix(ci): scope projection and recovery checks

* chore(ci): shrink plugin SDK surface budgets

* test: deflake loaded side question and worker checks

* test: align display projection CI fixtures

* style: format display projection fixture
2026-08-16 23:44:29 -07:00
Josh Avant f7a8638282 feat(audit): explain outbound message delivery (#123709)
* feat(audit): explain outbound message delivery

* fix(audit): record early message policy denials

* refactor(audit): split message delivery readers

* test(outbound): cover delivery audit lifecycle

* fix(audit): preserve message progress across downgrade

* fix(audit): keep progress out of activity protocol

* docs(audit): keep activity outcomes terminal-only

* fix(audit): bound merged delivery paging

* fix(audit): trust decision channel references

* fix(audit): keep retention constant private

* fix(audit): record broadcast target denials

* fix(audit): distinguish broadcast denial receipts

* test(qa): enforce message delivery receipt proof

* fix(audit): integrate C04 with schema v9 owners

* chore(audit): satisfy assertion safety gate

* refactor(agents): split explicit message target guard

* test(audit): materialize pinned reader in shallow CI

* test(qa): poll terminal audit persistence

* fix(delivery): defer audit terminal to recovery

* test(qa): isolate message delivery restart proof

* test(qa): keep gateway restart config canonical

* test(qa): exclude unrelated restart plugins

* test(qa): remove unused restart plugins

* test(qa): disable automatic memory plugin

* test(qa): converge replacement gateway startup

* fix(plugins): stabilize registry fingerprints

* fix(plugins): use public normalization export

* test(plugins): keep fingerprint fixture immutable

* fix(audit): bind delivery receipts to exact executions

* chore(audit): keep terminal binding helper private

* test(reply): cover unbound admission callbacks

* docs(agents): preserve durable delivery ownership
2026-08-16 21:26:27 -07:00
Peter Steinberger bf9b25ab7e fix(gateway): prevent control-plane polling stalls (#124891)
* fix(gateway): avoid repeated control-plane scans

* fix(tooling): allow concurrent worktree validation

* fix(ci): refresh protocol and runner inputs

* perf(ui): defer hidden session refreshes

* fix(ui): resolve session refresh lint failure

* fix(update): preserve pre-cache update channel

* fix(update): normalize cached update channel

* fix(gateway): lifecycle-cache update install identity

* fix(ui): preserve manual history retry after layout scroll

* test(codex): repair side-question tool schema fixture
2026-08-16 20:58:27 -07:00
Peter Steinberger 85baeecb57 refactor: consolidate record guards (#124970) 2026-08-16 19:34:57 -07:00
Peter Steinberger 022be60015 feat(agents): show creation hierarchy in Control UI (#124967)
* feat(agents): show creation hierarchy in Control UI

* chore(protocol): regenerate Swift agent model
2026-08-16 19:31:40 -07:00