The opaque per-key tool store (IAgentToolState / ToolStoreModel / updateStore) had no remaining producer or consumer in v2: its only real user, the todo list, was migrated to the dedicated session/todo domain (ISessionTodoService, TodoModel, todo.set). Drop the domain, its index exports, and the dangling references in kap-server actionMap, server-e2e resources, the test harness, ResumedAgentState, and the DI-scope docs. Replay silently skips the now-unregistered tools.update_store record type.
Reconciled with upstream de-barrel refactor (direct imports, removed per-domain barrels) and the loop-context interface rename. Kept the agent-run hook slot host on IAgentLifecycleService (run by mirrorAgentRun, observed by the Session-scope externalHooks adapter) and dropped the standalone IAgentRunHooksService, resolving conflicts in agentLifecycle, mirrorAgentRun, both externalHooks adapters, and the affected tests.
Move the requester-side agent-run hook slots (onWillStartAgentTask / onDidStopAgentTask) onto IAgentLifecycleService, the service whose run mirrorAgentRun wraps. mirrorAgentRun now resolves IAgentLifecycleService to run the slots, and the Session-scope SessionExternalHooksService observes them to translate into SubagentStart / SubagentStop external hook commands.
This removes the standalone Agent-scope IAgentRunHooksService (runHooks.ts) and the SubagentStart/Stop observation from the Agent-scope AgentExternalHooksService, so the slot host lives on the service that runs the agent task and the external-hooks adapters only observe. ISessionExternalHooksService becomes an empty observer token (it still registers SessionStart/SessionEnd via sessionLifecycle).
Update affected tests, IAgentLifecycleService stubs, and the DI Scope x Domain dependency map (puml + regenerated svg).
- rename packages/server-v2 to packages/kap-server and update every
@moonshot-ai/server-v2 reference across apps, packages, flake.nix,
and build scripts
- remove the experimental `kimi v2` CLI prefix and its tests from the
apps/kimi-code main entry
- add legacy BackgroundTask protocol types and the `blocked` turn
interrupt reason for cross-engine compatibility
- delete per-directory index.ts barrel files across src/ submodules
- update src and test imports to reference concrete module files directly
- rename a few index.ts entry files to named modules (migration.ts, providers.ts, builtin.ts)
- rewire src/index.ts to export from concrete modules instead of directory barrels
- add patch changeset noting the production-build fix
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.
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.
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.
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.
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.
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.
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.
- 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
- 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
- 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
* feat(agent-core): enable Preserved Thinking by default on the Anthropic provider
Default thinking.keep to "all" for the Anthropic provider (Claude and Kimi in Anthropic-compatible mode) while Thinking is on, via a context_management clear_thinking_20251015 edit, mirroring the Kimi default. Reuses [thinking] keep and KIMI_MODEL_THINKING_KEEP (env > config > default "all"); off-values disable it.
* feat(kosong): route Anthropic Preserved Thinking through the beta Messages API
Force the beta endpoint (client.beta.messages.create) when thinking.keep is enabled, since clear_thinking_20251015 is only honored there. Also prepend clear_thinking to any existing context-management edits (for example clear_tool_uses) instead of replacing them, keeping it first as Anthropic requires when combining edits.
* docs: clarify Anthropic beta endpoint and compaction keep behavior
Note in code comments and bilingual docs that enabling Anthropic Preserved Thinking routes requests to the beta Messages API (client.beta.messages.create), with keep=off as the escape hatch back to the standard endpoint. Correct the resolveThinkingKeep comment to reflect that compaction shares ConfigState.provider and intentionally carries the same keep.
* test(kosong): cover Anthropic beta endpoint (streaming and forced betaApi)
Add a streaming beta-endpoint capture and a test that withThinkingKeep forces the beta endpoint even when constructed with betaApi: false, pinning down the documented behavior.
AgentBlobServiceImpl no longer takes constructor arguments, so remove the
now-unnecessary [{}] from its SyncDescriptor registrations in the agent
lifecycle service and the blob tests.
- 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
* fix(web): clarify archive session confirmation copy
* fix(web): drop delete mention from archive confirmation copy
* feat(web): add archived-sessions restore entry to mobile settings
Mobile used a separate settings bottom-sheet with no archived-sessions
restore surface, so the archive confirmation pointing users to Settings
to restore was untrue on mobile. Add an Archived sessions sub-view to
the mobile settings sheet with search, sort and restore, mirroring the
desktop Settings tab.
* fix(web): refresh mobile archived list and use Input primitive
Refresh the archived-sessions list each time the mobile sub-view opens so
sessions archived from the mobile switcher show up without remounting the
sheet, and replace the hand-rolled search input plus inline svg with the
shared Input primitive.
* fix(web): use Button primitive for mobile archived restore
Render the mobile Archived sessions restore action with the shared Button
primitive instead of a hand-rolled button and scoped CSS, matching the
desktop archived list and the app's design-system rule.
* feat(kimi-web): render cron fire notices as in-transcript cards
Show scheduled-reminder fires as distinct notice cards in the web chat,
both live and after a reload, instead of hiding them. Each card carries
a humanized schedule, a dimmed job id, and a collapsible prompt body.
* fix(kimi-web): isolate cron notice prompt id and route prefixed events
- Give synthesized cron messages a fresh promptId so a fire mid-turn
cannot be reconciled into the optimistic user echo and hide it.
- Add cron.fired to KNOWN_AGENT_CORE_TYPES so event.-prefixed frames
reach the projector and render live too.
* fix(kimi-web): truncate long one-line cron prompts when collapsed
Slice the first line to the collapse limit with an ellipsis when a
single-line prompt exceeds it, so the collapsed state actually truncates
and the expand toggle is not a no-op for common one-line reminders.
* fix(kimi-web): keep cron notices from reconciling into optimistic user echoes
Skip optimistic-echo reconciliation for user messages whose origin is
cron_job or cron_missed: their prompt text can coincide with a still-
optimistic user message, and the loose content match would otherwise
replace the user's turn with the cron notice instead of appending.
* fix(kimi-web): keep in-turn cron injections from breaking tool results
A cron injection steered into an active turn lands inside that turn's
message sequence, between a tool use and its result. Treating it as a
hard user-turn boundary flushed the pending assistant group, so the next
tool result had no group to fold into and the tool rendered without
output. Embed such in-turn cron notices as a block inside the assistant
group instead, and only render a cron at a turn boundary as its own turn.
* fix(kimi-web): only embed cron notices while a tool is in flight
Embedding whenever a group was pending was too broad: on REST snapshots
without prompt ids the whole transcript shares one group, so an idle cron
fire merged into the previous assistant answer and its own reply kept
going in that group. Embed only while the group has a running tool (a
cron sandwiched between a tool use and its result); flush to its own
turn otherwise.
* fix(kimi-web): omit synthetic prompt id on cron notices
The synthesized cron message carried a cron_pr_ promptId that the web
client caches into promptIdBySession for Stop/abort. Because it is not a
real daemon prompt id, it clobbered the active promptId, so Stop first
aborted a nonexistent prompt and only recovered via the error fallback.
Omit the promptId; the reducer already skips optimistic-echo
reconciliation for cron-origin messages, so it is not needed for de-dup.
* fix(kimi-web): keep tool components from jumping on expand or collapse
Also show the scroll-to-bottom button whenever scrolled up, and render a
fallback icon for tools without a dedicated glyph.
* fix(kimi-web): preserve bottom follow during content-only resizes
Late-loading media can grow after scrollKey has run; keep chasing the
bottom on content growth, and only suppress follow during the pinned
expand/collapse window.
Set hyphens: none (with the -webkit- prefix) on body so chat and markdown text never gain a hyphen glyph at a line break; components still control where lines wrap via word-break/overflow-wrap.
Disable the ligatures that coding fonts enable by default (liga/calt/ss01) on native code elements so code renders literally, e.g. != stays as two characters.
Agent working notes (HANDOVER/handoff) and one-off UI prototype HTML files were committed by mistake. Remove the already-tracked ones, add .gitignore patterns for these classes of files and a .tmp/ scratch dir, and document the rule plus a pre-commit self-check in AGENTS.md so future mistakes are caught mechanically.
No publishable package is affected, so this PR needs no changeset.