From e7a4aa1377c650defd4a7d6bc2736b84f2a3b075 Mon Sep 17 00:00:00 2001 From: Jinwoo-H Date: Mon, 7 Sep 2026 14:36:06 -0400 Subject: [PATCH] fix(mobile): accept only a full digest shape as an already-digested tab id --- mobile/src/cache/session-tab-strip-cache.test.ts | 13 +++++++++++++ mobile/src/cache/session-tab-strip-cache.ts | 4 +++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/mobile/src/cache/session-tab-strip-cache.test.ts b/mobile/src/cache/session-tab-strip-cache.test.ts index ea167f4b666..d7815a912c0 100644 --- a/mobile/src/cache/session-tab-strip-cache.test.ts +++ b/mobile/src/cache/session-tab-strip-cache.test.ts @@ -221,6 +221,19 @@ describe('session tab strip cache', () => { expect(loaded?.activeTabId).toBe(digested) }) + it('digests a wire id that merely starts with the digest prefix', async () => { + const key = getSessionTabStripCacheKey('host-1', 'wt-1') + const id = 'cached:/Users/someone/private-file.md' + saveCachedSessionTabStrip(key, { + tabs: [{ id, type: 'file', title: 'x', agentId: null }], + activeTabId: id + }) + await vi.advanceTimersByTimeAsync(300) + + expectDigestedIds(readCachedSessionTabStrip(key)?.tabs.map((tab) => tab.id) ?? [], 1) + expect(String(asyncStorage.setItem.mock.calls.at(-1)?.[1])).not.toContain('private-file') + }) + it('keeps only a known agent id, since the hook-reported one is free text', async () => { const key = getSessionTabStripCacheKey('host-1', 'wt-1') saveCachedSessionTabStrip(key, { diff --git a/mobile/src/cache/session-tab-strip-cache.ts b/mobile/src/cache/session-tab-strip-cache.ts index 7ff6d2d6c0b..6ceccabe80b 100644 --- a/mobile/src/cache/session-tab-strip-cache.ts +++ b/mobile/src/cache/session-tab-strip-cache.ts @@ -27,6 +27,8 @@ const WRITE_DEBOUNCE_MS = 250 // the stored blob stays small. const WORKSPACE_DIGEST_LENGTH = 32 const TAB_DIGEST_PREFIX = 'cached:' +// The whole shape, not the prefix: a wire id that merely starts with the prefix is still wire text. +const DIGESTED_TAB_ID = /^cached:[0-9a-f]{32}$/ type StoredWorkspace = { key: string; preview: MobileSessionTabStripPreview } type StoredFile = { workspaces: StoredWorkspace[] } @@ -150,7 +152,7 @@ function digestWorkspaceId(worktreeId: string): string { // Prefixed so a raw id can never be mistaken for one already digested, and so a live tab's // id can never collide with a stored row's by construction. function digestTabId(tabId: string): string { - return tabId.startsWith(TAB_DIGEST_PREFIX) ? tabId : `${TAB_DIGEST_PREFIX}${digestHex(tabId)}` + return DIGESTED_TAB_ID.test(tabId) ? tabId : `${TAB_DIGEST_PREFIX}${digestHex(tabId)}` } function digestHex(value: string): string {