Skip to content

feat(kap-server): add page mode, updated_before, and batch archive/restore to v2 sessions - #2983

Merged
liruifengv merged 33 commits into
mainfrom
feat/v2-sessions-admin
Aug 18, 2026
Merged

feat(kap-server): add page mode, updated_before, and batch archive/restore to v2 sessions#2983
liruifengv merged 33 commits into
mainfrom
feat/v2-sessions-admin

Conversation

@liruifengv

Copy link
Copy Markdown
Collaborator

Related Issue

No tracking issue — the problem is explained below (this PR accompanies the session admin page in the desktop client, code-app PR #221).

Problem

The desktop client's new session admin page (an admin-style management view with numbered pagination, an updated-at range filter, and batch complete/reopen) needs three capabilities the v2 sessions API does not offer today:

  1. Numbered pagination with a total count. GET /api/v2/sessions only supports opaque keyset cursors — a management table cannot render "共 N 条 / page k of M" or jump to an arbitrary page. The route already drains the (workspace/archive-filtered) set into memory and sorts it there, so a total and offset slicing are free.
  2. No upper-bound time filter. Only meta.updated_after exists; a date-range filter needs meta.updated_before (a symmetric edge filter — the gap is a missing requirement, not a performance concern).
  3. No batch archive/restore. The v1 action routes are single-session only, and each archive resume()s the session first — because archive() only operates on live handles (a cold session is a silent no-op). Batch-archiving hundreds of cold sessions through that path would fully materialize every one of them (session scope + main agent + MCP seed) for no benefit.

What changed

GET /api/v2/sessions — page-number mode + total

  • Response always carries total (the filtered/sorted set size; cursor mode benefits too).
  • New optional page (1-based): a stateless slice over the in-memory sorted set — no pageToken issued, no fingerprint checked, since every request carries its full conditions. page and page_token are mutually exclusive (40001).

GET /api/v2/sessionsmeta.updated_before

  • Symmetric to updated_after (inclusive bound), wired into the normalized query and the query fingerprint.

POST /api/v2/sessions:archive and POST /api/v2/sessions:restore

  • Body { ids } (non-empty, ≤ 5000 unique), per-item results in input order plus succeeded/failed counts; only body validation fails the whole request, unknown ids fold into their own item (40401).
  • Cold/hot split: live sessions go through the full lifecycle (archive()/restore()); cold sessions are patched on disk directly — atomic state.json rewrite preserving every field incl. updatedAt, read-model mirror, and the same event.session.archived (restore publishes none, matching the live path) — without materializing anything. Bounded concurrency for live items, one ISessionIndexMirror.drain() at the end.

Tests: 12 new cases in packages/kap-server/test/v2Sessions.test.ts (page mode shape/exclusivity, updated_before bounds + fingerprint drift, cold archive equivalence without materialization, hot path, mixed batches, validation) — full kap-server (1071) and agent-core-v2 (5067) suites green. API reference updated (docs/{en,zh}/reference/server-api.md).

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. — One changeset for the agent-core-v2 behavior (cold-session-batch-archive.md, patch); the pure protocol additions (page mode / total / updated_before) ship no changeset per the skill's bundled-web-UI rule, same as the v2 API introduction.
  • Ran gen-docs skill, or this PR needs no doc update. — server-api.md updated in en + zh.

The v2 session list gains a stateless 1-based `page` parameter beside the
opaque page_token cursor for admin-style lists that jump arbitrarily:
each request stays a full independent snapshot, no token is minted, and
`page` + `page_token` together fail 40001. Every response now carries
`total` (the filtered/sorted set size) in both pagination modes.
Symmetric with meta.updated_after (inclusive boundary, Unix ms), applied
at the edge over the drained set and bound into the page_token query
fingerprint like every other condition.
…h endpoints

Batch archive/restore for session-management views: { ids } (non-empty,
≤5000 unique after dedup) answers per-item results in input order with
succeeded/failed counts — only a body validation failure fails the whole
request, and an unknown id folds into its own item as 40401.

The live/cold split keeps the batch cheap: a session with a live handle
goes through the full ISessionLifecycleService chain (agents drain,
scope teardown, mirror drain), while a cold session is never
materialized — the new setColdSessionArchived helper in agent-core-v2
patches the persisted state.json (archived/archivedAt, updatedAt
preserved, mirroring setArchived's touchUpdatedAt: false semantics),
mirrors the flipped summary into the read-model queue, and republishes
the same event.session.archived bus event the live lifecycle emits
(:restore publishes nothing, matching the live restore). Hot items run
with bounded concurrency and the batch ends with one shared
ISessionIndexMirror.drain().
@changeset-bot

changeset-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: be79e0e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

❌ Nix build failed

    kimi-code>     │         - ../../packages/node-sdk/src/index.ts
    kimi-code>     │         - src/main.ts
    kimi-code> ────╯
    kimi-code> 
    kimi-code>     at aggregateBindingErrorsIntoJsError (file:///build/source/node_modules/.pnpm/rolldown@1.0.1/node_modules/rolldown/dist/shared/error-CkdMJ9ps.mjs:48:18)
    kimi-code>     at unwrapBindingResult (file:///build/source/node_modules/.pnpm/rolldown@1.0.1/node_modules/rolldown/dist/shared/error-CkdMJ9ps.mjs:18:128)
    kimi-code>     at #build (file:///build/source/node_modules/.pnpm/rolldown@1.0.1/node_modules/rolldown/dist/shared/rolldown-build-BVD3dIdE.mjs:3275:34)
    kimi-code>     at async build (file:///build/source/node_modules/.pnpm/rolldown@1.0.1/node_modules/rolldown/dist/index.mjs:42:22)
    kimi-code>     at async Promise.all (index 0)
    kimi-code>     at async buildSingle (file:///build/source/node_modules/.pnpm/tsdown@0.22.0_@arethetypeswrong+core@0.18.2_publint@0.3.18_tsx@4.21.0_typescript@6.0.2__e3c4b77bdbb7c42f8dbfce92959948a8/node_modules/tsdown/dist/build-5FURNVr0.mjs:765:19)
    kimi-code>     at async Promise.all (index 0)
    kimi-code>     at async buildWithConfigs (file:///build/source/node_modules/.pnpm/tsdown@0.22.0_@arethetypeswrong+core@0.18.2_publint@0.3.18_tsx@4.21.0_typescript@6.0.2__e3c4b77bdbb7c42f8dbfce92959948a8/node_modules/tsdown/dist/build-5FURNVr0.mjs:721:18)
    kimi-code>     at async CAC.<anonymous> (file:///build/source/node_modules/.pnpm/tsdown@0.22.0_@arethetypeswrong+core@0.18.2_publint@0.3.18_tsx@4.21.0_typescript@6.0.2__e3c4b77bdbb7c42f8dbfce92959948a8/node_modules/tsdown/dist/run.mjs:22:2)
    kimi-code>     at async runCLI (file:///build/source/node_modules/.pnpm/tsdown@0.22.0_@arethetypeswrong+core@0.18.2_publint@0.3.18_tsx@4.21.0_typescript@6.0.2__e3c4b77bdbb7c42f8dbfce92959948a8/node_modules/tsdown/dist/run.mjs:46:3)
    kimi-code> Command failed: /nix/store/chsxa1iw7s3m51v4kj43yd1nw26xvi21-nodejs-24.15.0/bin/node /build/source/node_modules/.pnpm/tsdown@0.22.0_@arethetypeswrong+core@0.18.2_publint@0.3.18_tsx@4.21.0_typescript@6.0.2__e3c4b77bdbb7c42f8dbfce92959948a8/node_modules/tsdown/dist/run.mjs --config tsdown.native.config.ts
    kimi-code> 
    kimi-code>  ERROR  Error: Build failed with 1 error:
    kimi-code> 
    kimi-code> [UNRESOLVED_IMPORT] Could not resolve '../../app/workspaceLifecycle/sessionLookup' in ../../packages/agent-core-v2/src/workspace/sessionLifecycle/coldSessionArchive.ts
    kimi-code>     ╭─[ ../../packages/agent-core-v2/src/workspace/sessionLifecycle/coldSessionArchive.ts:42:39 ]
    kimi-code>     │
    kimi-code>  42 │ import { liveHandlerForSession } from '../../app/workspaceLifecycle/sessionLookup';
    kimi-code>     │                                       ──────────────────────┬─────────────────────
    kimi-code>     │                                                             ╰─────────────────────── Module not found.
    kimi-code>     │
    kimi-code>     │ Help: '../../packages/agent-core-v2/src/workspace/sessionLifecycle/coldSessionArchive.ts' is imported by the following path:
    kimi-code>     │         - ../../packages/agent-core-v2/src/workspace/sessionLifecycle/coldSessionArchive.ts
    kimi-code>     │         - ../../packages/agent-core-v2/src/index.ts
    kimi-code>     │         - ../../packages/node-sdk/src/sdk-rpc-client-v2.ts
    kimi-code>     │         - ../../packages/node-sdk/src/index.ts
    kimi-code>     │         - src/main.ts
    kimi-code> ────╯
    kimi-code> 
    kimi-code>     at aggregateBindingErrorsIntoJsError (file:///build/source/node_modules/.pnpm/rolldown@1.0.1/node_modules/rolldown/dist/shared/error-CkdMJ9ps.mjs:48:18)
    kimi-code>     at unwrapBindingResult (file:///build/source/node_modules/.pnpm/rolldown@1.0.1/node_modules/rolldown/dist/shared/error-CkdMJ9ps.mjs:18:128)
    kimi-code>     at #build (file:///build/source/node_modules/.pnpm/rolldown@1.0.1/node_modules/rolldown/dist/shared/rolldown-build-BVD3dIdE.mjs:3275:34)
    kimi-code>     at async build (file:///build/source/node_modules/.pnpm/rolldown@1.0.1/node_modules/rolldown/dist/index.mjs:42:22)
    kimi-code>     at async Promise.all (index 0)
    kimi-code>     at async buildSingle (file:///build/source/node_modules/.pnpm/tsdown@0.22.0_@arethetypeswrong+core@0.18.2_publint@0.3.18_tsx@4.21.0_typescript@6.0.2__e3c4b77bdbb7c42f8dbfce92959948a8/node_modules/tsdown/dist/build-5FURNVr0.mjs:765:19)
    kimi-code>     at async Promise.all (index 0)
    kimi-code>     at async buildWithConfigs (file:///build/source/node_modules/.pnpm/tsdown@0.22.0_@arethetypeswrong+core@0.18.2_publint@0.3.18_tsx@4.21.0_typescript@6.0.2__e3c4b77bdbb7c42f8dbfce92959948a8/node_modules/tsdown/dist/build-5FURNVr0.mjs:721:18)
    kimi-code>     at async CAC.<anonymous> (file:///build/source/node_modules/.pnpm/tsdown@0.22.0_@arethetypeswrong+core@0.18.2_publint@0.3.18_tsx@4.21.0_typescript@6.0.2__e3c4b77bdbb7c42f8dbfce92959948a8/node_modules/tsdown/dist/run.mjs:22:2)
    kimi-code>     at async runCLI (file:///build/source/node_modules/.pnpm/tsdown@0.22.0_@arethetypeswrong+core@0.18.2_publint@0.3.18_tsx@4.21.0_typescript@6.0.2__e3c4b77bdbb7c42f8dbfce92959948a8/node_modules/tsdown/dist/run.mjs:46:3)
    kimi-code> 
    kimi-code> 
    kimi-code> /build/source/apps/kimi-code:
    kimi-code>  ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL  @moonshot-ai/kimi-code@0.36.1 build:native:sea: `node scripts/native/build.mjs --profile=local`
    kimi-code> Exit status 1
    error: Cannot build '/nix/store/x1yq69kf5zhxqs0jp2fgqgmxlr6s4azg-kimi-code-0.36.1.drv'.
           Reason: builder failed with exit code 1.
           Output paths:
             /nix/store/20jakj2lxxrb52mdmbln8jdmsvlmvr3l-kimi-code-0.36.1
           Last 25 log lines:
           >     │                                                             ╰─────────────────────── Module not found.
           >     │
           >     │ Help: '../../packages/agent-core-v2/src/workspace/sessionLifecycle/coldSessionArchive.ts' is imported by the following path:
           >     │         - ../../packages/agent-core-v2/src/workspace/sessionLifecycle/coldSessionArchive.ts
           >     │         - ../../packages/agent-core-v2/src/index.ts
           >     │         - ../../packages/node-sdk/src/sdk-rpc-client-v2.ts
           >     │         - ../../packages/node-sdk/src/index.ts
           >     │         - src/main.ts
           > ────╯
           >
           >     at aggregateBindingErrorsIntoJsError (file:///build/source/node_modules/.pnpm/rolldown@1.0.1/node_modules/rolldown/dist/shared/error-CkdMJ9ps.mjs:48:18)
           >     at unwrapBindingResult (file:///build/source/node_modules/.pnpm/rolldown@1.0.1/node_modules/rolldown/dist/shared/error-CkdMJ9ps.mjs:18:128)
           >     at #build (file:///build/source/node_modules/.pnpm/rolldown@1.0.1/node_modules/rolldown/dist/shared/rolldown-build-BVD3dIdE.mjs:3275:34)
           >     at async build (file:///build/source/node_modules/.pnpm/rolldown@1.0.1/node_modules/rolldown/dist/index.mjs:42:22)
           >     at async Promise.all (index 0)
           >     at async buildSingle (file:///build/source/node_modules/.pnpm/tsdown@0.22.0_@arethetypeswrong+core@0.18.2_publint@0.3.18_tsx@4.21.0_typescript@6.0.2__e3c4b77bdbb7c42f8dbfce92959948a8/node_modules/tsdown/dist/build-5FURNVr0.mjs:765:19)
           >     at async Promise.all (index 0)
           >     at async buildWithConfigs (file:///build/source/node_modules/.pnpm/tsdown@0.22.0_@arethetypeswrong+core@0.18.2_publint@0.3.18_tsx@4.21.0_typescript@6.0.2__e3c4b77bdbb7c42f8dbfce92959948a8/node_modules/tsdown/dist/build-5FURNVr0.mjs:721:18)
           >     at async CAC.<anonymous> (file:///build/source/node_modules/.pnpm/tsdown@0.22.0_@arethetypeswrong+core@0.18.2_publint@0.3.18_tsx@4.21.0_typescript@6.0.2__e3c4b77bdbb7c42f8dbfce92959948a8/node_modules/tsdown/dist/run.mjs:22:2)
           >     at async runCLI (file:///build/source/node_modules/.pnpm/tsdown@0.22.0_@arethetypeswrong+core@0.18.2_publint@0.3.18_tsx@4.21.0_typescript@6.0.2__e3c4b77bdbb7c42f8dbfce92959948a8/node_modules/tsdown/dist/run.mjs:46:3)
           >
           >
           > /build/source/apps/kimi-code:
           >  ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL  @moonshot-ai/kimi-code@0.36.1 build:native:sea: `node scripts/native/build.mjs --profile=local`
           > Exit status 1
           For full logs, run:
             nix log /nix/store/x1yq69kf5zhxqs0jp2fgqgmxlr6s4azg-kimi-code-0.36.1.drv

@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: 6ae1b2f0e6

ℹ️ 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".

Comment thread packages/agent-core-v2/src/workspace/sessionLifecycle/coldSessionArchive.ts Outdated
Comment thread packages/agent-core-v2/src/workspace/sessionLifecycle/coldSessionArchive.ts Outdated
…ssions route

CI's tsgo/rolldown (Linux) fail to bind liveHandlerForSession and
IWorkspaceLifecycleService through the agent-core-v2 package-root
barrel even though it re-exports them; the same files use the
established deep-import pattern already used for the git domain.
@pkg-pr-new

pkg-pr-new Bot commented Aug 17, 2026

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

commit: be79e0e

The previous deep imports still fail to resolve on CI's Linux toolchain
(tsgo TS2307, rolldown MISSING_EXPORT) while every other module path
from the same package binds fine. Keep the route self-contained: the
hot-path lookup is a five-line loop over IWorkspaceLifecycleService's
handlers (mirrors agent-core-v2's liveHandlerForSession), and the tests
assert non-materialization behaviorally via the live map instead of
importing the same two symbols for spies.
The phantom only hits the workspaceLifecycle-group symbols in these two
files on CI's Linux toolchain; getLiveSessionById is observed to bind
fine there. It returns the session's live scope directly (no resume),
which is exactly what the batch hot path needs.
setSessionArchivedBatch owns the split next to the cold patch: live
sessions go through the full lifecycle chain via the workspace handler
accessor (the v1-proven resolution path), cold sessions through the
direct write. The route becomes a thin wire-code adapter, and the batch
tests assert the live chain behaviorally (disposal, events, index)
instead of spying through scope accessors.
…rchive

The '#/app/workspaceLifecycle/*' specifier resolves from src/ and
src/app/* files on CI's Linux toolchain but not from
src/workspace/sessionLifecycle/ (tsgo TS2307, rolldown follows); a
relative import bypasses the package-imports mapping.
Main's workspace/session DI refactor removed the workspaceLifecycle
lookup modules; the live branch now goes through the App-level
ISessionManager (the same entry the v1 action route uses post-refactor)
with getLiveSessionById from the new sessionManager lookup.
…sessions

fields=id,archived trims each item to { id, archived } for
select-all-matching flows (the session admin page's Gmail-style
select-all). Only that projection gets the relaxed page_size ceiling
(10000); unknown fields, non-pair subsets, and include=git combinations
are 40001, and the projection binds into the page_token fingerprint so
shapes never flip mid-pagination.
…resumes

Codex review on #2983: while a resume is in flight the live registry
hides the handle, so the batch route could classify the session as cold
and its direct write would race the materializing metadata service (its
stale in-memory document wins the next write, silently un-archiving the
session after the endpoint reported success).

The batch now settles the resume first: SessionManager registers the
whole resume promise synchronously at the App level (controllerForSession
is async, so the controller's own resuming map learns about it a few
microtasks late) and whenResumeSettled awaits it before classification —
a settled resume lands the item on the live chain, a failed one falls
back to the cold path. Also folds the module header down to the
package's external-role comment convention.
@liruifengv

Copy link
Copy Markdown
Collaborator Author

@codex

@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: 63a8130a7b

ℹ️ 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".

Comment thread packages/agent-core-v2/src/workspace/sessionLifecycle/coldSessionArchive.ts Outdated
Comment thread packages/agent-core-v2/src/workspace/sessionLifecycle/coldSessionArchive.ts Outdated
@liruifengv

Copy link
Copy Markdown
Collaborator Author

@codex review

1 similar comment
@liruifengv

Copy link
Copy Markdown
Collaborator Author

@codex review

@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: 46db11d6fd

ℹ️ 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".

Comment thread packages/agent-core-v2/src/workspace/sessionLifecycle/coldSessionArchive.ts Outdated

@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: 46db11d6fd

ℹ️ 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".

@liruifengv

Copy link
Copy Markdown
Collaborator Author

@codex review

@liruifengv

Copy link
Copy Markdown
Collaborator Author

@codex review

@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: e825bc2bc8

ℹ️ 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".

Comment thread packages/agent-core-v2/src/workspace/sessionLifecycle/coldSessionArchive.ts Outdated
Comment thread packages/agent-core-v2/src/workspace/sessionLifecycle/coldSessionArchive.ts Outdated
Comment thread .changeset/cold-session-batch-archive.md Outdated
@liruifengv

Copy link
Copy Markdown
Collaborator Author

@codex review

@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: aa05630e2e

ℹ️ 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".

Comment thread packages/agent-core-v2/src/app/sessionManager/sessionManager.ts Outdated
@liruifengv

Copy link
Copy Markdown
Collaborator Author

@codex review

@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: 46d88dedb1

ℹ️ 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".

}

async whenResumeSettled(sessionId: string): Promise<void> {
await this.pendingResumes.get(sessionId)?.catch(() => undefined);

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 Propagate resume failures before cold classification

When a concurrent resume fails after materializeSession() has inserted the handle into the workspace handler but before announceCreated() registers it with SessionManager (for example, main-agent creation fails), this catch suppresses the failure and the batch sees no live session through the App registry. It then performs a cold metadata write and reports success without archiving or disposing the handler-owned live scope, leaving the two lifecycle registries inconsistent. Propagate the resume rejection so this batch item becomes an internal error rather than continuing to cold classification.

AGENTS.md reference: packages/agent-core-v2/AGENTS.md:L7-L7

Useful? React with 👍 / 👎.

@liruifengv

Copy link
Copy Markdown
Collaborator Author

@codex review

@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: 2c0e315daa

ℹ️ 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".

Comment on lines +85 to +86
// A fresh attempt supersedes any earlier failure record.
this.resumeFailures.delete(sessionId);

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 Reconcile the orphaned handle before clearing resume failure

When the first resume fails during main-agent creation, materializeSession() has already inserted the handle into the workspace controller, but announceCreated() has not populated the App registry. A normal retry reaches this line, clears the recorded failure, and then the controller returns that existing handle without announcing it; the retry therefore reports success while owners remains empty. A subsequent batch operation sees neither a failure nor an App-visible live session and performs a cold write against the still-live scope. Fresh evidence beyond the earlier failed-resume comment is that the newly added “fresh attempt supersedes” path clears the only guard without reconciling this controller-owned handle.

Useful? React with 👍 / 👎.

@liruifengv

Copy link
Copy Markdown
Collaborator Author

@codex review

@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: 165fdf37c7

ℹ️ 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".

Comment on lines +53 to +54
const raw = await docs.get<SessionMeta>(metaScope, 'state.json');
if (raw === undefined) return 'not_found';

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 Read the legacy metadata location before returning not found

When an older v2 session still stores metadata only at <sessionDir>/session-meta/state.json, ISessionIndex.get() finds it because sessionIndexSource.ts explicitly falls back to that legacy location, but this read checks only <sessionDir>/state.json. Batch archive/restore therefore returns a per-item 40401 and leaves the archive state unchanged for a session that remains visible in session listings; use the authoritative reader's fallback and then write or migrate the same document before classifying the session as missing.

Useful? React with 👍 / 👎.

@liruifengv

Copy link
Copy Markdown
Collaborator Author

@codex review

@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: b0c43b6e3c

ℹ️ 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".

const applyOne = async (id: string): Promise<SessionArchiveBatchItemOutcome> => {
try {
const manager = accessor.get(ISessionManager);
return await manager.withLifecycleSerialization(id, async (unguarded) => {

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 Serialize target creation with batch archive operations

When an in-process caller creates a session with an explicit sessionId while a batch archives that same ID, this critical section does not exclude creation because SessionManager.create() bypasses the per-session chain. After creation has written and mirrored initial metadata but before announceCreated() populates the App registry, the batch classifies the session as cold and writes archived: true; the still-live metadata service retains archived: false, so a subsequent metadata update can overwrite the archive while the endpoint has already reported success. Queue creation on the target session ID's chain as well.

AGENTS.md reference: packages/agent-core-v2/AGENTS.md:L7-L7

Useful? React with 👍 / 👎.

Comment thread packages/agent-core-v2/src/workspace/sessionLifecycle/coldSessionArchive.ts Outdated
# Conflicts:
#	packages/agent-core-v2/src/workspace/sessionLifecycle/sessionLifecycleService.ts
#	packages/kap-server/src/routes/v2/sessions.ts
#	packages/kap-server/test/v2Sessions.test.ts
…ifecycle chain

create() with a caller-supplied sessionId bypassed the per-session chain,
so a concurrent batch archive could classify the half-created session as
cold and write archived state that the live metadata service later
overwrites. Creation now queues on the target id's chain whenever an
explicit id is present.

Also type the resume-failure maps as Error and normalize at the catch
site, satisfying only-throw-error.
@liruifengv

Copy link
Copy Markdown
Collaborator Author

@codex review

@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: b079259c5b

ℹ️ 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".

Comment thread packages/agent-core-v2/src/app/sessionManager/sessionManagerService.ts Outdated
…he lifecycle chain

fork() and createChild() with a newSessionId locked only the source id, so
a batch archive of the target could slip into the creation window: the
index already knows the half-created session, the batch writes archived
state to its document, and the fork's in-memory metadata later overwrites
it. Both operations now acquire the deduped, sorted key set so multi-key
sections always take locks in one deterministic order.
@liruifengv

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: be79e0e426

ℹ️ 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".

@liruifengv
liruifengv merged commit eaa3969 into main Aug 18, 2026
15 checks passed
@liruifengv
liruifengv deleted the feat/v2-sessions-admin branch August 18, 2026 05:57
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