Commit graph

150 commits

Author SHA1 Message Date
Kaiyi
1461143743 fix(kimi-code): show the swarm report when replaying a completed run
Session resume reconstructs a swarm card from the tool call + final
result, but the live tool.progress / subagent.* events that populate the
dashboard are not replayed. A resumed completed swarm therefore finalized
an empty model and rendered "0 workers · 0✓ 0✗" with the synthesized
report — the actual deliverable — hidden entirely.

When the card has no live worker data (nothing was replayed), render the
result body instead of the empty dashboard and drop the misleading worker
tail. A live whole-swarm failure still shows its reason via the existing
'✗ <reason>' line; live runs (which always have worker data) are
unaffected.
2026-05-30 14:34:13 +08:00
Kaiyi
f31bf2b7ba refactor(kimi-code): render the swarm dashboard in a dedicated SwarmCard
The swarm dashboard was stacked as branches inside ToolCallComponent,
against the guideline that new tool-result display should live behind a
dedicated renderer/component boundary. The static tool-renderer registry
can't host it (the dashboard needs live, per-event updates on a single
stable component — folding it in was how the original duplicate-card bug
was fixed), so extract it into a dedicated top-level SwarmCard selected
at tool-call-start, hosted by the same managed lifecycle.

- New SwarmCard (sibling to ToolCallComponent) + a narrow ManagedToolCard
  interface the streaming-UI registry is typed against; shared helpers
  (str/formatTokens/SWARM_ACTIVITY_MAX_LENGTH) moved to tool-call-shared.
- streaming-ui selects SwarmCard for name==='Swarm' at the single creation
  point; replay routes Swarm entries to SwarmCard too.
- session-event-handler narrows the generic subagent path to
  ToolCallComponent after the swarm guard.
- ToolCallComponent loses all swarm code (isSwarm() now returns false).

Behavior-preserving: same single stable card, in-place mutation, static
bullet, header from live args. Full suite green; swarm tests retargeted
to SwarmCard.
2026-05-30 14:28:57 +08:00
Kaiyi
e288ffa1b9 fix(kimi-code): show the /swarm request in the live transcript
/swarm drove a model turn via session.prompt(buildSwarmPrompt(task))
without ever putting the user's request in the transcript, so a live
session showed a Swarm tool card with no preceding user line.

Append a readable "/swarm <task>" user entry before starting the turn,
mirroring the normal send path. Adds appendUserTranscriptEntry to the
slash-command host for framed commands that prompt the model with an
internal wrapper.
2026-05-30 14:09:57 +08:00
Kaiyi
9d7c9a6e6a fix(agent-core): reject empty planner subtask fields
parsePlan accepted a syntactically valid plan whose role, systemPrompt,
or prompt was an empty (or whitespace-only) string, only checking the
type. Such a subtask spawns a swarm worker with no identity and no
instructions — a wasted run with a blank dashboard row and a low-value
contribution to synthesis.

Reject empty/whitespace-only required fields so decompose's existing
planner retry re-prompts for a valid plan, mirroring the non-empty
validation parseReviseDecision already applies to the reviser's output.
2026-05-30 14:01:42 +08:00
Kaiyi
a17cfeee2d fix(kimi-code): show swarm failures distinctly from cancellation
The swarm card finalized every is_error tool result as 'cancelled' with
a success-toned bullet, and the dashboard suppresses the result body, so
ordinary failures (planner produced no valid plan, synthesizer error)
rendered as a clean "cancelled" with the real "Swarm failed: ..." reason
hidden from the user.

SwarmTool now distinguishes a genuine cancel (ctx.signal aborted) from an
ordinary failure: on a real failure it emits a 'failed' swarm progress
event carrying the reason before returning the error result. The TUI adds
a terminal 'failed' phase (error bullet, ' · failed' tag, and a "✗ reason"
body line); finalizeSwarmModelIfNeeded only forces 'cancelled' when the
model is not already 'failed', so a genuine abort still shows 'cancelled'.
2026-05-30 01:05:28 +08:00
Kaiyi
38ba4b83d7 fix(kimi-code): route /swarm through the session-request lifecycle
handleSwarmCommand called session.prompt directly, bypassing
beginSessionRequest. streamingPhase therefore stayed 'idle' until the
SDK turn.started event round-tripped back, leaving a startup window in
which a fast follow-up message was dispatched as a second concurrent
prompt and silently dropped by the core as agent_busy, and in which the
UI showed no waiting state.

