Skip to content

feat(settings): opt in to Code Mode for new tasks - #5219

Merged
Astro-Han merged 12 commits into
apache:mainfrom
Astro-Han:feat/code-mode-global-setting
Sep 12, 2026
Merged

Astro-Han merged 12 commits into
apache:mainfrom
Astro-Han:feat/code-mode-global-setting

Conversation

@Astro-Han

@Astro-Han Astro-Han commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Add an opt-in Code Mode switch under Settings → General → Task defaults. The selected Runtime Host saves the global default, and newly created tasks persist their tool mode. Changing the switch leaves existing tasks unchanged; copies and child tasks inherit their source task's mode. Scheduled agent tasks also freeze the mode in their execution template, with legacy templates using direct tools. There is no per-task UI switch.

When enabled, the model receives only exec, with the current callable tool catalog in its description. Nested calls use the existing ToolRuntime permission checks, durable settlement, cancellation, and exclusive-tool admission. Tool search refreshes the catalog for the next cell; nested questions and plan/graph handoffs remain available. Denied sandbox tools are removed from the model-visible nested catalog.

Execution boundary

Keep @ai-sdk/code-mode, upgrading 1.0.42 → 1.0.56 and aligning the AI SDK family and provider-utils patch. Pin its run dependency to 2.1.4 and apply two documented dependency patches: the SDK forwards an opt-in execution-time policy, and the executor enforces it at synchronous QuickJS entry points.

Previously, a 30-second total deadline also aborted normal host-tool and user-input waits. Maka now uses a cumulative 30-second VM execution budget. External waits retain the VM and consume no execution budget; individual Promise completions immediately advance dependent code without a continuation batch or an extra parent-model request. Default SDK wall-time behavior remains available and unchanged.

The executor removes empty-job polling and waits for bridge responses or cancellation. It also uses the Promise handle returned by evaluation directly, removing a guest-writable global result slot. Runtime cancellation still drains started host operations before releasing cell admission. No new Worker pool, replay mechanism, or Runtime scheduling layer is added.

The patched run manifest uses Maka's Node >=22.19 baseline and removes the optional TypeScript peer used by the older-Node fallback. Supported Maka runtimes use native type stripping, so no second compiler is shipped. Attribution, generated notices, and source-header inventory are updated. The large generated patch hunk is the inline Worker with embedded WASM; patches/run-2.1.4-source.diff provides the readable source changes and rebuild instructions are in patches/README.md.

Host protocol compatibility epoch moves from 142 to 143.

Verification

  • 292 relevant Runtime tests passed, including dependent Promise.race progress across long waits, cumulative computation across awaits, computation while a host call is pending, cancellation/drain, and resource limits.
  • The 58 Code Mode/backend tests also passed on the minimum supported Node 22.19.0.
  • 69 scheduled-task and Session catalog tests passed; 196 release checks passed.
  • Patched upstream run: 364 tests, build, and typecheck passed.
  • A real 31-second host wait crossed the previous default deadline, returned its answer exactly once, and kept its signal live. Independent review also tested a 15-second wait under a 12ms execution budget and cancellation followed by reuse of a single Worker slot.
  • Independent clean npm ci, dependency patch application, production dependency inspection, and SDK smoke passed. Execution mode allows a long host wait; default wall mode still times out.
  • Real Electron settings test passed: enable/disable persists across reopening, with a screenshot attached to the test result.
  • Workspace test build, rebuilt desktop renderer, formatting, lint, license-header checks, and whitespace checks passed.

Two independent Reviewer Deep reviews covered adversarial behavior and simplification. No P0/P1 remains. Removed the old direct-only plan/graph assertions, redundant diagnostics, idle polling, and the mutable global result slot. Retained distinct tests for permissions, cancellation, durable settlement, and execution budgets.

Known limits

Waiting invocations retain their VM and Worker/memory quota until completion or cancellation. This includes guest-created Promises that never settle; Runtime supplies a reachable cancellation signal, and existing admission and process-wide resource limits remain in force. There is no automatic liveness deadline or deadlock detector. Provider-native and explicitly non-nestable tools remain unavailable through Code Mode; execution-time mode supports asynchronous host functions, not synchronous bridge/module-loader calls. These tests establish execution and settings behavior, not a live-model quality improvement.

AI use

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Codex implemented dependency alignment, executor patches, settings/runtime integration, and tests. Two Reviewer Deep agents performed independent reviews.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described above
  • No

@github-actions github-actions Bot added the effort/L Under 1000 readable lines label Sep 12, 2026
@Astro-Han
Astro-Han marked this pull request as draft September 12, 2026 06:23
Comment thread apps/desktop/src/renderer/settings/general-settings-page.tsx Outdated
Keep the VM alive during host waits and resume through the existing Promise bridge. Patch the executor at its synchronous VM boundary, remove idle polling and the guest-writable result slot, and preserve cancellation and resource limits.

