Skip to content

feat(agent-core-v2): read and write the v1 session index file - #1616

Closed
sailist wants to merge 1 commit into
MoonshotAI:mainfrom
sailist:feat/session-index
Closed

feat(agent-core-v2): read and write the v1 session index file#1616
sailist wants to merge 1 commit into
MoonshotAI:mainfrom
sailist:feat/session-index

Conversation

@sailist

@sailist sailist commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — the problem is explained below.

Problem

v2 (packages/agent-core-v2) and v1 (packages/agent-core) can share the same homeDir / data directory, but they discovered each other's sessions asymmetrically:

  • v1 writes a global append-only index at <homeDir>/session_index.jsonl (one {sessionId, sessionDir, workDir} line per create/fork). v2 never wrote to it, so sessions created by the v2 engine were invisible to a v1 CLI pointing at the same directory.
  • v2's FileSessionIndex.get() located a session by scanning every workspace bucket (list per workspace until a match) — O(workspaces) per cold point lookup — even though v1's index file usually already knows the answer.

What changed

1. Single definition of the v1 index byte format

Problem: the JSONL parse/validate logic for session_index.jsonl was duplicated inline in workspaceRegistryService and about to be needed in two more places (a writer in sessionLifecycle, a reader in sessionIndex).

What was done:

  • Added packages/agent-core-v2/src/app/sessionIndex/legacySessionIndex.ts: the single definition of the legacy file's scope/key, plus parseLegacySessionIndexLine (tolerant line parsing, same acceptance as v1's parseIndexLine) and validateLegacySessionIndexEntry (v1's read-side rules: absolute sessionDir inside sessionsDir, basename === sessionId; derives the workspaceId from the bucket directory).
  • workspaceRegistryService now imports these instead of keeping its own copy — no behavior change.

2. v2 writes v1-compatible index lines on create/fork

Problem: v2-created sessions were undiscoverable by v1.

What was done:

  • SessionLifecycleService.materializeSession gains a legacyIndex option, set for brand-new sessions (create, and the fork target) but never for resume — matching v1, which only writes on create/fork.
  • The line is appended through AppendLogStore (framing is byte-identical to v1's JSON.stringify(entry) + '\n') and explicitly flushed before create/fork returns, so the line is durable.
  • The append happens at the materialization point (not fork completion) on purpose: v2 does not roll back a failed fork's directory — the tree is the index, so the file line matches on-disk reality.

3. Point lookups consult the legacy index first

Problem: cold get() cost one list per workspace bucket.

What was done:

  • FileSessionIndex.getLegacy() and the read-model cold path (getFromReadModel()) first read the legacy index as a sessionId → workspaceId locator and try that bucket directly; a missing or stale line (deleted dir / unreadable metadata) falls through to the existing per-workspace scan, so correctness never depends on the file.
  • Listings are unchanged: the directory tree stays authoritative; the file is only ever a lookup hint.

Tests

  • sessionIndex.test.ts: lookup via the index file, rejection of lines failing v1 validation (relative path, outside sessionsDir, basename mismatch, corrupt JSON), later-lines-override, stale-line fallback to the scan, and read-model cold-miss backfill through the locator.
  • sessionLifecycle.test.ts: create appends exactly one v1-compatible line that round-trips through the v1 parser, resume appends nothing, fork appends a line for the target session.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

@changeset-bot

changeset-bot Bot commented Jul 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f1274fc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@moonshot-ai/agent-core-v2 Patch
@moonshot-ai/kap-server Patch
@moonshot-ai/klient Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eebd61ed28

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

*/
private async readLegacyIndex(): Promise<Map<string, string>> {
const result = new Map<string, string>();
const bytes = await this.storage.read(LEGACY_SESSION_INDEX_SCOPE, LEGACY_SESSION_INDEX_KEY);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fall back when the legacy index read fails

When <homeDir>/session_index.jsonl is present but unreadable (for example EACCES or a directory at that path), FileStorageService.read throws storage.io_failed; because this new read is outside a try/catch, get() / list({ sessionId }) now fail before the existing directory scan can run. v1's index reader and the helper comment both treat unreadable index files as empty, so catch read failures here and return the empty map to keep sessions discoverable from the authoritative directory tree.

Useful? React with 👍 / 👎.

Comment on lines +207 to +211
// Project the new session into v1's global index so a v1 CLI sharing
// this homeDir can discover it. `AppendLogStore` framing is
// byte-identical to v1's `JSON.stringify(entry) + '\n'`; the explicit
// flush makes the line durable before create/fork returns. Placed at
// the materialization point (not fork completion) because v2 does not

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Move inline implementation notes into the file header

packages/agent-core-v2/AGENTS.md says comments must live “solely in the top-of-file /** */ block” and “never beside functions, methods, or statements.” This newly added method-level rationale violates that package rule (as do the other new inline source comments in this patch), so move the explanation into the module header or remove it.

Useful? React with 👍 / 👎.

- add a legacySessionIndex module defining the v1 session_index.jsonl
  byte format: tolerant line parsing and v1's entry validation (absolute
  sessionDir inside sessionsDir, basename matches sessionId), shared by
  the v2 readers and writer
- append a v1-compatible index line on session create/fork (never on
  resume) through the append-log store, so a v1 CLI sharing the homeDir
  discovers v2 sessions
- locate sessions through the legacy index in FileSessionIndex.get (and
  read-model cold misses) with a directory-scan fallback for stale lines
- reuse the shared parser in the workspace registry's one-shot rebuild
@sailist
sailist force-pushed the feat/session-index branch from eebd61e to f1274fc Compare July 13, 2026 13:09
@pkg-pr-new

pkg-pr-new Bot commented Jul 13, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@f1274fc
npx https://pkg.pr.new/@moonshot-ai/kimi-code@f1274fc

commit: f1274fc

@sailist sailist closed this Jul 13, 2026
RealKai42 added a commit that referenced this pull request Jul 16, 2026
An explicit withThinking('off') collapsed to the same internal state as
"never configured" on chat-completions providers, so the history-based
auto reasoning_effort injection (#1616) silently switched reasoning back
on and could leak the field to models that reject it. Store the requested
effort verbatim and derive the wire encoding per request, suppress the
auto-enable for an explicit 'off', and report the accurate current effort
('on'/'off') instead of recording 'off' for both.
ywh114 pushed a commit to ywh114/kimi-code that referenced this pull request Jul 19, 2026
…shotAI#1774)

An explicit withThinking('off') collapsed to the same internal state as
"never configured" on chat-completions providers, so the history-based
auto reasoning_effort injection (MoonshotAI#1616) silently switched reasoning back
on and could leak the field to models that reject it. Store the requested
effort verbatim and derive the wire encoding per request, suppress the
auto-enable for an explicit 'off', and report the accurate current effort
('on'/'off') instead of recording 'off' for both.
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