Call beginSessionRequest() before prompting — flipping streamingPhase
synchronously so the input gate closes immediately and the waiting pane
shows — and failSessionRequest() on a prompt rejection, mirroring
sendSkillActivation / handleInitCommand.
2026-05-30 00:51:29 +08:00
Kai
cc9176b3d0
Merge branch 'main' into kaiyi/karachi 2026-05-30 00:19:09 +08:00
Kaiyi
d6942ec5d5 fix(agent-core): drop subagent summary-continuation re-prompt
The summary-continuation pass re-prompted any subagent whose first
summary was under 200 chars to "expand" it, then read back the
follow-up turn — replacing the original output rather than appending.

For swarm's structured-output subagents this was harmful: a reviser's
compact decision JSON (e.g. {"kind":"retry"}) is always under the
threshold, so the expand turn always fired and could replace the JSON
with prose, silently degrading the recovery loop into conservative
drops. It also taxed every short-but-complete handoff with an extra
turn.

Remove the heuristic entirely so a subagent's first summary is returned
as-is. The max-tokens truncation guard is unaffected.
2026-05-30 00:03:44 +08:00
Kaiyi
df04b8d2fb fix(swarm): resolve reassign orphan row, enrich stall context, decision-aware recovery UI 2026-05-29 23:24:26 +08:00
Kaiyi
53753002b0 feat(tui): surface swarm recovery (retrying/dropped) in the dashboard 2026-05-29 23:03:58 +08:00
liruifengv
2752bd9330
docs(changelog): sync 0.6.0 from apps/kimi-code/CHANGELOG.md (#227) 2026-05-29 22:58:42 +08:00
Kaiyi
f2cc14889b feat(agent-core): swarm coordinator failure-recovery loop (retry/regenerate/reassign/drop) 2026-05-29 22:48:34 +08:00
Kaiyi
60bc6beeee fix(agent-core): remove NUL byte from swarm stall-hook repeat key
The stall-detection repeat key joined the tool name and canonical args
with a literal NUL (0x00) separator. The control byte caused git to
classify stall-hook.ts as binary, so diffs, blame, and code review on
the file were opaque — which prevented confirming the test history for
this feature. Replace the NUL with a normal space (tool names are
identifiers and never contain spaces, so keys stay collision-free) so
the file is plain UTF-8 text and remains reviewable.

Behavior is unchanged: the key still uniquely combines tool name and
canonical args. Verified by reverting the hook to a no-op stub to show
the three stall-detection test files go red (the discriminating block,
canonical-key, e2e turn-abort, and worker-stall-translation cases all
fail), then restoring the real implementation to confirm they pass —
the failing-first the prior atomic commit never recorded.

Full suite: 5049 passed / 25 skipped; make typecheck clean.
2026-05-29 22:38:30 +08:00
Kaiyi
e88003f856 feat(agent-core): stall-detection hard-stop for swarm workers (repeat-based) 2026-05-29 22:31:18 +08:00
github-actions[bot]
d64b15d153
ci: release packages (#170)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-05-29 22:23:07 +08:00
qer
bab2da7b1c
feat(plugin): install plugins from a GitHub repository URL (#221)
* feat(plugin): install plugins from a GitHub repository URL

Allow `/plugins install <github-url>` (and marketplace `source` entries) to
take a GitHub repo URL directly. A new `github` source kind joins the
existing `local-path` and `zip-url` kinds.

Recognized URL forms (parsing in source.ts):

- `https://github.com/<o>/<r>`                       — bare; resolves to latest
                                                       release tag, falling back
                                                       to default branch HEAD.
- `https://github.com/<o>/<r>/tree/<ref>`            — branch / tag / SHA;
                                                       value passed to codeload
                                                       in its short form so the
                                                       backend resolves either.
- `https://github.com/<o>/<r>/releases/tag/<tag>`    — explicit tag, uses
                                                       refs/tags/<tag> to avoid
                                                       same-named-branch ambiguity.
- `https://github.com/<o>/<r>/commit/<sha>`          — explicit commit SHA.

The resolver deliberately avoids `api.github.com`: its 60/hour anonymous
quota is shared with every other tool on the egress IP (browser, gh CLI,
IDE integrations) and a first-time install failing because some other tool
ate the budget is unacceptable UX. Instead we:

- GET `github.com/<o>/<r>/releases/latest` with manual redirect and parse
  the `Location` header (302 → tag URL; 404 → no own release).
- Fall back to `codeload.github.com/<o>/<r>/zip/HEAD` for repos with no
  releases (or for forks that inherit upstream tags but have no own release
  page, which redirect to bare `/releases`).
- Only treat the explicit 404 from `/releases/latest` as "no release" — 5xx,
  403, 429, and any other non-2xx status surface a hard error rather than
  silently installing the default branch, so the user knows when transient
  GitHub issues changed the install path.

UI changes in the TUI:

- `/plugins install` now shows a live Braille spinner while resolving and
  downloading, then flips to a final status that distinguishes Installed
  (fresh) vs Updated (same repo identity, new version) vs Migrated (source
  changed, e.g. CDN zip-url → GitHub).
- `/plugins list`, the `/plugins` overview, and `/plugins info` show the
  install provenance inline. `zip-url` installs now display the URL host
  (e.g. `via code.kimi.com`, `via 127.0.0.1:port`) instead of the opaque
  `zip-url` literal. GitHub installs show `github <owner>/<repo>@<ref>`.
- Three-tier trust badge driven by the marketplace context recorded at
  install time: `official` (green) for `tier: official`, `curated` (blue) for
  `tier: curated`, `third-party` (muted) for anything not installed through
  the marketplace selector. CLI `/plugins install <url>` always records as
  third-party; the marketplace selector passes the tier through. A
  re-install replaces the marketplace context: switching to a third-party
  source clears the badge, which matches the underlying trust change.

`installed.json` gains optional `github` and `marketplace` fields
(back-compatible). PluginSummary surfaces `source`, `originalSource`,
`github`, and `marketplace` so the TUI can label installs without an extra
round trip to PluginInfo. The SDK's `session.installPlugin(source)` gains
an optional `{ marketplace }` second argument so the marketplace selector
can forward `{ id, tier }` through RPC; the CLI install path omits it.

Tests: 112 plugin-suite tests (URL parser, resolver, store round-trip,
manager integration). The manager integration tests assert codeload URLs
shape (short form for `/tree/<ref>`, explicit `refs/tags/` for
`/releases/tag/`) and verify marketplace context is persisted across
reloads and cleared on a third-party re-install.

* chore(changeset): plugin install from GitHub

* docs(plugins): document GitHub install URLs and trust badges

* fix(plugin): preserve URL-encoded characters in GitHub ref names

Git permits ref characters that have special meaning in URLs — most
notably `#`, which is a valid tag character (e.g. `release#1`) but the URL
fragment delimiter. The resolver decoded the tag from GitHub's
`/releases/latest` 302 redirect Location header and then interpolated the
raw value into the codeload URL. The literal `#1` became a fragment and
the HTTP request reached the server as `…/refs/tags/release` — a wrong or
truncated ref, leading to install failure for a release whose URL was
otherwise valid.

Two symmetric changes:

- The codeload URL builder now splits the ref on `/` (so multi-segment
  refs like `feat/foo` keep their path separators) and percent-encodes
  each segment.
- The GitHub URL parser now percent-decodes each segment from the URL's
  pathname when extracting `/tree/<ref>`, `/releases/tag/<tag>`, and
  `/commit/<sha>`. Storage and display see the human-readable Git ref
  name; the resolver re-encodes on the way out.

Malformed `%xx` sequences in user-typed URLs are tolerated: we keep the
raw segment so the caller surfaces a normal "ref not found" error
downstream instead of crashing during parse.

* fix: restrict plugin trust badges

* chore: remove fetch when show plugin list

---------

Co-authored-by: qer <Anna_Knapprfr@mail.com>
2026-05-29 22:18:16 +08:00
Kai
13e0fff462
fix(kosong): preserve unsigned thinking in anthropic history serialization (#222)
When converting assistant history to the Anthropic wire format,
convertMessage() dropped any thinking block that had no signature. That
was meant to satisfy api.anthropic.com (which requires a valid signature
on thinking blocks), but it broke Anthropic-compatible backends.

Kimi's Anthropic-protocol endpoint streams thinking without a
signature_delta, yet requires the thinking to be present on a tool-call
turn — once it was dropped, the next request failed with "thinking is
enabled but reasoning_content is missing in assistant tool call message",
making multi-step tool use unusable on those backends.

Preserve unsigned thinking instead, emitting it without a `signature`
field. The two backends are partitioned by signature presence:
api.anthropic.com always supplies a signature (its history takes the
signed branch unchanged), while Kimi never does (its thinking is now
kept). Empty-and-unsigned parts carry nothing and are still skipped.
2026-05-29 21:41:09 +08:00
Kai
2bbea75ee4
feat: define model via KIMI_MODEL_* environment variables (#212)
* feat: define model via KIMI_MODEL_* environment variables

Add a KIMI_MODEL_* environment-variable channel that synthesizes a
provider (__kimi_env__) and model alias (__kimi_env_model__) in memory
and selects it as the default model, without editing config.toml.
Supports provider type (kimi/anthropic/openai), base URL, API key,
context size, capabilities, anthropic max_output_size, openai
reasoning_key, and full thinking settings.

Runtime config reads go through a new loadRuntimeConfig wrapper; the
config.toml write-back paths keep using readConfigFile so the
synthesized model is never persisted back to disk.

* feat: env-model defaults and friendly welcome model name

- KIMI_MODEL_MAX_CONTEXT_SIZE defaults to 262144 (256K) when unset
- KIMI_MODEL_CAPABILITIES defaults to image_in,thinking when unset
- TUI welcome banner shows the model display name / id instead of the
  internal __kimi_env_model__ alias key

* fix: never persist env model to config.toml; validate default_thinking

- Strip the synthesized __kimi_env__ provider / __kimi_env_model__ model
  (and a default_model pointing at it) in writeConfigFile, so the env model
  and its shell API key cannot be persisted via a getConfig -> setConfig
  patch round-trip (e.g. running /login or /connect in env-model mode).
- Reject a non-empty but unparseable KIMI_MODEL_DEFAULT_THINKING value
  (fail-fast) instead of silently keeping config.toml's existing default.

* fix: preserve on-disk default_model when stripping env model; fix thinking docs

- stripEnvModelConfig restores config.toml's default_model from raw instead of
  erasing it when the runtime default points at the env alias, so a real
  default_model survives a getConfig -> setConfig round-trip.
- Correct KIMI_MODEL_DEFAULT_THINKING docs: unset follows the global default
  (Thinking on), not Off.

* fix: restore env-injected fields to on-disk values in stripEnvModelConfig; update tests for thinking behavior
2026-05-29 21:40:56 +08:00
_Kerman
33fa71bd6f
chore(agent-core): remove unused flag resolver exports (#220)
Remove unused exports:
- flagEnvKey
- EXPERIMENTAL_PREFIX
- createFlagResolver
2026-05-29 20:50:02 +08:00
qer
a9dcc2dd20
docs: order changelog entries by reader value (#217) 2026-05-29 20:16:44 +08:00
Kaiyi
adb68270f6 feat(tui): show live token counts for running swarm workers 2026-05-29 20:06:11 +08:00
Kaiyi
649596b7ce Merge remote-tracking branch 'origin/main' into kaiyi/karachi 2026-05-29 19:56:58 +08:00
liruifengv
96bbc471c4
feat: add experimental feature-flag system (#205)
Introduce a central, env-driven flag registry in agent-core. Each flag is declared once with an id, full env var name, default, and surface. Within agent-core, flags are consulted through a process-global 'flags' constant that reads live process.env. Resolution precedence: master switch KIMI_CODE_EXPERIMENTAL_FLAG > per-feature KIMI_CODE_EXPERIMENTAL_<NAME> > registry default, with lenient boolean parsing via parseBooleanEnv. FlagId is a literal union derived from the registry for compile-time autocomplete and typo-checking.

SDK boundary: KimiHarness.getExperimentalFlags() returns the resolved values over RPC, and the SDK re-exports only the flag *types* — no runtime value crosses the boundary. The TUI caches that snapshot once at startup and reads it synchronously for command gating.

Gate the plugin system behind the 'plugins' flag, off by default: PluginManager.load() consults flags.enabled('plugins'), so when off no installed plugins are loaded or activated, and the TUI /plugins command is hidden from the palette and resolves as an unknown command.

Tests cover the resolver precedence matrix, registry invariants, the FlagId type guard, the live-env singleton, the plugin-load gate, the getExperimentalFlags RPC, and the TUI command gating.
2026-05-29 19:55:10 +08:00
qer
b9860e9f6e
feat: align datasource plugin with generic workflow (#215) 2026-05-29 19:51:23 +08:00
Kaiyi
c03ba22f05 fix(tui): collapse multi-line swarm task to one line in header and tool description 2026-05-29 19:49:21 +08:00
liruifengv
caaa6d83ee
fix(update): don't report success when native update fails (#214)
* fix(update): don't report success when native update fails

The native auto-updater spawned `bash -c "curl -fsSL … | bash"`. A
pipeline's exit status is that of its last command, so when curl could
not connect (e.g. a dead proxy) it produced no output, the trailing bash
read empty stdin and exited 0, and the whole command looked successful —
printing "Updated … Restart the CLI" while nothing had been installed.

Run the spawned shell with `set -o pipefail` so curl's non-zero status
propagates. installUpdate() then rejects and runUpdatePreflight() warns
and continues on the current version instead of claiming success.

* chore: add changeset for native update fix
2026-05-29 19:45:01 +08:00
Kaiyi
0d11fbc097 fix(tui): match swarm card styling to AgentGroup conventions and fix empty task 2026-05-29 19:42:33 +08:00
_Kerman
2388f20bb3
fix: handle structured context overflow errors (#213) 2026-05-29 19:38:24 +08:00
_Kerman
54590d3d46
fix: back off compaction overflow retries by token budget (#211) 2026-05-29 19:29:00 +08:00
Kaiyi
e873370920 fix(tui): render swarm via the managed tool-call lifecycle to stop duplicate cards 2026-05-29 19:24:46 +08:00
Kaiyi
81749b9c51 fix(tui): count only workers in swarm dashboard, finalize on cancel, clean up on reset 2026-05-29 18:54:54 +08:00
Kaiyi
03e49e5640 feat(tui): render swarm runs as a live dashboard instead of nested tool calls 2026-05-29 18:43:18 +08:00
Kaiyi
7ed20f3749 feat(agent-core): emit structured swarm progress (planned/synthesizing/done) 2026-05-29 18:35:18 +08:00
Kaiyi
3475837c9d feat(tui): add SwarmDashboardComponent 2026-05-29 18:32:35 +08:00
Kaiyi
adc18ad512 feat(tui): add swarm dashboard model and reducer 2026-05-29 18:26:57 +08:00
Kaiyi
8021cecfab fix(agent-core): clarify swarm planner tool guidance, add profileOverride test and changeset 2026-05-29 17:36:18 +08:00
_Kerman
e280f33daf
fix: recover from model token limit errors (#207) 2026-05-29 17:27:59 +08:00
Kai
f3269eacb9
fix(tui): show real terminal status for background agents (#197)
* fix(tui): show real terminal status for background agents

The Agent tool's run_in_background=true call returns a non-error
ToolResult whose body just says "status: running". The transcript
card derived its done/failed badge from that result, so every
terminated background agent — including ones reconcile reclassifies
as lost on resume — kept the green "✓ Completed" label even when
the actual task failed, was killed, or never came back.

Push the real BackgroundTaskInfo.status into the matching Agent
card so the badge reflects what happened. The card's resolver
prefers subagent agentId (live) and falls back to the description
on resume; on resume the apply step also runs after replay
finishes so the agent group can reach the borrowed components.

Also adds an agent-core regression test that pins live, busy,
group, race, and resume scenarios for the bg notification chain.

* fix(tui): also propagate bg agent terminal status to standalone cards

Standalone Agent cards (only one Agent tool call in a step, never
upgraded into an AgentGroupComponent) bypassed the previous
`setBackgroundTaskTerminalStatus` path: the standalone header reads
`getDerivedSubagentPhase`, which still derived `done` from the
non-error spawn-success ToolResult, and the method did not request
a header/content rebuild. Lost/failed/killed bg agents in this
shape still rendered as `✓ Completed`.

Thread the override through `getDerivedSubagentPhase`, populate
`subagentError` with the friendly failure message so both render
paths share one source of truth, and trigger the same header +
content rebuild that `onSubagentFailed` does. Also include the
override in `hasSubagentState` / the subagent-block early-return
so a replayed solo bg agent (no replayed subagent block, no
sub-tool activity) switches to the subagent-aware layout instead
of the generic `Used Agent` rendering.

Adds two standalone-render regression tests so the path no longer
relies on the grouped snapshot to stay correct.

* feat(agent-core): make resume actionable from the lost-task notification

A backgrounded subagent that ends as `lost`/`failed`/`killed` is
already a soft-recoverable thing — `subagentHost.resume` will
reanimate the persisted Agent instance — but the LLM had to dig
through the original spawn-success ToolResult to find the right id
and figure out the recovery shape on its own. The two look-alike
identifiers (the BackgroundManager `task_id` aka `source_id`, and
the `subagentHost` `agent_id`) regularly got confused in practice.

Surface what the model needs at the moment of decision:

  - Add `agent_id` as a top-level `<notification>` attribute for
    agent-* tasks, so the right id is structural, not buried in
    prose. Render path keeps backward-compat by omitting the
    attribute when no agent_id is known (bash tasks, old sessions).
  - On non-success agent terminal states, append a recovery
    paragraph to the body: the precise `Agent(resume=...)` call,
    the disambiguation between `agent_id` and `source_id`, the
    `run_in_background` option, and what state survives the
    restart vs. what may need to be redone.
  - Tighten the spawn-time `resume_hint` with the same
    disambiguation and an explicit pointer at the
    `task.lost`/`task.failed`/`task.killed` recovery trigger.
  - Persist `agent_id` and `subagent_type` in PersistedTask so the
    recovery body still works after a session restart, where
    in-memory `BackgroundTaskInfo.agentId` would otherwise be
    undefined. Optional fields keep the disk schema
    forward/backward compatible — pre-PR records load without
    them and silently fall back to the original short body.

* fix(tui): route bg-agent terminal events by stable agent_id, not description

`tc.subagentAgentId` is left undefined for every backgrounded agent.
`handleSubagentSpawned` early-returns for `runInBackground` before
calling `tc.onSubagentSpawned`, and the wire replay path drops the
`subagent` block entirely (`toolCallFromReplayMessage` returns only
id/name/args). So the `agentId` branch in
`applyBackgroundTaskTerminalStatus` never matched in practice, every
call fell through to the description-based fallback, and the
persisted `agent_id` we added in the previous commit was effectively
dead. That fallback also has a real failure mode: if a foreground
Agent and a backgrounded Agent share the same `args.description`,
the only candidate found is the live (unrelated) card, which gets
incorrectly relabeled as the lost task's terminal state.

Parse `agent_id: agent-N` out of the AgentTool spawn-success
ToolResult body inside `getSubagentAgentId` so the id is always
recoverable, regardless of whether the in-memory subagent metadata
was ever populated. Foreground and backgrounded Agent cards now
carry distinct ids and route correctly.

Also pipe the real `subagent.failed` error through to the parent
card. The background branch of `handleSubagentFailed` previously
only appended the dedicated transcript entry; the parent Agent
card was left with the generic "Background agent failed" written
by the later `background.task.terminated` event. Add an optional
`errorText` to `setBackgroundTaskTerminalStatus` /
`applyBackgroundTaskTerminalStatus` and pass `event.error` through
on the failed branch — the real reason now reaches both the card
and the entry.

* fix(tui): treat agent_id as authoritative when matching bg terminal events

Previously `applyBackgroundTaskTerminalStatus` always tried agent_id
first and then fell back to description match on miss. That fallback
caused two cross-card bugs:

  1. On resume, `applyTerminalBackgroundAgentStatuses` iterates every
     persisted terminal task, including ones whose tool calls fell
     outside the `REPLAY_TURN_LIMIT` window and were never mounted.
     Description fallback could route an old `lost` status onto an
     unrelated recent Agent card sharing the same `args.description`.

  2. During the live spawn → terminate window, the same card briefly
     lives in both `_pendingToolComponents` and `transcriptContainer`.
     A description-only walk visits the same component twice and flags
     itself ambiguous, dropping the otherwise unambiguous update.

When `args.agentId` is provided we now match only by id and skip on
miss. With `getSubagentAgentId` already parsing `agent_id: agent-N`
out of the spawn-success ToolResult, the id path is reliable for
both live and resume even though `tc.subagentAgentId` is never
populated for backgrounded agents. Description fallback is preserved
solely for old pre-PR sessions whose persisted records lack
`agent_id` — same best-effort behavior as before.
2026-05-29 17:26:27 +08:00
_Kerman
07d51e4add
chore(agent-core): move tool services type (#206) 2026-05-29 17:20:57 +08:00
Kaiyi
fc5e4bf787 Merge remote-tracking branch 'origin/main' into kaiyi/karachi 2026-05-29 16:51:11 +08:00
liruifengv
14a0348855
fix(tui): avoid leaking footer when resuming a missing session (#202)
* fix(tui): 避免 resume 不存在的 session 时泄漏 footer

启动期间 footer 在构造时就被挂入渲染树,而 init() 早期的 setAppState
会排出一次渲染,在 await 时真正执行(pi-tui 的 stopped 默认为 false,
未 start 也会 doRender),把 footer 画到终端。resume 不存在的 session
时,init() 随后抛错,这次过早渲染就残留在错误信息上方。

将 footer 改为就绪态 chrome:从 buildLayout 移除挂载,改在 initMainTui
中 init() 成功之后再 mountFooter。致命启动错误会在挂载前抛出,footer
不进渲染树,过早渲染只画空树,错误信息落在干净的行上。

* chore: 精简 changeset 描述

* chore: 精简注释

* test: 适配跨工作目录 resume 校验,补全 workDir mock
2026-05-29 15:38:50 +08:00
Kaiyi
d6a3d91c72 fix(agent-core): enforce swarm worker tool allowlist and propagate abort 2026-05-29 15:33:41 +08:00
Kaiyi
b0b61c27ca feat(tui): add /swarm command that triggers the Swarm tool 2026-05-29 15:22:55 +08:00
qer
3da4daeade
fix(kosong): retry when a response stream is terminated mid-flight (#201)
A mid-stream SSE drop surfaces as a raw undici `TypeError: terminated`, which was classified as a non-retryable generic error and failed the turn on the first attempt. Route raw transport-layer errors through the connection-error heuristic so a dropped stream becomes a retryable APIConnectionError and is retried transparently. User aborts (ESC) are unaffected — the retry loop checks the abort signal before retrying.

Related to #149.
2026-05-29 15:18:55 +08:00
Kaiyi
9c309b19ef feat(agent-core): add Swarm tool wired to SwarmCoordinator with recursion guard 2026-05-29 15:18:44 +08:00
Kaiyi
985fd5c6f6 feat(agent-core): add SwarmCoordinator (plan, parallel workers, synthesize) 2026-05-29 15:14:23 +08:00
_Kerman
5159af341c
fix(agent-core): preserve blocked prompt hook context (#200) 2026-05-29 15:12:36 +08:00
Kaiyi
7591e679f5 feat(agent-core): add swarm types and pure plan-parse/concurrency helpers 2026-05-29 15:12:03 +08:00
_Kerman
8913440541
feat: show warning when resuming across working directories (#118) 2026-05-29 15:11:46 +08:00
liruifengv
588145dc9b
feat(tui): expand and prioritize footer rotating tips (#199) 2026-05-29 15:08:34 +08:00