diff --git a/mobile/src/agent-history/agent-history-scope-paths.ts b/mobile/src/agent-history/agent-history-scope-paths.ts index 5d08ff2e3cf..da3b98457d2 100644 --- a/mobile/src/agent-history/agent-history-scope-paths.ts +++ b/mobile/src/agent-history/agent-history-scope-paths.ts @@ -3,13 +3,10 @@ import { normalizeRuntimePathForComparison } from '../../../src/shared/cross-platform-path' import type { Worktree } from '../worktree/workspace-list-types' -import type { AiVaultScope } from '../../../src/shared/ai-vault-types' - -// Why: the aiVault.listSessions RPC rejects (does not truncate) scopePaths past -// AI_VAULT_SCOPE_PATHS_MAX_COUNT, so a repo with many sibling worktrees must cap -// here or the whole project-scope load hard-fails. scopePaths only widen -// discovery, so a truncated list omits some siblings instead of erroring. -const MOBILE_AI_VAULT_SCOPE_PATHS_MAX = 64 +import { + AI_VAULT_SCOPE_PATHS_MAX_COUNT, + type AiVaultScope +} from '../../../src/shared/ai-vault-types' // Why: the renderer's deriveAiVault* helpers are renderer-located and // Metro-unresolvable, so mobile does its own minimal derivation seeded by the @@ -34,7 +31,9 @@ export function deriveMobileAiVaultScopePaths( // covers same-repo sibling worktrees so the project view stays complete. if (scope === 'project') { for (const worktree of liveWorktrees) { - if (paths.length >= MOBILE_AI_VAULT_SCOPE_PATHS_MAX) { + // Why: the RPC rejects (does not truncate) oversized scopePaths, and the + // list only widens discovery — dropping tail siblings beats hard-failing. + if (paths.length >= AI_VAULT_SCOPE_PATHS_MAX_COUNT) { break } if (worktree.repoId === activeWorktree.repoId) { diff --git a/src/main/runtime/rpc/methods/ai-vault.ts b/src/main/runtime/rpc/methods/ai-vault.ts index 7c80dcb1b17..37a73a8f93f 100644 --- a/src/main/runtime/rpc/methods/ai-vault.ts +++ b/src/main/runtime/rpc/methods/ai-vault.ts @@ -1,12 +1,12 @@ import { z } from 'zod' import { defineMethod, type RpcMethod } from '../core' import { OptionalBoolean } from '../schemas' +import { AI_VAULT_SCOPE_PATHS_MAX_COUNT } from '../../../../shared/ai-vault-types' // Why: bound limit + scopePaths so a client cannot force an unbounded scan. // Each scopePath is a host-local match prefix (validated/capped, never used for // traversal); the count/length caps mirror the worktree-schemas bounding style. const AI_VAULT_SCOPE_PATH_MAX_LENGTH = 4096 -const AI_VAULT_SCOPE_PATHS_MAX_COUNT = 64 const AI_VAULT_LIMIT_MAX = 2000 export const AiVaultListSessionsParams = z.object({ diff --git a/src/shared/ai-vault-types.ts b/src/shared/ai-vault-types.ts index 1dddff36318..4309b68581e 100644 --- a/src/shared/ai-vault-types.ts +++ b/src/shared/ai-vault-types.ts @@ -23,6 +23,10 @@ export const AI_VAULT_AGENTS = [ 'kimi' ] as const satisfies readonly TuiAgent[] +// Why: the aiVault.listSessions RPC schema REJECTS (not truncates) requests +// past this bound, so every scopePaths producer must cap against the same value. +export const AI_VAULT_SCOPE_PATHS_MAX_COUNT = 64 + export type AiVaultAgent = (typeof AI_VAULT_AGENTS)[number] export type AiVaultScope = 'workspace' | 'project' | 'all' export type AiVaultSort = 'updated' | 'created'