Skip to content

release: v6.9.1 — GLM 5.3 Flash fix, resume from any directory, UI and install fixes - #58

Merged
code-crusher merged 8 commits into
mainfrom
release/6.9.1
Sep 30, 2026
Merged

code-crusher merged 8 commits into
mainfrom
release/6.9.1

Conversation

@code-crusher

@code-crusher code-crusher commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

Summary

  1. Fixes GLM 5.3 Flash reverting to DeepSeek 4.1 right after it's selected.
  2. /resume can browse and continue sessions from any directory.
  3. No more empty "●" rows when a model streams whitespace-only content before a tool call.
  4. No deprecation warnings on install/update: openai 4 → 7.

1. GLM 5.3 Flash reverting

Cause: zai/glm-5.3-flash is the static DEFAULT_MODEL_ID. The plan-aware default added in 6.8.6 replaced any selection equal to DEFAULT_MODEL_ID with the catalog's first model, deepseek/deepseek-v4.1-flash. So explicitly picking GLM 5.3 Flash was indistinguishable from never picking a model. Other models have different ids and were unaffected. This isn't plan- or OS-specific: the catalog flags no freePlan model, so every plan resolves to catalog index 0.

Fix: a persisted modelExplicit flag in config.json.

  • It is set by /model and picker selections, a model in settings.json, and MATTERAI_MODEL.
  • It is cleared by automatic switches: the plan default and the plan-access fallbacks.
  • The plan-aware default now only replaces a model that was never explicitly chosen, in both the TUI (App.tsx) and headless mode.

Known limitation: configs saved by earlier versions have no flag. A stored GLM 5.3 Flash is swapped one more time on the first launch after updating, and sticks once the user picks it again.

2. Resume from any directory

/resume (and orbcode --resume) only listed sessions whose cwd matched the current directory, so a conversation started in another project was hard to find.

  • The picker lists this directory's sessions first. Tab switches to sessions from all directories, each labelled with its (shortened) directory. When there's nothing to resume here, it opens on all directories.

  • Resuming a session from another directory chdirs into that session's directory, then reloads:

    • project settings,
    • MCP servers (the old manager is stopped and a new one started),
    • the project hook-trust prompt.

    Tools, AGENTS.md and relative paths then match the conversation. The header and status bar show the new directory.

  • If the session's directory no longer exists, an error is shown and nothing changes.

  • listSessions(cwd?) lists every directory when cwd is omitted.

  • Other pickers are unchanged: /task doesn't pass allSessions.

3. Empty "●" rows

Models often stream whitespace-only content (e.g. "\n\n") right before a tool call, which rendered as a bare ● between "Thought for …" and the tool row. Whitespace-only content is now dropped from:

  • the streaming preview and the committed transcript row (App.tsx),
  • the saved transcript and the assistant message sent back to the model (agent.ts, content: null).

Blank rows in already-saved sessions render as nothing and take no height (rows.tsx, rowHeight).

4. Deprecation warnings on install/update

  • DEP0040 punycode: openai@4 → node-fetch@2 → whatwg-url@5 → tr46 requires Node's built-in punycode.
  • npm warn deprecated node-domexception: openai@4 → formdata-node@4 → node-domexception.

openai@7.25.0 has no dependencies and uses native fetch. Only src/api/client.ts uses the SDK at runtime; the other imports are type-only, and the typecheck passed with no code changes.

Verified:

  • Gateway: live streaming against the gateway works (glm-5.3-flash, deepseek-v4.1-flash): text, usage and cached-token accounting.
  • Tool calls: 3 benchmark tasks with tool calls pass, including the CRLF edit task.
  • Clean install: npm pack + npm install -g into a temp prefix gives 180 packages and no npm warnings. 6.9.0 installs 203 and prints the node-domexception warning.
  • No punycode users left: no file in the new install loads the built-in punycode. 6.9.0 has two, tr46 and whatwg-url.
  • Update path: running orbcode update from a temp-prefix install of 6.8.7 reproduces the node-domexception warning.

The punycode warning didn't reproduce on Node 24.15. Node only reports it for some callers and versions, so this is confirmed by the absence of any punycode user in the new install. Updating from ≤6.9.0 can still show the warnings once, because the old version runs the update.