Generated-by: Codex
@Astro-Han
Astro-Han marked this pull request as ready for review September 12, 2026 07:39
@github-actions github-actions Bot added effort/XL Under 2500 readable lines and removed effort/L Under 1000 readable lines labels Sep 12, 2026
Resolve the combined Host contract at compatibility epoch 146 and regenerate dependency notices.

Generated-by: Codex
Keep single-slice success and cumulative timeout assertions while widening the test budget and repeated workload together.

Generated-by: Codex

@jackwener jackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve at 2fda64d5d310fe33dafb2ebd528606ebfd48d8b9. I found no blocking issues. Opt-in Code Mode is off by default, is frozen per Session at creation, and the execution-budget semantics match what the patch notes describe.

What I actually checked.

  • Budget, async and cancellation. In execution mode the budget accumulates only synchronous entries into the sandbox: the measuring wrapper returns immediately when already inside an outer entry, so reentrant calls do not double-count, and the interrupt check compares accumulated time plus the current span against the budget. No wall-clock timer is armed in this mode, which is the documented intent. Cancellation is wired into the existing host cancel/fail path and rejects the race that the resolver awaits, so a cancelled run unblocks without relying on polling.
  • Removal of the resolver's polling loop. This is not gated on the new mode, so I checked the default wall path separately: the wall deadline is enforced on the manager side by a timer that fails the run terminally, independent of the worker's await. Removing the per-tick poll therefore does not weaken the default timeout.
  • Settlement. The cell's promise drains every host operation it started on both the success and failure paths, looping while new operations appear, so repeated cancellation cannot accumulate unbounded host work. Nested calls now carry a step identity, are refused once the turn has yielded, and route plan and yield results through the same handlers as direct calls.
  • Mode freezing. New Sessions read the setting once at creation and store the result on the Session header; turns read that frozen value. Scheduled tasks capture the mode into their execution template at creation, and the stored template is validated on decode, so changing the setting later does not rewrite existing tasks or past jobs.
  • Dependency patches and install consistency. The override pinning, the patched package versions and the patch filenames agree with each other. The readable source diff is coherent and corresponds to the described behaviour.
  • The large generated hunk. I extracted the embedded WebAssembly payload from both sides of that hunk and compared it directly: it is byte-identical (848,536 base64 characters, identical digest). The entire difference in that line is rebuilt worker JavaScript, which confirms the patch notes' claim that the embedded binary is unchanged.

Two non-blocking observations.

  1. In this mode the tool plan returns no context-budget diagnostics at all, while the single callable tool's description now embeds the full serialized catalog. The catalog still costs prompt space, but nothing reports it, so a Session in this mode loses the visibility that other Sessions keep. Consider reporting the projected tool's schema size rather than suppressing the diagnostic.
  2. The rebuilt worker bundle is minified, so its difference is dominated by renamed local identifiers. Beyond the binary-identity check above, that hunk is not reviewable by reading; its provenance rests on the documented rebuild procedure and the pinned upstream commit. That procedure being written down is what makes this reviewable at all, and it is worth keeping accurate.

What I did not verify. I reviewed code and hosted check results only. I did not run the test suite, a build, an end-to-end run or any sandbox execution, so I did not measure real budget accounting, cancellation timing or memory behaviour. The no-polling change assumes the guest environment exposes no timers, so that nothing becomes runnable without a bridge response; that holds for the frozen guest surface in this diff, but I did not audit the whole upstream guest environment for a timer. This change also raises eight SDK package versions alongside the feature; I did not assess those upgrades independently, and hosted checks are the only evidence for them here. All required checks are terminal on this commit: seventeen successful and one skipped, with none failing or pending.


Automated review notice. This review was produced by an automated reviewer (agent seat kabi-opus, model Claude Opus) acting on its own review identity. It is posted from a shared machine account, so the seat is named here because the account alone does not identify the reviewer. It is not an independent human review and does not substitute for one. Its scope and limits are stated above; no merge was performed.

@Astro-Han
Astro-Han merged commit 3cfcb09 into apache:main Sep 12, 2026
18 checks passed
Shouly pushed a commit to Shouly/maka that referenced this pull request Sep 13, 2026
)

