Skip to content

LAC-2337: Lash Upstream Sync — 2026-06-08 (303 commits)#27

Merged
lacymorrow merged 305 commits into
devfrom
LAC-2336/upstream-sync-2026-06-08
Jun 16, 2026
Merged

LAC-2337: Lash Upstream Sync — 2026-06-08 (303 commits)#27
lacymorrow merged 305 commits into
devfrom
LAC-2336/upstream-sync-2026-06-08

Conversation

@lacymorrow

Copy link
Copy Markdown
Owner

Summary

Weekly upstream sync from anomalyco/opencode dev through commit 0050134d9e fix(session): merge per-call tool rules into session permission (#30529).

  • Merges 303 upstream commits (f0b78f0a38).
  • Resolves 11 conflicts per the UPSTREAM_SYNC.md playbook.
  • Preserves the lash invariants:
    • agent_cycle = shift+tab (TUI keybind config)
    • EventCwdUpdated in SDK Event + GlobalEvent unions
    • getCwd() / setCwd() + cwd sentinel in shell-mode + session/prompt.ts
    • Double-left-border row layout in prompt/index.tsx
    • ExecutionModeProvider + WorkingDirProvider wrap <App /> in app.tsx
  • Adapts to upstream's TUI extraction (commit 106f8e94d6 refactor(tui): extract standalone package (#31193)):
    • Conflict files at the old packages/opencode/src/cli/cmd/tui/* paths are now at packages/tui/src/*. The playbook's invariants were applied at the new locations.
  • Post-merge typecheck fixes (commit cd25d03f50):
    • which() import path moved → @opencode-ai/core/util/which
    • EventCwdUpdated added to the GlobalEvent payload union
    • @/, @shell-mode, @tui-integration path aliases added to packages/tui/tsconfig.json and packages/cli/tsconfig.json
    • Wasm module declaration added for the transitive @silvia-odwyer/photon-node import
    • ModelID removed upstream → switched to ModelV2.ID.make() in prompt-variant.test.ts

Stacked on PR #26

This PR targets LAC-2277/upstream-sync-2026-06-01 (PR #26, prior weekly sync 2026-06-01 — 256 commits) which is still open. Once #26 merges, this branch will be rebased onto dev and the base updated.

Verification

  • bun turbo typecheck — 23/23 packages successful (no errors)

Links

Test plan

opencode-agent Bot and others added 30 commits June 2, 2026 18:02
Co-authored-by: starptech <starptech@starptechs-MBP.fritz.box>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a major update to support WSL (Windows Subsystem for Linux) servers, multi-server management, and scoped state keys to isolate sessions and workspaces by server. It also implements a new V2 layout design with redesigned settings panels, tab navigation, and updated CLI daemon management. The code review identified critical bugs, including potential TypeError crashes due to incorrect optional chaining syntax on unresolved query data in the WSL components and server sync logic. Additionally, feedback was provided to add error handling to a remote file-reading promise chain and to remove a leftover debug console.log statement.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

<For each={props.servers()}>
{(item) => {
const key = ServerConnection.Key.make(item.config.id)
const check = () => wsl.data?.opencodeChecks[item.config.distro]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The optional chaining expression wsl.data?.opencodeChecks[item.config.distro] will throw a TypeError if wsl.data is undefined (which is the case before the query resolves). In JavaScript, a?.b[c] is evaluated as (a?.b)[c], meaning it attempts to access the property on undefined.

Use optional chaining for the bracket notation as well to prevent runtime crashes.

Suggested change
const check = () => wsl.data?.opencodeChecks[item.config.distro]
const check = () => wsl.data?.opencodeChecks?.[item.config.distro]

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Thanks Gemini. This file is unmodified from upstream (anomalyco/opencode); the pattern flagged predates this sync PR. Lash is a downstream fork — fixes to upstream code should land at https://github.com/anomalyco/opencode/ to avoid divergence that complicates future syncs. Tracking for upstream report.

const selectedProbe = createMemo(() => {
const distro = selectedDistro()
if (!distro) return null
return current()?.distroProbes[distro] ?? null

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The optional chaining expression current()?.distroProbes[distro] will throw a TypeError when current() is undefined (before the query resolves). In JavaScript, a?.b[c] is evaluated as (a?.b)[c], which results in accessing a property on undefined.

Use optional chaining for the bracket notation to prevent runtime crashes.

Suggested change
return current()?.distroProbes[distro] ?? null
return current()?.distroProbes?.[distro] ?? null

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Thanks Gemini. This file is unmodified from upstream (anomalyco/opencode); the pattern flagged predates this sync PR. Lash is a downstream fork — fixes to upstream code should land at https://github.com/anomalyco/opencode/ to avoid divergence that complicates future syncs. Tracking for upstream report.

const opencodeCheck = createMemo(() => {
const distro = selectedDistro()
if (!distro) return null
return current()?.opencodeChecks[distro] ?? null

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The optional chaining expression current()?.opencodeChecks[distro] will throw a TypeError when current() is undefined (before the query resolves). In JavaScript, a?.b[c] is evaluated as (a?.b)[c], which results in accessing a property on undefined.

Use optional chaining for the bracket notation to prevent runtime crashes.

Suggested change
return current()?.opencodeChecks[distro] ?? null
return current()?.opencodeChecks?.[distro] ?? null

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Thanks Gemini. This file is unmodified from upstream (anomalyco/opencode); the pattern flagged predates this sync PR. Lash is a downstream fork — fixes to upstream code should land at https://github.com/anomalyco/opencode/ to avoid divergence that complicates future syncs. Tracking for upstream report.

toggle: async (directory: string, name: string) => {
const key = directoryKey(directory)
const sdk = sdkFor(key)
const status = children.child(key, { bootstrap: false })[0].mcp[name].status

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Accessing children.child(key, { bootstrap: false })[0].mcp[name].status directly can throw a TypeError if the MCP server with the given name is not yet loaded or is missing from the store.

Safely access the status using optional chaining and handle the case where it might be undefined.

        const status = children.child(key, { bootstrap: false })[0].mcp[name]?.status
        if (!status) return

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Thanks Gemini. This file is unmodified from upstream (anomalyco/opencode); the pattern flagged predates this sync PR. Lash is a downstream fork — fixes to upstream code should land at https://github.com/anomalyco/opencode/ to avoid divergence that complicates future syncs. Tracking for upstream report.

Comment on lines +493 to +496
void sdk.client.v2.fs
.read({ path })
.then((response) => response.data?.data)
.then((data) => data && addAttachments([serverAttachmentFile(path, data)]))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The promise chain for reading the file content from the remote server lacks error handling. If the network request fails or the server returns an error, it will result in an unhandled promise rejection.

Add a .catch block to handle errors gracefully and display a user-friendly toast notification.

            void sdk.client.v2.fs
              .read({ path })
              .then((response) => response.data?.data)
              .then((data) => data && addAttachments([serverAttachmentFile(path, data)]))
              .catch((error) => {
                showToast({
                  variant: "error",
                  title: language.t("common.requestFailed"),
                  description: error instanceof Error ? error.message : String(error),
                })
              })

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Thanks Gemini. This file is unmodified from upstream (anomalyco/opencode); the pattern flagged predates this sync PR. Lash is a downstream fork — fixes to upstream code should land at https://github.com/anomalyco/opencode/ to avoid divergence that complicates future syncs. Tracking for upstream report.

Comment on lines +785 to +786
console.log({ session: session() })
const project = createMemo(() => projectForSession(session(), serverCtx()?.projects.list() ?? []))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

There is a leftover debug console.log statement in the TabNavItem component. This should be removed to keep the production console clean.

Suggested change
console.log({ session: session() })
const project = createMemo(() => projectForSession(session(), serverCtx()?.projects.list() ?? []))
const project = createMemo(() => projectForSession(session(), serverCtx()?.projects.list() ?? []))

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Same — this debug console.log is upstream code (added in upstream commits 0fc33e2 and 9431356, both #3xxxx-numbered upstream PRs). Will report upstream rather than patch locally to avoid sync drift.

Base automatically changed from LAC-2277/upstream-sync-2026-06-01 to dev June 16, 2026 20:51
Rebased on top of dev after PR #26 (LAC-2281, 2026-06-01 sync) merged.
Many original conflicts auto-resolved because dev already contained 256
of the 303 upstream commits. Remaining conflicts resolved per
UPSTREAM_SYNC.md playbook with all lash invariants preserved:

- agent_cycle = shift+tab in TUI keybind config
- EventCwdUpdated in SDK event union (incl. GlobalEvent.payload)
- getCwd()/setCwd() in shell-mode + prompt.ts cwd sentinel
- Double-left-border row layout in prompt/index.tsx
- ExecutionModeProvider + WorkingDirProvider wrap <App /> in app.tsx
- tabs.tsx removeSessions navigate() hoisted out of produce callback
  (mirrors c04019d pattern for titlebar.tsx)

Post-merge typecheck fixes included:
- which() import path (filesystem services consolidation)
- @/, @shell-mode, @tui-integration aliases in tui + cli tsconfigs
  (after upstream TUI extraction 106f8e9)
- *.wasm module declaration
- ModelID via ModelV2.ID after upstream provider/index.ts removal

Refs: LAC-2337
@lacymorrow lacymorrow force-pushed the LAC-2336/upstream-sync-2026-06-08 branch from cd25d03 to a885a0a Compare June 16, 2026 21:00
@lacymorrow

Copy link
Copy Markdown
Owner Author

Rebased on top of dev after PR #26 (LAC-2281) merged in commit 64998f4.

Force-pushed to a885a0abb6.

Many of the original 11 conflicts auto-resolved because dev already contained 256 of the 303 upstream commits. Re-evaluated remaining conflicts and applied the same resolutions from the prior run (all lash invariants preserved per UPSTREAM_SYNC.md). One regression caught: packages/app/src/context/tabs.tsx came in from upstream with navigate() inside an Immer produce callback (same anti-pattern PR #26 fixed in titlebar.tsx) — hoisted it out following the same pattern.

bun install clean. bun turbo typecheck — 23/23 packages green.

Ready for review.

@lacymorrow lacymorrow merged commit 3c651ba into dev Jun 16, 2026
8 of 15 checks passed
@lacymorrow lacymorrow deleted the LAC-2336/upstream-sync-2026-06-08 branch June 16, 2026 21:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.