chore: merge origin/main into feat/hook-permission-decisions- #3 - #5
Merged
Conversation
…nshotAI#3366) Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
* fix(transcript): preserve bundled prompt origin * refactor(transcript): narrow steer origin projection
* fix(transcript): preserve turn prompt identity * fix(transcript): backfill active prompt identity * fix(transcript): seed late-bound turn identity * fix(transcript): persist turn prompt identity * fix(transcript): skip undone prompt identities * fix(transcript): hide undone continuation turns * docs(agent-core): update wire manifest * fix(transcript): match cold turns by prompt identity * fix(transcript): match internal prompt turns by origin * fix(transcript): align undo anchors with context * fix(transcript): prioritize cold turn matches * fix(transcript): retire unmatched turn boundaries * style(transcript): align cold matcher
* feat(agent-core-v2): spawn tower workers on a snapshot of base checkout WIP Tower worktrees were created from the base branch tip, so a worker never saw uncommitted changes sitting in the base checkout — it would start building on a foundation that did not exist in its worktree. When a mission branch is first created, the store now captures the base checkout's dirty paths (staged, unstaged, and untracked per git status; .tower/ and git's own ignore rules excluded; unmerged paths refuse the spawn) into a synthetic snapshot commit built through a temporary index (read-tree + add + write-tree + commit-tree), so the user's checkout, index, and base branch are never touched. The mission branch starts at that commit and the mission records it as spawnBase. The merge gate diffs the branch from spawnBase instead of the base tip, so snapshotted WIP is never mistaken for a worker scope violation, and the reviewer briefing diffs from the same point. Alternatives considered: applying the WIP as uncommitted changes inside the new worktree (fragile — it mixes into the worker's first commit and can be lost before that), and excluding the WIP file set inside the gate (permanent holes in scope enforcement, hidden provenance). A snapshot commit keeps the gate strict and makes the WIP an explicit, mergeable part of the branch history. TowerMerge now also refuses to merge while the main checkout holds uncommitted changes in files the merge would overwrite (blocked reason base-dirty) and tells the user to commit or stash them first; dirt that does not intersect the merge no longer blocks it. * fix(agent-core-v2): harden tower base-WIP snapshot edge cases - fall back to the base branch as diff base once a rebase drops the snapshot commit (it is only used while still an ancestor), so scope checks, reviewer prompts, and conflict attribution stop blaming base changes on the worker - run snapshot index commands from the worktree top-level and filter .tower by path segment, fixing dirty-base spawning from a repo subdirectory (porcelain paths are worktree-root relative) - refuse to snapshot WIP collected from a checkout that is not the recorded base (or a detached HEAD) instead of mixing another branch's content into base history * fix(agent-core-v2): let tower mode take over from a live but idle owner session The tower-mode ownership check refused entry whenever the recorded owner session was still materialized in the process. kap-server keeps sessions materialized until explicit close, so a session that stopped abnormally (closed tab, failed turn) owned the workspace tower forever and no second session could enter tower mode. Now the owner is only protected while actually occupied: entry is refused when the owner session has an active turn or a pending interaction; a live but idle owner is exited remotely (durable TowerModeExit in its own event log) and the entering session takes over. * feat(agent-core-v2): keep tower mode active after tower teardown * chore: add changeset for tower teardown staying active * docs(tower): merge changeset * docs(tower): merge changeset * fix(agent-core-v2): make tower mode mutually exclusive with plan and swarm modes * chore: add changeset for tower mode mutual exclusion --------- Co-authored-by: konghuanjun <konghuanjun@moonshot.ai>
* fix(agent-core-v2): persist prompt resolution events so replays can reconcile queued prompts * docs(changset): update prompt desc --------- Co-authored-by: konghuanjun <konghuanjun@moonshot.ai>
…mental (MoonshotAI#3334) * feat(secondary-model): enable the subagent model pool by default * feat(secondary-model): graduate the subagent model pool out of experimental * fix(secondary-model): honor the v2 default_model key on the legacy engine * fix(secondary-model): enforce forced subagent pools on the legacy engine * fix(tower): keep reviewers on primary model * fix(secondary-model): live-apply picker saves and replace the legacy model key * fix(secondary-model): accept default_model in the live-apply setter and clear on removal * fix(secondary-model): revert the legacy v1 engine, keep the v2 pool opt-out * feat(secondary-model): graduate pool with model source telemetry * fix(secondary-model): keep legacy engine opt-in * test(sdk): pin secondary-model engine divergence * fix(tower): honor forced model for reviewers
…onshotAI#3392) * feat(agent-core-v2): preserve config.toml formatting on writeback Config persistence previously re-serialized the whole document on every write, destroying user comments, key order, and blank lines. Writes now go through a raw-text channel on the TOML document store: the file is scanned into top-level domain regions, changed domains are diffed at key level so only edited statements are rewritten, and untouched regions keep their original bytes (including CRLF). The effort max-to-high migration performs a single-line text replacement instead of a full rewrite. Ambiguous constructs (dotted keys, arrays of tables, unmapped files) fall back to re-serializing the affected domain, and any scanner failure falls back to the previous whole-document write. Byte-identical results skip the write entirely. * feat(agent-core-v2): support quoted table headers in config writeback The region scanner parsed only bare-key table headers, so files using quoted segments such as [models."acme/m1"] or ["x.y"] fell back to a whole-document rewrite, dropping user comments for exactly the configs with the most hand-written content. Header parsing now walks TOML key paths segment by segment (bare, basic-quoted with escape decoding, literal-quoted), so quoted regions participate in key-level preservation and are edited or removed in place. Emission of new sub-table headers already quotes non-bare segments through smol-toml. Unparseable headers still decline to the whole-document fallback.
…all permission modes (MoonshotAI#3290) * feat(agent-core-v2): require approval for dangerous bash commands in all permission modes * feat(agent-core-v2): deny dangerous commands in auto mode, unwrap command launchers, and skip the guard for non-interactive hosts * feat(agent-core-v2): support disabling the dangerous-command guard via config * test(node-sdk): project v2's env-materialized empty permission section in config parity
… degradation paths (MoonshotAI#3394) * feat(agent-core-v2): add P0 telemetry events for auth, execution, and degradation paths * test(agent-core-v2): drop telemetry emission assertions from existing test suites
…d Never Ask (MoonshotAI#3403) TUI copy for the three permission modes (manual/yolo/auto) now uses the new display names and descriptions across the /permission selector, footer badge, toggle notices, goal/swarm start prompts, session replay, and /status. The /yolo and /auto commands are renamed to /ask-when-needed and /never-ask, with the old names kept as aliases.
…ervices (MoonshotAI#3402) * refactor(agent-core-v2): migrate todo domain to agent-scoped DI service * refactor(agent-core-v2): migrate skill domain to agent-scoped DI service * refactor(agent-core-v2): migrate reminder domain to agent-scoped DI service * refactor(agent-core-v2): migrate interaction domain to agent-scoped DI service * refactor(agent-core-v2): migrate cron domain to agent-scoped DI service * refactor(agent-core-v2): migrate dateChange domain to agent-scoped DI service * refactor(agent-core-v2): migrate goal domain to agent-scoped DI service * refactor(agent-core-v2): remove agent runtime infrastructure * docs(agent-core-v2): rewrite agent domain standard as agent-scoped DI service * fix(agent-core-v2): restore actor services on late feature re-provide and order agent-created events after metadata registration
…er Ask (MoonshotAI#3406) Follow-up to MoonshotAI#3403 for the user docs: living pages (en + zh mirrors) now use the new mode names and descriptions — slash-commands and interaction references for /ask-when-needed (aliases /yolo, /yes) and /never-ask (alias /auto), CLI flag and config glosses, guides, customization pages, and the docs AGENTS.md terminology table. Wire and config ids (manual/yolo/auto) are unchanged; release notes are historical and untouched.
… staging (MoonshotAI#3405) * feat(cli): compress native update artifacts and decompress them while staging Co-authored-by: qer <wbxl2000@outlook.com> * fix(cli): generate compressed native artifacts in the publish job The zip's checksum never pairs with the bare executable the updater verifies after inflating, and artifacts produced in the matrix job never leave the runner (the workflow's upload-artifact path lists only the zip). Extract each zip in the publish job, hash the bare executable, and emit the .zst/.tar.gz artifacts there so the manifest entry pairs checksum with what compressed inflates to, and everything new reaches the release via the existing upload glob — no workflow changes needed. Co-authored-by: qer <wbxl2000@outlook.com> * fix(cli): keep the .exe suffix in windows manifest filenames The updater's bare-binary fallback downloads entry.filename, and the CDN layout carries .exe on Windows — without it the fallback 404s. The .zst and .tar.gz artifact names stay unchanged. Also fold the manifest coverage into the pre-existing release-artifacts test (its fake-zip fixture predates extraction) and pin the end-to-end chain there. Co-authored-by: qer <wbxl2000@outlook.com> --------- Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com> Co-authored-by: qer <wbxl2000@outlook.com>
…paths (MoonshotAI#3415) git status --porcelain=v1 without -z C-quotes non-ASCII paths under git's default core.quotePath, and parsePorcelain consumed the quoted form verbatim while the posix() helper turned each \3xx octal escape into a fake path segment — the changes tree then split those segments on '/' and rendered Chinese filenames as bogus directory chains, and diff requests for such entries failed the same way. Run porcelain with -z (NUL-separated, never quoted; rename records are XY new\0old with the new path first) and drop the posix() helper — porcelain already emits '/' separators on every platform. The v1 engine shares the bug and is intentionally left unchanged for now. Refs MoonshotAI#3414 Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com> Co-authored-by: liruifengv <liruifeng1024@gmail.com>
Signed-off-by: 7Sageer <sag77r@hotmail.com>
…#3418) MoonshotAI#3411 dropped two positional constructor params from WorkspaceInstanceManager, shifting the agent-profile stub the Reflect.construct fixture pins by index, and turned the broken subagent-pool session create from VALIDATION_FAILED into a success; both suites fail on main since then. Co-authored-by: user <user@userdeMacBook-Pro.local>
…mpaction (MoonshotAI#3409) * fix(agent-core-v2): re-remind subdirectory AGENTS.md after context compaction Deliver the discovery reminder through the reminder runtime's register() channel so it re-injects whenever the previous reminder leaves the context (compaction, /clear, or undo), instead of firing a one-shot notify() that compaction silently drops while the known-set survives. Discovery bookkeeping splits into agentsMdReminder.pending (discovered, not yet read) and agentsMdReminder.known (injected or read); the provider injects only pending paths not covered by the last injection's disclosure. A newly created AGENTS.md lands in pending until read; a modified one counts as injected. * fix(agent-core-v2): drop deleted AGENTS.md from reminder bookkeeping A discovered-but-unread AGENTS.md that gets deleted stayed in agentsMdReminder.pending, so the next re-injection after context loss pointed the model at a file that no longer exists. Remove deleted paths from both the pending and known sets on the watcher event. * Delete .changeset/agents-md-reminder-compaction-reset.md Signed-off-by: 7Sageer <sag77r@hotmail.com> --------- Signed-off-by: 7Sageer <sag77r@hotmail.com>
…rted telemetry (MoonshotAI#3419) * feat(agent-core-v2): report enabled experimental flags in session_started telemetry Add a sorted, comma-separated experimental_flags property to the v2 session_started event so feature-gated metrics gain a session-granular exposure denominator, and carry the post-apply enabled set on the TUI experimental_features_apply event to bound mid-session flag flips. * feat(node-sdk): carry experimental flags on the harness session_started row The v2 in-process path emits session_started twice (engine-side from SessionLifecycleService, harness-side from KimiHarness), and only the engine row carried experimental_flags, so consumers could double-count or miss the flag dimension. Merge a per-emission dynamic getter into the harness row, wired to the in-process engine's flag service, so both rows report the same enabled flag set. * fix(node-sdk): keep experimental_flags canonical on session_started A caller-supplied experimental_flags in per-call sessionStartedProperties could overwrite the engine-owned dynamic value, breaking row-to-row flag consistency. Merge the dynamic properties after the session-scoped ones so engine-owned keys always win. * fix(agent-core-v2): count tower as exposure only once assembled The tower flag reacts live to mid-process flips, but TowerFeature's tools and profiles are assembled at App scope construction, so a session started after an in-process flip cannot actually use tower. Filter it out of experimental_flags on both session_started producers unless isTowerFeatureAssembled says the feature is assembled in this process, keeping the exposure denominator honest. * test: recalibrate fixtures for the added constructor param and drop timing-sensitive tower assertion The WorkspaceInstanceManager test builds the service with positional args; the new flags param shifted unitHostFactory one slot. The tower inclusion assertion in the SDK integration test depended on feature assembly winning the race against async config load, which slow CI runners lose; tower assembled-state coverage stays with the tower service tests. * refactor(agent-core-v2): move flag exposure gating into the flag definition Telemetry consumers hardcoded tower's assemble-at-startup semantics to keep it out of experimental_flags until assembled. Push the gate into FlagDefinitionInput as an optional isExposed predicate owned by the flag's own domain, expose it as IFlagService.exposedIds(), and let both session_started producers call it — no feature-specific knowledge leaks into the SDK or the session lifecycle.
…overy reminder (MoonshotAI#3420) * fix(agent-core-v2): drop compaction mechanics from the AGENTS.md discovery reminder The re-injection trigger is harness mechanics the model cannot act on, and the unread-path condition never fires from the model's perspective. End the reminder at the actionable instruction. * fix(agent-core-v2): reword the AGENTS.md discovery reminder lead Lead with the files instead of the paths, and replace harness vocabulary (touched / covered by / injected instructions) with terms the model can place: apply to, your system prompt.
MoonshotAI#3399) Tower mode is now entered only manually — /tower on, or /tower <base-branch> to also pin the local branch missions merge back into. The agent can no longer enter it on its own: TowerInit refuses while the mode is off and points at the slash command. /tower <base-branch> is deterministic end to end: a missing base branch is created from the current checkout (uncommitted changes committed onto it as a labeled WIP snapshot), the checkout switches to it, and the tower workspace is initialized immediately instead of relying on the model to apply the base. With an existing workspace the tower rebases onto the requested base when no missions are open and refuses naming the blocking missions otherwise. The base rides the tower_mode.enter event into the new tower.base state key and TowerInit falls back to it. Tower agent deaths (failed/timed_out/killed/lost) are recorded in the tower protocol: TowerStatus marks dead roster entries and warns about missions whose owner died, with a resume hint. The tower console prompt now requires a per-worker deliverables summary before TowerTeardown. The session profile REST surface accepts tower_base so web clients can turn tower mode on with /tower <base-branch> instead of sending the argument as a prompt. Co-authored-by: konghuanjun <konghuanjun@moonshot.ai>
…oonshotAI#3421) * fix(agent-core-v2): persist plan.revision as agent-relative key - replace the session-scoped `path` field in durable plan.revision records with an agent-relative `key`, resolved to a display path at read and projection time against the current agent/session scope - migrate legacy `path` records during wire restore and rewrite the journal once migration succeeds, emitting low-cardinality `wire_plan_revision_migrated` telemetry - tolerate journal truncation during the migration rebuild and keep the legacy `path` display in cold transcript folds for unmigrated records - keep external transcript marker `path` and meta `reviewPath` projections unchanged * perf(agent-core-v2): fork sessions without materializing the target - copy session files byte-for-byte (wire.jsonl included, parallel file writes) and append the forked marker per agent instead of parsing and rewriting every wire journal - write the forked session's state.json, agent registry, and session index entry directly without creating a live session or agent scopes; fork returns SessionMeta and SessionForkedEvent drops the scope handle - keep turnIndex truncation on the per-record path - kap-server fork/createChild routes, the klient facade and wire contract, and the node-sdk forkSession resume the forked session through the normal resume path when a live handle is needed - stop healing truncated source wires during fork; the forked session repairs its own copy lazily on resume - add the `kimi fork [--cwd] [-y]` CLI command
…hotAI#3422) - add mtime to the file system storage service (node-fs stat, memory write clock) - record the source max mtime in the session index checkpoint at projection - compare the cheap mtime signal before adopting a persisted manifest on prepare - refresh the checkpoint mtime after reconcile so warm starts stay scan-free
… subagent stop reasons (MoonshotAI#3459) * feat(agent-core-v2): add a handoff step after forced stops and report subagent stop reasons - When the tool-call repeat breaker stops a turn, the loop now runs one text-only handoff step; tool calls issued in that step are refused and the turn ends with the same stop reason. - Tool results can carry stopTurnReason; LoopRunResult and turn.ended expose the forced stop reason. - Subagent results report stop_reason, a resume hint, and a next-step line; a turn that ends without a final message fails with agent.no_final_message, and the step-cap failure is rephrased for the parent model. - Remove the subagent summary length policy and its continuation prompt. * chore(agent-core-v2): regenerate the state manifest after merging main * fix(agent-core-v2): run the handoff step at the step cap and surface swarm stop reasons - The per-turn step cap no longer skips a queued handoff step, so a forced stop that lands on the last permitted step still produces the text handoff instead of a max_steps failure. - AgentSwarm results carry the worker's stop reason and render it as a stop_reason attribute; a stuck worker now also triggers the resume hint. * fix(agent-core-v2): keep the repeat-breaker classification and persist turn.ended stop reasons - A handoff that ends without text keeps stop_reason repeat_breaker on the parent-facing failure instead of collapsing to no_final_message. - Only a user cancellation maps to stop_reason cancelled; other kills report stopped and keep the resume hint. - turn.ended persists stopReason in its durable record and the wire manifest lists it.
…needed or never-ask mode (MoonshotAI#3473) * feat(kimi-code): show file-change warning when switching to ask-when-needed or never-ask mode * Update permission-mode-file-warning.md Signed-off-by: 7Sageer <sag77r@hotmail.com> * feat(kimi-code): show file-change warning on goal and swarm permission switches * fix(kimi-code): indent every line of multi-line notice details * feat(kimi-code): render the permission-mode file-change warning in warning color * refactor(kimi-code): render the file-change warning via showStatus * fix(kimi-code): defer the goal permission-switch notice until the goal starts * refactor(kimi-code): move the file-change warning copy into the TUI constant directory --------- Signed-off-by: 7Sageer <sag77r@hotmail.com>
…ayered registry (MoonshotAI#3430) * refactor(agent-core-v2): rebuild telemetry context as a scope-bound layered registry * fix(agent-core-v2): prefer ambient model in telemetry envelope and repair print-runner stub * fix(agent-core-v2): delete only the fragment registered by this telemetry binding * fix(agent-core-v2): bind telemetry context writes to the binding's own fragment * fix(agent-core-v2): keep stale telemetry emissions on the binding-owned fragment * refactor(agent-core-v2): mirror turn-request trace_id into ambient telemetry context * refactor(agent-core-v2): telemetry key-resolution cleanups Drop the dead defineAgentTelemetryEvent branch in declaredKeysFor: agent_id is already copied unconditionally as identity plumbing before the declared-key loop, and the loop itself skips agent_id, so the branch had no runtime effect. Name the wire session id property key (WIRE_SESSION_ID_PROPERTY) so the session_id-to-sessionId mapping is explicit. * refactor(agent-core-v2): flow ambient telemetry context to every event unconditionally Remove the registry-key filtering and the FILTERED_CONTEXT_KEYS hard filter from composeTelemetryProperties: the merged ambient now reaches every event's properties, with session_id still remapped to the camelCase sessionId key and explicit defined values winning on collision. Explicit undefined values no longer clobber ambient fields. With filtering gone, correctness for time-varying fields rests on lifecycle discipline, so the loop now clears the ambient trace_id when a step starts (mirroring the activeRequestTrace reset) instead of relying on an explicit undefined payload value to mask the stale mirror. * refactor(agent-core-v2): move thinking_effort into the turn telemetry context * refactor(agent-core-v2): replace the telemetry fragment registry with parent references
…oonshotAI#3471) FeatureAssemblyService ran during the eager bootstrap batch, while ConfigService was still loading config.toml asynchronously, so flags.enabled() always read false for config-sourced flags and gated features (e.g. tower via [experimental] tower = true) never assembled. registerFeature now accepts a { flag } option; gated registrations are assembled in a deferred pass once IConfigService.ready resolves, and IFeatureAssemblyService.ready exposes that pass. WorkspaceInstanceManager .materialize also awaits config.ready so session creation cannot race the deferred assembly.
* fix(agent-core-v2): rework tower mode enter failures and config-flag assembly - IAgentTowerService.enter() returns a typed TowerEnterResult: the 4 former silent failure points now carry their reason (not-main-agent / experiment-off / feature-not-assembled / owned-by-live-session with the owner session id + title); kap-server and node-sdk map each reason to a distinct SESSION_TOWER_MODE_INVALID message via towerEnterFailureMessage() instead of one misleading catch-all. - Fix config-sourced [experimental] tower flag never assembling the tower feature: ConfigService seeds its state synchronously at construction with a best-effort readFileSync of the config document it owns, so the flag is already visible when TowerFeature's constructor runs during App-scope creation. No event machinery: a runtime config flip updates flag reads but does not re-run feature constructors — a restart is required, pinned by test. docs/flag.md + docs/features.md document the contract. - Consolidated changeset for @moonshot-ai/kimi-code. * fix(tower): release handler when teardown --------- Co-authored-by: konghuanjun <konghuanjun@moonshot.ai>
…hotAI#3477) The process-level uncaughtException handler installed by startServer called process.exit(1), so a single stray exception killed every session the process served — and, embedded in the desktop main process, killed the whole app past the shell's own crash guard. Log the error at error level and keep serving instead. createServerLogger gains an optional stream so hosts can route server logs into their own log channel.
… ready (…" (MoonshotAI#3486) This reverts commit eb4cd4b.
…context (MoonshotAI#3484) * fix(agent-core-v2): write the bound model into the ambient telemetry context * fix(agent-core-v2): restore the ambient telemetry model after profile replay
… restart (MoonshotAI#3478) * fix(agent-core-v2): rebuild persisted subagents when resuming after a restart Agent(resume=<id>) only consulted the in-memory agent roster, so once a session was reopened in a new process every persisted subagent id failed with "does not exist" even though its wire log and metadata were intact. The resume path now reads the session metadata once, verifies the target is a subagent owned by the caller, and re-creates its agent scope from the persisted records when it is not live before running the turn. * fix(agent-core-v2): sync rebuilt subagents to the caller mode and match rules by their persisted profile A subagent rebuilt for resume replayed the permission mode it was spawned with, so a child persisted in yolo kept running in yolo after the parent had switched back to manual; the rebuild now copies the caller's current mode the way a fresh spawn does. Permission rules with a profile subject such as Agent(coder) were matched against the fallback label while the target was offline, because only live agents exposed their profile during execution resolution. Every spawn site now records the profile name in the subagent labels and the resume path reads it back for display and rule matching when the agent is not live. Adds a kap-server e2e case that spawns a subagent, restarts the server on the same home directory, resumes it through the Agent tool, and checks that its prior context reaches the next model request. * fix(agent-core-v2): keep pinned permission modes when rebuilding a subagent for resume Tower workers are pinned to auto mode at spawn and skipped by the permission-mode broadcast, but the rebuild path synced every restored subagent to the caller's mode, so a dead worker recovered through Agent(resume=...) after a restart lost its pinned mode. The exception is now a single predicate next to the worker profile, shared by the broadcast and the rebuild. * fix(agent-core-v2): record subagent profiles in the lifecycle instead of at each spawn site The persisted profile name that offline resumes use for display and permission-rule matching was written by three spawn sites, and the session-init child was still missing it. The lifecycle now records it centrally: create() takes the profile from the binding it is given and fork() takes the override or the source agent's profile, so every subagent spawn is covered and the spawn sites no longer pass it. * chore: merge the subagent-resume changesets into one
… failure episode (MoonshotAI#3493) * fix(agent-core-v2): track session index mirror give-up event once per failure episode The give-up telemetry fired on every failed flush once the consecutive failure count crossed the threshold, so a session with a persistently failing mirror reported hundreds of thousands of events per day. Emit it only when crossing the threshold; the counter resets on the next successful flush, so a later episode reports again. * fix(agent-core-v2): latch the mirror give-up event per failure episode The equality guard missed episodes where unpublished-manifest flushes (manifest === undefined also increments the counter) pushed the count past the threshold before the first throwing flush. Latch the report per episode instead; the latch resets on the next successful flush.
…cts (MoonshotAI#3504) * test: speed up kap-server suite and exclude minidb from default projects * test(kap-server): restore baseline server after sessions tests replace it
…try in v2 print mode (MoonshotAI#3498) * fix(kimi-code): honor KIMI_DISABLE_TELEMETRY and restore crash telemetry in v2 print mode * fix(kimi-code): attribute v2 print crash telemetry to the resolved session model
… scanning (MoonshotAI#3503) * fix(tree-sitter-bash): recognize heredocs in character-level balanced scanning scanBalancedStatements treated heredoc bodies as ordinary characters, so a stray quote, paren, or backtick inside a heredoc body (for example an apostrophe in a PR body passed through a command substitution) broke the scan and produced ERROR nodes; the resulting hasError made dangerous-command-ask judge the whole command unanalyzable and prompt for approval even in yolo mode. Parse << and <<- delimiters (excluding <<< herestrings) with the same unquoting rules as the token-level heredoc reader, queue pending bodies, and skip them line-wise at newlines. Skip arithmetic $(( ... )) regions via scanBalanced so a left-shift << is never mistaken for a heredoc operator. * fix(agent-core-v2): raise the bash parse wall-clock budget to 500ms A 20ms wall-clock budget could abort an otherwise fine parse under CPU contention, GC pauses, or cold-start JIT, flipping the permission verdict to unanalyzable (spurious approval prompts) or silently dropping AGENTS.md re-reminders. Normal commands parse in well under 1ms; maxNodes stays the deterministic cap, and 500ms remains a backstop against pathological parser loops. * fix(tree-sitter-bash): skip comments and legacy arithmetic during heredoc-aware scanning The heredoc-aware scan queued a heredoc for any << it encountered, including inside comments (echo $(printf x # <<EOF\n)) and legacy arithmetic expansions (echo $(echo $[x << 2]\n)); both are valid bash that parsed cleanly before, and the regression flipped them to hasError and an unanalyzable permission verdict. Skip # comments to end of line when the preceding character starts a new word, and skip ${ ... } / $[ ... ] expansions as balanced units (mirroring skipDollar), so << is only recognized where a redirection operator can actually appear. * fix(tree-sitter-bash): skip word-glued subscripts during heredoc-aware scanning Indexed assignments such as echo $(a[x<<2]=3\n) put arithmetic inside a word-glued [ ... ] subscript; the heredoc-aware scan read the << shift operator there as a heredoc start, regressing valid bash that parsed cleanly before to hasError and an unanalyzable permission verdict. Skip a [ ... ] region as a balanced unit when the bracket immediately follows a word character (subscripts and glued glob classes); a bracket at word start keeps the existing character scan, so real heredocs after words (cat foo[ab]<<EOF) and bare [ command arguments are unaffected. The subscript regression is covered by a unit case only: the reference parser splits subscript arithmetic into binary_expression while this parser keeps it an opaque word, a structural difference that predates this change and has no differential fixture yet. * fix(tree-sitter-bash): skip conditional regions and look through continuations in heredoc-aware scanning Two more character-scan contexts queued bogus heredocs after the heredoc-aware scan: a [[ ... ]] conditional region (echo $( [[ x == @(<<EOF) ]]\n) and regex right-hand sides such as [[ x =~ <<a ]]), and a # preceded by a backslash-newline continuation (echo $(printf foo\<newline>#bar)), where removing the continuation keeps the hash inside the preceding word instead of starting a comment. Both are valid bash that parsed cleanly before. Skip word-start [[ ... ]] as a balanced region (a << inside a conditional is never a heredoc operator; a bracket in argument position keeps the character scan), and walk back over \+newline pairs before classifying a # as a comment. The extglob conditional case joins the differential fixtures; the continuation case is unit-only because the reference parser errors on it. * fix(tree-sitter-bash): scan substitutions as part of heredoc delimiters A heredoc delimiter containing a substitution (echo $(cat <<$(foo)\nbody\n$(foo)\n)) was truncated at the first paren, so the queued delimiter never matched the body closing line and the scan swallowed the rest of the range, regressing valid bash to hasError and an unanalyzable permission verdict. scanHeredocDelimiter now scans $( ), ${ }, $[ ], and backtick regions wholesale as part of the delimiter word (recursing with the heredoc-aware statement scanner for $( )), mirroring how bash treats the whole word as the delimiter. The case stays unit-only because unquoted delimiters hit the already-registered heredoc-content-chunks structural difference with the reference parser.
…otAI#3485) * docs(zh): restyle configuration and customization sections Editorial pass across 11 pages: clear explanatory dashes, replace arrow cross-references with inline links, compress oversized table cells while keeping operational facts (value ranges, override precedence, activation conditions), split >5-sentence paragraphs by theme, fold interface contracts and low-frequency internals into details blocks, add map sentences to multi-paragraph sections, add subcommand overview table to kimi-command reference. Add /provider manager screenshot to media. * docs(zh): restore dangerous_command_guard, fix trust prompt default and secondary-model default - config-files: restore the dangerous_command_guard paragraph dropped during the style pass (regression, content from upstream MoonshotAI#3290) - mcp: the trust prompt defaults to Trust this folder per trust-prompt.test.ts; docs had the direction reversed (pre-existing) - config-files: subagent model pool defaults on since MoonshotAI#3334; KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL=0 disables (pre-existing staleness) * docs(zh,en): sync en mirrors and fix anchor slugs Add restyled en mirrors for all 11 configuration/customization pages, mirroring the zh structure (section parity, map sentences, compressed cells, details folds) while keeping en phrasing. Fix anchor slugs in both locales (underscore kept, dots dropped per @mdit-vue slugify): loop_control, openai_responses, kimi_model_, systemmd variants; retarget renamed permission-mode section (yolo/auto -> The three permission modes / 三种权限模式).
…oonshotAI#3499) * fix(telemetry): deduplicate session_started and model switch events * fix(telemetry): keep engine session_started for direct v2 SDK clients and avoid model_switch race * fix(telemetry): preserve model_switch when activation rebinds the same alias * fix(telemetry): route TUI reload through harness * test: update /reload message-flow test for harness reload route
* refactor(agent-core-v2): remove the staleGuard feature Drop the read-before-edit runtime guard: Edit/Write executions are no longer vetoed when the target file was never read or its mtime changed since the last read, and successful Read/Edit/Write no longer refresh a recorded mtime. Removes the staleGuard replayable state key and the staleGuard.recorded / staleGuard.cleared durable wire events; old wires keep replaying through the unknown-type skip path. apps/vis keeps projecting and rendering those historical records via locally declared legacy record types. * fix(agent-core-v2): skip retired wire record types silently during restore Restore reports every journal record whose type has no registered event class through onUnexpectedError. Sessions written before the staleGuard removal can hold a staleGuard.recorded entry per successful Read/Edit/Write, so loading one floods the log with WireError stacks. Keep a retired-type list of record types that were once durable vocabulary; restore skips them without reporting, while genuinely unknown records stay on the error path.
… compaction notes at the wire journal (MoonshotAI#3423) * feat(agent-core-v2): remind the model of its context budget and point compaction notes at the wire journal Add the contextBudget feature: a context_budget reminder that restates used/max/trigger tokens as usage crosses half, three quarters and ninety percent of the compaction trigger, and a compaction_ahead reminder delivered once per window when the trigger is within ten percent of the context window, so the model can persist and verify state while it can still call tools. Both read IAgentFullCompactionService.budget(), which derives from the same CompactionTriggerBudget that drives auto compaction, and both are stripped from the summarizer input. Behind compaction_recovery_pointer, compaction records the wire journal line range it covered (wireLines on context.apply_compaction, folded into the replayable fullCompaction.wireRanges key) and appends a Context Recovery footer to the model-facing contextSummary with the on-disk wire.jsonl path, every earlier window's line range, and a primer on reading the journal; the UI-facing summary stays the note plus TODO. Read returns wire.jsonl lines under the sessions directory untruncated and spill-exempt so a single record can be read back after Grep locates it, and the compaction instruction tells the summarizer a recovery pointer follows the note. Both flags default on; KIMI_CODE_EXPERIMENTAL_CONTEXT_BUDGET_REMINDERS=0 and KIMI_CODE_EXPERIMENTAL_COMPACTION_RECOVERY_POINTER=0 disable them. Telemetry gains context_budget_reminder, compaction_ahead_reminder, and ahead_* fields on compaction_finished. * feat(agent-core-v2): ship context budget reminders and the recovery pointer without flags - Remove the two experimental flags; both behaviors now ship unconditionally and the compaction instruction carries the recovery note in its template. - Lower-bound recovery windows at the latest context.clear journal record so a window never points into history the user discarded. - Add the appended recovery footer's estimated tokens to summaryOutputTokens so tokens_after and the post-compaction token floor stay honest. * fix(agent-core-v2): cap event log reads and refuse empty-history compaction - Cap a single wire.jsonl record read at 150k chars, below the window-minus-trigger margin, so one read can never push the context past the model window; the note points at sed | jq for longer records. - Keep at least the last record when tail-reading the event log instead of returning silently empty output with a contradictory note. - Fail the compaction when an overflow shrink would drop every message, instead of compacting an empty history and replacing the context with a groundless note. * fix(agent-core-v2): drop the redundant ninety bucket and soften ahead-reminder wording - Remove the 90% context-budget bucket: the compaction-ahead threshold is always at or below it, so it only echoed the stronger last-chance reminder moments later. - Stop suggesting a commit as a way to persist state before compaction; files and the todo list cover it without prompting unwanted commits. - Make the event-log note's primer reference conditional on a compaction having run. * chore: merge the compaction changesets into one
…nshotAI#3521) Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com> Co-authored-by: qer <wbxl2000@outlook.com>
…inline their answers (MoonshotAI#3522) * fix(agent-core-v2): keep background questions open past turn end and inline their answers Background AskUserQuestion reused the generic task pipeline end to end, which broke it in two ways: the pending interaction was still bound to the asking turn, so it was cancelled the moment the agent finished its turn, and the turn-end cancel response was then misread as an answer. On top of that the completion notification only carried a pointer to the task output file, forcing an extra Read round trip for a few bytes of JSON. - Detach background question interactions from the asking turn so they stay pending until answered, stopped, or the agent closes. - Treat cancelled interaction responses as dismissals. - Inline the answer JSON in the question task notification and word the notification as answered or dismissed; fall back to the output file only when the answer exceeds the inline budget. - Trim the background launch result to task id, status, and one next step. - Fold transcript notification summaries before inline answer blocks. * fix(agent-core-v2): fail background questions on tool errors and translate interaction cancellations in the question service Follow-up hardening from review: - The interaction kernel's cancellation response now has a named shape, InteractionCancellation, and SessionQuestionService.request translates it into a dismissal (null) before handing the result to callers. The AskUserQuestion tool no longer inspects answer maps for a cancelled key, so a bare answer map can never be mistaken for a cancellation. - QuestionBackgroundTask settles as failed with the tool's message as the stop reason when the question tool reports an error, instead of writing the error text as completed output. The generic task notification then carries the reason, and the answered/dismissed wording is not used. - The question notification only says answered or dismissed when the task output parses as an answers payload; any other output keeps the generic completed wording.
…ion mode (MoonshotAI#3529) * feat(agent-core-v2): allow unanalyzable bash commands in auto permission mode * feat(agent-core-v2): drop the dangerous command guard in auto permission mode
# Conflicts: # docs/en/customization/hooks.md # docs/zh/customization/hooks.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related Issue
No linked issue — this PR syncs the feature branch with upstream main on this fork.
Problem
feat/hook-permission-decisionshad fallen 77 commits behindorigin/main(up to60136588a). The branch needs the latest main to stay mergeable and to keep the hook permission decisions feature working against current code.Verified beforehand: upstream main has not implemented an equivalent feature — on main,
PermissionRequesthooks are still fire-and-forget (observation-only), with no way for a hook to approve or deny before the approval broker and no equivalent experimental flag.What changed
Single merge commit
8f592d72dmergingorigin/maininto the feature branch line.docs/en/customization/hooks.mdanddocs/zh/customization/hooks.mdconflicted (main restyled these pages while the branch added the PermissionRequest decision docs). Resolved by keeping main's restructured wording and re-inserting the branch's experimental content: thePermissionRequesttable row stays✓ (experimental)with a link to thehook_permission_decisionssection, and the blocking-events callout keeps the exception note. en/zh kept in sync.hook_permission_decisionsflag definition, the flag check inagentExternalHooksService, and the hook-decision path intoolApprovalService.Verification:
tsc --noEmitclean,check-no-commentsclean, oxlint 0 errors, and the full agent-core-v2 suite passes (348 files, 6285 tests), including the externalHooks (67) and toolApproval (34) tests.Checklist
/approve). — N/A, fork-internal sync PRgen-changesetsskill, or this PR needs no changeset. — No changeset needed; the feature changesethook-permission-decisions.mdis already on the base branchgen-docsskill, or this PR needs no doc update. — Docs conflict resolutions keep en/zh hooks pages in sync