Skip to content

feat(v2): land agent-core-v2 engine and kap-server behind experimental flag#1441

Open
sailist wants to merge 556 commits into
mainfrom
kimi-code-v2
Open

feat(v2): land agent-core-v2 engine and kap-server behind experimental flag#1441
sailist wants to merge 556 commits into
mainfrom
kimi-code-v2

Conversation

@sailist

@sailist sailist commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

N/A — integration PR that lands the long-lived kimi-code-v2 branch; see Problem below.

Problem

The v2 line — the DI × Scope based agent-core-v2 engine, the kap-server that hosts it, and the minidb read model — has been built up on the long-lived kimi-code-v2 integration branch. This PR merges that branch into main.

The v2 engine and server are not the default runtime yet. Both the kimi -p prompt path and kimi server run route to v2 only when KIMI_CODE_EXPERIMENTAL_FLAG is set, and the v2 module graph is lazy-imported so it stays off the default path. With the flag unset, the CLI keeps the v1 engine and @moonshot-ai/server.

What changed

Scope: 1,207 files, +192,653 / −285. The bulk is three new packages; everything else is CLI wiring, e2e coverage, skills, and small v1 touch-ups.

1. agent-core-v2: the new DI × Scope engine (packages/agent-core-v2, +146k, 872 files)

  • New agent-core-v2 package: a DI scope engine with an explicit agent / session lifecycle.
  • Wire/op state engine with derived wire models and ReplayTimelineModel; IEventBus and Op.toEvent alongside wire.signal; context writes routed through ops and declared tool delivery.
  • Per-agent wire records (wire.jsonl); flattened agent hierarchy with swarm lifted to session.
  • Execution environment reorganized into separate filesystem / process / tool domains.
  • Goal mode: core goal workflow, budget enforcement, and per-turn goal injection.
  • Model layer: Model god-object and protocol domains; kosong vendored as the internal llmProtocol/kosong implementation (drops the @moonshot-ai/kosong and @moonshot-ai/kaos dependencies).
  • Built-in tools registered via DI / contributions, plugin management, session subagent host + Agent tool, shared rg locator/runner, AGENTS.md hierarchy loading.

2. kap-server: the v2 server (packages/kap-server, +26k, 139 files)

  • New @moonshot-ai/kap-server package — the Kimi Code server backed by agent-core-v2 (codenamed "server-v2").
  • Hosts agent-core-v2 sessions over REST (/api/v1, ~25 route modules) and WebSocket (/api/v1/ws with seq/epoch watermark and resync), and serves the web assets.
  • Auth and request-security hardening, /openapi.json via @fastify/swagger, and v2 session export (diagnostic zip archives).
  • v1 parity fixes carried along: broadcast interaction question / approval events, honor the before_id pagination cursor, treat loopback origins as same-origin for WS upgrade, and align MCP media output with v1.

3. minidb: embedded read model (packages/minidb, +13k, 77 files)

  • New @moonshot-ai/minidb package — a pure-Node.js embedded KV database (Redis-style in-memory KV with SQLite-style WAL/snapshot persistence, secondary indexes, TTL), used as the session-index read model.