npm audit --omit=dev reports 11 vulnerabilities (5 low, 3 moderate, 3 high) both before and after this change, in hono, qs, @xmldom/xmldom, fast-uri, ip-address and @ai-sdk/provider-utils. They are unrelated and not addressed here.

Testing

  • npx tsc -p tsconfig.json --noEmit passes.
  • New tests pass:
    • test/settings-model.test.ts (6),
    • test/sessions.test.ts (1),
    • test/session-picker.test.tsx (3, rendered with OpenTUI under Bun),
    • a blank-row render test in the same file (it fails with the rows.tsx fix reverted),
    • an agent test that streams whitespace plus a tool call and checks the saved transcript and history.
  • Node suite: 82/82. Six tests that looked up retired axon-* catalog entries (and asserted the old default model) were removed; they failed identically on main. The plan-gate and id-helper tests for those model families are kept.
  • npm run test:ui (Bun): 16/16. The UI tests only fail under node --test because OpenTUI's native renderer needs Bun; test:ui now includes the picker test.
  • Reproduced through the headless selection path with the config model set to zai/glm-5.3-flash:
    • on main, the session ran on deepseek/deepseek-v4.1-flash;
    • on this branch after a pick, it ran on zai/glm-5.3-flash.
  • Not yet tested manually in the interactive TUI: the model pick surviving a restart, and resuming a session from another directory (the directory switch + MCP rebuild path is not covered by automated tests).

Release

Bumps package.json / package-lock.json to 6.9.1 and moves the CHANGELOG entry to [6.9.1] - 2026-09-30. After merge: git checkout main && git pull && git tag v6.9.1 && git push origin v6.9.1.

The plan-aware default replaced any selection equal to DEFAULT_MODEL_ID
(zai/glm-5.3-flash), so explicitly picking that model was treated as
"never chosen" and swapped for the catalog's first model. Persist a
modelExplicit flag (set by user picks, settings.json and MATTERAI_MODEL;
cleared by automatic defaults and plan fallbacks) and only re-resolve a
model that was never explicitly chosen, in the TUI and headless mode.
@matterai-app

matterai-app Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

✅ Reviewed the changes: Release v6.9.1 adds a modelExplicit flag so the plan-aware default only replaces a model the user never chose. The implementation is correct across all layers: settings.json/env inference, invalid-model reset, save/load round-trip (false is dropped via || undefined), headless gating, and TUI auto-switches (auto: true) correctly excluded from persistence. Effect dependency arrays were properly updated (including settings.modelExplicit), and the new tests cover the key scenarios. Reviewed src/config/settings.ts: no issues found. Reviewed src/ui/App.tsx: no issues found. Reviewed test/settings-model.test.ts: no issues found.

/resume only listed sessions whose cwd matched the current directory,
so a conversation started elsewhere was hard to find. The picker now
has a Tab toggle between this directory and all directories (showing
where each other-directory session lives), and opens on all
directories when there is nothing to resume here. Resuming a session
from another directory switches the working directory to it and
rebuilds the directory-bound state (project settings, MCP servers,
project hook trust), so tools and AGENTS.md match the conversation.
@code-crusher code-crusher changed the title release: v6.9.1 release: v6.9.1 — GLM 5.3 Flash selection fix, resume from any directory Sep 30, 2026
@matterai-app

matterai-app Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Code Quality bug fix type

Summary By MatterAI <a href="https://app.matterai.so" target=""_blank"> MatterAI logo

🔄 What Changed

  • Released version 6.9.1, bumping openai dependency to ^7.25.0 in package.json.
  • Integrated core fix for GLM 5.3 Flash selection state overriding through persisted explicit selection flags.

🔍 Impact of the Change

  • Ensures dependency parity and secures the updated versionbump release cycle.
  • Eliminates model resetting bugs, preserving explicit user choices like GLM 5.3 Flash across sessions.

📁 Total Files Changed

Click to Expand
File ChangeLog
Package Config
package.json
Bumped openai dependency version to ^7.25.0 and package version to 6.9.1.

🧪 Test Added/Recommended

Added

  • test/settings-model.test.ts (6 tests validating model selection persistence).

