Commit Graph
21 Commits
Author SHA1 Message Date
ZacharyZhang-NY d3c9380307 release: v0.1.7
Release / build (aarch64-apple-darwin) (push) Waiting to run
Release / build (x86_64-apple-darwin) (push) Waiting to run
Release / build (aarch64-unknown-linux-gnu) (push) Waiting to run
Release / build (x86_64-pc-windows-msvc) (push) Waiting to run
Release / publish GitHub Release (push) Blocked by required conditions
Release / build (x86_64-unknown-linux-gnu) (push) Failing after 7s
Since v0.1.6 — the cross-provider replay audit (Pi transform-messages
policy: every wire builder emits only items valid for its target):
- fix(responses): reasoning items without a native rs_* id dropped (the
  GPT/codex 400 Invalid 'input[N].id'); provenance gate drops foreign
  reasoning and demotes foreign backend tool calls on model switches
- fix(codex): bare rs_* reasoning references dropped (stateless backend)
- fix(wire): shared ASCII tool-call id sanitizer, symmetric call+result
  on both the Messages and Responses legs
- fix(messages): image-source whitelist (raster base64 only, http(s)
  urls only) and empty-user-content guard
- fix(conversation): char-boundary-safe code preview (CJK/emoji code
  panicked every request build)
- docs: AGENTS.md records the replay policy
2026-07-23 01:16:55 -04:00
ZacharyZhang-NY d6e49bcc7d release: v0.1.6
Release / build (aarch64-apple-darwin) (push) Waiting to run
Release / build (x86_64-apple-darwin) (push) Waiting to run
Release / build (aarch64-unknown-linux-gnu) (push) Waiting to run
Release / build (x86_64-pc-windows-msvc) (push) Waiting to run
Release / publish GitHub Release (push) Blocked by required conditions
Release / build (x86_64-unknown-linux-gnu) (push) Failing after 8s
Since v0.1.5:
- fix(messages): Claude models no longer 400 with 'Invalid signature in
  thinking block' — thinking is replayed only for the active tool loop
  and only when genuinely signed (cross-backend/cross-model histories
  are stripped)
- fix(codex): ChatGPT Codex no longer 400s with 'System messages are
  not allowed' — system prompts ride the instructions field and
  encrypted reasoning is requested for stateless replay
- docs: AGENTS.md records both wire contracts
2026-07-22 23:36:22 -04:00
ZacharyZhang-NY e53a66d113 feat(graph): /graph ships on by default — end the KIGI_GRAPH gray release
Release / build (aarch64-apple-darwin) (push) Waiting to run
Release / build (x86_64-apple-darwin) (push) Waiting to run
Release / build (aarch64-unknown-linux-gnu) (push) Waiting to run
Release / build (x86_64-pc-windows-msvc) (push) Waiting to run
Release / publish GitHub Release (push) Blocked by required conditions
Release / build (x86_64-unknown-linux-gnu) (push) Failing after 7s
resolve_graph() read only the KIGI_GRAPH env var with default(false)
(plan.md G0 gate), so /graph existed solely on machines whose environment
exported the dev flag — which presented as '/graph is missing on
Windows'. There was never any platform-conditional code: the Mac worked
because the dev env var was set there.

Default is now true (matching /goal's shipped state); KIGI_GRAPH=0
remains the off-switch, and availability still requires the goal harness
(BuiltinGate::Graph). AGENTS.md updated; new test pins fresh-install-on
plus env-zero-off.

Verified: kigi-shell 5262 + kigi-tui 6874 tests green, clippy clean.
2026-07-22 21:16:49 -04:00
ZacharyZhang-NY b9b7e6c989 release: v0.1.5
Since v0.1.4:
- fix(fs): Windows-safe atomic replace everywhere (util::fs::replace_file)
  — model+effort switches now persist on Windows; models cache and session
  state writes no longer fail silently under AV/indexer file locks
- fix(tui): a default-model persist failure keeps the live session's model
  instead of reverting the pick
- docs: AGENTS.md records the replace_file contract
2026-07-22 19:55:30 -04:00
ZacharyZhang-NY 815bd99356 docs(agents): stop embedding the workspace version literal
'(0.1.0)' had rotted three releases behind. Name the source of truth
(workspace.package.version) instead of snapshotting its value.
2026-07-22 17:54:51 -04:00
ZacharyZhang-NY 0e4356c34e docs(agents): stored subscription-OAuth sessions are catalog fetch sources 2026-07-22 17:38:44 -04:00
ZacharyZhang-NY 0e352c9914 docs(agents): record per-row OAuth routing, /login picker, and _meta.connected contract 2026-07-22 16:16:59 -04:00
ZacharyZhang-NY 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).
2026-07-22 15:12:00 -04:00
ZacharyZhang-NY 2d00a4e6e6 update 2026-07-22 11:08:54 -04:00
ZacharyZhang-NY 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).
2026-07-22 08:28:03 -04:00
ZacharyZhang-NY 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).
2026-07-22 04:21:02 -04:00
ZacharyZhang-NY 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 (dbce6bf). Sourced from Pi
(earendil-works/pi auth/oauth/anthropic.ts): client 9d1c250a..., authorize
claude.ai/oauth/authorize, token platform.claude.com/v1/oauth/token, scope
'…user:inference user:sessions:claude_code…'.