4. CLI wiring (apps/kimi-code, +762, 19 files)

  • kimi -p and kimi server run select v2 vs. v1 via KIMI_CODE_EXPERIMENTAL_FLAG (lazy import); v1 stays the default.
  • Add the v2 harness adapters (cli/v2/*) and the prompt-session abstraction shared by both engines; update TUI adapters to handle v2 events.

5. Tooling, e2e and docs

  • packages/server-e2e: typed v2 ServerClient SDK with a drift test and e2e coverage.
  • .agents/skills: add the agent-core-dev and write-tests skills plus other skill updates; add the dependency-graph dev viewer.
  • GOAL.md: design doc for the goal-mode feature split.
  • Minor: packages/protocol, packages/acp-adapter, packages/agent-core, packages/server, packages/node-sdk, apps/kimi-web, the hash-imports build loaders, flake.nix, and changesets.

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.

sailist added 2 commits July 6, 2026 22:57
- handle `kimi v2 <subcommand>` in main.ts, gated on KIMI_CODE_EXPERIMENTAL_FLAG via shared isKimiV2Enabled
- route `kimi server run` to server-v2 through the same switch; drop server-v2 standalone main.ts/dev script
- fold v1 context.append_loop_event records into messages at replay and snapshot so v1 sessions restore on agent-core-v2
- report backend: 'v2' in meta; serve session status via SessionLegacyService.status
- drop unused service options (blob/fullCompaction/goal/permissionGate); permissionGate reads agentId from scope context
…gistration

AgentBlobServiceImpl no longer takes constructor arguments, so remove the
now-unnecessary [{}] from its SyncDescriptor registrations in the agent
lifecycle service and the blob tests.
@changeset-bot

changeset-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9f1827e

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

This PR includes changesets to release 6 packages
Name Type
@moonshot-ai/kimi-code Minor
@moonshot-ai/agent-core-v2 Minor
@moonshot-ai/protocol Minor
@moonshot-ai/kap-server Patch
@moonshot-ai/agent-core Patch
@moonshot-ai/server 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: fbb4a3a0c6

ℹ️ 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 +239 to +240
const session = this.opts.core.accessor.get(ISessionLifecycleService).get(sessionId);
if (session === undefined) return 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.

P1 Badge Resume cold sessions before accepting WS subscriptions

When a client reconnects to a session that exists only on disk, such as after a server-v2 restart, the default snapshot path can succeed via the disk reader without materializing the session in ISessionLifecycleService. The following /api/v1/ws subscribe then calls ensureState, but this get() only checks live sessions and returns undefined, so the subscribe ack reports not_found and the socket is never registered; if a later prompt request resumes the session, that client still receives no live turn events. Use resume() here or otherwise create a broadcaster state for cold persisted sessions before returning false.

Useful? React with 👍 / 👎.

Comment on lines +1011 to +1015
'file.not_found',
'file.too_large',
'fs.path_not_found',
'fs.permission_denied',
'validation.failed',

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 Keep the Kimi error schema exhaustive

The KimiErrorCode union above includes additional codes such as prompt.not_found, session.busy, fs.path_escapes, fs.is_directory, fs.is_binary, fs.too_large, fs.already_exists, fs.too_many_results, fs.grep_timeout, and fs.git_unavailable, but this runtime z.enum omits them. Any error or turn.ended.error payload carrying one of those valid codes from agent-core-v2 will fail eventSchema / sessionEventMessageSchema validation even though the TypeScript type says it is valid, so add the missing literals or derive the schema from a single source.

Useful? React with 👍 / 👎.

# Conflicts:
#	apps/kimi-web/src/api/daemon/agentEventProjector.ts
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

❌ Nix build failed

Hash mismatch in pnpmDeps:

Hash
specified sha256-iBk+TV+rIhmd7bYnVFbW3kTGltojJl3pL2hhmsGO+Fk=
got sha256-Z3daIqAm/BikwRSMXydiorikn5PMsxvWtB07SujJYzQ=

Please update flake.nix with the got hash.

sailist and others added 24 commits July 7, 2026 11:20
- introduce ModelBlobCodec ({ dehydrate, rehydrate }) declared on ModelDef.blobs; the model owns record/state traversal, WireService owns storage I/O
- remove WireBlobSelector / WireBlobTarget and the contextBlobSelector seed; declare blob handling inline on ContextModel.blobs
- rename IAgentBlobService.rehydrateParts -> loadParts and PartsRehydrator -> PartsTransformer (same shape for both directions)
- WireService.appendToWireLog routes dehydrate through the per-record model codec instead of a construction-time selector
- derive session title and lastPrompt from the first prompt before the turn launches, then broadcast session.meta.updated on IEventService so the web title updates live
- generalize the prompt metadata update to cover prompt() and activateSkill(), not just plugin commands
- add turn.started origin and new prompt/session/fs error codes to the protocol schema
- rename the background.task_id_empty error code to task.task_id_empty
- add scripts/debarrel.mjs (ts-morph) to rewrite #/<dir> imports to
  precise leaf specifiers and regenerate src/index.ts
- rewrite src and test consumers across the package to import from leaf
  files instead of domain barrels
- delete src/app/event/index.ts barrel and load its leaves from the
  package entry
- extract applyPromptMetadataUpdate so /api/v1 prompt submission derives
  the easy session title, matching /api/v2
- rename the v2 prefix to beta in the CLI prefix tests
When the Kimi OAuth toolkit short-circuits via its `ensureFresh` fast path (the cached refresh token is still usable), `toolkit.login` resolves without firing `onDeviceCode`. The v2 `OAuthService.startLogin` previously rejected in this case, so `POST /api/v1/oauth/login` returned a 500 that the web client swallowed and mislabeled as "current daemon does not support login". CLI-authenticated users could not (re)enter the v2 daemon through the web UI.

Treat that path as already authenticated: provision the managed provider (idempotent), refresh OAuth provider models so `defaultModel` is seeded (`/auth` then reports ready, matching v1 parity), and return a new `status: 'authenticated'` `OAuthFlowStart` variant so the web LoginDialog can skip the device-code UI entirely instead of falling into its catch-all "unsupported daemon" state.

The protocol `OAuthFlowStart` schema is now a discriminated union on `status`; kimi-web's wire / composable / LoginDialog types are updated to handle the new `authenticated` branch.

Tests: updated the v2 OAuthService test that previously asserted the buggy rejection to instead assert the fast path returns authenticated and provisions the provider.
Move MicroCompactionEffect next to the service that produces it, and derive MicroCompactionConfig from the single zod schema in configSection.ts so the section schema and the runtime config type no longer drift.
Run SubagentStart/SubagentStop and SessionStart/SessionEnd external hooks by observing hook slots instead of being invoked directly, matching the other external-hook observers.

- Add Agent-scoped IAgentRunHooksService hosting onWillStartAgentTask / onDidStopAgentTask; mirrorAgentRun runs those slots rather than calling IAgentExternalHooksService directly.
- AgentExternalHooksService registers on those slots to emit SubagentStart/SubagentStop; drop runAgentTaskStart/notifyAgentTaskStop from its public interface.
- Add Session-scoped ISessionExternalHooksService. ISessionLifecycleService gains onDidCreateSession / onWillCloseSession slots announcing source/reason for create, resume, fork, close, archive.
- Move externalHooks from L5 to L6 in check-domain-layers.mjs, update the DI dependency map, and drop now-dead Hooks/OrderedHookSlot/@IAgentTurnService imports.
…fresh

OAuthService.invalidateFlows previously cleared every in-flight device-code login on any provider config change. Startup-time model catalog refreshes rewrite the providers section without changing the OAuth provider itself, which aborted the active login and left the web login dialog stuck polling an empty flow after the user authorized in the browser.

Scope invalidation to providers that were removed or had their config changed, so an in-flight login for an unaffected provider completes normally.
Context injections can now return either plain reminder text or pre-built content parts, with turn-boundary state exposed to providers instead of the cadence option.
Replace the standalone HookEngine class with an App-scope IExternalHooksRunnerService that owns loading (config + plugin.enabledHooks()) and matching/running hooks. The Agent- and Session-scope external-hooks adapters now just observe their hook slots and inject the shared runner, so the duplicated per-adapter engine lifecycle (loadDynamicHooks / reload / ready gating / plugin.onDidReload) goes away.

- Add src/app/externalHooksRunner/ with the contract, App-scope impl, and a pure runner module (indexHooks / runMatchedHooks / blockDecision).
- Drop public HookEngine, HookEngineTriggerArgs, HookEngineOptions, and the ExternalHooksServiceOptions escape hatch; move HookEngine run/match logic into the runner.
- Register externalHooksRunner at L6 and export it from the package index.
- Migrate tests to a real runner built by makeHookRunner() and the externalHookServices() harness override; split engine.test.ts traits into externalHookRunner.test.ts.
IAgentContextSizeService.getStatus() becomes get(start?, end?) returning { size, measured, estimated } where size = measured + estimated. start/end follow Array.prototype.slice semantics (default whole context, negative indices count from the end, inverted range is empty). Update consumers, the context:status action binding, tests, and the edge-exposure cheatsheet.
sailist and others added 30 commits July 9, 2026 15:11
- add optional `stack` field to errEnvelope and the envelope schema/interface; omitted when undefined so the wire shape stays byte-identical for callers without a stack
- thread `err.stack` through route error mappers plus the global and transport error handlers
- preserve `details` on `session.undo_unavailable` while adding its stack
- update tests to assert stacks are surfaced, reversing the prior no-leak contract
* feat(agent-core-v2): record shell command context

- add ShellCommandOrigin and compaction handoff disposition
- extract IAgentShellCommandService from AgentRPCService
- keep AgentRPCService as a thin shell:run facade

* fix(agent-core-v2): align skill priority and sync docs

- restore project > user > plugin > builtin skill precedence
- sync Skill tool description and parameter docs from v1
- update write-goal and custom-theme builtin skill copy

* fix(agent-core-v2): restore undo and thinking telemetry

- track conversation_undo after undoHistory
- emit thinking_toggle with enabled/effort/from payload
- add coverage for both telemetry events

* feat(agent-core-v2): add skill directory config

- add extraSkillDirs and mergeAllAvailableSkills config sections
- introduce extra skill source and shared source priorities
- align kap-server workspace skill preview with session catalog

* feat(agent-core-v2): support explicit skill dirs

- add ISkillCatalogRuntimeOptions for SDK-style explicit skill dirs
- suppress default user/project discovery when explicitDirs are set
- resolve explicit dirs per session workDir via explicitFileSkillSource

* fix(agent-core-v2): fix configured skill dir resolution

- expand ~ using OS home for configured skill dirs
- honor explicitDirs in kap-server workspace skill preview

* fix(agent-core-v2): await config ready before skill discovery

- wait for config.ready before reading extraSkillDirs
- wait for config.ready before reading mergeAllAvailableSkills
- cover extra skill dir loading behind config readiness

* fix(agent-core-v2): keep skill config live after changes

- await config.ready in kap-server workspace skill preview
- reload user and workspace skill sources when mergeAllAvailableSkills changes
- add `activity` domain: `IAgentActivityService` (Agent turn lane machine),
  `ISessionActivityKernel` (Session admission, PR1 placeholder), and the
  `ActivityLease` that owns the turn `AbortSignal`
- turnService launches and cancels through the kernel lease; `Turn` now
  exposes `signal` instead of `abortController`
- agentLifecycle.remove drives `beginDisposal`/`settled` and waits for the
  in-flight turn to drain before releasing the agent scope
- add `activity.*` error codes; deprecate `turn.agent_busy` in favor of
  `activity.agent_busy`
These four legacy session actions were thin delegations to the native v2
services (ISessionLifecycleService.fork/archive,
IAgentFullCompactionService.begin, IAgentRPCService.cancel) with no v1-only
projection to centralize. Drop them from ISessionLegacyService and call the
native services directly from the kap-server sessions route. updateProfile,
createChild, listChildren, undo and status stay in the adapter since they
carry real v1 adaptation logic.
- add native v2 print runner (v2/run-v2-print.ts) that consumes agent-core-v2
  DI services and awaits Turn.result directly
- extract shared print-mode rendering into prompt-render.ts for v1 and v2
- remove the V2PromptHarness/V2Session shim and v2->v1 event translation
- decouple initializeCliTelemetry from PromptHarness (homeDir/auth/track)
- add IAgentPromptLegacyService.submitAndSettle for authoritative completion
- make IAgentPromptService.undo throw session.undo_unavailable with a structured
  reason; move the precheck into contextMemory
- add ISessionLifecycleService.createChild (fork + child markers) and
  ISessionIndex.list({ childOf })
- slim ISessionLegacyService to updateProfile/status (drop createChild,
  listChildren, undo)
- rewire kap-server session routes to the native services and map
  SESSION_UNDO_UNAVAILABLE
- run-v2-print: use core ITelemetryService + CloudAppender instead of
  kimi-telemetry; remove kimi-code-sdk import (auth via IOAuthToolkit,
  config path from bootstrap, hook result via structural type)
- prompt-render: replace SDK HookResultEvent with a structural type so
  the shared renderer does not depend on the v1 SDK event shape
- telemetry: revert initializeCliTelemetry to its original signature now
  that v2 no longer calls it; keep v1 callers and assertions untouched
- update run-prompt and v2-run-print tests for the new wiring
- implement SessionActivityKernel lane machine (restoring→active⇄quiescing→closing→disposed) with admission table, atomic quiesce+drain, beginClosing/settled, markActive
- start AgentActivityService lane at initializing; add markReady driven by agentLifecycle.create after bootstrap
- project LaneModel + EventBus facts into structured AgentActivitySnapshot (ActivityModel / setActivitySnapshot Op) with pending-approval and active-tool-call sets; emit agent.activity.updated
- add IAgentTurnService.launchWithLease; goal continuation acquires the lane before appending its prompt
- resolve pending interactions on turn.ended to avoid stranded awaiting_approval
- fullCompaction registers a background activity and checks the activity lane
- extract contextMemory publishSplice / isFullyUndoable / recoverFoldedLength helpers
- kap-server: map activity snapshot into legacy status and sessionEventBroadcaster
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.

8 participants