🔒Security Vulnerabilities

  • None detected.

⏳ Estimated code review effort

LOW (~7 minutes)

Tip

Quality Recommendations

  1. Ensure lockfile is updated properly alongside package.json dependency bumps.

♫ Tanka Poem

Model choice held fast,
GLM shines bright and clear now,
No more silent swaps,
Version marks the path ahead,
Steady builds of code we trust.

Sequence Diagram

sequenceDiagram
    participant User
    participant TUI as App / Headless
    participant Config as config.json
    participant Catalog as Model Catalog
    
    User->>TUI: Select model (e.g., GLM 5.3 Flash)
    TUI->>Config: Save model & set modelExplicit = true
    
    Note over TUI,Config: Subsequent Launch
    
    TUI->>Config: Read model & modelExplicit
    alt modelExplicit is true
        TUI->>Config: Keep user's chosen model
    else modelExplicit is false
        TUI->>Catalog: Fallback to default plan model
        Catalog-->>TUI: Default model
    end
Loading

The Axon Auto, Eido 3.2 and Lumen 4 entries were removed from the model
catalog, so the tests that looked them up in BUILTIN_AXON_MODELS (and
asserted the old default) always failed. The plan-gate and id-helper
tests for those families still pass and are kept.

@matterai-app matterai-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧪 PR Review is completed: Solid implementation of cross-directory session resume with proper MCP/settings/hook reloading on chdir. Main concerns: an unhandled promise rejection on MCP restart, a doubled full-session-directory read in /resume, and Windows path handling in shortDir. Reviewed test/session-picker.test.tsx, test/sessions.test.ts, src/core/sessions.ts, and package.json: no issues found.

Skipped files
  • CHANGELOG.md: Skipped file pattern
⬇️ Low Priority Suggestions (3)
src/ui/App.tsx (2 suggestions)

Location: src/ui/App.tsx (Lines 913-916)

🟠 Error Handling

Issue: The new MCP manager restart after process.chdir fires manager.start().then(...) with no .catch(). If start() rejects (e.g. malformed MCP config in the resumed directory, connection setup failure), this becomes an unhandled promise rejection, which crashes the Node process by default — killing the whole TUI right after a resume. The sibling oldManager?.stop() call correctly has .catch(() => {}), so this looks like an omission.

Fix: Attach a .catch(() => {}) to the start chain, mirroring the stop handling.

Impact: A failing MCP setup in the resumed directory degrades gracefully instead of crashing the app mid-resume.

-            void manager.start().then(() => {
-              const pendingMcp = manager.getPendingApproval();
-              if (pendingMcp.length > 0) setPendingMcpApproval(pendingMcp);
-            });
+            void manager.start().then(() => {
+              const pendingMcp = manager.getPendingApproval();
+              if (pendingMcp.length > 0) setPendingMcpApproval(pendingMcp);
+            }).catch(() => {});

Location: src/ui/App.tsx (Lines 1203-1205)

🟡 Performance

Issue: /resume now calls listSessions(process.cwd()) and listSessions() back to back. Each call re-reads and JSON-parses every session file in the sessions directory — and session files contain full message transcripts, so they can be large. This doubles the disk I/O and parse work on every /resume invocation.

Fix: Read the directory once and derive the here list by filtering on cwd. Note the small tradeoff: here becomes the local sessions within the global top-25 cap rather than an independently capped top-25; given the picker shows 8 rows, this is negligible.

Impact: Halves the I/O and JSON parsing cost of opening the resume picker for users with many/large sessions.

-            const notCurrent = (s: SessionData) => s.id !== agentRef.current?.taskId;
-            const here = listSessions(process.cwd()).filter(notCurrent);
-            const everywhere = listSessions().filter(notCurrent);
+            const notCurrent = (s: SessionData) => s.id !== agentRef.current?.taskId;
+            const everywhere = listSessions().filter(notCurrent);
+            const here = everywhere.filter((s) => s.cwd === process.cwd());
src/ui/components/SessionPicker.tsx (1 suggestion)

Location: src/ui/components/SessionPicker.tsx (Lines 27-30)

🔵 Portability

