Commit Graph

39402 Commits

Author SHA1 Message Date
Peter Steinberger f07a1fb502 refactor: centralize bounded file reads in fs-safe (#111104)
* refactor: use fs-safe bounded descriptor reads

* build: update fs-safe to 0.4.2

* build: refresh root npm shrinkwrap

* fix: satisfy bounded read return paths

* fix: update fs-safe integration for latest main

* fix: adopt fs-safe overflow compatibility release

* build: complete fs-safe lockfile update

* build: update fs-safe to 0.4.4

* build: refresh plugin SDK API baseline

* test: follow fs-safe bounded read seam
2026-07-19 01:29:23 -07:00
Peter Steinberger 96c9cc112c fix(cron): preserve script state and stop stale runs after restart (#111292)
* fix(cron): preserve script execution boundaries

* chore: keep release changelog unchanged
2026-07-19 01:24:19 -07:00
Peter Steinberger 918f6615d0 feat(ui): Swarm progress widget — dot grid per swarm group (#110325) (#111297)
* feat(ui): Swarm progress widget — dot grid per swarm group (#110325)

* fix(ui): keep swarm renderer internals private
2026-07-19 01:19:56 -07:00
xingzhou 7b3a8dba14 fix(meeting-bot): preserve spawn failure diagnostics (#107614) 2026-07-19 16:17:46 +08:00
Peter Steinberger 3a5da4faa8 feat(ui): live agent activity subtitles on running sidebar sessions (#111221)
* feat(ui): live agent activity subtitles on running sidebar sessions

* refactor(ui): lazy-load narration controller off the startup chunk, unexport test-only symbols

* chore(ui): raise startup JS budget to 312 KiB for the lazy narration feature

* test(ui): restore real timers after narration controller cases
2026-07-19 01:08:06 -07:00
Peter Steinberger 4efcea1fd2 feat(browser): send pages to OpenClaw from the Chrome extension (#111158)
* feat(browser): send pages to the main session from the Chrome extension

One-click page share in the OpenClaw Chrome extension: toolbar popup with an
optional note, page/selection context menu, and Alt+Shift+S. Capture is
selection-first with a readability heuristic, X/Twitter thread extraction, and
Google Docs plain-text export via the user's session cookies. Payloads ride the
existing paired relay WebSocket as a new pageShare message; the gateway-only
page-share sink wraps page text in the external-content safety boundary, then
enqueues a main-session system event and requests an immediate heartbeat
(hooks/wake semantics). Node-hosted relays report a clear unsupported error.

Capture heuristics adapted from Nat Eliason's MIT-licensed send-to-openclaw.

Co-authored-by: Codex <codex@openai.com>

* fix(browser): keep page-controlled metadata inside the share safety boundary

Review findings: move title/URL inside wrapExternalContent (a hostile <title>
must not become trusted header text), prefer the user's selection over the
full Google Docs export, and pass the context-menu selectionText through so
iframe selections and selections cleared during relay reconnect still win.

* fix(browser): bind context-menu shares to the click-time document

Selection shares from the context menu now send the click snapshot directly
(no recapture), so navigations during relay reconnect cannot mislabel the
source and iframe selections are preserved. The Google Docs selection probe
scans all accessible frames before falling back to the full-document export.

* test(browser): expect the page-share handler in relay server args

* fix(browser): probe only the main frame for Google Docs selections

All-frame injection rejects wholesale when one frame is inaccessible and
returns child frames in nondeterministic order, so the probe now reads the
main frame only. Child-frame selections still share correctly through the
context menu's click-time selectionText; toolbar/shortcut entry sends the
full page for that case (named tradeoff in the code comment).

* fix(browser): satisfy page-share CI gates

---------

Co-authored-by: Codex <codex@openai.com>
2026-07-19 01:07:28 -07:00
Peter Steinberger 0f95e66b7f feat(talk): add durable client voice sessions (#111216)
Live-append voice transcripts into the agent session and persist a per-agent SQLite call record across relay and client transcript paths.

Add run-scoped spoken confirmation for high-impact actions, mutation digests, bootstrap-context injection, talk.client.transcript and talk.client.close protocol methods, and Control UI adoption. This adds zero new configuration.

Co-authored-by: Clifton King <clifton@users.noreply.github.com>
2026-07-19 01:06:49 -07:00
Peter Steinberger a8b7290f34 feat(gateway): durable user profiles with email aliases and avatars (#111224)
* feat(gateway): durable user profiles with email aliases and avatars

* fix(gateway): compress merge tombstones, content-hash avatar ETags, users CLI json output

* fix(gateway): lean profile listing, scoped avatar routing, typed email validation

* fix(gateway): protocol-complete profile payloads and consistent store reads

* fix(gateway): mark profile schema ensured only after commit

* fix(gateway): avatar endpoint HEAD support and RFC If-None-Match

* fix(gateway): profiles CI conformance — bindings, lint, knip, sql boundary

* feat(gateway): self-service profile edits for authenticated users

* fix(gateway): users.self bootstrap, tombstone-aware ownership, escaped CLI output

* fix(gateway): raw-DDL allowlist entry and lean profile exports
2026-07-19 00:58:43 -07:00
Peter Steinberger 58452de711 refactor(config): config-surface reduction tranche 1 — retire dead keys, dedupe channel schemas, add growth ratchet (#111142)
* refactor(config): retire dead and aliased config keys via doctor migrations

* refactor(config): dedupe bundled channel config schemas into shared builders

* feat(config): add config-surface count ratchet to doc-baseline check

* test(config): drop stale fixtures for retired config keys

* fix(doctor): migrate only positive finite MCP timeout aliases

* fix(migrate-hermes): emit canonical MCP timeouts only

* fix(config): satisfy lint and contract gates
2026-07-19 00:52:37 -07:00
Peter Steinberger 716f507564 fix(doctor): preserve config repairs during locked session import (#111280) 2026-07-19 00:44:48 -07:00
Jason (Json) 9c7800467c feat(mcp): open App views from channel replies (#111211)
* feat(mcp): add portable channel app actions

* test(gateway): keep origin reset private

* fix(mcp): require a resolved reply channel
2026-07-19 01:44:15 -06:00
Peter Steinberger c163daa4ed refactor(channels): share durable ingress monitor (#111249)
* refactor(mattermost): share ingress monitor

* refactor(nextcloud-talk): share ingress monitor

* refactor(msteams): share ingress monitor

* refactor(zalo): share ingress monitor

* refactor(sms): share ingress monitor

* refactor(line): share ingress monitor
2026-07-19 00:26:28 -07:00
Peter Steinberger 7c4292ee96 fix(agents): reject identity updates for unknown agents (#111268)
* fix(agents): reject identity updates for unknown agents

* fix(agents): preserve implicit default identity updates
2026-07-19 00:18:25 -07:00
Peter Steinberger 9aa6e672a7 fix(custodian): show mode-appropriate guidance and defaults (#111263)
* fix(custodian): align option-card defaults

* test(custodian): preserve default confirm choice
2026-07-19 00:17:09 -07:00
Peter Steinberger 8028288f05 fix(docs): make Mintlify anchor audit reliable (#111265) 2026-07-19 00:15:40 -07:00
Peter Steinberger 8f2ec62917 fix(streams): release guarded response reader locks (#111259)
* fix(streams): release guarded source reader locks

* test(streams): avoid unbounded response helpers
2026-07-19 00:15:23 -07:00
Peter Steinberger ea54060223 feat(codex): fork upstream-linked sessions at a message via thread/fork (#111149)
* feat(codex): fork upstream-linked sessions at a message via thread/fork

* fix(gateway): fail closed for rewind and branch switch on upstream-linked sessions

* fix(codex): fail closed on first-message forks, image-only prompts, and orphan archival

* fix(codex): baseline retained history and reject paginated threads on upstream fork

* fix(codex): validate the full fork prefix and fail closed across crash windows

* fix(codex): treat all non-text inputs as unverifiable in fork drift checks

* fix(codex): support first-message forks as empty-history upstream cuts

* fix(codex): reject source-id reuse and unverifiable hidden inputs in fork boundaries

* refactor(codex): materialize upstream forks from verified thread read-back

* fix(codex): satisfy strict type lanes and knip for upstream fork
2026-07-19 00:11:10 -07:00
jincheng-xydt 5a81e9fa81 fix(agents): preserve ANSI sanitizer state across bash chunks (#103706)
* fix(agents): preserve ANSI sanitizer state across bash chunks

* fix(agents): harden streaming ANSI sanitization

Keep incremental parser state in the canonical terminal owner, avoid a second sanitizer pass, and leave OutputAccumulator and public terminal APIs unchanged.\n\nCo-authored-by: Jicheng Xu <xu.jincheng@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 23:33:40 -07:00
Peter Steinberger ee0e3a4d47 refactor(channels): share durable ingress monitor (#111214)
* refactor(telegram): share durable ingress monitor

* refactor(whatsapp): share durable ingress monitor

* refactor(feishu): share durable ingress monitor

* refactor(qqbot): share durable ingress monitor

* refactor(zalouser): share durable ingress monitor

* refactor(nostr): share durable ingress monitor
2026-07-18 23:33:24 -07:00
Peter Steinberger ccb147518c feat(agents): Swarm core — collector spawn, agents_wait, structured output, caps (gated) (#110932)
* docs: add Swarm implementation spec

* feat(agents): Swarm core — collector spawn, agents_wait, structured output, fastMode, caps

Implements docs/plan/swarms.md §4-6: tools.swarm config gate (default off),
collector-mode sessions_spawn (collect/outputSchema/fastMode/groupId), fail-closed
child approvals, agents_wait race-semantics tool, per-group FIFO scheduler with
maxConcurrent/maxChildrenPerGroup/maxTotalPerGroup caps, additive registry and
state-schema columns (no schema-version bump), and colocated tests.

Part of #110325

* fix(agents): rebase reconciliation — preserve swarm state columns

* fix(agents): O(1) collector session index, cheap-first preflight, collector start lifecycle hooks

Autoreview findings: replace the per-request full registry scan with an
index-maintaining run map and gate the persisted-store fallback behind
isSubagentSessionKey; emit subagent_progress/subagent_spawned from the swarm
scheduler start callback so collector children produce balanced plugin
lifecycle events.

* chore(protocol): regenerate Swift gateway models for swarm fields

* fix(agents): lint cleanup — typed catch, explicit microtask flush, no executor return

* fix(agents): knip/test-support/max-lines cleanup for swarm surfaces

* fix(agents): keep tool-catalog ui-safe — callers pass prepared swarmEnabled fact

resolveSwarmConfig value-import in tool-catalog dragged the server graph into
the Control UI bundle via tool-policy-shared (UNLOADABLE_DEPENDENCY on
subpath aliases). Catalog stays pure; the gateway tools-catalog handler
resolves the gate and passes the boolean.

* fix(agents): reconcile swarm spawn pipeline

* fix(agents): refresh swarm tool metadata

* fix(apps): sync swarm tool localization

* style(agents): compact tool display metadata

* fix(plugin-sdk): account for swarm config surface
2026-07-18 23:30:11 -07:00
LZY3538 7055ed578d fix(wizard): honor process locale when overrides are blank (#111076)
* fix(wizard): ignore blank locale env overrides

* test(wizard): cover locale fallback chain

* docs(wizard): document locale env precedence

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 23:28:03 -07:00
Peter Steinberger ba62b944fa fix(streams): preserve stable results when body cleanup fails (#111245)
* fix(proxy): preserve validation after cleanup failure

* fix(msteams): preserve attachment cleanup outcomes

* fix(gateway): preserve loopback size diagnostics
2026-07-18 23:25:20 -07:00
Peter Steinberger a2ccbdfa96 feat(cli): list and resolve pending approvals headlessly (#111060)
* feat(cli): manage pending approvals

* fix(cli): show terminal-safe approval ids raw, reserve id64 tokens for hostile ids

* fix(cli): preserve opaque approval ids verbatim

* fix(cli): tokenize leading-hyphen approval ids for pasteability

* fix(cli): lossless utf16 id64 tokens for opaque approval ids

* fix(cli): resolve approval ids verbatim, no input trim

* fix(cli): scope-only approval auth, reviewer-safe system-agent summaries, skip ill-formed ids

* fix(cli): validate pending approval ids

* fix(cli): align approvals catalog and docs map
2026-07-18 23:23:49 -07:00
Jason (Json) b8b9825083 fix(plugins): report newer registry releases for exact-pinned npm installs (#111169)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 00:16:54 -06:00
Jason (Json) 7aa5ee594e fix(doctor): treat invalid legacy session stubs as warnings during session SQLite import (#111168)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 00:14:56 -06:00
Jason (Json) 25a8b33ff5 fix(state): make managed-image additive migration portable across SQLite versions (#111167)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 00:10:48 -06:00
LZY3538 29b4b74a5a fix(clawhub): preserve legacy telemetry opt-out behind blank primary env (#111078)
* fix(clawhub): preserve legacy telemetry opt-out

* test(clawhub): cover telemetry env precedence

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 23:04:48 -07:00
Peter Steinberger 932e8be06c feat(agents): canonical agent creation with Custodian hatch flow (#111052)
* refactor(agents): centralize agent creation

* feat(ui): add new agent hatch flow

* refactor(agents): keep creation paths lean

* fix(agents): hide internal creation types
2026-07-18 22:50:02 -07:00
Jason (Json) 2f7da3057c feat(mcp-apps): support bounded model context updates (#111212)
* feat(mcp-apps): support model context updates

* fix(mcp-apps): satisfy protocol and runtime guards
2026-07-18 23:08:35 -06:00
Peter Steinberger e23dde3de5 feat: disable automatic session resets by default (#111140)
* feat(config): disable automatic session resets by default

* fix(sessions): honor pending reset tombstones

* test(sessions): align reset coverage with disabled default

* fix(sessions): preserve explicit reset override fallback

* fix(sessions): inherit active mode in partial type resets
2026-07-18 21:50:48 -07:00
Marcus Castro d5cb708623 fix(qa): repair WhatsApp live scenario regressions (#110754)
* fix(qa): make WhatsApp scenarios account-aware

* test(qa): cover configured WhatsApp SUT account

* refactor(qa): centralize QA target encoding

* fix(qa): configure scenario policies at startup
2026-07-19 01:43:38 -03:00
Peter Steinberger 721aee1f19 feat(gateway): attribute prompts to authenticated users (#111207) 2026-07-18 21:37:57 -07:00
Peter Steinberger 1d1565245d fix(sessions): stop persisting media-only caption placeholder into transcripts (#111204)
* fix(sessions): stop persisting media-only caption placeholder into transcripts

Media-only user turns persisted the literal "[User sent media without
caption]" as message content, so every client rendered the model-facing
scaffolding as a visible subtitle under the image. Persist bare content
instead and inject the placeholder at the single LLM-boundary stamping
site (like per-message timestamps, #3658), keeping provider-visible
bytes identical for current turns, history replay, and legacy
transcripts.

* fix(agents): satisfy lint and test-type gates for boundary media injection
2026-07-18 21:37:02 -07:00
Peter Steinberger 938e3d5f20 refactor(channels): share durable ingress monitor (#111186)
* refactor(signal): share durable ingress monitor

* refactor(slack): share durable ingress monitor

* refactor(discord): share durable ingress monitor

* refactor(imessage): share durable ingress monitor

* refactor(twitch): share durable ingress monitor

* refactor(tlon): share durable ingress monitor
2026-07-18 21:33:09 -07:00
Peter Steinberger 5e51c4bbcc feat(gateway): auto-approve trusted-proxy browser device pairing (#111189)
* feat(gateway): auto-approve trusted-proxy browser device pairing

Adds gateway.auth.trustedProxy.deviceAutoApprove so team gateways behind an
identity-aware proxy (Cloudflare Access, oauth2-proxy, Pomerium) can skip the
manual `openclaw devices approve` step for new Control UI/WebChat devices.

Auto-approval fires only for a new (unpaired) operator browser device on a
connection that already passed trusted-proxy auth with a resolved allowUsers
user. Scope upgrades on existing devices and node pairing stay manual. Granted
scopes are capped to the configured set intersected with the connection's
x-openclaw-scopes proxy cap, operator.admin is rejected at config validation,
and the pairing-store approval rechecks new-device status under the store lock
so a repair/upgrade or concurrent approval can never be silently widened. Each
auto-approval emits an audit log line with the proxy user and granted scopes,
and `openclaw security audit` warns when the mode is enabled.

* docs: regenerate docs map for trusted-proxy auto-approval section
2026-07-18 21:23:55 -07:00
Peter Steinberger 16e967eb7f fix(gateway): reject unknown session agents (#111178) 2026-07-18 21:15:57 -07:00
Peter Steinberger 3af3493541 feat(config): journal every config change with source labels and manual-edit detection (#111147)
* feat(config): journal every config change with source labels and manual-edit detection

* fix(config): narrow journal exports and align plugin-write test expectations

* refactor(config): split journal snapshot store out of io.audit

* chore(config): keep journal snapshot context helpers module-local
2026-07-18 21:09:38 -07:00
Peter Steinberger c684b13213 feat(gateway): thread authenticated user identity into presence (#111179)
* feat(gateway): thread authenticated user identity into presence

* feat(gateway): regenerate Swift protocol bindings for presence user
2026-07-18 21:06:22 -07:00
Peter Steinberger cc57514e68 refactor(agents): make API registry ownership lifecycle-local (#111137)
* refactor(agents): make API registries lifecycle-owned

* refactor(agents): keep registry runtime ownership internal

* fix(agents): bind session streams to registry runtime

* refactor(agents): keep prepared runtime ownership internal

* test(agents): model lifecycle runtime fixtures

* fix(amazon-bedrock): adapt lifecycle stream types

* fix(agents): complete lifecycle runtime migration

* test(agents): satisfy lifecycle registry static gates
2026-07-18 20:55:07 -07:00
Bartok b49c5463fe fix(status): preserve thinking level for discovered Ollama models (#108789)
* fix(status): carry prepared thinking catalog

Resolve status thinking levels from model metadata prepared by each caller.
Model-state paths carry their resolved catalog forward, while standalone status
surfaces make one side-effect-free read without provider discovery.

Co-authored-by: Bartok9 <danielrpike9@gmail.com>

* test(status): assert read-only native catalog load

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 20:55:02 -07:00
Peter Steinberger 63f8b8454b fix(network): guarded redirects survive cleanup rejection (#111156)
* fix(network): preserve redirects when cleanup fails

* test(network): bound redirect response assertion

* test(network): honor stream cancellation contract
2026-07-18 20:48:52 -07:00
Peter Steinberger 2f00a417ed feat(gateway): session dashboard domain — board RPCs, ticketed widgets, dashboard tool, per-agent persistence (#110960)
* feat(gateway): add session dashboard domain

* test(gateway): cover board event guards

* fix(gateway): authorize board widgets with view tickets

* feat(boards): persist dashboards in agent databases

* test(boards): use tracked SQLite temp directories

* test(gateway): isolate board deletion lifecycle coverage

* fix(boards): harden widget view ticket bindings

* fix(boards): lazily ensure additive board schema

* fix(gateway): complete dashboard integration metadata

* fix(boards): satisfy generated integration checks

* fix(boards): align persistence with repository guards

* fix(boards): retain notice test reset

* fix(protocol): generate dashboard union models

* fix(boards): harden dashboard persistence and serving

* fix(boards): satisfy dashboard CI guardrails
2026-07-18 20:17:51 -07:00
YangManBOBO ed546bdcf5 fix(auth): expired OAuth credentials survive per-provider credential discovery and silently break background operations (#110678)
* fix(auth): reject expired OAuth credentials in provider credential discovery

* test(auth): verify expired first profile is skipped for same-provider validation

* fix(auth): prefer non-expired OAuth profile in per-provider credential map

* fix(auth): use canonical profile order in discovery

* docs(auth): document expired OAuth ordering

* test(auth): use synthetic credential fixtures

* test(auth): clarify resolved profile fixtures

* test(auth): keep profile result names consistent

* test(cli): relax ACP process deadlines under load

* style(cli): format ACP process timeout

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 20:13:34 -07:00
SunnyShu b79c141fc7 fix(cli): bound exec approvals --file JSON read size (#110755)
* fix(cli): bound exec approvals --file JSON read size

Replace raw fs.readFile with the shared readRegularFile helper
from @openclaw/fs-safe/advanced, which enforces regular-file
validation and a max-bytes limit. The --stdin path already had
a 1 MB bound via readStdin; --file now uses the same
EXEC_APPROVALS_STDIN_MAX_BYTES limit.

* test(cli): add --file read bounds regression tests

Covers normal (under limit), oversized (> 1 MiB), and non-regular
path (directory) --file inputs to the approvals set command.

* fix: import readRegularFile from ../infra/fs-safe.js for boundary compliance

* fix(cli): preserve approvals file path behavior

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 19:59:01 -07:00
Peter Steinberger 208036afdc fix(codex): session permissions persist across resumed turns (#111136)
* test: harden Codex compaction command oracle

* test: accept omitted Codex command exit code

* fix(codex): apply stored session permissions

* test: harden Codex gateway stress evidence

* test: accept successful Codex command retries

* test: classify Codex evidence as live helper
2026-07-18 19:56:42 -07:00
Peter Steinberger 075030c19e fix(channels): preserve single-account setup labels (#111134)
Fixes #111130
2026-07-18 19:32:31 -07:00
Yuval Dinodia 18b79d99ab fix(gateway): bound busy channel health by real run age (#103793)
* fix(gateway): bound busy channel health by real run age

The channel health policy treats a channel as healthy-busy even while
disconnected, bounded only by a 25 minute stale ceiling measured from
lastRunActivityAt. The run-state heartbeat refreshes lastRunActivityAt
every 60 seconds for as long as any run is active, so a run that hangs
forever (for example a send blocking on a dead socket after the
transport already reported connected:false) keeps that timestamp fresh
and the stuck ceiling is never reached. The account is then reported
healthy forever by the health monitor, readiness probe, and health CLI,
and no restart ever fires.

createRunStateMachine now tracks each in-flight run's start time keyed by
an opaque run handle and publishes the oldest still-active run's start as
activeRunStartedAt. The health policy busy override keys its ceiling off
the real run age, so a run stuck longer than the threshold reports stuck
and the monitor can restart it. Because the reported start is the oldest
active run and advances to the next-oldest as runs complete, a channel
churning through many short overlapping runs (activeRuns above 1 across
concurrent queue keys) stays healthy; only a genuinely hung run breaches
the ceiling. Short and active runs stay healthy and the existing
lastRunActivityAt fallback is preserved for snapshots without a start
time.

* fix(channels): retain run-state callback compatibility

Keep the released zero-argument onRunEnd callback source-compatible while allowing internal queue callers to pass a run handle for exact concurrent-run accounting. The compatibility path closes the oldest active run, preserving existing lifecycle behavior for consumers that do not use handles.

* fix(channels): keep anonymous runs out of age tracking

The zero-argument lifecycle callbacks cannot identify which concurrent run completed, so they must not update the identity-sensitive run start used by channel health. Keep their busy count separately and reserve exact start tracking for the shared queue's handle-aware lifecycle path.

* fix(channels): keep tracked runs internal

Keep the public run-state lifecycle callbacks unchanged. The channel queue now owns opaque run identity and augments its status updates with the oldest active queue run, so implementation details do not expand the SDK surface.

* fix(channels): type queue run start status

Keep activeRunStartedAt in the internal status patch type so the queue can publish its private tracked-run age through the existing status sink.

* fix(channels): wrap isActive to satisfy unbound-method lint

* fix(gateway): gate busy run-age ceiling on disconnected transport
2026-07-18 20:26:17 -06:00
Peter Steinberger 68771ebdfe feat(cron): script payloads behind the trigger gate (#111112)
Run script payloads through the shared headless code-mode executor with payload-grade budgets and success-only trigger.state persistence.

Reuse cron delivery, wake, pacing, and dangerous trigger-gate contracts for notify, wake, and nextCheck results.
2026-07-18 19:24:12 -07:00
Yuval Dinodia 80746b06b9 fix(sessions): commit reduced session index before deleting evicted transcripts (#108378)
* fix(sessions): commit reduced session index before deleting evicted transcripts

The file-backed session store disk-budget sweep evicted an old session by
removing its in-memory entry and immediately, permanently deleting its
transcript artifact, and only afterwards did the caller serialize and
atomically replace sessions.json. A crash, power loss, or store-write failure
in that window left durable metadata in sessions.json pointing at transcripts
that were already gone, an irreversible loss of evicted session history during
the low-disk maintenance when failures are most likely.

enforceSessionDiskBudget now plans the evicted entries' owned artifact
deletions during the sweep (accounting their freed bytes so the stop condition
is unchanged) and defers the physical unlink until after an injected
commitEvictedIndex callback atomically persists the reduced index. saveSessionStore
supplies that callback. A crash after the commit leaves only reclaimable orphan
files; a crash before it retains the transcript.

* fix(sessions): retain evicted artifacts without commit boundary

* fix(sessions): fsync reduced index before eviction
2026-07-18 20:22:45 -06:00
Yuval Dinodia 6ff963eb46 fix(transcripts): imported text can inject terminal escapes through transcripts show (#110060)
* fix(transcripts): imported text can inject terminal escapes through transcripts show

Imported transcript text, speaker labels, and session titles were rendered
into summary.md unsanitized, and openclaw transcripts show writes that file
directly to stdout, so a transcript could clear the terminal or spoof
colored status text. Sanitize external strings with the terminal-core
sanitizer when the summary is built, so summary.json, summary.md, and the
CLI output stay free of control bytes while transcript.jsonl keeps the raw
capture.

* fix(transcripts): protect terminal output boundaries

* fix(transcripts): keep canonical session identity separate from terminal presentation

* fix(transcripts): escape C1 control characters in transcripts json output
2026-07-18 20:20:13 -06:00