New machinery (foundation handles token routing — claude-pro-max is a
uses_oauth platform so its bearer/refresh/api_key already route to its own
pooled manager, never Kimi):
- OAuthConfig gains flow{DeviceCode|PkceLocalhost} + token_host + token_body
  {Form|JSON}; xai/kimi rows unchanged (DeviceCode/Form).
- auth/oauth_pkce.rs: PKCE S256 wire — loopback listener with STRICT state
  validation (CSRF, fail-closed), manual-paste fallback, JSON code→token
  exchange + rotating-refresh. Never logs code/verifier/tokens.
- Messages OAuth adaptation gated on SamplerConfig.anthropic_oauth (true only
  for a claude-pro-max managed key): Authorization: Bearer + anthropic-beta
  oauth + user-agent claude-cli + x-app cli, and the required 'You are Claude
  Code' system prefix. API-key anthropic/minimax Messages requests are
  BYTE-IDENTICAL (regression-guarded).
- Live /models under the OAuth Bearer + oauth-beta headers (Anthropic listing,
  enriched from models.dev anthropic); persistent 401 → 0 models + WARN, NO
  hardcoded fallback list (honest failure).

Adversarial review: no blocking findings (secret handling, CSRF/state, the
anthropic_oauth gate, token routing, non-regression all CONFIRMED). Full gate
green. Registry at 27; picker updated. Residual (unverifiable without a real
Claude Pro/Max account): whether GET /v1/models accepts the OAuth bearer, and
the real endpoint's acceptance of the OAuth Messages request.
2026-07-22 02:53:25 -04:00
ZacharyZhang-NY 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.
2026-07-22 01:36:29 -04:00
ZacharyZhang-NY fdf9b956f5 Add models.dev metadata-enrichment pipeline (providers P0c-2)
Provider /models listings that return bare ids (OpenAI-style) get context
windows, thinking levels, image support, and display names from models.dev:
kigi-models owns the transform (parse_api_json — ONE field interpretation
for the bundled snapshot AND runtime refreshes), enrich_wire_model fills
gaps with wire values always winning and model availability strictly
wire-truth. Spec rows gained models_dev_id + wire_serves_metadata; all
three current platforms are wire-served, so this pipeline is provably
inert for them (byte-identical catalogs, zero egress, zero ~/.kigi writes
— adversarially verified).

Shell side: enrichment_fetch with a 24h disk cache guarded by binary
version + keep-set + future-stamp sanity (a registry change or downgrade
refetches instead of serving a catalog missing new providers), refresh of
https://models.dev/api.json filtered to registry ids, KIGI_MODELS_DEV_URL
override with case/whitespace-tolerant kill switch, fallback chain
fresh-cache > refresh > stale-cache > bundled (each step logged). The
fast path returns an empty catalog without forcing the bundled parse.

From the review: blast-radius-confined parsing (one drifted provider on
models.dev warn-skips instead of failing the whole refresh), registry-
coverage and field-coverage tests guarding script/parser drift, a path-
injectable core with 8 state-machine tests (one of which caught a guard
patch that had failed to apply), _meta provenance stamp in the snapshot,
and models.dev (MIT) attribution in NOTICE. Snapshot: 29 providers, 1124
models, 246KB, regenerated by scripts/gen_enrichment_snapshot.py (pure
filter, no transform).
2026-07-21 03:36:51 -04:00
ZacharyZhang-NY c5ddaec71e Add per-provider auth.json keys; make auth methods registry-generic (P0b)
Platform API keys now live in auth.json under the platform-id scope (the
per-provider auth.json key contract), resolved env > auth.json > legacy
[platforms.*] config.toml (read-only fallback). The TUI login picker,
paste box, auth-method advertising, and authenticate handler are all
registry-generic: a new PlatformSpec row appears in the login UI and
authenticates with zero UI changes. Spec rows gained vendor/console_host/
login_label display fields (moonshot strings byte-identical, pinned by
tests).

Adversarial review caught that auth.json keys were validated at login but
never stamped onto catalog entries (completions would 401; restart lost
eager auth). Fixed red-green: resolve_model_list/resolve_model_catalog now
take a resolved PlatformApiKeys snapshot consumed by the credential-
stamping layer (auth.json beats stale config.toml, matching the login
validator), with production callers resolving fresh per catalog build.
Also from review: the new auth.json writer takes the manager's cross-
process flock (bounded retry — an unlocked RMW racing a token refresh
could revert a rotated refresh token); the oauth-401 wiremock test is
hermetic (KIGI_SHARE_DIR tempdir; it could read a dev's real auth.json
and hit live moonshot); cli_models resolves real keys; auth.json is read
once per registry sweep; caller-less lock_config_writes deleted; catalog
resolvers tightened to pub(crate); stale config.toml doc comments and the
no-credentials error copy updated.
2026-07-21 01:18:46 -04:00
ZacharyZhang-NY 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.
2026-07-20 20:21:49 -04:00
ZacharyZhang-NY 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.
2026-07-20 19:17:24 -04:00
ZacharyZhang-NY 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.
2026-07-20 16:35:02 -04:00
ZacharyZhang-NY 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.
2026-07-20 15:32:35 -04:00
ZacharyZhang-NY 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.
2026-07-20 14:07:45 -04:00
ZacharyZhang-NY 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).
2026-07-17 05:31:01 -04:00