feat(v2): land agent-core-v2 engine and kap-server behind experimental flag - #1441
Conversation
🦋 Changeset detectedLatest commit: 58dde0b The changes in this PR will be included in the next version bump. This PR includes changesets to release 7 packages
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 |
There was a problem hiding this comment.
💡 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".
| const session = this.opts.core.accessor.get(ISessionLifecycleService).get(sessionId); | ||
| if (session === undefined) return undefined; |
There was a problem hiding this comment.
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 👍 / 👎.
| 'file.not_found', | ||
| 'file.too_large', | ||
| 'fs.path_not_found', | ||
| 'fs.permission_denied', | ||
| 'validation.failed', |
There was a problem hiding this comment.
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 👍 / 👎.
|
❌ Nix build failed |
commit: |
- port probeLoginShellPath/mergeLoginShellPath/applyLoginShellPath into _base/execEnv/loginShellPath.ts as a pure helper (no DI) - export execFileText from environmentProbe for reuse by the probe - run applyLoginShellPathFromNode concurrently with the host probe in HostEnvironmentService, mirroring kaos LocalKaos.create() Aligns agent-core-v2 with kaos 021786f so the Bash tool finds user-installed tools (e.g. Homebrew's gh) when kimi-code is launched from a GUI or non-login shell.
…into kimi-code-v2
…into kimi-code-v2
…into kimi-code-v2
…into kimi-code-v2
- protocol: add optional agent_filter to client_hello and subscribe - kap-server: carry per-subscription agent allowlists through the broadcaster and connection, narrowing live fan-out and replay to selected agents while keeping a single global sequence and bypassing the filter for global events - agent-core-v2: degrade MiniDbQueryStore to a no-op read model when the query-store lock is held by another process instead of crashing the host
…m from built-in commands (#1492)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…into kimi-code-v2
…models
- GET /api/v1/models now awaits refreshProviderModels({ scope: 'all' })
before returning the model list, so the response always reflects the
latest provider model metadata
- refresh failures are logged and swallowed, falling back to the
persisted catalog instead of failing the request
Replace fire-and-forget OrderedHookSlot hooks with Emitter/Event-based notifications for consumers that only observe, never intercept: - usage: hooks.onDidRecord -> onDidRecord event - permissionMode: hooks.onDidChangeMode -> onDidChangeMode event - fullCompaction: hooks.onDidFinishCompaction -> onDidFinishCompaction event - wireRecord: hooks.onDidFinishResume -> onDidFinishResume event - agentLifecycle: hooks.onDidStopAgentTask -> onDidStopAgentTask event, announced via new notifyAgentTaskStopped() called by mirrorAgentRun Interception-capable slots (onWillStartAgentTask, onWillCompact, onDidRestoreRecord) stay as ordered hooks.
… when logged in When the managed Kimi provider has an oauth ref, WebFetchService builds a MoonshotFetchURLProvider (bearer token + host identity headers) with the local fetcher as fallback, re-reading login state on every call; logged-out setups keep the local fetcher.
…ests WebSearchProviderService now passes the host's IHostRequestHeaders (User-Agent + X-Msh-* device identity) as default headers to the Moonshot search provider, mirroring v1's kimiRequestHeaders.
The v2 boot path (kimi server run with the experimental flag) now seeds the CLI's Kimi identity headers (User-Agent + X-Msh-* device identity) into the engine through kap-server's seeds option, so outbound model, WebSearch, and FetchURL requests carry the same identity as direct CLI runs. kap-server's own package version is 0.0.0, so the identity has to come from the CLI.
- task: idle terminal notifications now use activeOrNewTurn admission, launching their own turn instead of waiting for the next user prompt (matches v1 turn.steer) - workspaceRegistry: createOrTouch rejects missing or non-directory roots with fs.path_not_found, so a phantom cwd never reaches session creation - kap-server: map FS_PATH_NOT_FOUND to protocol error 40409 on the session and RPC surfaces - server-e2e: migrate the v2 smoke test from the local ServerClient to the typed Klient - misc: switch loop/prompt clear() iteration to .slice(), align terminal event handler names, and tidy klient examples
The v1 WS broadcaster only re-emitted event.session.status_changed(running) on turn.started and never emitted the idle/aborted transition on turn.ended. kimi-web treats that event as the single source of session status (its turn.ended projector deliberately does not synthesize idle), so a session stuck at 'running' after the turn finished — most visibly for background tasks, where ISessionActivity keeps reporting non-idle while the detached task lives and even a REST pull never corrected it. Re-emit event.session.status_changed after turn.ended on the same dispatch queue, mapping reason cancelled/failed/blocked to aborted and otherwise idle (previous_status 'running'), matching v1's _computeStatus. Update the broadcaster tests and harden the wsV1Resync test helper so non-matching frames no longer strand a waiter's timeout.
- listen messages accept a service name; kap-server resolves the Service via resolveService and subscribes through its onUpperCase member - add listen_result acknowledgement and per-listen error reporting (onDidListenError) so failed subscriptions surface to the client - support onWill-style events: payload carries eventId/signal/waitUntil, the client replies with event_result and the server can event_cancel - klient proxy maps onUpperCase members to channel.listen; WsChannel shares one remote subscription across first/last listeners - channel.call now forwards the complete argument array
- register business telemetry events with compile-time property contracts - redact sensitive values and reject invalid cloud properties - centralize cloud appender construction and version context
- add describeChannels() to channelRegistry: scope derived from the scoped DI registry, public methods/getters enumerated from the prototype chain (framework plumbing and events excluded) - export ChannelDescriptor / ChannelMethodDescriptor from the contract - serve GET /api/v2/channels so clients (kimi-inspect) can render a dynamic service browser without handwritten method lists - cover with rpc test, e2e channel registry test, and API surface snapshot
- add `params` field to ChannelMethodDescriptor, parsed from function source - extract declared parameter list via Function#toString with paren-depth tracking - cover param introspection in rpc and server-e2e channel registry tests
- run-v2-print: drive turns via IAgentPromptService.enqueue() and handle.launched; detect hook-blocked prompts via handle.completion; read the LoopRunResult type discriminator in formatNativeTurnFailure - bootstrap stubs: add clientVersion required by IBootstrapService - v2-run-print test: mock enqueue, stub IBootstrapService for createCloudAppender, add track2 to the telemetry stub - node-sdk test: cover prompt.completed/aborted/steered in the exhaustive event switch
… and wire layers - add `os.fs.*` codes with `HostFsError` and the `toHostFsError` boundary translator - add `os.process.*`, `storage.*`, and `wire.*` domains with coded error classes - register new codes in the protocol `KimiErrorCode` union - translate raw OS and parse failures into domain codes across services, persistence backends, and kap-server transport - rename `KimiError` to `Error2` in the v2 base errors - remove obsolete kimi-csdk init example
The MCP initial-connect assertion used a single setTimeout(0) tick, but connectAll is gated on Promise.all([resolveSessionMcpConfig(...), enabledMcpServers()]); the session-config side walks the real filesystem (project-root search + mcp.json reads), which does not settle within one macrotask under CI load. Use vi.waitFor so the test is robust on CI.
In valueMode 'disk' the write path installed a disk ValueLoc using the predicted WAL offset before the frame's bytes were flushed (appendLoc returns the offset synchronously; the writev lands on a later tick). Under load a concurrent compaction snapshot could read a pointer past the WAL end and fail with a short read. Apply the record as an in-memory ref first and only publish the disk pointer after appended.done resolves, guarded against WAL rotation and stale record seqs.
- resolveScope now throws Error2 for missing session/agent instead of returning undefined, distinguishing agent.not_found from session.not_found - map AGENT_NOT_FOUND onto the session-not-found protocol envelope for v1 parity
- remove KIMI_PRINT_V2_ENV / isPrintV2Enabled and the dedicated KIMI_CODE_EXPERIMENT_FLAG switch - route `kimi -p` to the agent-core-v2 runner via isKimiV2Enabled, the same master switch that gates server-v2
- unwrap the HostFsError cause before matching EISDIR in FileEditService, restoring the "is not a file" edit output broken by the error taxonomy - map os.fs.* codes to the closest v1 wire codes in the kap-server fs route and /api/v2 transport instead of collapsing to INTERNAL_ERROR - map storage.io_failed / storage.locked to PERSISTENCE_FAILURE in the /api/v2 transport
A User-target set/replace mutates raw/rawSnake, awaits persist(), then rebuilds effective, while a reload() replaces all three wholesale from disk. Without serialization a reload whose file read resolves inside a write's persist window (before the atomic rename lands) restores the stale pre-write state, so the write's post-persist rebuild drops the just-written domain from effective. This surfaced as POST /config responses missing the field they had just written when the startup model-catalog refresh's reload() raced the write. Run User-target writes and reloads through a promise chain so they can no longer interleave.
- rename tool_call_dedupe_detected to tool_call_dedup_detected - emit turn_ended on every turn end; add mode/provider/protocol tags and interrupt_reason to turn_started/turn_interrupted - enrich api_error with alias, protocol tags, and input_tokens - tag tool_call with dup_type via an executor-side map (avoids the executor/dedupe DI cycle) - rename compaction usage fields to input_tokens/output_tokens - add context_projection_repaired, session_started, and session_load_failed events
- add guarded synchronous and asynchronous state transitions - support commit, rollback, cleanup, and compensation actions - cover transition conflicts, action ordering, and failure aggregation
- Degrade plugin consumption reads to empty when installed.json fails to load, surface plugin.load_failed with a repair hint on management calls, and recover after an explicit reload; serialize the initial load and mutations so concurrent first callers share one load. - Clean up zip temp dirs on every failure path, report the original source in zip/github manifest errors, and roll back to the previous managed copy when an install or persist fails. - Restore managed Kimi endpoint env injection for stdio plugin MCP servers. - Check plugin updates concurrently with per-repo failure isolation and 10s timeouts, track branch installs by commit SHA, and stop false update reports for tag/SHA pins. - Throw plugin.not_found from getPluginInfo and the manager's not-installed paths. - Count plugin skills through the real skill discovery path. - Re-sync context injection positions after silent wire replay so cold resumes do not duplicate injections, and fire plugin session-start reminders only when the plugin skill source finishes refreshing.
- keep model catalog reads free of provider refresh side effects - broadcast deduplicated session lifecycle and interaction statuses - cover catalog loops and global session status fan-out
Related Issue
N/A — integration PR that lands the long-lived
kimi-code-v2branch; see Problem below.Problem
The v2 line — the DI × Scope based
agent-core-v2engine, thekap-serverthat hosts it, and theminidbread model — has been built up on the long-livedkimi-code-v2integration branch. This PR merges that branch intomain.The v2 engine and server are not the default runtime yet. Both the
kimi -pprompt path andkimi server runroute to v2 only whenKIMI_CODE_EXPERIMENTAL_FLAGis 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)agent-core-v2package: a DI scope engine with an explicit agent / session lifecycle.ReplayTimelineModel;IEventBusandOp.toEventalongsidewire.signal; context writes routed through ops and declared tool delivery.wire.jsonl); flattened agent hierarchy with swarm lifted to session.Modelgod-object and protocol domains; kosong vendored as the internalllmProtocol/kosongimplementation (drops the@moonshot-ai/kosongand@moonshot-ai/kaosdependencies).rglocator/runner, AGENTS.md hierarchy loading.2. kap-server: the v2 server (
packages/kap-server, +26k, 139 files)@moonshot-ai/kap-serverpackage — the Kimi Code server backed byagent-core-v2(codenamed "server-v2").agent-core-v2sessions over REST (/api/v1, ~25 route modules) and WebSocket (/api/v1/wswith seq/epoch watermark and resync), and serves the web assets./openapi.jsonvia@fastify/swagger, and v2 session export (diagnostic zip archives).before_idpagination 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)@moonshot-ai/minidbpackage — 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 -pandkimi server runselect v2 vs. v1 viaKIMI_CODE_EXPERIMENTAL_FLAG(lazy import); v1 stays the default.cli/v2/*) and theprompt-sessionabstraction shared by both engines; update TUI adapters to handle v2 events.5. Tooling, e2e and docs
packages/server-e2e: typed v2ServerClientSDK with a drift test and e2e coverage..agents/skills: add theagent-core-devandwrite-testsskills plus other skill updates; add the dependency-graph dev viewer.GOAL.md: design doc for the goal-mode feature split.packages/protocol,packages/acp-adapter,packages/agent-core,packages/server,packages/node-sdk,apps/kimi-web, thehash-importsbuild loaders,flake.nix, and changesets.Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.