Skip to content

fix: sync tasks-index format and 3.3.0 protocol for ZCode App session visibility#11

Merged
william0wang merged 1 commit into
mainfrom
chore/sync-v3.3.0
Jul 8, 2026
Merged

fix: sync tasks-index format and 3.3.0 protocol for ZCode App session visibility#11
william0wang merged 1 commit into
mainfrom
chore/sync-v3.3.0

Conversation

@william0wang

Copy link
Copy Markdown
Owner

Summary

Bridge-created sessions were invisible in the ZCode App sidebar, and several extension methods diverged from the ZCode 3.3.0 app-server protocol. This PR fixes the tasks-index sync layer and aligns the bridge with 3.3.0's schema.

Changes

tasks-index session sync (root cause of sidebar invisibility)

  • node:sqlite API fix (tasks-index.ts): the original code used Python-style con.run(sql, params) / con.get(sql, params), but Node's DatabaseSync only exposes con.prepare(sql).run(...) / .get(...). The wrong API threw TypeError, which the try/catch silently swallowed — every session sync was a silent no-op. Fixed to the prepared-statement pattern.
  • model format alignment: bridge wrote model = "GLM-5.2" (bare id), but the App stores "builtin:bigmodel-coding-plan/GLM-5.2" (full providerKey/modelId path). The App's sidebar filters/grouping excluded rows with the bare format. resolveProviderModel() now reads the provider key from config.json and returns the full path.
  • searchable_text population: bridge left searchable_text empty. The App uses this column for full-text search (built via buildSearchableTextFromMessages, capped at 200k chars). updateSessionTitle() now accepts a searchableText param; the first user prompt is passed as the initial value.

3.3.0 protocol alignment

  • session/fork response field: 3.3.0 returns forkedSessionId, not sessionId. The bridge was reading the wrong field, so forked sessions were never registered in sessionMap — any subsequent ACP call targeting the fork failed to resolve. Fixed in both extensions.ts and slash.ts.
  • auto mode: 3.3.0's mode enum is ["plan","build","edit","yolo","auto"] (5 values); the bridge advertised only 4. settings.mode carries only current (no available list), so the full enum is hardcoded. Added "auto" to buildModes() and CONFIG_META.
  • session/setThoughtLevel: 3.3.0 marks thoughtLevel as optional (omitting resets to the model default). The bridge threw if it was absent. Now forwards it only when present.

Session title

  • Title is now the first non-empty line of the prompt (split on \r\n / \n / \r), truncated to 80 chars. Previously text.slice(0, 80) could include newlines from multi-line prompts.

Testing

  • 135 tests pass (10 files), including 9 new regression tests in tests/tasks-index.test.ts covering: correct prepared-statement API usage, model format (builtin:bigmodel-coding-plan/GLM-5.2), INSERT OR IGNORE idempotency, searchable_text population + 200k cap, title_overridden respect, and the override-branch searchable_text refresh.
  • TypeScript type-check passes.
  • Tests use vi.mock("node:sqlite") with an in-memory fake DB — no dependency on ~/.zcode/ or real sqlite, CI-stable.

@william0wang william0wang merged commit 7f7fc38 into main Jul 8, 2026
1 check passed
@william0wang william0wang deleted the chore/sync-v3.3.0 branch July 8, 2026 09:45

@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 updates the client to support ZCode 3.3.0, including adding the 'auto' mode, making thoughtLevel optional, and updating the session fork handler to use forkedSessionId. It also fixes a critical bug in tasks-index.ts where node:sqlite's DatabaseSync was incorrectly called without preparing statements, and adds comprehensive regression tests. The reviewer feedback highlights that returning only forkedSessionId from fork breaks compatibility with standard ACP clients expecting sessionId, and suggests mapping it back. Additionally, the reviewer recommends optimizing the title extraction logic in session.ts using a regular expression instead of splitting potentially large prompt strings.

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.

Comment on lines +56 to 63
// 3.3.0 returns `forkedSessionId` (not `sessionId`) for the new session id.
// Register it so subsequent ACP calls targeting the fork can resolve the sid.
const result = (resp.result ?? {}) as { forkedSessionId?: string };
if (result.forkedSessionId) {
server.sessionMap.set(result.forkedSessionId, result.forkedSessionId);
}
log(`session/fork → ${result.forkedSessionId ?? "?"}`);
return result;

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 ACP specification for session/fork expects the response to contain sessionId for the newly created session. Returning only forkedSessionId from ZCode 3.3.0 will break compatibility with standard ACP clients (such as the Zed editor) that look for sessionId. We should map forkedSessionId to sessionId in the returned object to ensure compatibility with both the ACP client and the ZCode backend.

  const result = (resp.result ?? {}) as { forkedSessionId?: string; sessionId?: string };
  const forkedSessionId = result.forkedSessionId ?? result.sessionId;
  if (forkedSessionId) {
    server.sessionMap.set(forkedSessionId, forkedSessionId);
  }
  log(`session/fork → ${forkedSessionId ?? "?"}`);
  return {
    ...result,
    sessionId: forkedSessionId,
  };

Comment thread src/handlers/slash.ts
Comment on lines +88 to +91
const result = (await fork(server, { sessionId: acpSid })) as {
forkedSessionId?: string;
};
return ok(`✓ forked new session: ${result.forkedSessionId ?? "?"}`);

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

Align the slash command handler to use the mapped sessionId from the fork response, falling back to forkedSessionId if necessary.

        const result = (await fork(server, { sessionId: acpSid })) as {
          forkedSessionId?: string;
          sessionId?: string;
        };
        return ok(`✓ forked new session: ${result.sessionId ?? result.forkedSessionId ?? "?"}`);

Comment thread src/handlers/session.ts
Comment on lines +361 to +366
const title =
text
.split(/\r\n|\r|\n/)
.map((l) => l.trim())
.find((l) => l.length > 0)
?.slice(0, 80) ?? text.slice(0, 80);

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

Splitting and mapping the entire prompt text by newlines to find the first non-empty line is highly inefficient and can cause performance bottlenecks or out-of-memory issues when handling very large prompts (e.g., pasted code or logs). Using a regular expression to match the first non-empty line is much more efficient as it avoids scanning or allocating arrays for the entire string.

      const firstLine = text.match(/^[ \t]*(\S[^\r\n]*)/m)?.[1];
      const title = (firstLine ?? text).trim().slice(0, 80);

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