dd5711f24f62f2ca9f0dc3f080cc0c4c502f1dae
32
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
63d26d4a3e |
fix(persistence): FlushAndAck is a durability barrier that reports lost writes
The persistence actor acked FlushAndAck unconditionally after writing its pending merged notification: nothing was fsynced and every storage failure was only warn-logged, so persist_ack, the reconnect flush and history repair treated lost writes as persisted. The barrier now writes the pending notification, reports the first storage failure latched since the last barrier (every actor arm latches), and fsyncs the whole session directory tree (files, directories, deletions). Session directories are created durably (init and fork copy), and the shared summary writer syncs its staged bytes before publishing and the parent afterwards, so a concurrent /rename cannot publish unsynced bytes over a synced summary.json. FlushComplete and the persist_ack, history repair and end-of-turn callers propagate the io::Result; CopyFile fails on a sync error but keeps the latch for the barrier. Ported from upstream 1.0.1x (grok-build persistence.rs pending_write_error + durable storage helpers), reshaped without the dirty-file set. Claude-Session: https://claude.ai/code/session_01RUqV8HVGXcAETyYbZ2tPQ4 |
||
|
|
5f7416f3ef |
fix(permissions): native path deny/ask rules apply to the file the tool opens
Edit/Read/Grep rules matched only the raw argument string, so a workspace symlink (or a quoted, `~`, `./`, `..`, or absolute spelling) reached a denied file. The policy now resolves the path exactly as the tools do (sanitize, expand `~`, display-cwd remap, join to the requesting session's cwd), follows symlinks parent-first, and re-checks deny/ask rules on the opened path and its target as absolute, `./`-relative and relative spellings. Escalation only: no allow is granted through a link. A link that cannot be followed forces a prompt that survives YOLO, auto mode and the session edit grant, with event reason `unresolved_link`. Permission requests now carry the requesting tool's cwd and display cwd so a child worktree session sharing its parent's handle is checked where it runs. Ported from upstream 1.0.14 (grok-build permission/policy.rs symlink re-check), reshaped for Kigi's lexical rule matcher. Claude-Session: https://claude.ai/code/session_01RUqV8HVGXcAETyYbZ2tPQ4 |
||
|
|
b957e12370 |
fix(tui): repaint the header context window on a /model switch
After `/model`, the header kept the previous model's window ("15K /
1.0M" on a 262K model) until the next turn or a restart. The pager
caches the window inside its context snapshot (streaming `totalTokens`
updates refresh it from the catalog) and nothing refreshed the cache
when the session's model changed. Comparing cached numbers against
catalog windows cannot tell a catalog-derived total from a shell
effective limit (KIGI_DEBUG_CONTEXT_WINDOW, header upgrades), so the
shell, which installs the effective window, now reports it:
- kigi-shell: `handle_set_session_model` returns the installed window;
`model_switch::apply` stamps it as `totalContextTokens` in the
`session/set_model` response meta and carries it on the
`ModelChanged` broadcast (`context_window`, serde-default: old pagers
ignore it, old leaders omit it). The response meta previously
serialized the actor's inner `Result` and reported success on an
actor failure; that error now propagates.
- kigi-tui: `Effect::SwitchModel` reads the meta; `SwitchModelComplete`
carries `Ok(Option<u64>)`; the completion handler and the follower
`ModelChanged` arm call `AgentView::apply_context_window`, which
re-derives the snapshot (pct, free tokens) and reports whether it
changed so a follower redraws on a window-only change.
- Tests: the bare `/model <name>` dispatch path asserts the snapshot and
the drawn header; follower with/without the field; same-model new
window redraw; actor returns the window; serde round trip.
Claude-Session: https://claude.ai/code/session_01RUqV8HVGXcAETyYbZ2tPQ4
|
||
|
|
97de661554 |
fix(startup): thread-exhaustion resilience — cap workers, never panic
Port of upstream grok-build 0.2.113/0.2.114 fixes, extended to Kigi's startup order. - kigi-tty-utils runtime module: worker-cap policy (8 max) — tokio's per-core default pins 100+ thread slots on many-core shared hosts against RLIMIT_NPROC / pids.max, and later spawns die with EAGAIN. - kigi-bin builds the main runtime with capped workers and reports an out-of-threads host with a diagnostic + exit(1) instead of a panic. - The pre-runtime optimization threads (HTTP warmup, early model prefetch) spawn recoverably and degrade — their panic would have preempted the runtime's own diagnostic. - The per-session actor thread and its runtime report spawn/build failure through the existing acp::Error / AgentBuildError boundaries (new RuntimeBuild variant) instead of panicking the agent host. - fsnotify's watcher thread spawn maps into FsNotifyError::WatcherStart so callers degrade instead of crashing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KSy9S31pZNWXtRZNVJ1sDf |
||
|
|
1ae803519e |
fix(compaction): configurable summarizer tool_choice; wider overflow detection
Port of upstream grok-build 0.2.105 + 0.2.118. - CompactionToolChoice (auto default | none), resolved env KIGI_COMPACTION_TOOL_CHOICE > [features].compaction_tool_choice > remote settings. Some OpenAI-compatible servers reject tool_choice:"none" with tools attached, failing every long-session compaction; auto keeps them working (the summarization prompt already forbids tool use). Threaded from agent + subagent spawn through CompactionConfig into generate_session_compact; the Responses gate counts hosted tools too, so a hosted-only request still carries the configured choice. - is_context_length_error recognizes the "current message ... exceeds budget" phrasing, so that overflow is Fatal instead of burning the retry budget on a deterministic failure. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KSy9S31pZNWXtRZNVJ1sDf |
||
|
|
ac23ebc9a1 |
feat(turn): halt a turn stuck repeating one identical tool call
Ports grok-build's action stationarity breaker. A polling loop looks like progress from inside the turn — every step succeeds — so nothing stops it and it burns the whole budget re-reading one file. Nudge at 8 identical batches, halt at 16, halt a pure no-op run at 4. Three defects were found and fixed before this landed, none of which the gates caught: - The check runs AFTER execute_tool_calls, not before. Before it, the assistant's tool_calls are recorded with no results, so pushing the nudge triggers repair_dangling_tool_calls: synthetic "cancelled by the user" results get spliced in, the real results land after a user message, and dedup misses them — the orphan tool_result shape that draws a provider 400. It is Ok-gated too, since one `?` inside can leave a call resultless. - `TurnOutcome::StationarityHalted` is its own variant grouped with Completed, not a reuse of Cancelled. As a cancellation it would report StopReason::Cancelled, fire the abort lifecycle, kill the turn's subagents, grow the goal harness back-off streak until three halts paused a running goal, and let completion-requirement recovery re-run the very turn halted for looping. - The no-op gate is size-based, not name-based. kigi registers the shell tool as `run_terminal_cmd` and renames it `run_terminal_command`, so matching on "bash" silently disabled the tighter ceiling while every test still passed. |
||
|
|
16328e55f9 |
feat(swarm): /swarm mode — a standing instruction to fan work out
The `agent_swarm` tool works with the mode off; what the mode adds is the doctrine — decompose finely, give every member a disjoint scope, do not do the work yourself. `/swarm`, `/swarm on|off`, `/swarm <task>`, gated on the tool actually being in the toolset. Two triggers, not upstream's three. Upstream's `tool` trigger exists so invoking the tool makes the doctrine appear; kigi's tool carries its own description and works with the mode off, so that trigger would add a state only the tool can reach. `Manual` persists until switched off, `Task` expires at turn end — both are real user intents. Load-bearing decisions, each from a defect found in review: - Expiry is a DROP guard, not a post-loop call. A user interrupt aborts the turn future rather than cancelling it, so post-loop code never runs; three `?` paths skip it too. Either way a `/swarm <task>` mode would leak into the next, unrelated prompt. - `enter` is a total no-op while armed, so the `/swarm <task>` shorthand cannot downgrade a standing `/swarm on` into a per-turn mode that then disarms itself. This matches upstream; the first cut diverged, and the test asserting the divergence was inverted with the fix. - An explicit `/swarm off` retracts UNCONDITIONALLY. The doctrine rides the conversation and so survives a resume, a fork and a compaction that an in-memory flag does not; trusting the flag left a session reading "off" with the instruction still steering and no way to clear it. That also retires the `reminder_live` field, whose doc comment claimed to know something the code cannot. - The mode is deliberately NOT persisted, unlike /goal and /graph: those strand real work when lost, this is a prompt hint whose recovery is retyping one command. Recorded in AGENTS.md so the omission reads as a decision, not a gap. - The pre-session gate is `subagents_enabled`, not a hardcoded `true`: the builder strips `agent_swarm` wherever it strips `task`, and advertising it then offers a menu entry that resolves to literal text. - The exit reminder is as emphatic as the doctrine it revokes; a single weak clause is the likelier of the two to be summarised away. The doctrine rides the existing `push_system_reminder` channel rather than a second injection path of its own. |
||
|
|
9edb8729ef |
feat(swarm): agent_swarm — one prompt over many items, paced as a fleet
Ports kimi-code's AgentSwarm: a `prompt_template` containing `{{item}}`
expanded over an `items` list into up to 128 subagents, run to completion
and returned as one aggregate. Kigi already exceeds upstream on planning,
verification, isolation and merge via /graph; what it lacked was cheap
immediate fan-out. Entirely client-side — no new backend surface.
The engine is three pure pieces plus a runner: `plan` validates and
expands (every fault reported before a single member starts — a
half-launched swarm is expensive to unwind), `schedule` is the launch
ramp as testable arithmetic, `run` drives it against the existing
`SubagentBackend`. Reuses the single-spawn coordinator rather than
inventing a batch API.
Load-bearing decisions, each the result of a defect found in review:
- `backgrounded` is its own outcome. A member that outlives the 600s
foreground budget is detached by the coordinator and KEEPS RUNNING;
reporting it as failed invites the model to relaunch its item, putting
a second agent on the same files. It is never offered for resume.
- `InFlightGuard` cancels live members on Drop. Send-now cancels the turn
WITHOUT cancelling subagents and aborts the task; the dropped receivers
read as "parent gone" and each child re-attaches itself. There is no
cooperative path to use instead — `Cancellation` is constructed nowhere
in the tree — so Drop is the only seam that fires.
- Retries and wall clock are both bounded. The swarm blocks the caller's
turn, so every wait needs a ceiling it cannot argue past; stragglers at
the deadline are reported as still-running, with their ids.
- `ToolKind::AgentSwarm` is its own variant: `TemplateRenderer`'s
`by_kind` map holds one tool name per kind, so sharing `Task` would
silently redirect `${{ tools.by_kind.task }}` in other tools' prompts.
- An explicitly requested model that cannot be validated is refused, as
the task tool already does — one loud error beats `items.len()` quiet
ones. Depth stays capped at 1: upstream's unlimited nesting is a
hazard, not a feature.
- `SubagentResult.rate_limited` is classified where the typed ACP error
code is still in hand; a scheduler re-deriving it from a formatted
string would stop adapting the day the wording changed.
- Aggregate output is clamped per member (head+tail, loss stated):
native tool output is truncated nowhere downstream.
42 agent_swarm tests. The fake backend awaits, so the concurrency and
ordering assertions can actually fail; the cap test also proves the
fixture can exceed the cap.
|
||
|
|
ed8049cf77 |
fix(memory): scope embedding credentials to the endpoint that may receive them
`MemoryBackendParams` carried the primary session `AuthManager` and the session api-key provider unconditionally, while `embed_base_url` is the CURRENT MODEL's endpoint and `AuthRetryMiddleware` stamps `Authorization` on every request it wraps. A user who enables `[memory.embedding] model` while running a BYOK or subscription-OAuth model therefore sent the Kimi session bearer to that third party. `EndpointScopedCredentials` binds the credential to the one endpoint it may reach: `for_endpoint` drops the handle unless the caller vouches for the URL, and `approved_for` re-checks at provider-build time in release too, because `MemoryBackendParams` is `Clone` and callers rewrite fields on the copy. The shell decides through `CredentialAuthority::manager_for` rather than a second URL predicate — it answers both whether a credential may ride and which manager governs it, so a subscription-OAuth platform gets its own pooled manager. The session's `SharedApiKeyProvider` is not forwarded at all: it is hard-wired to the primary manager, so at a pooled platform's host it would resolve the wrong bearer. A platform's own `embed_api_key` is untouched and keeps serving its own endpoint. The background reindex built a second provider straight from `ApiEmbeddingProvider::from_session`, outside the chokepoint and without 401 refresh; it now embeds through the session's own params. Test strength verified by mutation: with the guard reverted, exactly `session_credentials_are_withheld_from_a_foreign_endpoint` and `a_cloned_param_set_cannot_redirect_scoped_credentials` fail. |
||
|
|
a02b555e66 |
docs(comments): rewrite comments across all crates to the guidelines
Sweep every first-party crate source (1956 .rs files) to the project comment guidelines: delete redundant restatements, decorative banners, change narration, and end-of-line comments; keep and tighten the crucial ones (invariants, bug rationale, SAFETY blocks, ported-source attribution). No functional code changed. Every edit is proven comment-only against the prior tree by a comment-stripping lexer (string/char/raw-string aware) plus a separate doctest-fence check. Where removing a comment made rustfmt or clippy want to re-lay-out adjacent code, the minimal triggering comment is restored so code tokens stay byte-identical. Gates green: cargo fmt --all --check (0 diffs), cargo check and cargo clippy --workspace --all-targets (0 warnings). Adds scripts/check_codegen_comment_guidelines.py — the enforcement gate for these guidelines (flags banners, end-of-line comments, change narration, and commented-out code). |
||
|
|
48d89c7830 |
refactor(auth): centralize inference-credential routing in CredentialAuthority
One authority answers 'which credential may ride this request': credential_class / manager_for / credential_for / bearer_resolver_for, keyed by (platform, base_url). SessionCredential is an opaque type with no production constructor, so a new call site cannot re-introduce the session-bearer leak. Platform-scoped tests extended across all bearer channels (session, aux, summary, subagent override). Verified: cargo check --workspace --all-targets clean; kigi-shell and kigi-tui suites green (6611+ tests). |
||
|
|
2d00a4e6e6 | update | ||
|
|
422e241e13 |
feat(providers): add ChatGPT/Codex subscription OAuth (PKCE + account-id, hardcoded catalog)
29th platform `openai-codex` (uses_oauth, Responses wire). PKCE-localhost login at auth.openai.com (client app_EMoamEEZ73f0CkXaXp7hrann, redirect localhost:1455/auth/ callback, form token exchange, fresh-random state) reusing the claude-pro-max flow; OAuthFlow::PkceLocalhost gained a redirect_path and OAuthConfig an authorize_extra (empty elsewhere, so claude/xai/copilot authorize URLs stay byte-identical). Codex-specific: the access token is a JWT carrying chatgpt_account_id, which becomes the `chatgpt-account-id` inference header. It is derived STATELESSLY from whichever bearer rides each request (so a rotated token needs no persisted field), and BOTH login and refresh fail fast when the claim is absent — gated on the explicit OAuthConfig.requires_chatgpt_account_id fact, never inferred from the token-body encoding (a plain form endpoint is the OAuth norm and must not inherit this). Inference rides the existing Responses wire at chatgpt.com/backend-api/codex → /responses, with codex headers (chatgpt-account-id, originator, OpenAI-Beta responses=experimental, codex UA) gated on SamplerConfig.openai_codex so API-key `openai` stays byte-identical; store:false was already the global Responses default. Catalog is HARDCODED (no live endpoint exists for this backend; read from the official Codex CLI's model cache): gpt-5.6-sol/terra/luna + gpt-5.5, ctx 272000, each with its real reasoning levels (low..ultra — ReasoningEffort gained Ultra). Excluded: gpt-5.3-codex-spark (supported_in_api=false), gpt-5.4/-mini and codex-auto-review (hidden) — they would list but fail at inference. The fetch short-circuits before any HTTP; Kigi never shells out to the codex CLI or reads ~/.codex. Security review fixes: redact any `account-id` header from request logs (it was reaching debug logs), strict 3-segment JWT check (fail closed), refresh no longer fails open on a missing claim. Inherits leak-safe pooled routing (scope oauth/openai-codex) — never the Kimi token. Full gate green (234 suites, 0 warnings). |
||
|
|
8179438278 |
feat(providers): add GitHub Copilot subscription OAuth (device flow + copilot-token re-mint)
28th platform `github-copilot` (uses_oauth, ChatCompletions wire). Two-stage auth: RFC-8628 GitHub device flow (client Iv1.b507a08c87ecfe98, scope read:user, errors in a 200 body) mints the DURABLE github token; a GET api.github.com/copilot_internal/ v2/token exchange re-mints the SHORT-LIVED copilot token. Persisted as key=copilot token, refresh_token=github token, expires_at=copilot expiry; the "refresh" is a copilot-token re-mint (not a refresh_token grant), dispatched via OAuthTokenBody::GithubCopilotExchange in the generic refresher. VS Code editor-identity headers on /models + /chat/completions, gated on SamplerConfig.github_copilot / PlatformId::sends_copilot_editor_headers() so every other ChatCompletions provider stays byte-identical. Live /models filtered (parse_github_copilot_listing) to the openai-completions-served models: keep iff model_picker_enabled && policy.state!="disabled" && tool_calls!=false AND not a claude-4.x/5.x (messages) or gpt-5/oswe/mai- (responses-only) id — those need per-model wire routing (documented debt), excluded rather than mis-routed. Inherits the leak-safe pooled routing (scope oauth/github-copilot); its bearer/ refresh/api_key never touch the Kimi token (regression test added). Fail-fast on an out-of-range copilot expires_at (would otherwise silently 401 mid-session). Adversarial security review: GO, no CRITICAL/HIGH. Known limitation: Pi's per-model policy-enablement POST is not ported (documented in AGENTS.md). |
||
|
|
5a9183b08b |
feat(providers): add Claude Pro/Max subscription OAuth (PKCE-localhost)
27th registry variant, 2nd subscription-OAuth provider. Log in with a Claude
Pro/Max subscription via PKCE authorization-code + S256 (loopback callback on
127.0.0.1:53692, with a manual code-paste fallback), then use it against
api.anthropic.com — reusing the existing Anthropic Messages wire + Anthropic
listing + the multi-provider OAuth foundation (
|
||
|
|
dbce6bf305 |
feat(providers): add xAI Grok subscription OAuth (device-code) + per-provider session auth
First subscription-OAuth provider beyond Kimi Code (26th registry variant). Log in with a Grok/SuperGrok/X subscription via RFC-8628 device-code OAuth (auth.x.ai), then use it against api.x.ai/v1 — reusing the existing xai wire (ChatCompletions + OpenAI listing + Passthrough + restrict + models_dev_id xai). Sourced from Pi (earendil-works/pi auth/oauth/xai.ts): client b1a00492..., scope 'openid profile email offline_access grok-cli:access api:access', standard Bearer (no x-xai-token-auth). Foundation (generalizes Kigi's Kimi-singleton OAuth to per-provider, root cause, not a patch): - Registry: OAuthConfig on PlatformSpec (client_id/host/device+token paths/scope/scope_key); XAI_OAUTH_CONFIG + XAI_GROK_SPEC (uses_oauth, method id 'xai-grok', an interactive login after kimi-code). - Generic device-code wire (auth/oauth_device.rs) + GenericDeviceRefresher, sharing the RFC-8628 core with Kimi; Kimi's bespoke flow is byte-identical (X-Msh headers, KIMI_CODE_OAUTH_SCOPE, keyring gating unchanged). - Per-provider AuthManager via a process-global pool (auth/oauth_registry.rs): build-on-demand with start_proactive_refresh, keyed by scope. The session resolves the AuthManager for the ACTIVE model's platform for bearer/refresh/ 401-recovery/api_key — an oauth-platform model always uses its OWN token, never the primary. - Live /models under OAuth; base routes oauth().is_some() -> platform.base_url() (kimi-code stays on proxy_url). Security: adversarial review + a systematic token-leak audit found and closed FIVE channels where the primary Kimi token could reach api.x.ai (bearer resolver, api_key stamping, aux summary/classifier/image-describe models, and subagent model-override). Each fix routes through the platform-aware resolver (the oauth model's pooled token or None, NEVER the primary) and is revert-to-red verified. No access/refresh token is ever logged. Registry at 26; picker updated (xai-grok interactive login row); TUI context-window already auto-updates per model. Full gate green (234 suites, fmt, clippy -D warnings, deny). GPT/Claude/Grok officially permit third-party subscription use. |
||
|
|
7efb4b07cc |
Add DeepSeek platform + ChatCompletions dialect system (provider 3)
The 6th registry row: id "deepseek", DEEPSEEK_API_KEY > auth.json "deepseek" scope, base https://api.deepseek.com (chat rides {base}/chat/completions per official docs) with KIGI_DEEPSEEK_BASE_URL override, enrichment-backed metadata (1M context, 384k output cap, high/max effort menu). Structural fix the cycle exposed: kigi's Kimi-specific body adaptation ran UNCONDITIONALLY on every ChatCompletions request. New ChatCompat dialect, declared per platform row and threaded through SamplerConfig, ClientDefaults, and the session-persisted SamplingConfig (serde-default Kimi keeps restored pre-field sessions and BYOK endpoints byte-identical; production persist seams copy it; subagents inherit it): - Kimi: full legacy pipeline (dispatch ≡ legacy pinned) - DeepSeek: thinking:{type, reasoning_effort} per api-docs.deepseek.com (server maps low/medium→high, xhigh→max itself; none disables; absent leaves the server default) - Passthrough: OpenAI-style reasoning_effort scalar untouched (unblocks Groq and the rest of the OpenAI-compatible list) Review-confirmed release blocker fixed: kigi replays Kimi's reasoning_content (and its private model_id) on input assistant messages — Kimi consumes these, but DeepSeek documents input reasoning_content as prefix-mode-only (historically a 400) and other providers don't know either field. The DeepSeek and Passthrough arms now strip both; Kimi's own pipeline is untouched. Pinned on both message shapes. |
||
|
|
02cf5deebd |
Add /graph G6: plan-boundary topology optimizer
A restricted optimizer pass now reviews the graph at plan boundaries —
right after initial planning and piggybacked on each replan version
boundary, never mid-execution. An optimizer subagent may emit four ops
over Waiting/Ready nodes only: remove_dep (delete a false dependency,
restoring parallelism — the highest-value edit), reorder (pending
priority for the serial scheduler), merge (fold two tiny nodes; specs
concatenate, deps union, dependents re-point, absorbed self-deps drop),
and split (2-3 focused replacements inheriting the original's deps and
dependents). The optimizer changes graph DATA only; the executor stays
pure deterministic Rust. KIGI_GRAPH_OPTIMIZER=0 disables it entirely.
apply_optimization enforces the contract twice: per-op checks
(pending-only targets, known ids, terminal node untouchable, dead-node
deps rejected as DeadDep, merge/split targets with non-pending
dependents rejected with the true reason instead of tripping the
immutable invariant later), then FINAL invariants — every non-pending
node byte-identical in the result, the gn-final gate rebuilt over all
survivors, node cap, whole-graph acyclicity, and a BIDIRECTIONAL status
re-derivation for pending nodes (adversarial review caught the critical
hole: a merge grafting unsatisfied deps onto a Ready node would
otherwise dispatch it ahead of its new prerequisites, since
recompute_ready is promote-only). Applied passes bump plan_version,
freeze an immutable baseline, and consume a slot of the SHARED replan
cap; an explicit {"ops": []} is a respected free no-op; any failure
degrades to keeping the current plan. Plumbing reuses a new shared
artifact-pass runner (stale-artifact delete, size cap, missing-file
fail-closed) extracted from the replanner.
Tests: remove_dep parallelism restore + loud no-such-dep, immutable and
terminal-node rejections across all four ops, merge/split dependent
rewiring incl. final-gate rebuild and intra-split dep resolution,
result-cycle rejection, dead-dep splits, Ready-demote-on-merge, and
three e2e flows — false-dep removal proven ACTUALLY parallel by the
held-reply fan-out gate, OPTIMIZER=0 spawning zero passes, and the
shared-cap guard. kigi-shell 4959 lib tests green; clippy clean.
|
||
|
|
db05ed0751 |
Add /graph G5: box-drawing DAG rendering for /graph show
/graph show now renders the dependency graph as box-drawing text via a deterministic Sugiyama-lite pipeline in session/graph_render.rs: longest-path layering, dagre-style dummy pass-throughs so every drawn edge spans exactly one layer gap (long edges route as vertical lanes through intermediate bands), one-pass barycenter ordering, and greedy bus-lane allocation in the connector gutters with box-drawing-aware glyph merging (a bus crossing a pass-through renders ┼). Node boxes carry a status glyph (✓ ▶ ○ · ✗ ⊘) and a clamped title; a legend line closes the view. Only Blocks edges are drawn — DiscoveredFrom is audit metadata whose origin is always terminal, so drawing it would double edges without scheduling meaning. The output rides ordinary scrollback (the pager already scrolls it), so no new wire variant or pager view was needed. Honest ceiling: a graph wider than the 120-column budget — or an empty/pre-planning graph — degrades to the indented status tree, because box art wrapped by the terminal is worse than no art; a cycle reaching the renderer (upstream validation bypassed) refuses to render rather than looping. Tests: six-node structural snapshot (layering order, fan-in bands, arrowheads, corners, legend, no trailing whitespace, width bound), determinism across runs, too-wide fallback, dummy-lane routing through intermediate bands, DiscoveredFrom exclusion, title clamping, empty graph; plus a handle_prompt e2e driving /graph show end to end (no-graph degrade + seeded chain rendering). kigi-shell 4949 lib tests green; workspace clippy clean. |
||
|
|
771de1822d |
Add /graph G4: project-level shared graph in .kigi/graph.jsonl
The graph now follows the REPOSITORY, not the session. Every checkpoint projects the orchestration to .kigi/graph.jsonl at the git root in a beads-style, line-mergeable shape: line 1 is the header (orchestration minus nodes — omitted entirely, and a header carrying inline nodes is rejected on load rather than silently duplicating the per-line entries), then one content-hash-id node per line. The session tracker stays the single source of truth; projection failures warn loudly but never block progress. kigi only writes the file — committing it stays a user decision. Single-writer discipline via an fs2 flock on a sidecar .lock: the session that creates or resumes a graph owns the projection; other instances get a read-only /graph status view rendered from the file and an explicit refusal on resume. Cross-session revive: /graph resume in a fresh session loads the file UNDER the lock (locking after reading raced the owner's final checkpoint and could resurrect a just-cleared graph), sanitizes it with the from_snapshot demotions, and re-dispatches. Holding the lock proves nobody writes NOW — not that the file's content is yours. Every lock-then-mutate site therefore identity-checks the projected graph_id: /graph <objective> refuses to overwrite a foreign non-Complete projection (revive-or-clear guidance, mirroring the session-level guard); session-restored graphs claim writership on resume (and best-effort at spawn re-emit, so the shared file learns the demoted truth immediately) but refuse when the projection belongs to a different graph; /graph clear skips projection teardown entirely when no session graph exists, leaves foreign projections in place, and warns instead of swallowing lock errors. Resume arms validate their flags before taking the lock. Tests: projection round-trip pinning the one-line-per-node shape, inline-nodes rejection, malformed-content loud errors, exclusive-lock semantics across handles, and an e2e driving create → checkpoint projection → second-instance read-only refusal → owner death → fresh- session revive (demoted node relaunches) → clear removing the projection. kigi-shell 4941 lib tests green; workspace clippy clean. |
||
|
|
bb5cbff62d |
Add /graph G3: dynamic replan from DISCOVERED work items
Workers, verifiers, and serial node goals can now surface out-of-scope work
as line-anchored 'DISCOVERED: <text>' markers (fence-stripped and
placeholder-filtered — the templates' own examples are fenced so verbatim
echoes never parse; worker summaries embedded in verifier prompts get the
marker neutralized alongside NODE_RESULT/NODE_VERDICT). Discoveries queue on
the orchestration as persisted state and fold into the graph at dispatch
boundaries: a replanner subagent produces a strictly APPEND-ONLY appendix,
validated against the live graph (existing-id deps allowed; edges onto
gn-final rejected — they would cycle the moment the final-gating extension
lands; Blocks deps on Failed/Blocked nodes rejected as DeadDep so the
attempt-2 feedback loop repairs the artifact). Installing an appendix bumps
plan_version, freezes an immutable graph.baseline.v{N}.json next to the
prior versions, extends gn-final's gate (demoting a Ready final back to
Waiting), and recomputes readiness.
DiscoveredFrom edges are audit metadata, never scheduling gates: an origin
is always terminal at replan time, so gating on it is either a no-op or a
permanent wedge — and a failed node's discoveries are still real work.
Replanning is bounded by KIGI_GRAPH_REPLAN_CAP (default 3; 0 disables it
quietly): past the cap, after the final node has achieved, or on replan
failure, discoveries drain to history only — a working graph is never
paused for a failed enhancement pass, and it always converges. The budget
gate now precedes the replan boundary (a budget-dead graph keeps its
discoveries queued for a later --budget top-up instead of spending two
replanner runs first), and both planner runners delete stale artifacts
before spawning so a child that responds without writing can never get a
previous pass's file validated as its own output.
Tests: validate_replan unit coverage (existing-id resolution,
DiscoveredFrom dedup, collisions, dead deps vs dead origins, terminal-node
edges, combined-graph cycles), tracker appendix/regate/audit-edge tests,
and two e2e flows — discovery → replan → appended node runs to Achieved
with both baselines frozen, and cap-0 draining to history while the graph
still converges. kigi-shell 4935 lib tests green; workspace clippy clean.
|
||
|
|
1579558b56 |
Add /graph G2: resumable budget, GraphUpdated status chip, PTY + turn-level coverage
BudgetLimited is now a resumable state: a budget trip demotes in-flight
nodes to Ready (a resource stop, not a verdict — no forever-Running node is
ever persisted) and '/graph resume --budget <tokens>' re-arms the graph with
fresh headroom (new budget = spent-so-far + extra). The tripped node's
partial burn is charged into tokens_spent_nodes at BOTH cascade sites before
the demotion clears current_node, so the top-up arithmetic never runs on an
under-counted ledger. Any input starting with 'resume' resolves to a resume
(case-insensitive; malformed top-ups surface the usage hint) and setup_graph
refuses to replace any non-Complete graph — a typo can no longer silently
destroy a resumable graph. An explicit --budget on a merely-paused graph is
rejected loudly instead of silently discarded; all trip-time messages now
advertise the top-up.
The pager gains a graph status chip: a new GraphUpdated wire variant
(extensions/notification.rs, old pagers degrade via #[serde(other)]) is
emitted from the single persist_graph_state chokepoint — every transition is
both a checkpoint and a badge tick — with a 'cleared' sentinel on /graph
clear and a one-shot re-emit after session restore (the replayed updates log
otherwise shows the pre-shutdown Active state that from_snapshot just
demoted in memory). TUI side: GraphDisplayState, session-notification arm,
and a goal-idiom chip with node progress, clamped current-node title, and
budget-aware spend. Pre-session command availability now advertises /graph
from the flags (it was fail-closed to the in-session path only, so the
welcome-screen slash menu never showed it).
Coverage: GraphUpdated wire round-trip + minimal-payload + unknown-tag
tests; PTY scenarios graph_slash_presession{,_disabled}.yaml (both run
green against the real pager binary); handle_prompt-level e2e for terminal
slash outcomes (/graph status|resume|pause, /goal refusals while the graph
owns the engine); budget top-up e2e driving a BudgetLimited diamond back to
Complete. Not shimmed: pre-G2 persisted snapshots with budget-Failed nodes
(the KIGI_GRAPH flag has never shipped enabled, so none exist).
kigi-shell 4927 and kigi-tui 6610 lib tests green; workspace clippy clean.
|
||
|
|
4d1e4fdc52 |
Add /graph G1: parallel fan-out with worktree isolation and merge-back
With KIGI_GRAPH_CONCURRENCY > 1 (default 3, clamp [1,8]) and >=2 Ready nodes, drive_graph — the single dispatch loop shared by setup/advance/resume — runs parallel batches: each node executes as a bounded worker<->verifier subagent loop (KIGI_GRAPH_NODE_ROUNDS, default 3; general-purpose children with the full implementer toolset; worktree isolation on round 1, resume keeps context and worktree on later rounds; NODE_RESULT/NODE_VERDICT terminal contracts parsed fail-closed with fence-stripping and line anchoring). Achieved nodes merge back SEQUENTIALLY via kigi_workspace apply_worktree in Merge mode; a conflict fails the node, block_dependents fires, and surviving chains keep going. gn-final always runs serially on the full goal engine. Concurrency=1 is byte-identical to the serial G0 path; non-git projects degrade to serial. Merge primitive hardened for real use (kigi-workspace): the 3-way apply is now byte-safe (binary files no longer read as UTF-8 and silently deleted) and gains the identical-content rule (ours==theirs => already present, not a conflict) — without it, any dirty file inherited via PreserveWorkingTree false-conflicted every node merge and multi-wave graphs self-poisoned. Adversarial review pass (16 confirmed findings, all fixed): in-session resume demotes orphaned Running nodes instead of wedge-pausing forever after a mid-batch Esc; cancel re-sweeps subagents AFTER the turn abort so workers spawned in the cancel window die too; empty child ids are never adopted as resume targets (no unisolated escape to the shared tree); a successful isolated round returning no worktree fails the node (soft-fallback can no longer put N writers in one tree); main-HEAD movement during a batch aborts merges instead of reverse-applying external commits; failed nodes still charge the token budget; budget trips terminally fail the in-flight node; runaway (>600s) rounds are cancelled by spawn id and retried via resume; worker summaries are marker-sanitized before verifier embedding. Merged worktrees are removed immediately (storage discipline); failed nodes keep theirs for postmortem. Tests: 4922 kigi-shell lib tests green (58 graph-specific), including fan-out proven by a held-reply gate, real-git batch merge with cleanup assertions, budget charging across verdicts, cap trimming, resume-after- cancelled-batch, and backgrounded-round cancel semantics. |
||
|
|
4361b03259 |
Add /graph G0: serial graph engineering mode over the goal engine
A deterministic DAG scheduler layered on the existing goal engine: /graph <objective> decomposes the objective via a graph-planner subagent, gates the result through Agentproof-style static validation (cycles, unknown deps, duplicate slugs, caps), appends a structural final-verification node, then executes each node as one ordinary goal — planner, worker loop, adversarial verifier, budget and pause machinery all reused verbatim. The in-turn loop advances nodes within the same turn (multi-loop closed loop); goal-side auto-pauses cascade to the graph at a single chokepoint; node goals are armed with the remaining graph budget so mid-node overruns trip graph-wide. Gated by KIGI_GRAPH=1 (default off) + the goal harness. State persists to <session_dir>/graph/state.json with a clear-tombstone; per-version immutable baselines and per-node artifact archives live under graph/<graph_id>/. Restore demotes Active->UserPaused and Running->Ready (verifier-gated re-run). Review hardening (adversarial multi-agent pass, 24 confirmed findings fixed): the goal-inactive loop break now consults the graph seam (mid-turn classifier-disabled completions can no longer strand an Active graph), the pause cascade fires even when no node goal is in flight (cancel during planning), node bookkeeping precedes the long setup await, /graph clear only resets the engine it owns, budget trips terminally fail the in-flight node, and the pause transitions in /goal pause + /graph pause no longer hide inside debug_assert! (a release-build no-op inherited from upstream). Tests: 4908 kigi-shell lib tests green, including a serial 4-node closed-loop e2e, restore/resume, cascade, mutual-exclusion, planning-retry, persistence round-trip + tombstone, and status rendering. |
||
|
|
6f31415ed6 |
§9 acceptance: grep-zero sweep — every internal x.ai/grok identifier renamed
The PRD's first acceptance gate now holds: grep -RinE '\bx\.ai\b|grok' crates/ --include='*.rs' → 0 matches (exempt: NOTICE and third-party license archives, README provenance, and the required 'Based on Grok Build Open Source' attribution, now sourced from version_attribution.txt). Wire-visible renames (both sides in this repo, changed in lockstep): - Auth method id 'grok.com' → 'kimi-code' (AuthMethodKind::KimiCode). - Every x.ai/* and _x.ai/* ACP ext method and meta key → kigi/* / _kigi/* (~200 names; grokShell → kigiShell). Session-file replay keeps a read-side alias for the legacy '_x.ai/session/update' method so existing updates.jsonl histories load; writes emit only the new name (both directions test-pinned). - Agent types grok-build* → kigi* with a documented legacy-prefix alias at resolution time so persisted sessions keep resolving. - ToolNamespace/BuiltinAgentName GrokBuild* → Kigi* (wire snake_case kigi/kigi_concise/kigi_hashline; schema regenerated); grok_build implementation dirs renamed to kigi*. - x-grok-* headers → x-kigi-*, __GROK_* sentinels → __KIGI_*, themes grokday/groknight → kigiday/kiginight (old persisted values fall back to the default theme), web_fetch allowlist xAI hosts → kimi.com + moonshot platforms, changelog CDN → this repo, grok-build changelog archives deleted. - BYOK default endpoint removed: [endpoints] api_base_url is now truly optional with NO default — consumers fail fast with the flag name when unset (no silent x.ai egress). Mock harnesses inject it explicitly. - System-prompt identity fixed: 'released by xAI' → 'an unofficial community CLI for Kimi' (template + regenerated encrypted form). Also repaired pre-existing grok-era test debt found by the sweep: the stale trace_classify default-model pin, the grok-pager UA label test, pty-harness stale-binary reuse and non-hermetic moonshot routing (a PTY test could previously reach the real api.moonshot.cn), and the outdated oauth fixture scope key. Gates: §9 grep 0; fmt clean; workspace check/clippy 0/0 (-D warnings); FULL cargo test --workspace: 234 suites, 21,961 passed, 0 failed; deny advisories ok. |
||
|
|
5e4e24db99 |
M2 audit: excise managed connectors and xAI media-gen tools
Managed connectors (grok.com MCP admin) removed root-and-branch: - The managed-MCP fetch/injection pipeline is gone, including the whole kigi-shell-session-support crate (managed-config fetch client, gateway tool catalog + dispatch, header injection, refresh task), reactive managed re-auth, mcp_doctor's grok.com-source discovery, and the [managed_mcps] config surface. - TUI: the 'Managed by grok.com' section, connectors URL/deep-link, Action::OpenManagedConnectors, and session_team_id are gone. Local MCP management (list/toggle/add/remove/auth/tools) is fully intact. - Kept as LOCAL policy: managed-settings.json MCP allow/deny enforcement, the multi-source local MCP merge, folder-trust gating. PluginOrigin Project/User labels kept (they tag locally discovered plugin dirs). imagine/media-gen tools (xAI image/video generation) removed: - image_gen, image_edit, video_gen, image_to_video, reference_to_video implementations, registrations, ToolKind/ToolInput/Output variants (serde-safe), config plumbing end to end, ZDR video machinery, /imagine + /imagine-video commands and guidance text, the bundled imagine skill (added to legacy cleanup so user installs delete it), and the media-gen render path. - Kept: image INPUT (paste/attach, [Image #N] meta, pdf/image fetch, clipboard wrap), generic media-ref rendering, and the generic tool 401-retry machinery (tests renamed, assertions unweakened). - deploy_app stays: it is a permanently-disabled local stub deploying nowhere. 121 files changed, 8 deleted. Gates: workspace check/clippy 0/0, fmt, deny ok; suites green (tools 2554, shell 4862, tui 6608, workspace 1042). Remaining grok.com strings live only in the auth-method ids and changelog archives (§9/M3 sweep). |
||
|
|
fa75eb139a |
M2 audit: excise the Computer Hub stack — Kigi's last remote-cloud surface
Removed root-and-branch for the zero-egress guarantee (the hub was xAI's remote-workspace/cloud-sandbox service): - Crates deleted: kigi-computer-hub-core, kigi-computer-hub-sdk, kigi-computer-hub-mcp-adapter, kigi-workspace-client (hub-proxied workspace RPC client), and kigi-tracing (its sole network path was the OTLP gRPC exporter; zero consumers remained). kigi-tracing-macros (purely local) stays. - kigi-workspace: every hub surface deleted — hub server/channel/auth, HITL-over-hub permissions, donation/metrics pumps, file upload RPCs, hub tool-snapshot merge (resolve pipeline is MCP-only now), WorkspaceOps::Proxy. Local worktrees, sessions, leader IPC, MCP, and the ACP permission prompt path are untouched; LocalRegistry re-homed into kigi-tool-runtime on the existing ToolDyn types so in-process tool dispatch is unchanged. - kigi-shell: leader workspace-exposure control surface (incl. the wss://computer-hub... URL), [hub] config, ObservabilityBridge, hub WebSocket proxy, dead OTLP config knobs. ClientMode::Headless (never constructed) removed. - kigi-tui/bin: hidden `kigi workspace` command removed (`kigi worktree` stays). - Renames: --xai-api-base-url → --api-base-url / KIGI_API_BASE_URL / [endpoints] api_base_url (serde alias keeps old configs working; the flag feeds BYOK/custom-endpoint routing, not main inference); grok_version → kigi_version in inspect/models-cache/trace metadata (old caches self-heal via version-mismatch refetch). - Dependency tree: dropped fastrace*, opentelemetry-otlp/http/proto, tokio-tungstenite from the workspace; fixed the 4 real useless_format violations the fastrace lint allowance was masking and removed the allowance. - marketplaceAllowlist kept: it gates the LOCAL plugin-marketplace feature, not an xAI service. Known §9 leftover (deliberate, for the M3 sweep): the BYOK default base URL string. Gates: workspace check/clippy 0/0, fmt, deny ok; suites green (workspace 1042, shell 4918, tui 6634, tools 2608, tool-runtime 47, mcp 154). |
||
|
|
74b210535e |
F5: web search/fetch on the Kimi services (kimi-cli parity)
web_search now speaks the Kimi search service (kimi-cli tools/web/search.py,
wire-verified against api.kimi.com):
- POST {coding_base}/search with {text_query, limit 1-20 (default 5),
enable_page_crawling, timeout_seconds: 30}, OAuth bearer +
X-Msh-Tool-Call-Id; results render in kimi-cli's Title/Date/URL/Summary
schema with result URLs as citations.
- The old implementation called the xAI Responses API with a search model;
that client is fully replaced and the entire model-based config surface
is excised root-and-branch: web_search_model config keys/env/CLI plumbing,
resolve_web_search_sampling_config, toolset web_search SamplerConfig,
RemoteSettings.web_search_model, default_web_search_model.
- Enablement is now purely structural: the service exists only on the Kimi
Code subscription channel, so OAuth sessions get Enabled and API-key-only
sessions get Disabled (tool absent) — per PRD F5.
web_fetch gains the Kimi fetch service as its primary path (kimi-cli
tools/web/fetch.py): POST {coding_base}/fetch with {url}, Accept:
text/markdown, OAuth bearer + X-Msh-Tool-Call-Id; the 200 body is the
extracted markdown (still overflow-budgeted). Any service failure falls
back to the existing local pipeline (SSRF guards, cache, extraction).
The tool gate defaults ON now (kimi-cli always offers FetchURL) and the
egress User-Agent no longer claims grok-agent/x.ai.
Verified end-to-end against the scripted mock service: a headless session
drove web_search (limit/crawling/call-id observed on the wire) then
web_fetch (Accept + call-id observed) to completion.
|
||
|
|
ea0ce9d15f |
F3: Kimi inference pipeline + full grok cloud-surface excision
Sampler / inference (PRD F3):
- kimi_compat.rs: single adaptation point for the Kimi chat/completions
dialect (thinking-field mapping, model_id stripping, empty-content
tool-call message fix, stream_options.include_usage), with kimi-cli
source citations
- Rate-limit handling reworked for Kimi/Moonshot semantics; UA kigi/{version}
- /models replaces the xAI models-v2 endpoint everywhere; idle model
refresh carries X-Msh-* device headers only (X-XAI-Token-Auth and
x-grok-client-mode/CLIENT_MODE_HEADER machinery deleted)
Cloud-surface excision (PRD §5, zero-egress):
- remote/ conversations lane, cli-chat-proxy-types crate, prod/ dir,
share command, credit bar: deleted (single local session lane;
paginate() replaces merge_and_paginate)
- Subscription/tier gate stack deleted end-to-end: AppView
gate/tier/team/ZDR fields, app/subscription.rs watch loop,
dispatch/billing.rs paywall + SuperGrok upsell, free-usage-exhausted
chain, tier-restricted commands, GateInfo, RemoteSettings gate fields,
SettingsUpdateNotification gate fields
- /privacy + coding-data-sharing setting deleted (backed by a dead xAI
RPC; Kigi is zero-egress — nothing to share or retain remotely)
Auth UX correctness (user-reported):
- Device-flow fixtures now mirror the live Kimi payload shape
(https://www.kimi.com/code/authorize_device?user_code=..., verified
against auth.kimi.com); the fabricated auth.kimi.com/device?code=...
URLs are gone
- open_browser_detached is a no-op under cfg(test): unit tests drove
wiremock fixture URLs into the real browser (root cause of the
"garbage mock link" ABCD-1234 tabs)
- Welcome/pager-minimal rebrand: Grok Build -> Kigi, grok.com ->
kimi.com, "Sign in to Grok" -> "Sign in to Kimi"
|
||
|
|
fe1f885bb3 |
M1/F2+F4: platform registry, Moonshot API-key channel, dynamic model sync
F2 — fixed three-platform registry in kigi-models: kimi-code (subscription, OAuth bearer, base kigi_env::coding_api_base_url()), moonshot-cn (https://api.moonshot.cn/v1), moonshot-ai (https://api.moonshot.ai/v1) with kimi-k model-prefix filtering. Moonshot API keys via KIGI_MOONSHOT_CN_API_KEY / KIGI_MOONSHOT_AI_API_KEY (+ KIGI_MOONSHOT_API_KEY shared fallback) or ~/.kigi/config.toml; values redacted from logs/display. F4 — model catalog now syncs from GET {base}/models (Bearer auth, wire shape per official kimi-cli: id/context_length/supports_reasoning/ supports_image_in/supports_video_in/display_name) with the official capability-derivation rules (thinking / always_thinking-in-name / kimi-k2 implicit set). Managed keys {platform_id}/{model_id}; default model = first list entry; default thinking iff capabilities contain thinking/always_thinking. Sync failure → last cache; no cache → built-in fallback table seeded from ids sourced in official kimi-cli (kimi-for-coding, kimi-k2-turbo-preview, kimi-k2-thinking-turbo). 401 during sync forces one token refresh and retries. Model resolution priority preserved: CLI > env > config > server > fallback. Grok model artifacts (grok-4*/grok-build catalog, tier gating remnants) removed from non-test code. All first-party endpoints re-verified live: device_authorization mints real codes; /models on all three platforms answers with real API auth errors when unauthenticated. Gates: check/clippy --all-targets 0/0, fmt clean, deny ok, kigi-shell lib 5136 green, kigi-tui lib 6819 green, kigi-models 8. |
||
|
|
021b82443d |
M1/F1: Kimi Code OAuth device-code flow
Replace the xAI OAuth stack with the Kimi device authorization grant:
- kimi_oauth.rs wire layer (device_authorization + token poll + refresh
against kigi_env::oauth_host(); client_id per PRD; retryable statuses
429/5xx with backoff; expired_token restarts authorization)
- X-Msh-Device-{Name,Model,Id} headers; device_id minted uuid4-hex at
~/.kigi/device_id (0600)
- Storage: system keyring service `kigi`, entry `oauth/kimi-code`
(macOS/Windows native backends), atomic-file fallback under ~/.kigi;
official client's keyring/~/.kimi never touched
- Refresh manager: 60s tick, threshold max(300, expires_in*0.5),
401-tombstone keyed by rejected refresh token with 300s cooldown and
rotation auto-clear, cross-process lock with sibling-adoption
triple-check, sleep/wake forced refresh
- Deleted xAI machinery: enterprise OIDC (PKCE/JWKS/teams), devbox login,
external auth provider, JWT tier gating + subscription paywall stack,
X-XAI-Token-Auth marker headers, ZDR gates, /user enrichment
- kigi login / TUI /login both drive the device flow; login-host display
now derives from kigi_env::oauth_host()
- 264 auth unit/wiremock tests; live contract probe of
auth.kimi.com/api/oauth/device_authorization matches the wire shapes
Gates: check/clippy --all-targets clean, fmt, deny ok, kigi-shell lib
5131 tests green.
|
||
|
|
d6c20fc13f |
M0: compilable skeleton — Kigi 0.1.0 fork surgery
Hard fork of xai-org/grok-build (Apache-2.0) re-targeted as Kigi, an
unofficial Kimi Code CLI community build.
Rename & identity
- 72 xai-*/xai-grok-* crates -> kigi-* (explicit: xai-grok-pager-bin ->
kigi-bin [binary `kigi`], xai-grok-pager -> kigi-tui; rest mechanical);
ptyctl, ptyctl-cli, third_party/ unchanged; proto package
xai.grok.tools.v1 -> kigi.tools.v1
- Config home ~/.kigi (KIGI_SHARE_DIR override), env prefix GROK_* ->
KIGI_*, `kigi --version` carries the unofficial-community-build notice
- clap identity, help text, startup banner, prompt templates rebranded
(templates re-encrypted)
Deletions (PRD removal list #5/#6/#7/#9/#10)
- voice input (xai-grok-voice) and all TUI wiring
- telemetry: Mixpanel client, external OTel stream, Sentry, OTLP layers,
trace/GCS/S3 upload queues (kigi-file-utils halved), workspace upload
module & dc_log, heap-profile uploader, auth-diagnostics uploader,
session-analytics halves of feedback; local zero-egress observability
preserved in new kigi-log crate (unified log, --debug firehose,
subsystem file logs, opt-in instrumentation)
- announcements (crate, remote-settings fields, TUI surfaces)
- plugin marketplace (crate, sources/browse/CTA/extensions-modal tab);
direct plugin install/uninstall/update via kigi-agent git_install kept
- relay/gateway/assets endpoints and features (agent relay, headless
relay transport, gateway bridge, LeaderEnvUrls); leader IPC socket now
~/.kigi/leader.sock + KIGI_LEADER_SOCKET, no ws-url derivation
- functional types rehomed instead of deleted: PermissionMode ->
kigi-config-types, McpInitStrategy -> kigi-mcp, PrCreationSource ->
session signals, TerminalDiagnostics -> kigi-pager-render, agent_id ->
shell util
Endpoints
- kigi-env rewritten: single production KigiEndpoints {coding_api_base_url
https://api.kimi.com/coding/v1 (KIGI_CODE_BASE_URL), oauth_host
https://auth.kimi.com (KIGI_OAUTH_HOST), update_base_url (GitHub
Releases API), upgrade_page_url}; GrokBuildEnvironment enum deleted
Toolchain & workspace hygiene
- Rust 1.97.0 pinned; edition 2024; full cargo update; git2 hoisted to
workspace at 0.21 (Option->Result API migration), quick-xml 0.41
- Root Cargo.toml hand-maintained (PRD §8.1): version 0.1.0 inherited by
all members, members sorted, unused deps pruned
- cargo-deny advisories gate (deny.toml with documented transitive
exceptions); CI workflow (check/clippy/fmt/deny/test, macOS+Linux)
- cross-crate test seams re-gated behind `test-support` cargo feature;
insta snapshot baselines renamed to the kigi_tui prefix
- clippy --workspace --all-targets: zero warnings; fmt clean
Fixes surfaced by the port
- updater probe/installer divergence (bin/kigi vs bin/grok symlink set)
- idle model-metadata refresh dead under KIGI_CODE_BASE_URL override
(new is_effective_coding_endpoint_url, loopback+override aware)
- macOS symlinked-TMPDIR fixture canonicalization (foreign_sessions,
fast-worktree); RSS measurement tests serialized via serial_test
Docs & legal (Apache §4)
- NOTICE added (upstream attribution + change statement); THIRD-PARTY
notices sustained; kigi-tools ported-code notices extended; README,
CONTRIBUTING, SECURITY, AGENTS.md rewritten
Out of scope for M0 (tracked): Kimi auth/inference (M1), search/fetch,
command parity, config import (M2), Computer Hub excision & final
brand-token sweep (M2), distribution & self-update rewrite (M3).
|