* fix(cron): keep isolated run status ok when delivery fails
A successful isolated cron agentTurn whose post-run delivery phase returns
an error collapsed the execution status into the delivery error, so the
outer scheduled run persisted status=error even though the isolated session
ended successfully.
Decouple execution status from delivery outcome in finalizeCronRun: when the
agent payload is non-fatal and the run was not aborted, a delivery dispatch
error now keeps status=ok and records the failure separately via
delivered/deliveryAttempted plus delivery diagnostics, which drive the
already-decoupled run-log deliveryStatus (not-delivered). Aborted runs and
fatal agent payloads keep their error status.
Fixes#94058
* fix(cron): keep deliberate delivery-target refusals as error status
The #94058 fix kept isolated turn status=ok when post-run delivery
fails, but the broad `status === "error"` check also caught the
#91613 keyless implicit last-target refusal, which is a deliberate
delivery-target guard (errorKind "delivery-target") and must stay
status=error. Exclude that errorKind so genuine delivery dispatch
failures still become ok while target-guard refusals remain errors.
* fix(cron): surface successful run delivery errors to run logs
A successful isolated agent turn keeps status=ok when post-run delivery
fails (#94058), but the ok/not-delivered resolveRunOutcome branch only
forwarded delivery diagnostics and dropped the delivery dispatch error.
That left lastDeliveryError empty and the finished-event deliveryError
field blank, so CLI/UI/API run logs could not show why delivery did not
land for an otherwise successful run.
Thread the delivery error on a dedicated CronRunOutcome.deliveryError
field from resolveRunOutcome through executeDetachedCronJob and
applyJobResult into resolveDeliveryState, so it persists as
lastDeliveryError and reaches the finished event (and thus the run log)
without conflating it with a run-level error (lastError stays empty on a
successful run).
Add a service/run-log readback test proving the delivery error survives
the cron boundary into a persisted run-log entry, and extend the
isolated-turn regression to assert the dedicated deliveryError field.
* fix(cron): preserve successful delivery outcomes
* fix(cron): preserve startup delivery errors
* fix(cron): retain best-effort delivery errors
* fix(cron): preserve delivery errors on fallback paths
* fix(cron): preserve delivery errors on fallback paths
---------
Co-authored-by: Alix-007 <li.long15@xydigit.com>
* feat(tooling): enforce noUncheckedIndexedAccess in the scripts lane
Burns down all 153 scripts-lane errors (bench aggregation, release
checks, i18n inventories, argv indexing) and flips the flag in
tsconfig.scripts.json. Direct-Node-executed release harness scripts use
local narrowing instead of workspace imports, which do not resolve
under plain node execution. Benchmark measured loops untouched.
* fix(scripts): import expect helpers via relative package sources
tsconfig path aliases resolve from cwd under tsx, so release wrapper
scripts running against old release target cwds could not resolve
@openclaw/normalization-core (not a linked root dependency). Relative
package-source imports match the established pattern on the adjacent
lines and are cwd-independent; old-target planning verified directly.
* feat(mac): swap dashboard titlebar buttons with sidebar state
The Control UI reports sidebar collapsed/width over a new openclawNav
WKScriptMessage; the titlebar accessory shows toggle+back/forward
right-aligned to the sidebar edge while expanded, and
toggle+search+new-session while collapsed. Search opens the command
palette and + opens the new-session surface via openclaw:native-*
events. Older gateway bundles that never report keep the shipped
toggle/back/forward layout.
Refs #105129
* docs: describe state-dependent mac titlebar buttons
* feat(nodes): route alerts by active computer
* fix(ci): allowlist node presence mobile coverage
* test(prompts): refresh node presence snapshots
* fix(macos): await presence reporter actor hop
* fix(macos): type presence test delivery state
* docs(nodes): add active presence guide
* docs(nodes): format presence troubleshooting
* improve(ui): compact the new-session screen on phones
The /new target controls condense on phones: Where and Worktree share one
line and the folder picker takes its own full-width line below, so long
paths stay readable instead of stacking three loose rows. The shared
welcome hero (start screen and /new) scales down on phones so the
composer and recent chats fit the first screenful, and the /new scroll
inset matches the chat thread's mobile padding.
* fix(ui): keep mobile target rows in DOM order
Review caught that order: 2 on the folder chip made visual and tab order
diverge on phones. The folder chip now flexes next to Where with a 140px
floor that pushes Worktree to the second line, so the compact layout
keeps visual, reading, and focus order aligned.
* fix(ui): wrap the worktree fields with their toggle
Groups the worktree toggle and its branch/name fields in one flex unit so
line breaks never separate the dependent fields from the toggle at any
width; review caught that the previous flex sizing let the branch control
wrap alone on wider phones.
* fix(tlon): cap SSE payload JSON.parse at 16 MiB to prevent OOM
* fix(tlon): cap SSE stream buffer and JSON.parse at 16 MiB to prevent OOM
* chore(tlon): add production-style SSE bounded proof script (#101274)
Adds extensions/tlon/proof-sse-bounded.mts which drives the real
UrbitSSEClient.processStream and processEvent against Node Readable streams
(not unit-test mocks) to demonstrate:
- normal SSE events are still delivered through the stream path;
- an unterminated stream that would grow beyond 16 MiB is rejected before
unbounded accumulation;
- a single SSE payload above 16 MiB is rejected before JSON.parse.
The script passes on this branch and fails 2/3 assertions when run against
origin/main's sse-client.ts, providing the before/after proof ClawSweeper
requested.
* chore(tlon): fix oxlint catch type in proof script (#101274)
* fix(tlon): reject oversized SSE chunk before buffer concatenation
Move the stream byte-limit check before buffer += chunkStr so a
single oversized chunk never lands in the pending buffer. The old
guard ran after concatenation, which still allowed the memory spike
this hardening is meant to prevent.
Add a single-oversized-chunk test to prove the guard fires before
the chunk is concatenated into the pending buffer.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore(tlon): remove PR-specific SSE bounded proof script
The proof script was review-only validation for #101274. Durable behavior
coverage lives in sse-client.test.ts (stream buffer bounding, oversized
chunk rejection before concatenation, 16 MiB boundary, normal delivery,
1000 small events). Per ClawSweeper P3 finding, drop the one-off script
from the plugin tree.
* fix(tlon): bound SSE event buffering safely
* test(tlon): avoid unsafe optional chaining
* fix(tlon): count split Unicode at SSE limit
* fix(tlon): parse split SSE delimiters at limit
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
The /new draft page now renders the same welcome block as the empty-chat
start screen, with the draft controls folded in launcher-style: hero on
top, a quiet borderless target row (agent, node, folder, worktree)
directly above the mid-screen composer, and the recent chats below. The
page reuses renderWelcomeState via hint/composer slots so the surfaces
cannot drift apart; the shell treats /new as a chat-like route that owns
its scrolling, with the top inset doubling as the native titlebar drag
band. The welcome is content-sized on this route so a tall draft (open
folder browser, short window) stays scrollable from the top. Also
repairs the stale new-session e2e heading wait (the h1 was removed in
Closes#105068