Thirty-one upstream commits. The one that reaches the new renderer is apache#5170,
which gives the Renderer the transcript window: Main keeps a tail cache and
answers page requests pass-through, `loadBefore` / `loadAfter` return a page,
`loadAround` / `loadLatest` a reset, `acknowledgeTail` is new, and a batch
carries `extends` / `coversFrom` / `navigation` instead of
`evictedDurableSequences` / `completedOverlayMessageIds`. Also in: apache#5217's
observation contract (`subscribeEvents` loses `onSeeded`; readiness follows
seed consumption as the `ready` phase, and the execution projection it offers
is not consumed here yet), the memory work across composer and stream
(apache#5153), interactions cleared per Turn on abort/complete (apache#4562), Session
bundles and external agents in main/preload (apache#5197, apache#5164), Code Mode
(apache#3615, apache#5219), and the scheduled-task snooze fix (apache#5226).

Resolution per the sync policy: conflicts under the old renderer's trees,
packages/ui's deleted components, their stories, e2e specs and the main tests
that import them stay deleted; upstream's new files in those trees are dropped
(`application/contracts/settings-presentation`, `features/external-agent-settings`,
`features/session-bundle`, `workhub/ui/return-button`, `model-wheel-picker`,
the prompt-rail and live-turn-buffer tests, `workhub-return-rail.spec.ts`).
The renderer side of apache#5217 (one live Turn per Session → a buffer keyed by
Turn, `liveTurn` → `liveTurns`, `phase` gone) stays out: `packages/ui`
`live-turn-projection.ts`, `transcript-projection.ts` and their tests keep
ours and `live-turn-buffer.ts` is dropped; `session-event-handlers.ts` keeps
ours plus upstream's display-frame scheduler. `packages/ui`
`conversation-copy.ts` keeps `transcriptGap` (our gap rows use it),
`transcript-row-projection.ts` is restored, `use-pending-selection.ts` goes.
Astryx stays out of package.json and the lockfile; `@ai-sdk/provider-utils`
moves to 5.0.40 and the `@ai-sdk/code-mode` override lands.

Re-implemented for the new contract:
- `lib/ported/desktop-transcript-range-store.ts` and
  `transcript-reading-position.ts` are re-ported from upstream head (the
  previous copies were format-only ports of the old versions);
  `TranscriptReadSupersededError` lives in the latter, and
  `display-frame-scheduler.ts` joins `lib/ported`.
- `store/active-session-store.ts`: the window is the store's — the display
  follows a store subscription rather than `accept`'s return; the paging gate
  and `loadTranscriptHistory` are gone (the controller refuses a read against
  an edge it already read), `loadHistory` keeps only the gap-row indicator;
  `prefetchHistory` and `retainWindow` serve `useChatScroll`'s geometry-driven
  filling and trimming; `setReadingAnchor` only moves the bookmark; the
  bookmark re-anchors after a replica generation change, by sequence within a
  Host epoch and by Turn through the landmark index across one; a read
  superseded by an epoch change is not an error.
- `SessionView` passes `onPrefetchHistory` / `onRetainWindow`; the gap rows
  and the return-to-latest button keep their explicit commands.
- `bridge/sessions.ts` drops `onSeeded`.
- Main tests for the range store, navigation race, overlay settlement and the
  two new probes are upstream's with paths under `lib/ported`; the
  reading-position test keeps upstream's pure-module cases (send pinning,
  overlay-only bookmark, superseded read) — the shell-shaped cases live with
  the store's tests.
- `settings-sections.ts` and the copy files name core's new `external-agents`
  section id as a deferred page.
- Ported apache#5226: an edit that leaves the schedule fields alone omits
  `schedule` from its patch, so the Host keeps a snoozed fire
  (`scheduled-task-form-payload.ts`, `ScheduleFormDialog.tsx`, a test in
  `scheduled-module.test.tsx`).

Also in this tree, found while verifying the sync and not caused by it: a live
Turn's finished steps vanished after switching to another task and back,
leaving only "Working on it…". The Host re-seeds only what is still incomplete
(the streaming text, pending interactions) and Main's transcript overlay is
bootstrapped once per replica, so the steps that finished while the Session
was on screen existed only in the renderer's live projection — which the
store wiped on every selection and reseed. The projection now survives the
switch (a reseed drops only the incomplete text and thinking it replays; a
Turn that ended meanwhile is retired by the transcript it left behind).
`test:streaming-switch` drives the real app through it with a new fake-backend
scenario that settles a text step and a tool call, then holds the Turn open.

The compatible-change declaration `base64-length-allocation.json` is re-pinned
from 143 to the epoch this branch carries (147, upstream's own): upstream left
it at the epoch of its commit and its per-commit hook never re-judged it, while
our merge stages it next to the epoch bump. Its reason (Base64 byte counting
in `artifact.ts` / `session-transcript.ts` without observable change) still
holds against the protocol as merged.

Gates: build:test + build:renderer, typecheck, biome lint and format, locale
hygiene, ASF headers, renderer architecture ledger (rewritten with `--write`;
the new range store's `window` local reads as environment capabilities to the
checker), e2e budget, third-party notices, knip (same findings as before the
merge), desktop dist tests (1599), renderer state (282), Electron smoke (44
checks, no renderer errors), core-dialogue smoke, streaming-switch smoke. `packages/storage`
`workspace-identity` (git worktree ENOTEMPTY) is a parallel-run flake that
passes in isolation, as is `packages/eval` `lifecycle-boundaries` (relay
cancellation timing); `packages/runtime` `model-adapter-onerror` fails on this
machine before and after the merge (asynchronous activity after the test
ended; the file is unchanged this round).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/XL Under 2500 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants