Commit Graph

270 Commits

Author SHA1 Message Date
Peter Steinberger e011e9db61 fix: child sessions honor inherited model pins over channel defaults (#125880)
* fix: honor inherited model pins over channel defaults

Child sessions now keep parent model pins consistently across reply, status, harness prediction, and agentCommand.

* test: align agent command model selection fixtures

Keep concurrent override and threaded parent-selection coverage aligned with canonical model normalization and runtime snapshot access.
2026-08-18 09:24:15 -07:00
Peter Steinberger 7f0a87de1d feat(agents): eager delegation default in the main session + coalesced session-state wakes (#125691)
* feat(agents): default to eager delegation in the main session

The delegation prompt section now defaults to "prefer" in each agent's
canonical main session and "suggest" elsewhere; explicit config wins in
both directions. The section is rewritten to be token-leaner and now
covers the hidden-vs-visible spawn distinction (hidden subagents are
invisible and auto-archived; deliverable-bearing work spawns
visible=true and replies with the link) plus the run-end notification
contract. The Messaging spawn-mechanics line is suppressed when the
Delegation section renders, and the stable Tooling visible:true hint is
aligned with the new guidance.

* perf(sessions): coalesce session-state wake bursts

Watched-session change notices woke the watcher's main session with the
generic 250ms heartbeat coalesce, so a burst of changes across several
watched sessions produced one wake per change. Wakes now coalesce for
20s; notices are already queued and deduped, so none are lost.

* test(agents): regenerate prompt snapshots after rebase

* test(sessions,agents): update sibling assertions for coalesced wakes and visible guidance

session-state-events tests advance timers past the new 20s wake
coalesce window; the sessions_spawn schema test tracks the updated
visible description.
2026-08-18 07:03:11 -07:00
Jason (Json) 7c65bbcee3 fix(sessions): track context window provenance (#124303)
* fix(sessions): track context window provenance

* fix(sessions): complete context provenance coverage

* fix(sessions): honor authored context window caps

* fix(agents): distinguish context source helpers

* fix(sessions): clear context provenance on model invalidation

* test(codex): preserve local operator authority provenance

* fix(context): clamp effective caps to native windows

* fix(cron): use the runtime model facade

* fix(status): honor session context provenance

* test(status): use public context token inputs

* test(status): split session row cases

* test(status): classify split cases as test support

* test(status): declare runtime context provenance

* fix(sessions): preserve context ownership through finalization

* fix(sessions): apply context provenance to listings

* fix(cron): preserve projected context ownership

* fix(sessions): scope locked context ownership

* fix(status): share session context projection
2026-08-18 05:03:01 -06:00
Peter Steinberger 6afaf82e1e fix: keep main session aware of isolated groups (#125667)
Keep ambient main-session group notices working when direct messages use an isolated dmScope, while preserving explicit session watches and routed group topology.
2026-08-17 23:56:57 -07:00
Peter Steinberger 08622cd766 fix(ui): keep streamed replies ordered across steering (#125197)
* fix(ui): preserve steering stream order

Record exact steering provenance and keep cumulative assistant output on its causal side of persisted user turns across live streaming, terminal events, tool boundaries, and history reloads.

* chore(ci): refresh post-rebase gates

Tighten the inherited environment-variable budget and remove a stale test import exposed by the rebased lint gate.

* fix(ui): preserve terminal steer stream segment

Assert the causal stream rollover when a steer lands and move persisted split-layout normalization off the startup path to keep the Control UI bundle within budget.

* refactor(ui): extract split layout types

Keep the persisted split-layout normalizer off the interactive module cycle while preserving the Control UI startup bundle reduction.

* fix(gateway): resolve steering provenance at injection

* fix(gateway): confirm steering provenance after persistence

* refactor(ui): move live tool filtering to identity owner

* fix(ui): preserve queued user stream ceiling
2026-08-17 06:00:40 -07:00
Peter Steinberger 91d3f533f1 fix: new sessions open promptly in Git workspaces (#124796)
* fix(gateway): unblock new session creation

* fix(gateway): fence session baseline capture

* chore(types): keep baseline claim private

* fix(sessions): settle reset diff baselines

* fix(sessions): preserve baseline invalidation

* test(agents): consolidate compaction ownership cases

* fix(sessions): fail closed on baseline persistence

* fix(sessions): fence baseline generations

* test(sessions): align authoritative baseline fixtures
2026-08-17 04:16:02 -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 7349177ce3 feat: main-session agent-wide visibility + session.groupScope routing (#124965)
* feat: add main session group routing

* docs: explain main session routing scopes

* fix: align memory session visibility

* test(qa): cover main-scoped group bindings

* fix(sessions): preserve binding-scoped outbound routes

* fix(routing): preserve explicit outbound owners

* fix(sessions): recognize global main visibility

* chore(ci): prune assertion safety baseline
2026-08-16 19:57:18 -07:00
Peter Steinberger edd9234e54 fix(sessions): report self-archive before stopping the turn (#124896) 2026-08-16 16:25:27 -07:00
Peter Steinberger 9873b0f6ad feat(ui): named glyphs and custom emoji entry for session icons (#124629)
* feat(protocol): add named session icon glyphs

* feat(ui): add session glyph and custom emoji picker

* docs: describe named and custom session icons

* fix(ui): give the custom emoji input an accessible name

ClawSweeper P2: the input had no label relationship; assistive tech announced an unnamed edit control.

* fix(protocol): guard the v-flag icon regex for browser module loads

ClawSweeper P1: the picker's static import evaluates this module in the browser; pre-Unicode-Sets engines threw at module scope and took down the session menu. Lazy capability-guarded construction; such engines fall back to the grapheme heuristic as client pre-validation while the Gateway keeps exact RGI validation.
2026-08-16 07:55:31 -07:00
Peter Steinberger 17df3f6102 refactor(types): remove chained type assertions in agents and sessions (#124060)
* refactor(types): remove chained assertions in agents and sessions

* fix(types): annotate erased file tool adapter

* fix(types): preserve tool metadata and message contracts

* fix(types): delegate through original file tool execute

* fix(types): retain bash execution type ownership

* fix(types): own before-tool metadata access
2026-08-15 00:56:18 -07:00
Peter Steinberger d2be00e171 feat(ui): persistent emoji session icons in the sidebar (#124034)
* feat(gateway): persist emoji session icons

* feat(ui): add persistent session icon menu

* docs: explain persistent session icons

* docs: list Set icon in the session context menu enumeration

* test(gateway): canonicalize group defaults workspace path

* fix(ci): sync session icon protocol coverage

* fix(sessions): enforce RGI emoji sequences for session icons

ClawSweeper P2: single non-emoji graphemes like 漢 passed the segmenter-based predicate. Anchored \p{RGI_Emoji} admits exactly one interchange emoji sequence and deletes the length cap and ASCII carve-out.

* fix(sessions): compile RGI emoji validation
2026-08-15 00:15:55 -07:00
Peter Steinberger c574f403e9 fix(gateway): committed transcript updates bypass the sessions.list cache fence (#123290)
Message-batch transcript commits broadcast sessions.changed via the raw
transcript-update handler (server-session-events.ts), which bumps no fence
version — only emitSessionsChanged does. A completed sessions.list result
cached just before the commit stayed valid, so list rows kept stale
transcript-derived previews/titles until an unrelated session mutation.
Give the transcript-update emitter its own monotonic version, advanced
before listeners run (commit-then-broadcast), and add it to the list fence.
2026-08-13 16:30:29 -07:00
Peter Steinberger b54034d5c2 fix: isolated gateways list and open the operator's HOME Claude/Codex/OpenCode/Pi sessions (#122983)
* fix(sessions): isolated gateways no longer inherit HOME external session catalogs

A gateway on isolated state (custom OPENCLAW_STATE_DIR/CONFIG_PATH/OPENCLAW_HOME,
relocated home, or any named profile) listed, read, continued, archived, and
reopened the operator's real Claude Code/Codex/OpenCode/Pi sessions from the
process HOME. External catalogs now require the default install identity for
process-HOME scans: every catalog verb receives the isolation policy and rejects
HOME-fallback local targets, unknown providers fail closed unless they declare
supportsProcessHomeIsolation, and one structured warning records the skip.
Paired-node hosts and explicitly rooted stores (CLAUDE_CONFIG_DIR, CODEX_HOME,
OPENCODE_DB, Pi session dirs) keep working; default-identity gateways are
unchanged.

* fix(sessions): inject catalog HOME-isolation fact at registry construction

* chore(sdk): regenerate plugin API baselines after rebase

* chore(sdk): regenerate plugin API baselines after rebase
2026-08-13 01:12:03 -07:00
Peter Steinberger 1ca60fbc3a refactor(agents): make multi-agent ownership explicit (H2-1 core) (#114388)
* refactor(agents): make roster ownership explicit

* feat(config): materialize legacy agent roles

* fix(cron): migrate legacy owners at startup

* feat(gateway): expose agent selection contracts

* fix(gateway): enforce agent-scoped authorization

* docs(config): document explicit agent ownership

* fix(config): pin retained owner workspace

* fix(gateway): target hook wakes at effective agent

* fix(sessions): preserve fixed-store ownership

* fix: preserve retained agent ownership

* fix: preserve legacy agent ownership across runtime surfaces

* fix: fail closed on ambiguous session ownership

* fix: preserve compatibility owners across dispatch and writes

* fix: preserve retained agent projections

* fix: preserve agent ownership compatibility

* fix: preserve per-agent heartbeat guidance

* fix: preserve compatibility owners in generic paths

* fix: enforce configured ownership in session paths

* fix: defer remote roster selection

* fix: preserve ownership across session and config writes

* fix: fail closed on ambiguous restored ownership

* fix: preserve explicit ACP and legacy ownership

* fix: honor durable fixed-store ownership

* fix: enforce fixed-store owner authority

* fix: preserve ownership evidence boundaries

* fix: honor resolved session ownership

* fix: align compatibility ownership paths

* fix: persist legacy main store ownership

* fix: close ownership fallback gaps

* fix(agents): close retained owner compatibility gaps

* fix(agents): enforce session owner resolution

* fix(agents): complete session owner resolution sweep

* fix(agents): preserve durable session ownership

* fix: complete persisted session owner routing

* fix: thread prepared session owners

* fix: preserve stable session ownership

* fix: enforce session ownership boundaries

* fix: close session ownership delta gaps

* fix: reconcile session ownership after rebase

* fix: reconcile ownership with current main

* fix: align session store path imports

* fix: align session store config path import

* fix: reconcile explicit ownership CI

* fix: reconcile ownership rebase checks

* fix: align ownership ci contracts

* fix: align ownership rebase checks

* fix: preserve compatibility owner during setup

* fix(doctor): migrate ownerless heartbeat monitors

* fix(gateway): preserve explicit session ownership

* test: align ownership fixtures after rebase

* test: complete plugin manifest fixture

* test: align runtime context mocks

* fix(gateway): preserve alias routing for existing sessions

* style: format agent routing update

* fix(gateway): preserve selected owner during alias routing

* style: normalize rebased ownership files

* fix(gateway): preserve owner through global alias routing

* fix(gateway): preserve explicit ownership at HTTP boundaries

* fix(gateway): validate compatibility model ownership

* fix(agents): reconcile strict session ownership

* fix(agents): contain media yield callback failures

* fix(agents): avoid eager bare-key owner resolution

* chore: refresh rebased ownership baselines

* chore: align hosted plugin SDK baseline

* chore: refresh ownership baselines after main sync

* chore: refresh ownership baselines after main sync

* test: align routed event owner fixtures

* chore: retrigger CI after runner startup failure

* chore: refresh ownership SDK budgets after main sync

* fix(tasks): require agent identity for bare owners

* chore: align Linux plugin SDK baseline

* chore: remove release-owned changelog entry
2026-08-12 15:55:16 -07:00
Peter Steinberger c23d66e3b5 refactor: consolidate coercion ownership (#122692)
* refactor: consolidate coercion ownership

* test: align shard check with weighted planning

* chore: refresh plugin SDK API baseline
2026-08-12 09:25:28 -07:00
Peter Steinberger 85a176b98a feat(ui): commit-scoped session diff viewer with file, sync, and view menus (#122470)
* feat(ui): commit-scoped session diff viewer with file, sync, and view menus

The Control UI session diff panel becomes a dense Amp-style viewer: per-file
menus (copy path, open file, reveal in file tree, open in editor), a Sync
Locally popover with a copyable git fetch command, view options (collapse all,
wrapping, split/unified layout), and a sticky footer that reports how far the
branch is ahead of its merge base and switches between all changes,
uncommitted work, and individual commits.

sessions.diff gains an additive scope param (all | uncommitted | commit) plus
commits, aheadCount, and mergeBase metadata. Commit-scope diffs read only the
object database, skip untracked collection, and bypass session-start baseline
filtering; unknown commits surface a typed unavailableReason. Offscreen file
bodies use content-visibility so large diffs stay responsive.

* fix(ui): satisfy session diff CI gates

* fix(gateway): fence commit-scoped session diffs to the advertised branch history

ClawSweeper found that commit-scoped sessions.diff accepted any commit resolvable in the checkout. Fence operator.read commit reads to the advertised merge-base..HEAD history and cover sibling-branch and base-history commits.
2026-08-12 01:08:33 -07:00
Josh Lehman 585503896d fix: preserve WebChat reply context after reloads (#122169) 2026-08-11 15:42:01 -07:00
Peter Steinberger e74be5d41d refactor: eliminate final wrapper-shadowing hazards (#122157)
* refactor: disambiguate wrapper-shadowed exports

* test: align renamed session and facade boundaries

* test: cover renamed runtime mock exports

* refactor: align remaining wrapper owner call sites

* test: align overlap-rebased runtime mocks

* refactor: preserve public SDK names after overlap rebase

* chore: regenerate wrapper shadowing baselines

* test: align cron model selection mocks
2026-08-11 13:34:24 -07:00
Peter Steinberger 3d76246792 refactor: eliminate final export name collisions (#122083)
* refactor: resolve final export name collisions

* refactor: update remaining collision rename consumers

* style: format rebased auth helpers

* test: update remaining session entry mocks

* test: update remaining runtime mock exports

* test: update delivery info path mock

* refactor: reconcile combined collision sweeps

* chore: regenerate collision and sdk baselines
2026-08-11 11:18:24 -07:00
Peter Steinberger ebb2770000 refactor: eliminate export name collisions (#122084)
* refactor: eliminate export name collisions

* chore(scripts): burn resolved collision baselines

* refactor: narrow legacy session load options

* chore: refresh SDK and session debt baselines

* refactor: adopt upstream secrets collision fix

* test(plugin-sdk): mock renamed session store core

* fix(scripts): track renamed session accessor core
2026-08-11 10:41:50 -07:00
Peter Steinberger eef860d972 feat(ui): unify system notices in chat with a minimal line family (#122144)
* feat(ui): unify system notices in chat with a minimal line family

Class A (model-facing system turns: restart recovery, gateway restarted,
generic internal_system fallback) and class B (lifecycle facts: compaction)
now share one hairline+icon+label rendering; known kinds show operator
summaries instead of raw injected prompts. Core producers share one
formatSystemTurnPrompt owner for the [System] prefix; subagent resume copy
unified from "gateway reload" to "gateway restart". No protocol, channel,
or marker changes.

* fix(ui): keep system-turn prompt helper out of the plugin SDK surface

Relocate formatSystemTurnPrompt to src/sessions/system-turn-prompt.ts so the
core-internal [System] convention does not widen the SDK API baseline; drop
the unused prefix/type exports; use var(--cursor-action) per cursor policy.
2026-08-11 10:24:38 -07:00
Peter Steinberger 6dc77a37d9 refactor(agents): render failover user copy from one reason-keyed module (#121717)
* refactor(agents): centralize failover user copy

* refactor(agents): route failure callers through user copy

* refactor(qa): carry typed reply failure markers

* refactor(agents): keep failover copy import-light

* refactor(agents): pass structured failure copy context

* refactor(agents): isolate copy rendering from runtime state

* refactor(agents): separate copy rendering from sanitization

* refactor(agents): keep failover copy internals private

* fix(qa-channel): type failure markers on bus sends

* style(agents): brace failover copy conditions

* test(qa-lab): avoid map spread in failure cases

* chore(plugin-sdk): refresh failover closure hashes

* fix(agents): preserve generic runner fallback

* fix(qa): preserve text-only failure markers

* test(qa): type failure delivery fixture
2026-08-10 16:43:51 -07:00
Peter Steinberger 59d545d81f fix(gateway): fence session worktree lifecycle (#121666)
Same-key replacement sessions no longer retain a managed worktree that predecessor deletion can remove.
2026-08-10 13:13:04 -07:00
Marvinthebored 01884775aa fix(context-engine): preserve fresh-turn engine selection (#121461)
Keep configured engines selected before the current turn is persisted, then durably advance accepted turns across embedded and CLI paths. Preserve runtime plugin ownership and canonical transcript anchors through prepared execution.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: Peter <noreply@anthropic.com>
2026-08-10 22:23:24 +05:30
Patrick Erichsen d4c3b9e783 refactor(sessions): remove custom icon feature (#121263)
* refactor(sessions): remove custom icon feature

* test(sessions): expect legacy icons to be stripped

* fix(gateway): preserve retired icon request compatibility

* chore(plugin-sdk): refresh API baseline

* fix(plugins): reserve retired session icon slot
2026-08-10 04:51:56 +00:00
Peter Steinberger b4104e29b5 fix(sessions): stop active runs before archiving (#120892)
* fix(sessions): stop active runs before archiving

Archive now fences the exact session, stops and drains active work before commit, preserves main/global-main/unknown protections and Delete behavior, and keeps patchMany per-target ordering.

* test(gateway): provide archive lifecycle context

* test(ui): expect active sessions to remain archivable

* refactor(gateway): keep archive drains internal

* fix(gateway): keep abort lifecycle contract acyclic

* fix(sessions): fence sharing across archive drains

* test(gateway): type archive lifecycle responses

* fix(sessions): reclaim cloud workers before archive

* fix(sessions): align archive request integration

* test(gateway): omit default deferred type

* docs(sessions): clarify cloud archive retries
2026-08-09 09:03:20 -07:00
Peter Steinberger 4fec524243 fix: new session finds models after fresh gateway start (#121057)
* fix(gateway): align chat metadata with prepared auth

* fix(gateway): complete prepared auth mode contract

* fix(agents): canonicalize prepared auth aliases

* test(sessions): finish temp-dir helper migration
2026-08-09 06:40:25 -07:00
Peter Steinberger 8616c0c374 refactor: finish shared test helper migrations (#120996)
* test: finish shared helper migrations

* test: fix helper migration CI

* style: fix test import ordering

* test(acpx): restore deferred void types

* test: fix helper migrations after rebase
2026-08-09 06:00:06 -07:00
Peter Steinberger dfbc95e4ea fix: replies fail after transcript projection repair (#121025)
* fix(reply): rebind prepared session ownership

* test(mcp): correlate queued wait events

* fix(sessions): await transcript admission projection

* fix(recovery): distinguish admission failure stages

* refactor(sessions): split runtime transcript projection

* fix(ci): preserve transcript API baseline

* fix(sessions): await target projection only
2026-08-09 05:54:39 -07:00
licheer-zte 42500d13b1 fix(hooks): target hook-triggered heartbeat wakes at the hook agent/session (#119808) (#119817)
* fix(hooks): allow scoped hook wakes for unscheduled targets; keep announce wake aligned with queued session

- heartbeat-wake-policy: add isTargetedImmediateHookWake so a scoped hook
  wake (source=hook, reason=hook:*) counts as an immediate targeted wake;
  a known agent without a recurring heartbeat schedule can now be woken
  once so its queued hook event gets consumed instead of sitting unread.
- scheduler + execution: accept the hook wake in the allowsUnscheduledTarget
  gate alongside notifications-event wakes; unconfigured agents stay rejected.
- hooks announce path: only pass agentId when the hook explicitly named an
  agent; an unnamed hook resolves its event session from fresh config at
  announce time, so pairing the dispatch-time default agent with that
  session could wake a stale agent after a default-agent reload.
- regression tests: targeted hook wake for an unscheduled agent runs once;
  unconfigured hook targets rejected; announce wake omits default agentId
  for unnamed hooks.

* test(infra): split hook-wake heartbeat tests out of scheduler test file

Keep heartbeat-runner.scheduler.test.ts inside the oxlint max-lines budget
(1045 -> 995 effective lines) by moving the two hook-source wake targeting
tests into heartbeat-runner.hook-wake.test.ts. No production code changes.

* fix(gateway): target global-scope hook wakes at the fresh default agent

Global session scope resolves hook event keys to the unscoped "global"
sentinel, which carries no agent identity. The scheduler refuses to
resolve a target from it, so announce and failure wakes for unnamed
hooks left the queued event unread.

Carry the fresh default agent alongside the global key on both paths
(announce and failure), mirroring the enqueue-wake path. Non-global
unnamed-hook wakes are unchanged: their session key embeds the agent,
and pairing a frozen default agent could wake a stale agent after a
default-agent reload.

* fix(gateway): target recovered global failure wakes at the explicit or fresh default agent

The failure-wake agent selection gated both branches on hookEventSessionKey.
When initial event-key resolution fails (early config failure), that key is
absent even though recovery still yields the unscoped "global" sentinel; the
wake then carried no agentId and the queued failure event sat unread.

Use the recovered session key for the sentinel check and attach the explicit
agent, or the fresh default agent, so the global failure event is consumed.
Non-global unnamed-hook wakes are unchanged: their session key embeds the
agent, and pairing a frozen default agent could wake a stale agent after a
default-agent reload.

* fix(gateway): preserve agent identity for global hook wakes

Signed-off-by: sallyom <somalley@redhat.com>

* test(infra): allow seeding a heartbeat session without delivery fields

The new hook-wake test seeds an empty session for the hooks agent, which
has no real delivery context. Relax seedSessionStore's seed type to
Partial so empty seeds typecheck.

* fix(gateway): keep global hook wakes on the dispatch-time agent

The announce and failure wake paths resolved the default agent from
fresh config at queue-execution time. If a default-agent reload happened
between dispatch and execution, a global-scope wake retargeted at the new
default while the isolated run used the accepted agent, so the event
produced under one agent was consumed by another. Freeze the accepted
agent at dispatch (acceptedHookAgentId) and prefer it in both wake paths;
fresh resolution remains the fallback for pre-acceptance failures. Adds
announce + failure reload regressions (default main -> work mid-queue).

* test(hooks): annotate reload fixtures as OpenClawConfig

The default-agent reload fixtures widened session.scope to string and
fixed agents.entries to the first assignment's shape, breaking
check-test-types. Annotate both fixtures so each reload conforms to the
config contract.

* fix(hooks): partition global-scope hook events by owning agent

Global-scope hook announce and failure events all land in the shared
literal `global` system-event queue while each wake targets a single
agent, so concurrent hook completions can cross-consume each other's
events: the first targeted wake drains every agent's queued result or
failure into one prompt.

Give transient system events an ownerAgentId set by the hook dispatch
paths (announce, failure, and wake-only enqueues) whenever the event
lands on the unscoped sentinel queue, and filter the heartbeat preflight
peek to the running agent's owned events. The store's literal global
session row stays shared; unowned events remain visible to every agent,
so non-hook global events are unchanged.

Add a two-agent concurrent regression proving the first targeted wake
leaves the other agent's event queued for its owner.

* fix(hooks): isolate global events by agent

Signed-off-by: sallyom <somalley@redhat.com>

* fix(infra): unexport internal targeted-wake helpers

isTargetedImmediateSystemEventWake and isTargetedImmediateHookWake are
only consumed by isTargetedImmediateUnscheduledWake within the same
module; keep them module-private so knip deadcode stops flagging the
unused exports.

---------

Signed-off-by: sallyom <somalley@redhat.com>
Co-authored-by: sallyom <somalley@redhat.com>
2026-08-08 23:03:50 -04:00
Peter Steinberger e7a9f33d89 refactor(core): adopt normalization-core leaf helpers across production (#120350)
* refactor(core): adopt normalization-core leaf helpers across production

* fix(ci): keep plugin contract source-resolvable

* fix(errors): preserve adapter-owned error fields

* fix(errors): short-circuit existing errors before stringifying

* fix(errors): skip throwing structured getters

* ci: retrigger checks on current base

* fix(errors): guard structured error enumeration

* fix: harden error detail copying
2026-08-08 12:00:49 -07:00
Peter Steinberger 47f78a32eb fix(ai): preserve long Responses sessions after server compaction (#120457)
* fix(ai): preserve Responses server compaction state

Persist opaque Responses compaction items as fenced provider replay state so long stateless sessions can resume authoritative compressed history without exposing it in display or diagnostics. Carry state through worker transcripts and prune replay prefixes without splitting tool pairs.

Release note: Preserve long OpenAI Responses sessions across server-side compaction and worker restarts.

Related: #95788

* test(ai): align long-context fixtures with CI contracts

Make tool-result fixtures type-complete, use the canonical model selector helper, remove unused test-helper exports, and route the paid long-context live probe through the dedicated Gateway profile shard.

* test(ai): type mocked Responses terminal events

Give the mock SSE event collection an explicit open event shape so terminal response events coexist with output-item events under the root test typecheck.

* fix(ai): suppress rejected compaction replay

Persist a route-fenced suppression tombstone when encrypted-content recovery rejects a compaction item, so later turns do not retry the same opaque state. Preserve the tombstone through transcript redaction and cover successful fallback followed by the next turn.

* fix(ai): keep compaction suppression transport-private

Keep the suppression contract local to its sole Responses transport owner and make the regression fixture satisfy root type and lint checks without widening the Plugin SDK surface.

* refactor(ai): remove compaction suppression re-export

* fix(ai): scope compaction suppression to replay route

Keep foreign-route rejection tombstones from hiding the newest compatible Responses compaction while preserving same-route suppression.

* fix(ai): harden Responses replay recovery

Stage encrypted replay recovery so compaction is only suppressed after an attributable rejection. Preserve terminal ordering and keep provider replay within worker frame budgets without truncating opaque state.

* refactor(ai): centralize Responses output indexes

Keep normalized output identity tracking in the stream-slot owner, move response failure state to its diagnostic owner, and remove the obsolete replay clone export so exact-head static gates remain shrink-only.

* fix(ai): retain idless terminal tool identity

Use the canonical empty identity only when a provider supplies neither call nor item id, preventing terminal recovery from duplicating a done-only tool call while preserving stronger identities when available.

* fix(sessions): hide provider replay from public events

* fix(ai): stage encrypted replay recovery

* fix(ai): keep replay attempt kind internal

* fix(ai): route Azure through replay recovery

Use the shared encrypted-content retry owner for Azure Responses so compaction suppression and prompt-observer variants stay coherent across transports.

* fix(ai): harden replay persistence boundaries

Fence Azure replay by the resolved request endpoint, drop invalid replay during transcript sanitization, and surface worker-launch replay omissions through the existing redacted diagnostic path.
2026-08-08 11:55:26 -07:00
Peter Steinberger 1d9b52d73a fix(gateway): refresh attributed message avatars (#120381)
* fix(gateway): refresh attributed message avatars

* fix(gateway): avoid avatar revision collisions

* fix(gateway): refresh avatar revisions across streams
2026-08-07 18:47:08 -07:00
Peter Steinberger 06d5e4457f fix(gateway): refresh session lists after external identity writes (#120203)
* fix(gateway): invalidate session lists on external mutations

* test(sessions): cover identity mutation fence
2026-08-07 02:45:14 -07:00
Vito Cappello 5621979a46 fix(models): preserve session selection across fallback turns (#119325)
* feat(models): add session-only model selection

* fix(models): use trailing session scope option

* test(models): satisfy session scope lint

* fix(models): reject duplicate model options

* fix(models): clarify default and session scope

* fix(models): require complete session option tokens

* fix(models): report configured default dispatch

* fix(models): keep directive handler within lint limit

* fix(models): parse model options in either order

* fix(models): apply session scope to aliases

* fix(models): align alias scope with reply routing

* fix(discord): surface model selection scope in picker

* fix(models): preserve mixed-text model selection

* fix(models): centralize command selection ownership

* fix(models): align session scope lifecycle

* fix(models): preserve command and auth ownership

* fixup! fix(models): preserve command and auth ownership

* fix(auth): preserve scoped CLI provider discovery

* test(models): align result and cron fixtures

* test(models): nest result timing metadata

* fix(discord): narrow silent dispatch results

* fix(transcript): preserve admitted turn identity

* fix(context-engine): fence the admitted transcript turn

* fix(context-engine): stabilize plugin compatibility contract

* chore(plugin-sdk): refresh context engine API baseline

* chore(plugin-sdk): use Linux context engine API baseline

* fix(context-engine): align fallback ownership

* fix(fallback): scope auth skip cache by profile

* fix(context-engine): settle only accepted fallback turns

* refactor(sessions): issue canonical turn admissions

* refactor(context-engine): own logical turn advancement

* fix(context-engine): settle cron fallback winners

* fix(models): align picker and fallback transactions

* fix(delivery): notify block admission after queueing

* fix(sessions): preserve canonical admission receipts

* chore(plugin-sdk): refresh API baseline hash

* fix(context-engine): commit accepted turns durably

* fix(context-engine): validate durable host transitions

* fix(context-engine): preserve fallback turn ownership

* fix(context-engine): preserve queued turn order

* fix(models): preserve fallback retry ownership

* fix(context-engine): enforce durable transcript anchors

* fix(runtime): close fallback persistence gaps

* fix(context-engine): preflight fallback harnesses

* chore(plugin-sdk): use Linux API baseline

* fix(context-engine): drain durable commits before reads

* fix(models): scope harness auth failures by profile

* fix(codex): fence legacy transcript history

* fix(commands): honor suppressed directive interpretation

* chore(runtime): remove unused branch exports

* test(context-engine): derive private outbox payload type

* fix(context-engine): apply durable drain degradation

* fix(context-engine): recover durable turn intents

* fix(context-engine): settle durable turn intents

* refactor(context-engine): satisfy branch quality gates

* fix(context-engine): close durable recovery gaps

* fix(discord): preserve dropped model command outcome

* test(copilot): keep journal fixture types local

* fix(auto-reply): preserve model alias provenance

* fix: close model scope review gaps

* fix(models): close review-found scope leaks

* fix(review): satisfy branch line budgets

* fix(agents): preserve context engine turn facts

* fix(agents): finalize silent context turns

* fix(context-engine): preserve compatibility window

* test(agents): cover both harness preparations

* fix(context-engine): retain blocked turn advancements

* fix(models): parse compact runtime options

* fix(telegram): report runtime resets accurately

* fix(models): isolate automatic auth failure skips

* fix(context-engine): project commit turn host params

---------

Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-08-07 16:19:12 +08:00
Peter Steinberger 39bbd8afd3 refactor(sessions): consolidate transcript and state helpers (#119435) 2026-08-04 20:45:41 -07:00
Dinesh H Suthar 50c7444edf fix(sessions): preserve compatible auth profiles on model changes (#117550)
* fix(model-picker): preserve compatible auth profiles
* chore(plugin-sdk): refresh API baseline
* fix(model-picker): keep profile policy core-owned
* fix(model-picker): preserve profiles across auth aliases
* chore(plugin-sdk): update public surface budgets
* fix(model-picker): honor explicit profile clearing
* fix: preserve compatible auth profiles on model changes
* perf: reuse prepared auth profile metadata
* docs(plugin-sdk): document direct model persistence fallback
* test: expose auth snapshot in directive mock

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: Dinesh Suthar <dineshsld20@gmail.com>
2026-08-05 07:40:00 +05:30
Peter Steinberger 2c79d1d7fc fix(memory): persist trusted owner provenance at canonical transcript boundary (#118987) 2026-08-03 15:18:01 -07:00
Peter Steinberger d347cd1097 refactor: canonicalize cache mechanics (#118262)
* refactor: adopt shared map pruning

* refactor: share config-scoped ttl cache

* refactor: share backup link cache

* refactor: centralize native relay retention cap

* chore: remove obsolete bundled channel lint suppression

* chore: shrink max-lines baseline

* refactor: remove cache pruning wrappers
2026-08-02 19:33:13 -07:00
Peter Steinberger 357087b9c7 refactor(gateway): lifecycle-owned ambient state via factory-registered drains (#118271)
* refactor(runtime): own ambient state lifecycle cleanup

* chore(plugin-sdk): refresh additive API baseline

* fix(runtime): restore agent wait listener lifecycle

Keep agent-job data process-shared while preserving module-local event subscription ownership. Ratchet the public wildcard budget down after replacing text-runtime's wildcard singleton export with named exports.
2026-08-02 18:06:46 -07:00
Peter Steinberger 1becbdcb60 fix(control-ui): restore sandbox media previews (#117689)
* fix(control-ui): restore sandbox media previews

* fix(media): preserve anchored history pruning

* style: format anchored history pruning

* chore: drop release-owned changelog entry

* test(ui): cover sandboxed inbound image previews
2026-08-01 19:02:19 -07:00
Vincent Koc 602b5759f2 fix(exec): preserve approved command output when sessions resume (#117177)
* fix(exec): preserve approved exec continuation output

Approved async exec continuations reused the compact background notification
formatter, so the agent resumed from the last 400 characters of output with all
whitespace collapsed and the head silently dropped.

Both hosts now render the continuation through a shared whitespace-preserving
formatter bounded at 16,000 UTF-16 units. Compact `notifyOnExit` notifications,
poll/retained output, and the gateway diagnostics branch are unchanged.

The truncation marker deliberately reports no exact omission count: output can
already be capped at capture time without leaving a marker, so an exact number
would describe only this cut while reading as though nothing else was lost.

Closes #41152

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d9762e2-f967-4cdd-9895-299512a20114

* fix(exec): secure approved continuation handoff

* test(exec): cover authenticated approval handoff

* fix(exec): retain approved followup delivery ownership

* fix(exec): retain compact continuation fallback

* fix(exec): bound accepted followup observation

* fix(exec): keep observer diagnostics internal

---------

Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d9762e2-f967-4cdd-9895-299512a20114
2026-08-01 11:59:51 +08:00
Peter Steinberger cd8a96491e fix: repair canonical session keys with doctor (#116703)
* fix: repair canonical session keys with doctor

* test: align canonical validation with reopen boundary

* test: align session fixtures with canonical keys

* fix: copy only canonical session repair winners

* fix: cover canonical repair edge cases

* test: remove retired preview helper import

* test: align canonical repair delivery scope

* fix: close canonical repair validation gaps

* fix: preserve destination-only repair generations

* fix: preserve canonical repair window projections

* style: format canonical repair imports

* fix: preserve canonical repair delivery ownership

* fix: preserve winner-owned cross-store state

* fix: guard canonical delivery rehoming

* fix: align canonical session contracts after rebase

* test: accept internal canonical repair fixtures

* test: move sharing alias coverage to doctor
2026-07-31 10:04:08 -07:00
Peter Steinberger 39d4ca8f4e fix(sessions): prevent stale upstream activity after session changes (#116864)
* fix(sessions): fence upstream monitor results

* fix(sessions): recheck provenance ownership
2026-07-31 08:08:22 -07:00
Peter Steinberger 4c4aa2ed12 feat(gateway): manage audio and video attachments end to end (#115842)
* feat(gateway): manage audio and video attachments

* feat(gateway): preserve generated media metadata

* fix(gateway): align seeded managed media

* fix(gateway): scope pending media trust by URL

* fix(gateway): split managed media by trust

* fix(gateway): require terminal media trust unanimity

* fix(gateway): align managed media metadata types

* fix(gateway): refresh managed media contracts

* chore(ci): model managed media test seams

* test(sessions): expect canonical media kinds
2026-07-29 07:27:36 -04:00
Peter Steinberger c402688894 feat(media): probe duration and dimensions for playback metadata (#115728)
* feat(media): probe playback metadata

* fix(media): satisfy CI gates

* fix(media): satisfy lint rules
2026-07-29 04:52:17 -04:00
Vincent Koc dd3d1d1a8b fix(ui): hide pre-thread changes from session diffs (#115708)
* fix(ui): scope session diffs to thread changes

* docs: refresh generated map
2026-07-29 15:17:47 +08:00
Peter Steinberger 18ecde3d9f refactor(chat): keep web and terminal sessions synchronized (#115377)
* refactor(chat): unify web and TUI session synchronization

* refactor(chat): extract canonical projection and transcript owners

* test(gateway): verify private transcript store identity

* test(agents): protect public transcript storage boundary

* fix(chat): harden cross-client identity and subscription lifecycles

* fix(gateway-client): preserve type-safe release callback rejection

* test(ui): complete shared sidebar gateway fixture
2026-07-28 16:08:10 -04:00
Peter Steinberger 8ee1d046d3 refactor(sessions): consolidate entry state (#115299)
* refactor(sessions): consolidate entry state

* refactor(gateway): unify session kind classification

* refactor(sessions): keep entry state types private

* test(sessions): drop retired pending delivery case

* fix(sessions): clear stale transport-only delivery
2026-07-28 14:31:02 -04:00