Issue: shortDir hardcodes / as the path separator (dir.startsWith(home + "/"), withHome.split("/")). On Windows, where paths use \, the home-directory ~ substitution never matches and split("/") yields a single segment, so users see full unshortened C:\Users\... paths in the picker.

Fix: Detect the separator from the input path and use it consistently for the home prefix check, splitting, and the ellipsis join.

Impact: Directory labels shorten correctly on Windows while remaining unchanged on POSIX.

-  	const home = os.homedir()
-  	const withHome = dir === home || dir.startsWith(home + "/") ? "~" + dir.slice(home.length) : dir
-  	const parts = withHome.split("/")
-  	return parts.length > 4 ? "…/" + parts.slice(-3).join("/") : withHome
+  	const home = os.homedir()
+  	const sep = dir.includes("\\") ? "\\" : "/"
+  	const withHome = dir === home || dir.startsWith(home + sep) ? "~" + dir.slice(home.length) : dir
+  	const parts = withHome.split(sep)
+  	return parts.length > 4 ? "…" + sep + parts.slice(-3).join(sep) : withHome

@matterai-app

matterai-app Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

✅ Reviewed the changes: Deletion-only cleanup of stale Axon registry tests (DEFAULT_MODEL_ID pin, BUILTIN_AXON_MODELS metadata lookups) that no longer match the live registry; the remaining predicate/plan-gate tests are registry-independent and all retained imports are still used — no issues found in the new code. Note: the changed files contain only these two test deletions; the modelExplicit fix, the v6.9.1 version bump, and the new test/settings-model.test.ts described in the PR body are not among the changed files — worth confirming nothing was left out of the PR before tagging the release.
Reviewed test/axon-auto-models.test.ts: no issues found.
Reviewed test/models.test.ts: no issues found.

@matterai-app matterai-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧪 PR Review is completed: Trivial one-line UI tweak (hint text color dim → info) in SessionPicker; the only concern is that COLORS.info does not appear to be defined in the branding module.

⬇️ Low Priority Suggestions (1)
src/ui/components/SessionPicker.tsx (1 suggestion)

Location: src/ui/components/SessionPicker.tsx (Lines 121-121)

🟡 Type Safety / Runtime Bug Risk

Issue: The new line references COLORS.info, but a search of src/branding.ts shows no info key defined in the COLORS object (only primary, dim, accent, etc. are used elsewhere in this file). If COLORS is loosely typed (e.g. Record<string, string>), COLORS.info resolves to undefined at runtime and Ink silently falls back to the default color — making this change a visual no-op. If COLORS is strictly typed, this is a compile error.

Fix: Use a nullish fallback to an existing color (or add an info entry to COLORS in src/branding.ts if a distinct info color is intended).

Impact: Guarantees the hint line actually renders with the intended color instead of silently reverting to the default.

-  			<Text color={COLORS.info}>
+  			<Text color={COLORS.info ?? COLORS.accent}>

@code-crusher code-crusher changed the title release: v6.9.1 — GLM 5.3 Flash selection fix, resume from any directory release: v6.9.1 — GLM 5.3 Flash selection fix, resume from any directory, empty ● rows Sep 30, 2026
@matterai-app

matterai-app Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Note

PR Review Skipped

PR review skipped as no relevant changes found due to large diff hunk OR part of a non-reviewable file.

📄Files skipped in review
  • undefined: undefined
  • undefined: undefined
  • undefined: undefined
  • undefined: undefined
  • undefined: undefined
  • undefined: undefined
  • undefined: undefined
  • undefined: undefined
  • undefined: undefined
  • undefined: undefined
  • undefined: undefined
  • undefined: undefined
  • undefined: undefined
  • undefined: undefined
  • undefined: undefined
  • undefined: undefined
💡Tips to use MatterAI

Command List

  • /matter summary: Generate AI Summary for the PR
  • /matter review: Generate AI Reviews for the latest commit in the PR
  • /matter review-full: Generate AI Reviews for the complete PR
  • /matter release-notes: Generate AI release-notes for the PR
  • /matter : Chat with your PR with MatterAI Agent
  • /matter remember : Generate AI memories for the PR
  • /matter explain: Get an explanation of the PR
  • /matter help: Show the list of available commands and documentation
  • Need help? Join our Discord server: https://discord.gg/fJU5DvanU3

