A flow activation enqueued behind an active turn could slip past the
flag-disable cleanup: the disable listener scans queuedFlowPrompts
before the post-await tracking ran, leaving the prompt to launch the
supervisor blueprint with no flow runtime. Both the bundled and the
single-activation paths now re-check the flag right after tracking the
handle and abort the prompt (discarding its pending activation and
carried definition) when the flag went off during the await.
If the process stops after the durable final FlowVerdict but before
the separate FlowRunEnded lands, replay used to leave an inactive run
with no ended_reason, making a completed run indistinguishable from an
incomplete termination. The verdict fold now stamps endedReason
'finished' when the stage pointer passes the last stage; the later
FlowRunEnded replay stays idempotent over it.
The startup banner now comes from the backend client_configs endpoint
(config name client_banner) instead of the CDN-hosted tips.json, and is
fetched fresh on every startup with no caching. The payload keeps the
tips.json shape, plus two targeting additions:
- banner_platform (top level and fallback entries) limits display to
the given platform; missing, empty, or all means every platform, and
the CLI only shows entries targeting all or cli.
- banner_start_time/banner_end_time on fallback entries add scheduled
visibility windows with the same semantics as the active banner.
An ordinary SKILL.md can no longer claim a name inside the flow:
namespace — the shared skill parser rejects it, so the projected-flow
predicate's name-and-type check is backed by real provenance; and the
Agent tool's profile descriptions filter a flow tool name only when it
resolves to the builtin registration, so a user/MCP tool shadowing one
of those names stays advertised.
The flows skill source now watches ~/.kimi-code/flows/ so user-level
definitions hot-reload like project ones (a missing directory degrades
to load-time scanning); FlowStart declares both candidate definition
paths as read accesses so path-aware hooks and the access conflict
check see the user-level fallback; and the supervisor contract teaches
concurrent worker dispatch for independent sub-goals within one stage
(background workers joined before a single per-branch-evidenced
verdict, limited to read-only or non-overlapping file scopes).
Adapts the flow feature to main's session-scoped event contract: flow
state folds and the flow service stamp agentId on AgentStatusUpdated
(flow events fold only in the main agent's journal), the duplicated
scope-context parameter from the merge is dropped, and the flow service
tests publish agent-domain events through activated lifecycle contexts.
The TodoList veto now applies only when both TodoList and the batched
FlowStart resolve to builtin registrations; worker-agent undo events no
longer publish a session flow status of null over the main agent's
active run; FlowStart mirrors discovery precedence by falling back to a
valid user-level definition when the project file is readable but
invalid; and spawnable profile descriptions no longer advertise the
supervisor-only flow tools that a worker can never receive.
Three sites still treated a bare type: flow declaration as an
engine-managed flow: queued-prompt tracking (a flag-off toggle aborted
an unrelated skill's queued prompt), the steer multi-flow limit (two
ordinary flow-typed skills could not be steered together), and the
TUI/ACP slash builders (a flow-typed file skill took a bare /<name>
that could shadow builtins). All now use the shared isProjectedFlowSkill
predicate (type: flow AND the flow: catalog namespace), exported from
flowsSkillSource; the flow_invoked telemetry marker follows suit.
The flow_jump_review display now attaches to every resolvable jump, not
only under the approval policy, so UIs can show the from → to move and
the model's stated reason even when no review happens; the execution
hook gates the human review on the run's jump policy instead of display
presence, and the tool output echoes the reason for the transcript.
Flows are now discovered from the user-level flows directory under the
kimi home in addition to the project's .kimi-code/flows/, mirroring how
user skills work; a project flow shadows a user flow with the same id.
The activation auto-start accepts the user-level definition path, and
FlowStart falls back to the user directory when the project file is
missing.
Finished `!` output collapses to the first 10 visual rows with a `... (N more lines, ctrl+o to expand)` marker, sharing the global ctrl+o toggle with agent tool output; ctrl+o also expands the live buffer while the command runs. Replayed output mounts the same card and behaves identically. The shared truncation component, the running card's default view, and the agent bash path are unchanged.
* feat(datasource): add NDA/NBS, standards, IGO, xhcj, and caixin sources
* fix(datasource): narrow real-time-news ban to coverage gaps, require PublishTime citation
* fix(datasource): scope the real-time-news limitation to coverage gaps only
* fix(datasource): trim redundant clause in the real-time-news limitation
* fix(datasource): stop on a result that covers the question, not the first success
* fix(datasource): front-load trigger terms in the skill listing description
* fix(datasource): exempt discovery calls from the one-call workflow
GET /api/v2/sessions gains view=by_workspace: one request returns every
workspace with a matching session, each carrying its first group.page_size
sessions under the requested sort plus the workspace's full matching total,
with group-level page_token pagination (40922 on condition drift). Groups
key on the alias-canonical workspace id, so legacy split buckets of one
physical directory merge into a single group, matching the v1 alias
semantics. meta.has_prompt filters sessions by prompt presence (the v1
exclude_empty equivalent) in both views. The flat view and v1 routes stay
byte-compatible.
The global WS stream now fans out event.session.archived (live and cold
paths; payload carries the session id and workspace_id) and
event.workspace.created/updated/deleted, published by the core
IWorkspaceService on every mutation path including the implicit
createOrTouch on session creation.
kimi-inspect consumes the grouped projection as a single-column
workspace/session tree in the chat view; the session pane merges into the
right dock as the Session tab. The server API reference (en + zh) documents
the new parameters, the grouped response, and the new events.
* feat(agent-core-v2): rework the title generation excerpts
- Rebalance the excerpt budgets toward the user's prompts (400 chars
each) and trim the assistant segments (300) so titles follow the
user's task instead of narrating the assistant's reply.
- Cap each prompt in the default user_prompts excerpt so one long
paste no longer starves the remaining prompts.
- Compose the digest excerpt from the full conversation arc: every
natural-language user prompt in the live window paired with its own
turn's final assistant text, interleaved chronologically, with
per-segment caps and a 3000-char total budget (middle turns elided).
* chore: scope the title changeset to agent-core-v2
* fix(agent-core-v2): dedupe digest prompts and elide whole turns
- Drop the redundant `| undefined` from the optional
TitleDigestTurn.assistant per the monorepo optional-property
convention.
- Deduplicate user messages by id when constructing digest turns, so a
prompt already in the context and still active in the queue does not
produce two turns.
- Elide the over-budget digest at whole-turn granularity, keeping each
assistant line paired with its own user line.
* docs(agent-core-v2): describe the full-arc digest in the SessionTitleSource contract
* fix: fail fast on provider-filtered empty responses
An APIEmptyResponseError carrying finishReason 'filtered' (OpenAI
content_filter, Anthropic refusal) is deterministic: replaying the same
request re-triggers the provider safety filter. Both isRetryableGenerateError
implementations (kosong, agent-core-v2) treated every empty response as
retryable, so step retry replayed the doomed request the full 10 attempts
before the filter notice surfaced. Return non-retryable for filtered empty
responses in both engines; the error already carries the provider.filtered
code, so the turn fails immediately with the existing filter notice.
* fix: skip the compaction shrink-retry for filtered empty responses
Both full-compaction loops routed every APIEmptyResponseError into the
shrink-and-continue branch before isRetryableGenerateError was consulted,
so a filtered response was retried with shrinking input instead of failing
fast. Exclude finishReason 'filtered' from the shrink branch in both
engines; it now falls through to the retryability check and throws
immediately. Add end-to-end tests (real kosong generate over a filtered
think-only stream) asserting a single attempt with the history untouched.
---------
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
* feat(kimi-code): add China/International region selection for OAuth login
- Add region profiles (cn/overseas) and resolver in @moonshot-ai/kimi-code-oauth:
env override → persisted login host → install-channel marker → default cn
- /login now offers Kimi Code (China) / Kimi Code (International); the CLI
login entries (kimi login, kimi acp --login) accept --region cn|overseas
- Update/plugin/site/telemetry endpoints derive from the selected region;
plugin trust list covers both .com and .ai hosts
- kap-server: POST /oauth/login accepts an optional region; new GET /oauth/region
* fix(oauth): keep an explicit default-slot login ahead of the install marker
A China login persists no oauthHost (the default credential slot carries
no host trace), so after switching back from International the resolver
fell through to a stale overseas install marker. Treat a persisted
default-slot oauth ref (key === oauth/kimi-code) as an explicit-cn signal
that outranks the marker; getRegion() on the v2 side mirrors it.
* fix(agent-core-v2): thread the default-slot key through capability region resolution
Capability installs resolved the region from the persisted oauthHost only,
so an explicit China login (which persists no host) lost to a stale
overseas install marker. Pass the oauth ref key through as well, matching
getRegion(). Also move the region contract notes into the auth.ts file
header per the package comment convention.
* fix(agent-core-v2): honor the region-marker opt-out for the telemetry endpoint
Hosts that set KIMI_CODE_REGION_MARKER=off (the desktop embedded server)
skip the install marker in getRegion(), but the default telemetry endpoint
still consulted it, so a stale overseas marker could split the reported
region from the telemetry destination.
* feat(cli): show region site domains in login platform selector
* chore: reword oauth login changesets
* fix: honor the region marker opt-out in the CLI and capability resolvers
* refactor: rename login region values to mainland-cn and global
* fix: keep the --region help text in English
* fix: simplify the --region help text to site domains
* feat: drop the suggested login platform order
* feat: split a browser-safe region profile table out of the region resolver
* Revert "feat: split a browser-safe region profile table out of the region resolver"
This reverts commit a037b1143e.
* fix: read the install marker from the bootstrapped home directory
* fix: resolve the server plugin marketplace from the active login region
* feat: expose the login region option through the klient auth facade
* fix: drop a comment from the v2 auth region test
* fix: keep scoped base-only logins on their environment for a bare login
* fix: invalidate the region cache on the provider-manager logout path
* fix: route client-config fetches through the active region profile
* fix: resolve the telemetry endpoint per flush so a login region switch applies in-process
* test: expect the telemetry endpoint resolver in the CLI init assertions
* fix: resolve the default telemetry endpoint from the bootstrapped home
* chore: reword the oauth login changeset around the two login methods
* chore: trim the oauth login changeset to the headline
* feat: let hosts override the region marker env through the server bootstrap env bag
- give the builtin agent profile an explicit subagents allowlist (coder, explore, plan), restoring v1 semantics
- inherit the default profile's allowlist when a caller profile declares none, instead of leaving delegation unrestricted
- pass a lone "*" subagents field through as an explicit unrestricted marker
* docs: document KIMI_CODE_CUSTOM_HEADERS on the env vars page
* docs: address review on KIMI_CODE_CUSTOM_HEADERS entry
- use a neutral gateway header name in the example
- correct the release version to 0.20.2
- scope the override claim to exact-name matches and warn against
case-variant auth headers
* docs: describe protocol-dependent Authorization precedence
On the OpenAI-compatible protocols (kimi/openai/openai_responses) an
exact Authorization custom header is applied after the SDK-generated
bearer token and therefore replaces it; /models listing keeps its own
authentication.
* docs(zh): add the required space before the config-files link
Per the mixed-content spacing rule in docs/AGENTS.md.
---------
Co-authored-by: bj456736 <bj456736@users.noreply.github.com>
* fix(kap-server): serve real session usage in snapshot and persist per-turn context readings
* fix(kap-server): omit unknown session usage fields instead of reporting zero
* refactor(agent-core-v2): persist cron tasks as durable wire records
- write CronAdd/CronDelete/CronCursor as durable wire records and rebuild the cron task table from dispatcher replay
- migrate legacy per-workspace cron JSON files into the wire on first resume, then drop the file-based persistence service, its registrations, and the bootstrap cron scope
- derive the session cron view from the agent replayable cron state and remove the redundant session-level copy
- let session forks inherit cron tasks through the copied wire instead of duplicating task files
* fix(agent-core-v2): keep legacy cron tasks on cold forks and flush before cleanup
- inherit legacy cron task files into a full fork's wire so cold sessions forked before their first post-upgrade resume do not silently lose scheduled tasks
- flush the migrated wire records before deleting legacy files so a crash cannot lose both copies
* refactor(agent-core-v2): drop the legacy cron file migration
- stop reading legacy per-workspace cron JSON files entirely; pre-upgrade tasks simply stop applying instead of being migrated into the wire
- remove the legacy read path, the fork-time legacy inheritance, and the now-unused session context/document store injections
* refactor(kap-server): table-driven dispatch for multi-action routes
- add a shared action-dispatch helper; sessions, prompts, plugins, questions, and modelCatalog collection routes declare action tables with module-level handlers
- add ISessionManager.status returning the session summary; the archive action checks it instead of resuming the session
- archive cold sessions through the persisted-metadata path shared with batch archive
* fix(agent-core-v2): read-your-writes for session index point gets
Overlay pending mirror summaries in getFromReadModel so a freshly recorded
summary (e.g. cold-session archive) is visible to GET immediately, matching
the existing pending overlays in list and cursor resolution.
* feat(agent-core-v2): carry prompt attachments on turn.started so the live transcript projects them
* fix(transcript): clear the transcript goal when the goal is cleared
* fix(agent-core-v2): count a prompt media part as a transcript attachment only when its id matches its daemon file URL
* test(kap-server): expect the session-media file id on converted prompt parts
Review round 46: the batching vetoes and gate/jump approval hook
resolved flow tools by name only, so a user/MCP tool shadowing a
flow-tool name inherited flow semantics (solo-response vetoes, gate
review). The hook now confirms the builtin registration in the tool
registry, mirroring the approval-policy fix.
Review round 45:
- A metadata persistence failure between bundle preparation and the
activation dispatch now discards the stored activation payloads
before rethrowing, so retries cannot accumulate entries in the
unbounded activation-data map.
- The flow admission guards (flag, active/queued run, main-agent)
apply only to projected flow skills (the flow: catalog namespace):
an ordinary SKILL.md that declares type: flow is not engine-managed
and activates like any other skill again.
Review round 44:
- promptWithSkills rejects a deterministically-known flow conflict
(active or queued run, disabled feature) before the prompt metadata
update persists the doomed request as lastPrompt or a session
title; the post-metadata check stays as the race-closing final
admission next to the SkillActivated dispatch.
- The flow-reviewer profile description no longer claims read-only
tools: shell access is verification-only by contract and every
command still goes through the approval policy.
Move the external hook services out of app/externalHooksRunner,
session/externalHooks, and agent/externalHooks into
features/externalHooks, assembled as the ExternalHooksFeature unit:
- services live under per-scope subdirectories (app/, session/, agent/);
shared pure helpers (types, hook matching/dispatch, process spawn,
prompt result rendering) live under internal/
- the runner and the two observers are contributed through the Feature
seams (ScopeUnits materialization); the hooks config section stays on
the static import=register channel
- update the package entry leaf exports, the plugin domain imports, the
kap-server events-zod import, and the affected tests; regenerate the
state manifest
Conflicts were all generated or baseline-tracking artifacts: the wire
manifest is regenerated (now including flow_run.jumped), the tool
snapshot tests are re-recorded, and the compaction token baselines are
re-shifted on top of main's new values (+11 for the FlowJump tool).
* fix(vscode): multi-select question jumps to next after only one answer selected
* chore: add changeset
---------
Co-authored-by: gaoyuan <gaoyuan@moonshot.ai>
Review round 43:
- FlowStart reports a queued activation reservation with a dedicated
message instead of racing it: the queued prompt keeps its start.
- The default-approve policy resolves the flow tools' registry source
and auto-approves them only when they are the builtin registrations,
so a user/MCP tool shadowing a flow tool name (possible while the
flag is off) no longer inherits the approval bypass.
- A failed prompt delivery during a flow activation (inject/enqueue
rejection) now releases the pending reservation and stored
definition on the error path.
- The projected blueprint no longer promises that every UI shows flow
progress (UIs with flow support do).
Review round 42:
- The skills route maps REQUEST_INVALID activation failures (active
or queued run, disabled feature, non-main agent) to the
VALIDATION_FAILED envelope instead of rethrowing them as a 500 —
these are client conflicts, not server failures.
- The package entry point re-exports the FlowJump input schema and
tool contract alongside the other flow tools.
* fix(agent-core-v2): emit subagent.spawned after task registration
The spawned signal previously fired at launch, before the run's task
registration, so clients learned the agent id with no task id to bind
cancel/status actions to; a failed registration also left a spawned row
behind for a run that never registered. Emit it only after registerTask
succeeds and carry the task id on the event.
* fix(agent-core-v2): keep spawned ahead of started for Agent-tool runs
The TUI drops subagent.started until spawned has established the row,
and a failed registration must not leave a started row behind with no
terminal event. Defer the mirrored started dispatch so the Agent tool
can emit it itself after registration and spawned.
* fix(agent-core-v2): void the deferred started dispatch
* fix(kap-server): key Agent-tool transcript rows by the registered task id
Transcript-protocol clients suppress the raw task.*/subagent.* session
events, so they only saw a subagent row keyed by agent id that cannot
address /tasks/{id}, plus a second row once task.started landed. Key the
spawned row by the task id it now carries, fold task.started and the
subagent lifecycle back into it, and keep the agent-id path for spawns
without a registration (swarm/session-init/tower). Statement-level
ordering notes move to the file headers per package convention.
* test(agent-core-v2): split the spawned/started ordering contract into its own test
* fix(kap-server): keep subagent result details across task termination and drop stale task mappings on taskless respawns
* fix(kap-server): recover the agent-to-task association from a backfilled task.started
* fix(kap-server): seed pre-attach Agent task mappings on the transcript binding
* fix(kap-server): seed the full in-flight task row on transcript bind, not only its id
* docs(agent-core-v2): name the state-domain event dispatcher in the Agent tool header
* style(kap-server): drop comments in transcript services per the no-comments lint rule
Review round 41:
- The activation guard now treats a disabled flow flag as a rejection
(the tools and injector are gone), instead of as permission to
continue — closing the window where a request prepared from the old
catalog published its activation after the feature was retracted.
- The skill service tracks queued flow prompts (bundled and single)
and aborts them when the flag turns off, so an accepted supervisor
blueprint no longer launches into a session whose flow machinery
has been withdrawn; the abort flows through the existing completion
cleanup, releasing the reservation and stored definition.
The supervisor can now move an active run to another stage: backward
to redo work whose conclusion was invalidated, forward to skip stages
that do not apply to the task. The definition's frontmatter governs
it via jumps: approval (default — every jump goes through a user
review), free (unsupervised), or disabled (strictly linear, the old
behavior).
- New FlowJump tool (to + reason, both required): validated like the
other flow tools, solo-response only, supervisor-only, flag-gated.
- A durable FlowJumped event moves the undoable stage pointer and
appends a kind: 'jump' record (from/to/reason/decidedBy) to the
retained audit trail; the run epoch bumps on every jump so verdicts
and reminders prepared before it are void, and a revisited stage
re-discloses its reminder.
- The approval flow reuses the gate-review machinery: a new
flow_jump_review display (protocol + kap-server + engine schemas),
epoch-bound reviews, observed-rejection provenance, and no
session-wide approval anywhere (TUI choices, ACP options, VS Code
downgrade all treat jumps like gates).
- GET /sessions/{id}/flow exposes run.jump_policy and the audit
records as a verdict/jump union.
- The supervisor contract's Order rule now teaches FlowJump instead
of mandating abort-and-restart.
* feat(kimi-code): specialize the WaitFor tool's transcript display
* feat(agent-core-v2): emit status progress while WaitFor is pending
* fix(kimi-code): route WaitFor dimming through the TUI theme
* feat(kimi-code): support replaceable status updates in tool progress
* fix(kimi-code): forward status progress to subagent activity surfaces
* fix(agent-core-v2): drop the redundant undefined from ToolUpdate.replace
* fix(kimi-code): honor replace semantics in the subagent live status path
* test(agent-core-v2): drive the WaitFor progress test through a manual tick
* fix(kap-server): mirror ToolUpdate.replace in the ws event schema
* refactor(agent-core-v2): expose the WaitFor progress scheduler as a public seam
* fix(kimi-code): pass child wait statuses without the trailing newline
* feat(agent-core-v2): tick the WaitFor progress status every second
* feat(agent-core-v2): format WaitFor progress durations as 1m 15s
* feat(agent-core-v2): omit zero seconds and minutes in WaitFor durations
Review round 40:
- The completion cleanup also treats the blocked terminal state as a
release: a hook-blocked bundled flow prompt never reaches a step
head, so its reservation must not outlive it (the blueprint's
FlowStart recovery covers a later retry).
- Turning the flow flag off clears queued reservations, since the
retracted step-head injector can no longer consume them; re-enabling
no longer inherits a dead pending activation that rejects every new
flow.
* refactor(agent-core-v2): unify the loop-event fold into one core with two materializations
The loop-event stream was reduced by two hand-mirrored state machines:
loopEventFold.ts for the live/replayed context and contextTranscript.ts
for the full transcript behind the messages endpoints, kept in sync by
comments alone and already drifted (transcript dropped tool-result note
metadata and never closed a dangling tool exchange at step.end).
createLoopEventFold now owns the shared state machine once (settle,
pending tool exchanges, deferred appends, vacuous tracking) and both
views plug in as LoopEventFoldSink materializations. New parity tests
pin the foldedLength === live length invariant the endpoints splice on.
* fix(agent-core-v2): drop every removed prompt's injections on multi-turn transcript undo
The transcript undo only walked prompt-owned injections off the oldest
counted anchor, so with count > 1 an injection owned by a newer removed
prompt (e.g. an image-compression caption) survived the display undo
while the live context removed it. Collect every counted anchor's id
during the walk and sweep their owned injections afterwards, keeping
the transcript's 'prompt-owned ones leave with their prompt' contract
for every count and matching the live view.
* refactor(agent-core-v2): drop module headers from the context fold modules
The comment-free zone lint only allows JSDoc on exported symbols.
* fix(agent-core-v2): recover fold state after rehydration
* fix(agent-core-v2): scope undo injections to their prompt
* fix(agent-core-v2): settle open transcript frames when compaction lands mid-fold
An overflow-triggered compaction arrives with the failed attempt's
frame still open. The transcript appended the summary marker and reset
the fold but left the frame, so a vacuous partial stayed in the entries
while the live context dropped it, and a pending tool exchange lost its
interrupted result. Settle through the shared fold core at the marker
instead: close pending tool calls, drop or seal the open frame, then
append the summary. recoverFoldedLength recomputes the absolute count
right after either way.
* fix(agent-core-v2): keep legacy compaction recovery on the pre-settlement count
A legacy context.apply_compaction record (compactedCount without
keptUserMessageCount) recovers foldedLength as 1 + (foldedLength -
compactedCount), and the live legacy tail shape keeps the unsettled
open frame inside history.slice(compactedCount). Settling the fold for
those records shifted foldedLength by the settlement delta before the
recovery read it, leaving the transcript count one off the live
context. Gate the settle to modern records; legacy records keep the
previous freeze-and-reset behavior.
Review round 39:
- A queued flow prompt that is aborted (or fails) before reaching a
step head now releases its reservation: the prompt handle's
completion discards the pending activation and its stored
definition, so an orphaned entry can no longer block every later
flow activation. The single-activation busy path does the same.
- The active/pending admission check moved after the awaited metadata
update, directly adjacent to the synchronous SkillActivated
dispatch, so two concurrent promptWithSkills requests can no longer
both pass the check before either publishes its activation.
- Bundle preparation rolls back stored activation data when a later
skill in the bundle fails to prepare.
- The profile catalog's builtin protection now covers every
builtin-priority contribution (features included), so a workspace
file profile named flow-reviewer cannot silently replace the flow
reviewer without override: true.
Review round 38:
- The activation entry points also reject a flow activation while one
is already queued (pending, not yet consumed at a step head), so two
flow prompts admitted behind a running turn no longer deliver a
second blueprint whose run never starts.
- The VS Code webview downgrades approve_for_session on a flow gate
to a one-shot approval — the engine re-asks every human gate, so
recording a session rule would promise what is never honored
(mirrors the ACP option change).
- The activation-data store no longer caps entries (a concurrent
burst could evict a definition before its SkillActivated consumed
it); failure paths in both entry points now discard what they
stored.