@matterai-app matterai-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧪 PR Review is completed: Whitespace-filtering changes in agent.ts/App.tsx/rows.tsx are consistent and well-tested, but the assistant message construction can now emit a content: null assistant message with no tool_calls, which some OpenAI-compatible APIs reject. Reviewed src/ui/App.tsx, src/ui/components/rows.tsx, test/agent-context.test.ts, test/session-picker.test.tsx: no other issues found. Note: the modelExplicit flag described in the PR body does not appear in the provided changed files (grep finds no modelExplicit; App.tsx:998 still gates on settings.model !== DEFAULT_MODEL_ID) — please confirm those changes are actually part of this PR.

Skipped files
  • CHANGELOG.md: Skipped file pattern
⬇️ Low Priority Suggestions (1)
src/core/agent.ts (1 suggestion)

Location: src/core/agent.ts (Lines 1341-1341)

🟡 API Compatibility / Regression Risk

Issue: The old code (content: assistantText || null) kept whitespace-only text as a string. The new code nulls it unconditionally, so when a model streams whitespace-only content with no tool calls (e.g. an empty/aborted completion), the assistant message is pushed as { role: "assistant", content: null } with no tool_calls. Several OpenAI-compatible providers reject assistant messages that have neither content nor tool_calls ("Invalid value for 'content': expected a string, received null"), breaking the next turn with a 400. The new test only covers the whitespace + tool-call path, where content: null is valid.

Fix: Only collapse to null when tool calls are present; otherwise keep the original (whitespace) string, matching the previous API-safe behavior.

Impact: Prevents a potential hard API failure on whitespace-only, tool-call-free completions while preserving the intended null content for tool-call turns.

-  			content: assistantText.trim() ? assistantText : null,
+  			content: assistantText.trim() ? assistantText : toolCalls.length > 0 ? null : assistantText,

openai@4 depended on node-fetch@2 (whatwg-url -> tr46 loads the
deprecated built-in punycode, DEP0040) and formdata-node@4 (deprecated
node-domexception, warned on every install/update). openai@7 has no
dependencies and uses native fetch. Only the gateway client uses the
SDK at runtime; no code changes were needed.
@code-crusher code-crusher changed the title release: v6.9.1 — GLM 5.3 Flash selection fix, resume from any directory, empty ● rows release: v6.9.1 — GLM 5.3 Flash fix, resume from any directory, UI and install fixes Sep 30, 2026

@matterai-app matterai-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧪 PR Review is completed: The only change visible in this diff is the addition of the openai dependency to package.json, which is not mentioned anywhere in the PR description (the described modelExplicit fix, version bump, and CHANGELOG changes are not present in the provided diff). Flagging the undocumented dependency for confirmation.

Skipped files
  • CHANGELOG.md: Skipped file pattern
  • package-lock.json: Skipped file pattern
⬇️ Low Priority Suggestions (1)
package.json (1 suggestion)

Location: package.json (Lines 55-55)

🟠 Undocumented / Possibly Unused Dependency

Issue: This PR adds "openai": "^7.25.0" as a dependency, but the PR description — which meticulously documents the model-selection fix, version bump, CHANGELOG move, and test additions — makes no mention of an OpenAI SDK integration. The described fix (a persisted modelExplicit flag) requires no OpenAI SDK, and the repo's OpenAI-compatible gateway support goes through the AI SDK layer (usesAiSdk), which would normally pull @ai-sdk/openai, not the official openai package.

Fix: Confirm this dependency is actually imported somewhere in src/ and belongs in this release. If it was added accidentally (or left over from a local experiment), remove it — an unused direct dependency ships unnecessary install weight and an extra supply-chain surface in a tagged release.

Impact: Keeps the v6.9.1 release artifact clean and the PR description accurate; avoids shipping an unexplained third-party SDK to every user.

-      "openai": "^7.25.0",
+  

@code-crusher
code-crusher merged commit 82fb848 into main Sep 30, 2026
1 check passed
@code-crusher
code-crusher deleted the release/6.9.1 branch September 30, 2026 03:52
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.

1 participant