Commit graph

111 commits

Author SHA1 Message Date
e129745319 feat(pet): agent emojis + multi-agent display + plan emoji + language icons
Some checks are pending
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / preflight (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / vps-smoke (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:frustration-matrix,kei-frustration-loop,kei-skill-importer,kei-projects-index,kei-projects-watcher,kei-gdrive-import,kei-leak-matrix,kei-skills,kei-gateway,kei-cron-scheduler,kei-export-trajectories,kei-backend-daytona,kei-d… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-compute-baremetal,kei-compute-vultr,kei-compute-linode,kei-compute-digitalocean,kei-svc-systemd,kei-llm-bridge-mlx name:hosted-sleep-compute]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-diff,kei-scheduler,kei-watch,kei-prune,kei-discover,kei-brain-view,kei-hibernate,kei-ledger-sign,kei-fork name:wave13-15]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-git-gitea,kei-git-forgejo,kei-git-gitlab,kei-git-bitbucket,kei-memory-sled,kei-memory-redis,kei-memory-postgres,kei-memory-sqlite,kei-auth-google,kei-auth-apple,kei-auth-magiclink,kei-auth-webauthn,kei-notify-slack,kei-n… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-ledger,kei-migrate,kei-changelog,kei-memory,kei-store,kei-conflict-scan,kei-refactor-engine,kei-graph-check,kei-shared,kei-dna-index,kei-pet name:core]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-machine-probe,kei-llm-ollama,kei-llm-llamacpp,kei-llm-mlx,kei-llm-router,kei-model name:llm-stack]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-router,kei-sage,kei-task,kei-chat-store,kei-crossdomain,kei-search-core,kei-content-store,kei-social-store,kei-curator,kei-auth,kei-artifact name:mcp-lbm]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:keisei,kei-forge,kei-runtime,kei-runtime-core,kei-atom-discovery,kei-agent-runtime,kei-capability,kei-provision,kei-entity-store,kei-pipe,kei-cache,kei-spawn,kei-replay name:atom-substrate]) (push) Blocked by required conditions
2026-05-21 12:20:45 +00:00
cb694cc96a fix(web-install): tolerate missing /dev/tty in non-interactive curl|bash
Some checks are pending
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / preflight (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / vps-smoke (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:frustration-matrix,kei-frustration-loop,kei-skill-importer,kei-projects-index,kei-projects-watcher,kei-gdrive-import,kei-leak-matrix,kei-skills,kei-gateway,kei-cron-scheduler,kei-export-trajectories,kei-backend-daytona,kei-d… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-compute-baremetal,kei-compute-vultr,kei-compute-linode,kei-compute-digitalocean,kei-svc-systemd,kei-llm-bridge-mlx name:hosted-sleep-compute]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-diff,kei-scheduler,kei-watch,kei-prune,kei-discover,kei-brain-view,kei-hibernate,kei-ledger-sign,kei-fork name:wave13-15]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-git-gitea,kei-git-forgejo,kei-git-gitlab,kei-git-bitbucket,kei-memory-sled,kei-memory-redis,kei-memory-postgres,kei-memory-sqlite,kei-auth-google,kei-auth-apple,kei-auth-magiclink,kei-auth-webauthn,kei-notify-slack,kei-n… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-ledger,kei-migrate,kei-changelog,kei-memory,kei-store,kei-conflict-scan,kei-refactor-engine,kei-graph-check,kei-shared,kei-dna-index,kei-pet name:core]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-machine-probe,kei-llm-ollama,kei-llm-llamacpp,kei-llm-mlx,kei-llm-router,kei-model name:llm-stack]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-router,kei-sage,kei-task,kei-chat-store,kei-crossdomain,kei-search-core,kei-content-store,kei-social-store,kei-curator,kei-auth,kei-artifact name:mcp-lbm]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:keisei,kei-forge,kei-runtime,kei-runtime-core,kei-atom-discovery,kei-agent-runtime,kei-capability,kei-provision,kei-entity-store,kei-pipe,kei-cache,kei-spawn,kei-replay name:atom-substrate]) (push) Blocked by required conditions
2026-05-21 10:53:21 +00:00
305787fae3 fix(install): make fresh install complete + ship tamagotchi (#1)
Some checks are pending
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / preflight (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / vps-smoke (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:frustration-matrix,kei-frustration-loop,kei-skill-importer,kei-projects-index,kei-projects-watcher,kei-gdrive-import,kei-leak-matrix,kei-skills,kei-gateway,kei-cron-scheduler,kei-export-trajectories,kei-backend-daytona,kei-d… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-compute-baremetal,kei-compute-vultr,kei-compute-linode,kei-compute-digitalocean,kei-svc-systemd,kei-llm-bridge-mlx name:hosted-sleep-compute]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-diff,kei-scheduler,kei-watch,kei-prune,kei-discover,kei-brain-view,kei-hibernate,kei-ledger-sign,kei-fork name:wave13-15]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-git-gitea,kei-git-forgejo,kei-git-gitlab,kei-git-bitbucket,kei-memory-sled,kei-memory-redis,kei-memory-postgres,kei-memory-sqlite,kei-auth-google,kei-auth-apple,kei-auth-magiclink,kei-auth-webauthn,kei-notify-slack,kei-n… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-ledger,kei-migrate,kei-changelog,kei-memory,kei-store,kei-conflict-scan,kei-refactor-engine,kei-graph-check,kei-shared,kei-dna-index,kei-pet name:core]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-machine-probe,kei-llm-ollama,kei-llm-llamacpp,kei-llm-mlx,kei-llm-router,kei-model name:llm-stack]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-router,kei-sage,kei-task,kei-chat-store,kei-crossdomain,kei-search-core,kei-content-store,kei-social-store,kei-curator,kei-auth,kei-artifact name:mcp-lbm]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:keisei,kei-forge,kei-runtime,kei-runtime-core,kei-atom-discovery,kei-agent-runtime,kei-capability,kei-provision,kei-entity-store,kei-pipe,kei-cache,kei-spawn,kei-replay name:atom-substrate]) (push) Blocked by required conditions
2026-05-20 18:50:09 +00:00
53ada048b2 fix(install): backup_file mv→cp -a, install --activate-hooks больше не падает
Some checks failed
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / preflight (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / vps-smoke (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:frustration-matrix,kei-frustration-loop,kei-skill-importer,kei-projects-index,kei-projects-watcher,kei-gdrive-import,kei-leak-matrix,kei-skills,kei-gateway,kei-cron-scheduler,kei-export-trajectories,kei-backend-daytona,kei-d… (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-compute-baremetal,kei-compute-vultr,kei-compute-linode,kei-compute-digitalocean,kei-svc-systemd,kei-llm-bridge-mlx name:hosted-sleep-compute]) (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-diff,kei-scheduler,kei-watch,kei-prune,kei-discover,kei-brain-view,kei-hibernate,kei-ledger-sign,kei-fork name:wave13-15]) (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-git-gitea,kei-git-forgejo,kei-git-gitlab,kei-git-bitbucket,kei-memory-sled,kei-memory-redis,kei-memory-postgres,kei-memory-sqlite,kei-auth-google,kei-auth-apple,kei-auth-magiclink,kei-auth-webauthn,kei-notify-slack,kei-n… (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-ledger,kei-migrate,kei-changelog,kei-memory,kei-store,kei-conflict-scan,kei-refactor-engine,kei-graph-check,kei-shared,kei-dna-index,kei-pet name:core]) (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-machine-probe,kei-llm-ollama,kei-llm-llamacpp,kei-llm-mlx,kei-llm-router,kei-model name:llm-stack]) (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-router,kei-sage,kei-task,kei-chat-store,kei-crossdomain,kei-search-core,kei-content-store,kei-social-store,kei-curator,kei-auth,kei-artifact name:mcp-lbm]) (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:keisei,kei-forge,kei-runtime,kei-runtime-core,kei-atom-discovery,kei-agent-runtime,kei-capability,kei-provision,kei-entity-store,kei-pipe,kei-cache,kei-spawn,kei-replay name:atom-substrate]) (push) Has been cancelled
gx10 fail при первой установке (фото юзера 2026-05-18):
  jq: error: Could not open file /home/keisei/.claude/settings.json
  [error] jq-merge produced invalid output; ... unchanged
  [error] install failed at line 126; rolled back

Root cause:
  backup_file() (install/lib-backup.sh:56) делал `mv target backup`,
  затем activate_hooks() → _jq_merge_hooks() пыталась читать оригинал
  target'а через jq — но оригинал уже унесён mv'ом, открыть нечего.
  jq fail → rollback всей установки.

Fix: cp -a вместо mv (как уже сделано в backup_path() для директорий).
Атомарный безопасный бэкап; оригинал остаётся для jq, потом
_jq_merge_hooks атомарно переписывает его mv tmp → target.
2026-05-18 14:33:22 +08:00
fc0758d2bb chore: версия 0.38.0 единая + warning-fixes + mold для release-job
Some checks are pending
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / preflight (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / vps-smoke (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:frustration-matrix,kei-frustration-loop,kei-skill-importer,kei-projects-index,kei-projects-watcher,kei-gdrive-import,kei-leak-matrix,kei-skills,kei-gateway,kei-cron-scheduler,kei-export-trajectories,kei-backend-daytona,kei-d… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-compute-baremetal,kei-compute-vultr,kei-compute-linode,kei-compute-digitalocean,kei-svc-systemd,kei-llm-bridge-mlx name:hosted-sleep-compute]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-diff,kei-scheduler,kei-watch,kei-prune,kei-discover,kei-brain-view,kei-hibernate,kei-ledger-sign,kei-fork name:wave13-15]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-git-gitea,kei-git-forgejo,kei-git-gitlab,kei-git-bitbucket,kei-memory-sled,kei-memory-redis,kei-memory-postgres,kei-memory-sqlite,kei-auth-google,kei-auth-apple,kei-auth-magiclink,kei-auth-webauthn,kei-notify-slack,kei-n… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-ledger,kei-migrate,kei-changelog,kei-memory,kei-store,kei-conflict-scan,kei-refactor-engine,kei-graph-check,kei-shared,kei-dna-index,kei-pet name:core]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-machine-probe,kei-llm-ollama,kei-llm-llamacpp,kei-llm-mlx,kei-llm-router,kei-model name:llm-stack]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-router,kei-sage,kei-task,kei-chat-store,kei-crossdomain,kei-search-core,kei-content-store,kei-social-store,kei-curator,kei-auth,kei-artifact name:mcp-lbm]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:keisei,kei-forge,kei-runtime,kei-runtime-core,kei-atom-discovery,kei-agent-runtime,kei-capability,kei-provision,kei-entity-store,kei-pipe,kei-cache,kei-spawn,kei-replay name:atom-substrate]) (push) Blocked by required conditions
1. Версии npm-пакетов приведены к 0.38.0 (был зоопарк 0.14.0/0.14.6):
   _ts_packages/{,packages/{gmail,grok,mcp-server,recall,telegram,youtube}-adapter}

2. Rust warnings (cargo check workspace):
   - kei-cortex: deprecated validate_path → validate_path_lexical,
     удалён orphan-wrapper в read.rs, struct Input → pub(crate)
   - frustration-matrix: #[allow(dead_code)] на confusion_* поля
     EvalReport + train_from_dir (будущий CLI)

3. CI release.yml job 'release' падал на Build kei-changelog:
   clang invalid linker '-fuse-ld=mold' — в .cargo/config.toml
   жёстко прописан mold для linux. Добавлен Install mold шаг
   (как уже сделано в build-release matrix).
2026-05-18 13:41:37 +08:00
d1cbdd8892 fix(ci): vendored openssl-sys для cross-compile x86_64-apple-darwin
Some checks are pending
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / preflight (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / vps-smoke (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:frustration-matrix,kei-frustration-loop,kei-skill-importer,kei-projects-index,kei-projects-watcher,kei-gdrive-import,kei-leak-matrix,kei-skills,kei-gateway,kei-cron-scheduler,kei-export-trajectories,kei-backend-daytona,kei-d… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-compute-baremetal,kei-compute-vultr,kei-compute-linode,kei-compute-digitalocean,kei-svc-systemd,kei-llm-bridge-mlx name:hosted-sleep-compute]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-diff,kei-scheduler,kei-watch,kei-prune,kei-discover,kei-brain-view,kei-hibernate,kei-ledger-sign,kei-fork name:wave13-15]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-git-gitea,kei-git-forgejo,kei-git-gitlab,kei-git-bitbucket,kei-memory-sled,kei-memory-redis,kei-memory-postgres,kei-memory-sqlite,kei-auth-google,kei-auth-apple,kei-auth-magiclink,kei-auth-webauthn,kei-notify-slack,kei-n… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-ledger,kei-migrate,kei-changelog,kei-memory,kei-store,kei-conflict-scan,kei-refactor-engine,kei-graph-check,kei-shared,kei-dna-index,kei-pet name:core]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-machine-probe,kei-llm-ollama,kei-llm-llamacpp,kei-llm-mlx,kei-llm-router,kei-model name:llm-stack]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-router,kei-sage,kei-task,kei-chat-store,kei-crossdomain,kei-search-core,kei-content-store,kei-social-store,kei-curator,kei-auth,kei-artifact name:mcp-lbm]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:keisei,kei-forge,kei-runtime,kei-runtime-core,kei-atom-discovery,kei-agent-runtime,kei-capability,kei-provision,kei-entity-store,kei-pipe,kei-cache,kei-spawn,kei-replay name:atom-substrate]) (push) Blocked by required conditions
CI run 26014515768 — `x86_64-apple-darwin` упал на webauthn-attestation-ca
→ openssl-sys 0.9.115: pkg-config не настроен для cross-compile
(macos-latest = arm64, target = x86_64).

Фикс: добавлен прямой dep `openssl-sys = { version = "0.9", features =
["vendored"] }` в kei-auth-webauthn — компилит openssl из исходников,
не требует системного. Транзитивно достаточно одного объявления feature
во всём workspace (Cargo unifies features).
2026-05-18 13:08:40 +08:00
5f6b4f2620 fix(ci): regenerate bun.lock — sync с package.json для CI release
Some checks are pending
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / preflight (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / vps-smoke (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:frustration-matrix,kei-frustration-loop,kei-skill-importer,kei-projects-index,kei-projects-watcher,kei-gdrive-import,kei-leak-matrix,kei-skills,kei-gateway,kei-cron-scheduler,kei-export-trajectories,kei-backend-daytona,kei-d… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-compute-baremetal,kei-compute-vultr,kei-compute-linode,kei-compute-digitalocean,kei-svc-systemd,kei-llm-bridge-mlx name:hosted-sleep-compute]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-diff,kei-scheduler,kei-watch,kei-prune,kei-discover,kei-brain-view,kei-hibernate,kei-ledger-sign,kei-fork name:wave13-15]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-git-gitea,kei-git-forgejo,kei-git-gitlab,kei-git-bitbucket,kei-memory-sled,kei-memory-redis,kei-memory-postgres,kei-memory-sqlite,kei-auth-google,kei-auth-apple,kei-auth-magiclink,kei-auth-webauthn,kei-notify-slack,kei-n… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-ledger,kei-migrate,kei-changelog,kei-memory,kei-store,kei-conflict-scan,kei-refactor-engine,kei-graph-check,kei-shared,kei-dna-index,kei-pet name:core]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-machine-probe,kei-llm-ollama,kei-llm-llamacpp,kei-llm-mlx,kei-llm-router,kei-model name:llm-stack]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-router,kei-sage,kei-task,kei-chat-store,kei-crossdomain,kei-search-core,kei-content-store,kei-social-store,kei-curator,kei-auth,kei-artifact name:mcp-lbm]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:keisei,kei-forge,kei-runtime,kei-runtime-core,kei-atom-discovery,kei-agent-runtime,kei-capability,kei-provision,kei-entity-store,kei-pipe,kei-cache,kei-spawn,kei-replay name:atom-substrate]) (push) Blocked by required conditions
CI v0.38.0 mcp-server билды все 5 упали на `bun install --frozen-lockfile`
с «lockfile had changes, but lockfile is frozen». В lockfile отставали:
  - @keisei/mcp-server 0.14.0 → 0.14.6
  - ajv 8.18.0 → 8.20.0
  - express-rate-limit 8.3.2 → 8.4.1
  - hono 4.12.14 → 4.12.16

Пересоздан `rm bun.lock && bun install` под bun 1.3.14 (тот же что в CI).
2026-05-18 13:01:36 +08:00
a8101b8f60 fix: install audit 2026-05-18 — cortex profile + TTY in web-install
Some checks are pending
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / preflight (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / vps-smoke (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:frustration-matrix,kei-frustration-loop,kei-skill-importer,kei-projects-index,kei-projects-watcher,kei-gdrive-import,kei-leak-matrix,kei-skills,kei-gateway,kei-cron-scheduler,kei-export-trajectories,kei-backend-daytona,kei-d… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-compute-baremetal,kei-compute-vultr,kei-compute-linode,kei-compute-digitalocean,kei-svc-systemd,kei-llm-bridge-mlx name:hosted-sleep-compute]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-diff,kei-scheduler,kei-watch,kei-prune,kei-discover,kei-brain-view,kei-hibernate,kei-ledger-sign,kei-fork name:wave13-15]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-git-gitea,kei-git-forgejo,kei-git-gitlab,kei-git-bitbucket,kei-memory-sled,kei-memory-redis,kei-memory-postgres,kei-memory-sqlite,kei-auth-google,kei-auth-apple,kei-auth-magiclink,kei-auth-webauthn,kei-notify-slack,kei-n… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-ledger,kei-migrate,kei-changelog,kei-memory,kei-store,kei-conflict-scan,kei-refactor-engine,kei-graph-check,kei-shared,kei-dna-index,kei-pet name:core]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-machine-probe,kei-llm-ollama,kei-llm-llamacpp,kei-llm-mlx,kei-llm-router,kei-model name:llm-stack]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-router,kei-sage,kei-task,kei-chat-store,kei-crossdomain,kei-search-core,kei-content-store,kei-social-store,kei-curator,kei-auth,kei-artifact name:mcp-lbm]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:keisei,kei-forge,kei-runtime,kei-runtime-core,kei-atom-discovery,kei-agent-runtime,kei-capability,kei-provision,kei-entity-store,kei-pipe,kei-cache,kei-spawn,kei-replay name:atom-substrate]) (push) Blocked by required conditions
audit-2026-05-18 (соседняя сессия на gx10, 3 бага из 5 подтверждены):

#1 [HIGH] cortex profile сломан — kei-cortex depends on kei-router
   path-dep'ом, а kei-router → kei-model. kei-model не было в
   profile list, cargo build падал с «failed to read kei-model/
   Cargo.toml». Добавил kei-model в cortex profile.

#3 [HIGH] cortex-ui отсутствует в репо — был в profile list, но
   исходника нет ни в _primitives/_rust/, ни в _ts_packages/.
   Удалил из profile.

#4 [MEDIUM] web-install.sh теряет /dev/tty — exec | tee + curl|bash
   делают stdin pipe, wizard'у read нечего читать, onboarding падает
   на первом prompt. Переподключаю stdin к /dev/tty перед exec
   bootstrap.sh, если /dev/tty доступен.

#2 (linux-arm64 prebuild) — tag v0.38.0 не запушен на github, CI
   release.yml не сработал. Требует RULE 0.1 triple-confirm.
#5 (non-TTY default = minimal) — уже корректно, install.sh:141
   `PROFILE="${PROFILE:-minimal}"` + lib-menu.sh skip on non-TTY.
2026-05-18 12:38:35 +08:00
1d958b3587 fix(security): patent-leak + classical-safety audit fixes
Some checks are pending
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / preflight (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / vps-smoke (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:frustration-matrix,kei-frustration-loop,kei-skill-importer,kei-projects-index,kei-projects-watcher,kei-gdrive-import,kei-leak-matrix,kei-skills,kei-gateway,kei-cron-scheduler,kei-export-trajectories,kei-backend-daytona,kei-d… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-compute-baremetal,kei-compute-vultr,kei-compute-linode,kei-compute-digitalocean,kei-svc-systemd,kei-llm-bridge-mlx name:hosted-sleep-compute]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-diff,kei-scheduler,kei-watch,kei-prune,kei-discover,kei-brain-view,kei-hibernate,kei-ledger-sign,kei-fork name:wave13-15]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-git-gitea,kei-git-forgejo,kei-git-gitlab,kei-git-bitbucket,kei-memory-sled,kei-memory-redis,kei-memory-postgres,kei-memory-sqlite,kei-auth-google,kei-auth-apple,kei-auth-magiclink,kei-auth-webauthn,kei-notify-slack,kei-n… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-ledger,kei-migrate,kei-changelog,kei-memory,kei-store,kei-conflict-scan,kei-refactor-engine,kei-graph-check,kei-shared,kei-dna-index,kei-pet name:core]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-machine-probe,kei-llm-ollama,kei-llm-llamacpp,kei-llm-mlx,kei-llm-router,kei-model name:llm-stack]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-router,kei-sage,kei-task,kei-chat-store,kei-crossdomain,kei-search-core,kei-content-store,kei-social-store,kei-curator,kei-auth,kei-artifact name:mcp-lbm]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:keisei,kei-forge,kei-runtime,kei-runtime-core,kei-atom-discovery,kei-agent-runtime,kei-capability,kei-provision,kei-entity-store,kei-pipe,kei-cache,kei-spawn,kei-replay name:atom-substrate]) (push) Blocked by required conditions
PATENT-LEAK (HIGH):
- hooks/no-python-without-approval.sh: genesis-verify пример → my-project
- docs/encyclopedia/rust-crates-H-N.md: убран термин «Genesis IP, ITAR»
PATENT-LEAK (MEDIUM):
- CHANGELOG: project-vortex → reduced scope
- _blocks/registries (submodule bump): убраны имена приватных
  project-specialists из комментария agent-profiles.toml
- docs/encyclopedia/skills-and-agents.md: ML/RL/CfC → ML/RL

CLASSICAL-SAFETY (MEDIUM):
- install/lib-preflight.sh: eval "$version_cmd" → bash -c "..."
  (защита от инъекции если providers.toml расширят)
- _primitives/provision-{vultr,hetzner}.sh: /tmp/$$ → mktemp
  (устраняет symlink TOCTOU race)
- web-install.sh: chmod 600 + umask 077 на ~/.keisei-install.log
  (Forgejo admin creds + токены в логе)
- scripts/regen-counts.sh: eval "$1" → bash -c

NOT FIXED (требуют действий юзера):
- HIGH: @keisei scope не зарегистрирован на npmjs.org — typosquat
  возможен пока не задан NPM_TOKEN и не сделан publish
- HIGH: install.keisei.app DNS не настроен — DNS-hijack возможен
- LOW: parfionovich@keilab.io в SECURITY.md, plugin.json, ~40 Cargo
  файлах — intentional contact, оставлен

Локальный git author установлен на parfionovich@keilab.io вместо
parfionovichd@icloud.com (только для будущих коммитов в этом репо).
2026-05-18 12:05:25 +08:00
Parfii-bot
170c850ca0 fix(install): public install via keigit.com (Vultr) — no github needed
Some checks are pending
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / preflight (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / vps-smoke (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:frustration-matrix,kei-frustration-loop,kei-skill-importer,kei-projects-index,kei-projects-watcher,kei-gdrive-import,kei-leak-matrix,kei-skills,kei-gateway,kei-cron-scheduler,kei-export-trajectories,kei-backend-daytona,kei-d… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-compute-baremetal,kei-compute-vultr,kei-compute-linode,kei-compute-digitalocean,kei-svc-systemd,kei-llm-bridge-mlx name:hosted-sleep-compute]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-diff,kei-scheduler,kei-watch,kei-prune,kei-discover,kei-brain-view,kei-hibernate,kei-ledger-sign,kei-fork name:wave13-15]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-git-gitea,kei-git-forgejo,kei-git-gitlab,kei-git-bitbucket,kei-memory-sled,kei-memory-redis,kei-memory-postgres,kei-memory-sqlite,kei-auth-google,kei-auth-apple,kei-auth-magiclink,kei-auth-webauthn,kei-notify-slack,kei-n… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-ledger,kei-migrate,kei-changelog,kei-memory,kei-store,kei-conflict-scan,kei-refactor-engine,kei-graph-check,kei-shared,kei-dna-index,kei-pet name:core]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-machine-probe,kei-llm-ollama,kei-llm-llamacpp,kei-llm-mlx,kei-llm-router,kei-model name:llm-stack]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-router,kei-sage,kei-task,kei-chat-store,kei-crossdomain,kei-search-core,kei-content-store,kei-social-store,kei-curator,kei-auth,kei-artifact name:mcp-lbm]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:keisei,kei-forge,kei-runtime,kei-runtime-core,kei-atom-discovery,kei-agent-runtime,kei-capability,kei-provision,kei-entity-store,kei-pipe,kei-cache,kei-spawn,kei-replay name:atom-substrate]) (push) Blocked by required conditions
Что меняется:
- README.md: git clone instruction → https://keigit.com/keisei/KeiSeiKit-1.0.git
  (был приватный github, внешний clone падал 404)
- .gitmodules: kei-registries submodule → keigit.com/keisei/kei-registries.git
  (был приватный github, --recurse-submodules падал)
- web-install.sh: KEISEI_REPO default → https://keigit.com/keisei/...
  (был git@github.com:KeiSeiLab/...)
- install.sh: NO_EXECUTE check ПЕРЕД check_prereqs, чтобы --no-execute
  работал без установленных зависимостей.
- install/lib-args.sh: новый флаг --skip-prereqs (SKIP_PREREQS) — для CI
  и dry-run сценариев.
- marketplace.json + plugin.json: новые манифесты (version=0.38.0 из git tag)
  для Claude Code /plugin marketplace add + install.

На keigit.com (45.77.41.204, публичный) залиты публичные репо
keisei/KeiSeiKit-1.0 + keisei/kei-registries. Anonymous git clone работает.
GitHub mirror (KeiSeiLab/*) остаётся приватным как backup.
2026-05-18 11:47:13 +08:00
Parfii-bot
5b8e066888 refactor(install): production-ready финальный круг
Some checks are pending
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / preflight (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / vps-smoke (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:frustration-matrix,kei-frustration-loop,kei-skill-importer,kei-projects-index,kei-projects-watcher,kei-gdrive-import,kei-leak-matrix,kei-skills,kei-gateway,kei-cron-scheduler,kei-export-trajectories,kei-backend-daytona,kei-d… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-compute-baremetal,kei-compute-vultr,kei-compute-linode,kei-compute-digitalocean,kei-svc-systemd,kei-llm-bridge-mlx name:hosted-sleep-compute]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-diff,kei-scheduler,kei-watch,kei-prune,kei-discover,kei-brain-view,kei-hibernate,kei-ledger-sign,kei-fork name:wave13-15]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-git-gitea,kei-git-forgejo,kei-git-gitlab,kei-git-bitbucket,kei-memory-sled,kei-memory-redis,kei-memory-postgres,kei-memory-sqlite,kei-auth-google,kei-auth-apple,kei-auth-magiclink,kei-auth-webauthn,kei-notify-slack,kei-n… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-ledger,kei-migrate,kei-changelog,kei-memory,kei-store,kei-conflict-scan,kei-refactor-engine,kei-graph-check,kei-shared,kei-dna-index,kei-pet name:core]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-machine-probe,kei-llm-ollama,kei-llm-llamacpp,kei-llm-mlx,kei-llm-router,kei-model name:llm-stack]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-router,kei-sage,kei-task,kei-chat-store,kei-crossdomain,kei-search-core,kei-content-store,kei-social-store,kei-curator,kei-auth,kei-artifact name:mcp-lbm]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:keisei,kei-forge,kei-runtime,kei-runtime-core,kei-atom-discovery,kei-agent-runtime,kei-capability,kei-provision,kei-entity-store,kei-pipe,kei-cache,kei-spawn,kei-replay name:atom-substrate]) (push) Blocked by required conditions
1. lib-onboarding.sh раскидан на 3 куба (Constructor Pattern <200 LOC):
   - lib-onboarding-registry.sh (79 LOC) — парсеры providers/models.toml
     + onboarding_fallback_providers (14 провайдеров)
     + onboarding_auth_env_for_provider helper (был inline в collect_auth)
   - lib-onboarding-ui.sh (189 LOC) — pick_language/transport/provider/model
     + collect_auth (whiptail/bash select)
   - lib-onboarding-state.sh (57 LOC) — write_secrets + write_config
     + user-model-override.toml для kei-model-router
   - lib-onboarding.sh (95 LOC) — тонкий оркестратор: should_run + run

   Сам lib-onboarding.sh source'ит 3 подкуба автоматически. Глобалы
   (ONBOARDING_*, REGISTRY_*, ONBOARDED_FLAG, etc.) объявлены в
   оркестраторе, подкубы их используют через имена.

2. lib-menu.sh локализован:
   - whiptail title + radiolist prompt через ${STR_MENU_TITLE} +
     ${STR_MENU_SUBSTRATE} + ${STR_MENU_PROFILE_PROMPT}.
   - Plain heading тоже использует словарь.
   - 12 коротких имён профилей (minimal/core/dev/...) — оставлены EN
     как стабильные id (не переводятся).

3. _blocks/build-index.sh — детерминированная регенерация INDEX.md.
   Группировка по 14 категорийным префиксам + "Прочие" для остальных.
   Безопасно перезапускать. INDEX.md обновлён через этот скрипт
   (минимальный diff — добавлена ссылка на build-index.sh в шапке).

Проверено: bash -n чисто, unit тесты onboarding_list_providers/
transports/models OK, non-TTY smoke ./install.sh --profile=minimal
--no-execute проходит.
2026-05-17 23:57:23 +08:00
Parfii-bot
5327befef6 docs(_blocks): INDEX.md — реестр 84 блоков для assembler
Авто-сгенерирован из _blocks/*.md по категориям префикса:
  API / AUTH / CI / DB / DEPLOY / DOCS / DOMAIN / MODE / OBS /
  PATH / RULE / SCRAPER / SECURITY / STACK / TEST + прочие.

Каждая запись: `name` — первая H1-строка файла.

Использование в _manifests/<agent>.toml:
  blocks = ["baseline", "rule-pre-dev-gate", "api-anthropic", ...]

Assembler читает блоки из _blocks/, склеивает в финальный
_generated/<agent>.md. Новый блок = просто .md в _blocks/.
Регенерация INDEX.md — TODO build-index.sh скрипт (сейчас
сгенерирован вручную bash циклом).
2026-05-17 20:39:06 +08:00
Parfii-bot
33f1376ee1 feat(i18n): расширил язык-набор с 2 до 16
Новые словари:
  uk    Українська
  de    Deutsch
  fr    Français
  es    Español
  pt    Português
  it    Italiano
  tr    Türkçe
  ar    العربية
  hi    हिन्दी
  zh    简体中文
  ja    日本語
  ko    한국어
  id    Bahasa Indonesia
  vi    Tiếng Việt

Каждый файл — 17 STR_* ключей (тот же контракт что en.sh + ru.sh).
lib-i18n.sh::i18n_available_languages — единый список (en + 15)
для меню выбора в мастере. i18n_load_lang упрощён: всегда грузит
английский как fallback, потом поверх — словарь языка (отсутствующий
ключ остаётся английским).

onboarding_pick_language теперь генерирует whiptail/bash select из
i18n_available_languages динамически — добавление нового языка =
один файл `install/i18n/<код>.sh` + одна строка в available_languages,
дальше всё подхватится автоматически.

Перевод формальный, без излишеств. Welcome баннер всегда EN (юзер
ещё не выбрал на момент показа).

Проверено: bash -n всех 16 словарей чисто, roundtrip всех языков
работает (i18n_load_lang en/ru/uk/de/fr/es/pt/it/tr/ar/hi/zh/ja/ko/id/vi
выдают локализованные STR_DONE_TITLE + STR_TR_DIRECT_API), non-TTY
smoke install --no-execute проходит.
2026-05-17 16:51:44 +08:00
Parfii-bot
305140f20b fix(install): close MEDIUM/LOW from RULE 0.26 audit
- preflight failure handling: вместо `|| true` (молчаливое продолжение
  при упавшем preflight) — явный prompt «продолжить? [y/N]» с return 1
  при отказе. Без TTY печатает warning и продолжает. Это закрывает
  HIGH bug-9: «.onboarded флаг выставляется при нерабочей конфигурации».

- lib-preflight.sh::preflight_check_cli — общий helper (command -v +
  offer-install + version echo). Убирает 6-file boilerplate (хотя сами
  per-provider файлы пока не переписаны под него — это отдельный шаг).

- onboarding_fallback_providers: расширен с 3 до 14 провайдеров,
  покрывает все 7 транспортов. Был дрейф vs providers.toml (14 vs 3),
  юзер без submodule видел только anthropic+openai+ollama.

- STR_PICK_PROVIDER plural mismatch: whiptail и plain ветки теперь
  используют один fallback "Provider within" (раньше plain имел
  "Providers within", whiptail — "Provider within").

- STR_DONE_NEXT удалён из en.sh + ru.sh (мёртвый ключ).

- Новые ключи: STR_MENU_* (для lib-menu.sh) + STR_PREFLIGHT_FAILED +
  STR_PREFLIGHT_CONTINUE. lib-menu.sh начал использовать
  STR_MENU_TITLE / STR_MENU_SUBSTRATE (частичная локализация, остальное
  меню — отдельной задачей).

Тесты: bash -n чисто, i18n round-trip EN/RU работает, non-TTY smoke
install --no-execute проходит.
2026-05-17 16:37:28 +08:00
Parfii-bot
a3ffaed374 fix(install,router): close 5 HIGH audit findings
1. HIGH-1: onboarding ↔ kei-model-router связка
   До: onboarding мастер писал ~/.claude/config/onboarding.toml,
   но router его не читал — выбор провайдера декоративный.
   После: lib-onboarding.sh::onboarding_write_config доп. пишет
   ~/.claude/config/user-model-override.toml; registry.rs::Registry
   получил load_user_override() возвращающий UserModelOverride.
   Приоритет: --pinned > user-override > agent-profiles default_model_ref.
   2 новых теста (round-trip TOML, optional transport).

2. HIGH-2: eval "$install_cmd" → bash -c "$install_cmd"
   До: lib-preflight.sh::preflight_offer_install делал eval.
   После: bash -c с явным subshell + печать команды юзеру до запуска.

3. HIGH-3: codex.sh regex false-pass
   До: grep -qiE "logged.in|active" пропускал "not logged in" как pass.
   После: сначала negative-pattern (not logged|signed out|please log in),
   потом positive (\blogged in\b|status: active|auth: yes).

4. HIGH-4: path traversal в source preflight
   До: lib-preflight.sh::preflight_run делал source без валидации
   provider id — `../../../evil` сработал бы.
   После: whitelist regex ^[a-z0-9][a-z0-9_-]{0,63}$ + realpath
   проверка что resolved путь не вышел за PREFLIGHT_DIR.

5. HIGH-5: curl|sh без verification
   ollama-local.sh + google-vertex.sh теперь печатают предупреждение
   что Linux-установка тянет shell-скрипт с внешнего сервера без
   проверки хэша/подписи, и предлагают альтернативу.

MEDIUM попутно:
   - anthropic-bedrock.sh: один вызов aws sts get-caller-identity
     вместо двух (экономит 1-3с), различает cred-error от network
     по тексту stderr, маскирует account ID в ARN перед печатью.
   - mlx-local.sh: pip install --user mlx-lm вместо global pip install
     (не требует sudo, не загрязняет system Python).

Тесты: cargo test --lib 80/80, bash -n всех изменённых файлов чисто.
2026-05-17 16:28:33 +08:00
Parfii-bot
0a8c93561f feat(install): preflight модуль — проверка CLI по выбранному провайдеру
Добавлен шаг между выбором модели и сбором ключей: для провайдеров
требующих внешний CLI/daemon — проверка наличия, инструкция по
установке, опциональный авто-install (TTY only).

install/lib-preflight.sh — диспетчер:
  preflight_run <provider-id>
  - ищет install/preflight/<id>.sh, source'ит, вызывает
    preflight_check_<sanitized_id>
  - функция возвращает 0/1, печатает инструкцию в stderr
  - non-TTY: только печать, без вопросов

preflight_offer_install <cli> <install-cmd>:
  - TTY: спрашивает [y/N/skip], выполняет install-cmd
  - non-TTY: печатает и пропускает

install/preflight/ — 6 файлов (только для провайдеров с CLI):
  anthropic-bedrock.sh  — aws CLI + sts get-caller-identity
  google-vertex.sh      — gcloud CLI + project config
  codex.sh              — codex CLI (npm) + login status
  ollama-local.sh       — ollama binary + 127.0.0.1:11434 daemon
  mlx-local.sh          — mlx_lm.server (arm64 only) + 127.0.0.1:8080
  lmstudio-local.sh     — порт 127.0.0.1:1234 (desktop app)

Direct-api провайдеры (anthropic, openai, xai, deepseek, google) +
proxy (litellm, openrouter) + openai-azure — preflight-файла нет,
диспетчер тихо пропускает, ключ собирается обычно.

Тесты: bash -n чисто на всех 8 файлах, unit dispatcher показывает
silent-pass для anthropic, warn+exit-1 для bedrock без aws на PATH.
2026-05-17 15:57:54 +08:00
Parfii-bot
ab260f429e feat(install): i18n модуль + welcome banner
Структура локализации:
  install/i18n/en.sh    — английский словарь (дефолт, fallback)
  install/i18n/ru.sh    — русский словарь
  install/lib-i18n.sh   — лоадер + welcome banner

Поток:
  1. install.sh source'ит lib-i18n.sh и зовёт i18n_load_default →
     все строки на английском.
  2. Если onboarding нужен — печатается welcome banner ASCII-рамка
     на английском (язык ещё не выбран).
  3. onboarding_pick_language — единственный двуязычный шаг
     ("Choose language / Выберите язык"). По выбору вызывает
     i18n_load_lang ru|en — перегружает словарь.
  4. Все последующие шаги (transport / provider / model / auth /
     completion) идут на выбранном языке.

Fallback: если ru-словарь не имеет ключа — используется английское
значение (load_default вызывается до загрузки ru.sh, переменные
перезаписываются поверх).

lib-onboarding.sh переведён со смешанных hardcoded строк на
${STR_*} placeholders.

Тесты: bash -n всех 5 файлов чисто, i18n loader unit-тест показывает
EN/RU перегрузку, non-TTY smoke install --no-execute проходит.
2026-05-17 15:35:10 +08:00
Parfii-bot
c844524f68 fix(kei-buddy): close 3 HIGH audit findings from session multi-critic swarm
1. OID-check в parse_x25519_pkcs8_pem
   До: брался последний 32-байтный slice любого PKCS#8 DER, OID не
   проверялся. RSA/EC/Ed25519 ключ молча давал 32 неправильных байта
   → decrypt падал с generic "wrong key" без объяснения.
   После: строгая проверка длины (48 байт) + OID 1.3.101.110 (X25519,
   byte slice 9..12 = 0x2b,0x65,0x6e). Внешний openssl ключ другого
   алгоритма теперь даёт явную ошибку с указанием реального OID.
   Константы X25519_OID + X25519_PKCS8_DER_LEN.
   RFC 8410 §3 + §7 ссылка в doc-комментарии.

2. x25519-dalek feature `zeroize`
   До: features=["static_secrets"] — StaticSecret хранил priv-ключ
   в куче без затирания при Drop. Локальный priv_raw.zeroize() стирал
   только стек-копию, оригинал в куче оставался до GC.
   После: features=["static_secrets","zeroize"] — StaticSecret сам
   реализует ZeroizeOnDrop, ключ затирается при выходе из scope.

3. Два новых теста:
   - parse_rejects_wrong_length_der — 32-байтный DER (вместо 48)
     отклоняется с сообщением про "48 bytes"
   - parse_rejects_wrong_oid — DER с OID Ed25519 (0x2b,0x65,0x70)
     отклоняется с сообщением про "X25519"

   8/8 тестов модуля проходят, cargo check workspace чисто.

Старая 0.14.5 mcp-server (с source maps содержавшими /Users/
denisparfionovich/...) удалена с keigit.com отдельной операцией
через Forgejo DELETE API.
2026-05-17 13:41:18 +08:00
Parfii-bot
9c6df65ae2 feat(install): onboarding wizard — transport→provider→model→keys
Новый интерактивный мастер при первой установке:
  1. Язык интерфейса (RU/EN)
  2. Транспорт (direct-api / aws-bedrock / azure-openai / google-vertex
                / local / proxy / subscription)
  3. Провайдер внутри транспорта (14 вариантов суммарно)
  4. Модель из выбранного провайдера (3 моделей Anthropic, и т.д.)
  5. Ключи/креды (silent read, пишет в ~/.claude/secrets/.env chmod 600)

Skip-логика:
  - флаг ~/.claude/.onboarded
  - env KEISEI_SKIP_ONBOARD=1
  - не-TTY запуск

Запись:
  ~/.claude/config/onboarding.toml — выбор lang/transport/provider/model
  ~/.claude/secrets/.env           — ключи провайдера
  ~/.claude/.onboarded             — флаг прохождения

Парсер toml — pure awk (без зависимостей). Реестры из submodule
_blocks/registries. Submodule bumped до afe0c6f с новым полем transport.

Fallback если submodule не подтянут: anthropic + sonnet.
2026-05-17 02:24:50 +08:00
Parfii-bot
35136a9840 feat(npm-publish): keigit as primary registry, npmjs reserved for future
- _ts_packages/tsconfig.base.json: sourceMap=false, declarationMap=false
  (source maps leaked absolute dev paths in published tarballs).
- All 6 @keisei/* packages: publishConfig.registry = keigit.com.
  mcp-server bumped 0.14.5 -> 0.14.6 (republished without maps).
- .github/workflows/release.yml split into two jobs:
    npm-publish-keigit: primary. Activates on KEIGIT_NPM_TOKEN +
      KEIGIT_NPM_USER secrets. Publishes via direct curl PUT
      (Forgejo requires Basic auth; npm CLI sends Bearer).
    npm-publish-npmjs: reserved for future. Activates on NPM_TOKEN
      secret. Currently no token -> job skipped gracefully.

End-to-end verified: clean dir + scope @keisei -> keigit + npm install
pulls 145 deps, no leaked paths, no .map files in any of 6 packages.
2026-05-17 00:18:44 +08:00
Denis Parfionovich
3c06e98092 feat(web-install): curl-pipeable bootstrapper at install.keisei.app
Thin wrapper (88 LOC) that lets a fresh machine install KeiSeiKit with
one line, no prior clone:

    curl -fsSL https://install.keisei.app | bash
    curl -fsSL https://install.keisei.app | bash -s -- --profile=dev --yes

## Why a third install entry point

Existing install path required `git clone` + `cd` + `./bootstrap.sh` —
three commands the user has to type, plus they must already have the
repo URL handy. For private repos with SSH auth this is real friction.

`web-install.sh` is meant to be served as a static file (Cloudflare
Pages / nginx) at install.keisei.app. It does ONE thing: prereq → clone
→ delegate to ./bootstrap.sh. Single source of truth — no duplicated
install logic.

## What it does

1. Splash + log to ~/.keisei-install.log
2. Hard prereq: git (the one thing bootstrap.sh cannot self-install)
3. SSH auth probe for git@github.com — clear error message if key missing
4. Clone/pull KeiSeiLab/KeiSeiKit-1.0 to $KEISEI_ROOT (default ~/.local/share/keisei)
5. git submodule update --init (pulls kei-registries)
6. exec ./bootstrap.sh "$@" — pass-through all flags

## Env overrides

- KEISEI_ROOT — install location
- KEISEI_REPO — git URL
- KEISEI_REF  — branch/tag/sha

## Hosting

Push this file to install.keisei.app (Cloudflare Pages, S3+CF, nginx
static — anything that serves a single .sh over HTTPS).

## README updated

Quick start section now shows the curl one-liner as the recommended
path. Repo URL corrected from KeiSei84 → KeiSeiLab org.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 16:44:26 +08:00
Parfii-bot
86834b82af feat(kei-buddy): provision_decrypt — VPS-side blob decryption
Mirrors keisei-marketplace/src/lib/crypto-box.ts::sealBoxToVps.

Two new subcommands on kei-buddy bin:
  - genkeys --key <path>          → writes PKCS#8 PEM x25519 priv,
                                    prints standard-base64 pub (44 char)
  - decrypt-and-export --vps-key <pem> --blob <json> --env-out <env>
                                  → ECDH(vps_priv, ephPub) → HKDF-SHA256
                                    info=keibuddy-token-v1 → XChaCha20-Poly1305
                                    decrypt → append BOT_TOKEN/TELEGRAM_BOT_TOKEN
                                    to env file (replaces stale, keeps other lines)

Cloud-init in hetzner.ts already calls these. Without this commit the
VPS could decode its own pubkey but had no way to recover the sealed
bot-token blob — the bot would never log into Telegram.

Crypto stack (mirror of @noble in TS):
  - x25519-dalek 2 (static_secrets feature)
  - chacha20poly1305 0.10 (XChaCha20Poly1305)
  - hkdf 0.12, sha2 0.10
  - base64 0.22 (accepts URL_SAFE_NO_PAD + STANDARD)
  - zeroize 1 for priv-key wipe

Tests (6/6 pass):
  - roundtrip_seal_then_decrypt — re-implement marketplace sealing in Rust,
    verify our decryption recovers plaintext
  - decrypt_and_export_writes_env_file — full e2e through CLI surface
  - decrypt_and_export_replaces_existing_token — stale BOT_TOKEN replaced,
    other env lines preserved
  - decrypt_rejects_wrong_key — XChaCha20 AEAD tag fails on wrong key
  - pem_roundtrip — write_pkcs8 + parse_pkcs8 round-trip
  - b64decode_accepts_urlsafe_and_standard — handles both encodings

Cross-verified end-to-end:
  $ node marketplace_seal.mjs <pub> <token>  →  /tmp/blob.json
  $ kei-buddy decrypt-and-export --vps-key ... → BOT_TOKEN matches input

Constructor Pattern: 1 file (provision_decrypt.rs, 344 LOC), 1 module,
1 responsibility (token-blob decryption + key generation).

=== STATUS-TRUTH MARKER ===
shipped: functional
stubs: 0
cargo-check: PASS
behaviour-verified: yes (e2e marketplace-seal → kei-buddy-decrypt round-trip)
follow-up-required:
  - none
2026-05-15 17:49:59 +08:00
Parfii-bot
e9b0debec3 merge: feat/agent-dna-three-layer — agent_shell_dna cube + registry submodule + audit closures 2026-05-14 15:16:26 +08:00
Parfii-bot
40a5c2e55f fix(audit-r2): HIGH+MEDIUM closures from second round audit
HIGH-1: submodule URL ssh → https + shallow (DNS spoofing surface, both repos)
HIGH-3: docs/DNA-MIGRATION.md — two-format coexistence policy (4-seg legacy
        task-class vs 5-seg agent-shell marketplace)
HIGH-5: agent_shell_dna doc — explicit consumer = marketplace, planned ledger
        integration; module-doc clarification
MEDIUM: Haiku model id pinned to claude-haiku-4-5-20251001 across
        pricing.rs::from_slug + ::name + escalate.rs tests + select_posterior
        fixture + kei-registries submodule (pushed c39e528→7aaa6a7)
2026-05-14 13:18:14 +08:00
Parfii-bot
d01649b355 chore: registries → submodule from KeiSeiLab/kei-registries
Closes audit HIGH-1 (SSoT drift between KSK and marketplace).
_blocks/registries/ now tracks the single canonical kei-registries repo;
marketplace consumes the same submodule.
2026-05-14 01:59:36 +08:00
Parfii-bot
7281e7ecea fix(registries+router): MEDIUM/LOW audit batch
closes MEDIUM/LOW from feat/agent-dna-three-layer audit:

- models.toml: cache_write_1h_per_mtok_micro added to all 11 entries
  (Anthropic: 200M/600M/1000M micro = $2/$6/$10 per MTok per pricing page;
   other providers: 0 placeholder)
- main.rs (kei-model-router): WAL pragma + busy_timeout errors now logged
  to stderr instead of silently dropped (previously .ok() swallowed both)
- models.toml: Haiku id pin TODO documented (router still hardcodes alias)
2026-05-14 00:04:18 +08:00
Parfii-bot
7cd74071a1 feat(kei-model-router): agent_shell_dna parser for 5-segment marketplace DNA
Adds `agent_shell_dna` cube parsing the new agent-shell::<p>:<m>:<c>::<scope>::<body>-<nonce>
format emitted by keisei-marketplace/src/lib/cryptoid.ts::agentDna.

Companion to legacy 4-segment `dna_class` (untouched per RULE Don't-Rewrite).
Accepts both 8-hex (legacy) and 16-hex (current) lengths for forward-compat.

- new file: src/agent_shell_dna.rs (235 LOC, 13 tests all pass)
- lib.rs: pub mod agent_shell_dna + module doc

Closes HIGH-2 (dna-three-layer audit).
2026-05-13 23:54:35 +08:00
Parfii-bot
766504d345 fix(kei-model-router): close 2 HIGH regressions from re-audit
Re-audit (codex + critic-bug) on commit 18766171 confirmed all 10 original
findings closed, but flagged 2 HIGH regressions the fix itself introduced.

NEW-1 fix — non-Claude profile no longer falls back to Opus
  - main.rs::build_select_input: when Model::from_slug returns None
    (any non-Anthropic provider — codex/xai/deepseek/google/local),
    cmd_select bypasses posterior+kernel entirely and prints
    (provider, model_id) directly with reason="profile_default_non_claude".
  - Posterior machinery stays Claude-only by design — RULE 0.20 escalation
    ladder operates on Anthropic family. Cross-provider routing is
    registry-driven, not enum-driven.
  - Test non_claude_profile_triggers_provider_bypass:
    pick("codex-reviewer") = ("codex","gpt-5-codex");
    Model::from_slug("gpt-5-codex") = None — assertion locks behaviour.

NEW-2 fix — no-ledger path preserves profile-resolved fallback
  - print_decision_no_ledger signature changed from (dna, prompt) to
    (input: &DecisionInput, dna). Uses input.fallback.slug() so the
    Sonnet/Haiku selected by profile resolution survives instead of
    being overwritten with constructor-default Opus.
  - Test decision_input_preserves_set_fallback verifies the round-trip.

Verification (orchestrator-side):
  - cargo check        → clean
  - cargo test --release → 65 passed / 0 failed (was 63 → +2 regression tests)
  - Constructor Pattern → all files ≤ 200 LOC (main.rs 197 at limit)

DNA-INDEX.md regenerated by kei-registry hook (cosmetic).

=== STATUS-TRUTH MARKER ===
shipped: functional
stubs: 0
cargo-check: PASS
behaviour-verified: yes
follow-up-required:
  - CLI stdout for non-Claude profiles gains a "reason: profile_default_non_claude"
    line; downstream parsers must handle the new variant.
2026-05-13 22:44:00 +08:00
Parfii-bot
187661714f fix(kei-model-router): close 10 audit-blocker findings
Codex CRITICAL + 4 HIGH + 5 MEDIUM/LOW from RULE 0.23 dual-review and
RULE 0.25 multi-critic swarm — all closed.

CRITICAL fix
  - Model::slug() ledger compatibility: posterior.rs + select_kernel.rs
    query `WHERE model = ?2 OR model = ?3`, binding canonical + legacy
    slug pair via new `Model::legacy_slug()`. Production ledger rows
    written under "haiku"/"sonnet"/"opus" remain visible to posterior
    aggregation. Regression test ledger_legacy_slug_counted.

HIGH fixes
  - cmd_select(): no longer early-returns on profile match. Profile's
    default_model_ref now becomes DecisionInput.fallback; select() always
    runs, posterior/kernel evidence wins if present. RULE 0.20 cost
    optimisation restored for all 18 registered agents.
  - Registry pricing SSoT: DecisionInput now carries Option<Arc<Registry>>.
    estimated_cost() tries registry first; hardcoded match is documented
    fallback only. select_posterior.rs no longer duplicates models.toml
    constants.
  - registry.rs portability: include_str!() embeds the three TOMLs at
    compile time. load_embedded() new; disk path tried first via
    KEI_REGISTRIES_DIR, embedded as fallback. `cargo install`d binaries
    now find registries unconditionally. embedded_registry_matches_disk
    test ensures embedded ≡ disk source.
  - next_model() ambiguity: replaced Option<&Model> with EscalationResult
    enum (Next(&Model) / AtTop / NotFound). Callers can distinguish typo
    from ceiling. 5 new tests.

MEDIUM fixes
  - posterior.rs u32 overflow: `(n_plus + n_minus) as u32` →
    `u32::try_from(n_plus.saturating_add(n_minus)).unwrap_or(u32::MAX)`.
    overflow_guard_on_huge_n test with i64::MAX.
  - pick() unknown-model: now returns None when default_model_ref's model
    is absent from registry. Inverted the deprecation guard.
  - HOME unset: disk_registries_dir() returns None on empty HOME and
    falls through to embedded registries. open_ledger() logs warning
    and returns None instead of opening at malformed path.
  - SQLite WAL + busy_timeout: applied to ledger connection in
    open_ledger() — concurrent CLI invocations no longer SQLITE_BUSY.

LOW fixes
  - impl Model consolidation: next_tier() moved to pricing.rs.
    escalate.rs uses current.next_tier() instead of duplicating logic.
  - complexity.rs: removed duplicate "ml-implementer" in HEAVY_ROLES.
  - dna_class.rs: role("") now returns None instead of Some("").

Verification (orchestrator-side, RULE 0.13 §Verify-before-commit):
  - cargo check        → clean
  - cargo test --release → 63 passed / 0 failed (was 58 → +5 new tests
    cover legacy-slug, EscalationResult, overflow, unknown-model, embedded)
  - Constructor Pattern → all files ≤ 200 LOC (max registry.rs 196)
  - Largest fn from_ledger 28 LOC / limit 30

DNA-INDEX.md regenerated by kei-registry hook (cosmetic).

=== STATUS-TRUTH MARKER ===
shipped: functional
stubs: 0
cargo-check: PASS
behaviour-verified: yes
follow-up-required:
  - (none from this commit; next audit pass before merge to main)
2026-05-13 22:09:19 +08:00
Parfii-bot
4d79049eff feat(kei-model-router): registry-driven, three-layer DNA
Removes hardcoded Claude-only Model enum. Pricing constants now read
from _blocks/registries/models.toml at startup; provider/model lookup
goes through a typed Registry returned by registry.rs.

New API surface:
  - Registry::load(dir) → (providers, models, profiles)
  - pick(profile_id, &Registry) → Result<(provider_id, model_id)>
  - cost_micro_cents(model_id, in, out, &Registry) → Option<u64>
  - next_model(model_id, &Registry) → Option<&Model> (ascending cost,
    same provider, skip deprecated)

Files:
  - registry_types.rs      new   107 LOC  (Provider/Model/Profile structs)
  - registry.rs            new   152 LOC  (TOML load + lookups)
  - pricing.rs             rew   127 LOC  (registry-backed, no constants)
  - escalate.rs            rew   181 LOC  (registry-backed ladder + skip deprecated)
  - select.rs              rew   131 LOC
  - select_kernel.rs       new    74 LOC  (Constructor-Pattern split)
  - select_posterior.rs    new   178 LOC  (Constructor-Pattern split)
  - posterior.rs           rew   197 LOC
  - calibrate.rs           rew   175 LOC
  - lib.rs                 rew    53 LOC
  - main.rs                rew   163 LOC  (CLI updated to new API)
  - Cargo.toml             dep   added toml 0.8

Verification (orchestrator-side, RULE 0.13 §Verify-before-commit):
  - cargo check                 → clean
  - cargo test --release        → 58 passed / 0 failed / 0 ignored
  - LOC limit (Constructor)     → max 197 / limit 200
  - largest fn cmd_select       → ~27 LOC / limit 30

DNA-INDEX.md regenerated by kei-registry hook (primitive count
144 → 150 reflects the 6 new/split modules).

=== STATUS-TRUTH MARKER ===
shipped: functional
stubs: 0
cargo-check: PASS
behaviour-verified: yes
follow-up-required:
  - select.rs `estimated_cost` still embeds inline cost constants
    mirroring models.toml; if non-Anthropic providers need dynamic
    pricing in select-time estimation, thread Registry through.
  - External callers of old `cost_micro_cents(Model, ...)` signature
    will break — intentional, no external callers in this workspace.
2026-05-13 21:23:53 +08:00
Parfii-bot
3aef8678c0 feat: three-layer agent registries (providers/models/profiles)
Splits agent definition into stable provider + swappable model + role-bound
profile. Adding a new LLM API is one row in providers.toml; new model is
one row in models.toml; agent invocation picks any (provider, model) pair
through agent-profiles.toml default_model_ref.

- providers.toml: 10 providers — anthropic, openai, codex (OAuth), xai,
  deepseek, google, ollama-local, mlx-local, lmstudio-local, litellm-proxy
- models.toml: 11 models with cost_*_per_mtok_micro + context_window +
  verified_at + deprecated_at
- agent-profiles.toml: 18 representative profiles; manifest_path points
  to the canonical .md in ~/.claude/agents/

Three-layer DNA per the new architecture:
  agent-shell::<provider>:<model>:<caps>::<scope8>::<body8>-<nonce8>

This commit only adds registries — kei-model-router still hardcodes
the Claude-only Model enum. Wave 4 will rewire it to read TOML.
2026-05-13 20:51:04 +08:00
Parfii-bot
b87c7685c6 feat(install): add profile=buddy for marketplace 1-click bot provisioning
Profile bundles the crates kei-buddy needs at runtime:
kei-buddy, kei-telegram-webhook, kei-shared, kei-chat-store,
kei-social-store, kei-memory-sqlite, kei-router, kei-llm-bridge-mlx.

Used by keisei-marketplace cloud-init when user clicks 'хочу своего
KeiBuddy' on /keibuddy/setup — Hetzner VPS spawn pulls KeiSeiKit and
runs install.sh --profile=buddy --yes.
2026-05-13 10:29:40 +08:00
KeiSei87
e7f6c197ca chore: dual-identity push smoke-test #2 (SSH key now on KeiSei87) 2026-05-12 23:11:02 +08:00
KeiSei87
4749e28d9b chore: smoke-test push from KeiSei87 SSH identity
Empty commit to verify dual-identity push setup:
- Author: KeiSei87 <noreply email>
- Push via dedicated SSH key ~/.ssh/id_ed25519_keisei87

After landing, GitHub Insights → Contributors should list KeiSei87
alongside KeiSei84.
2026-05-12 23:09:53 +08:00
Parfii-bot
b5d093fbec fix(kei-cortex/test): serial_test on env-mutating openai tests + wiremock warm-up
Previous wiremock conversion fixed the listener-lifecycle race but
left the underlying problem unsolved: `ensure_env()` mutates the
process-global ANTHROPIC_ENDPOINT, and parallel `cargo test` threads
race on that write. Manifested as 502 / "error sending request for
url …" on the first concurrent test pair under both macOS and Linux.

Annotate every #[tokio::test] in openai_loop_wiring.rs +
openai_compat.rs with `#[serial_test::serial]` — these are the only
tests that touch ANTHROPIC_ENDPOINT via shared_mock_anthropic.
serial_test enforces process-wide ordering so the env mutation +
HTTP request pair is atomic per test. All other tests stay parallel.

Stress: 5 parallel `cargo test` runs all green.
2026-05-12 22:26:42 +08:00
Parfii-bot
b103a9aa64 fix(kei-cortex/test): replace hand-rolled mock with wiremock — closes macOS CI flake
Previous `tests/common/mod.rs` spawned a mock Anthropic upstream via
hand-rolled axum + std:🧵:spawn + own current-thread tokio runtime
bound to 127.0.0.1:0. Stable on Linux runner; flaked on macOS GitHub
Actions runners:
  thread 'streaming_responses_runs_real_loop_not_stub' panicked at
  kei-cortex/tests/openai_loop_wiring.rs:277:5:
  no responses delta event in stream: event: response.error
  data: {"error":"model: anthropic request: error sending request
         for url (http://127.0.0.1:49312/v1/messages)"}

Root cause traced to macOS-runner loopback / fd-limit pressure on the
dedicated-thread current-thread runtime. wiremock crate runs a
production-quality hyper-based mock server, manages its own listener
lifecycle, and survives the macOS runner constraints.

## Change

- `Cargo.toml`: add wiremock = workspace dev-dep (already 0.6 in workspace)
- `tests/common/mod.rs::MockAnthropicServer` rebuilt over wiremock::MockServer
- `build_mock(text)` mounts `POST /v1/messages → 200 + canned body` on a
  wiremock instance
- `mock_anthropic_responding_with()` spins one per call on a parked
  helper thread (preserves `MockAnthropicServer: 'static` lifetime for
  `shared_mock_anthropic` `OnceLock` singleton)
- `shared_mock_anthropic()` API unchanged; existing test sites in
  `tests/openai_loop_wiring.rs` + `tests/openai_compat.rs` continue to
  work without modification

## Verification

`cargo test -p kei-cortex --test openai_loop_wiring`: 7/7 pass locally
`cargo test -p kei-cortex`: full suite green (428 lib + integration)

Also includes DNA-INDEX regenerate (auto-encyclopedia hook artefact;
0 vortex matches preserved).
2026-05-12 21:17:58 +08:00
Parfii-bot
fd7d1cd2a5 merge: feat/kei-buddy-runtime-1778560000 → main (16 commits, 2026-05-12 prod-prep) 2026-05-12 20:41:13 +08:00
Parfii-bot
a3f8e1f847 chore(prod-prep): root docs (CHANGELOG/CONTRIBUTING/SECURITY) + cargo update
Root-level docs added per production-readiness audit:
- CHANGELOG.md — unreleased + pointer to git tags
- CONTRIBUTING.md — setup + PR checklist + Constructor Pattern
- SECURITY.md — reporting channel + threat model + known RUSTSEC list

cargo update applied: 19 patch/minor bumps (base64urlsafedata, blake3, cc,
crc-catalog, digest, filetime, h2, hashbrown, hybrid-array, idna_adapter,
js-sys, kqueue-sys, libc, nix, openssl, openssl-sys, pin-project,
pin-project-internal, redox_syscall).

9 RUSTSEC advisories from transitive deps remain (rsa 0.9 Marvin,
rustls-webpki x5, sqlx 0.8 Binary Protocol, async-std discontinued,
lru unsound IterMut, fxhash/instant unmaintained) — require major-version
bumps in direct deps, tracked in SECURITY.md "Known advisories" section.
2026-05-12 20:41:13 +08:00
Parfii-bot
302ca661ec chore(docs): regenerate DNA-INDEX without project-vortex
Removes the two banned-project references (project-vortex::vortex and
project-vortex::vortex-constraints at lines 703/707 of DNA-INDEX.md
pre-regenerate) that surfaced in the public-readiness audit (P0
finding from sub-agent a2c1199a).

Source: ~/.claude/registry.sqlite row 391 +
~/.claude/registry-fragments/project-vortex__vortex-constraints.md.
Both removed locally so kei-registry encyclopedia regen no longer
emits the lines. auto-encyclopedia-refresh.sh PostToolUse:Edit|Write
hook will not re-add them on next run since the source row is gone.

If the Vortex agent project (cyber-banned per ~/.claude/rules/security.md)
needs that rule again, it should be registered into a SEPARATE local-only
registry (e.g. ~/.claude/registry-private.sqlite) so it never leaks into
the public encyclopedia path.

After regen: 0 vortex/neuralcloak/keidog/keinet matches in entire
KeiSeiKit-public tree (git grep). Public-readiness P0 = 0.
2026-05-12 20:10:52 +08:00
Parfii-bot
b61b17ea7b feat(kei-buddy): conversational LLM-driven flow + kei-sage retrieval (graph-RAG)
Replaces the rigid FSM after Intro/AskLanguage with a single LLM call per
turn that sees:
  * persona (what's already known — slots not re-asked)
  * recent 10 chat_log messages (history)
  * top-5 kei-sage atoms relevant to user_text (graph-RAG, not embeddings)
  * raw user_text

LLM returns JSON {slot_updates, response_text, done, focus} which drives
the next state + persona patch + reply. No embeddings, no vector store —
kei-sage's FTS5 + Obsidian-style atom graph is the retrieval layer.

New files:
  * src/retrieval.rs (101 LOC) — retrieve_context(chat_log, topics,
    chat_id, query, history_n, atoms_k) -> RetrievalContext
  * src/conversational.rs (157 LOC) — conversational_step
    (state, persona, context, text, extractor, lang) -> StepOutput

Modified:
  * src/serve.rs::run_fsm — branch on state: Intro/AskLanguage still go
    through legacy handle_step (jump-start); everything else routes to
    conversational_step with retrieval context.
  * src/lib.rs — module declarations.

Tests (5 new, 60 total passing):
  * parses_well_formed_llm_response
  * done_true_transitions_to_ready
  * invalid_json_falls_back_gracefully
  * retrieve_returns_empty_on_empty_stores
  * retrieve_finds_seeded_data

Verify:
  * cargo check -p kei-buddy: PASS
  * cargo test -p kei-buddy --lib: 60/0 (was 55, +5)

Why graph-RAG instead of embeddings: kei-sage already in tree (atoms +
edges + BFS + PageRank + FTS5). Explicit edges (message → topic →
contact) beat opaque cosine similarity for personal-assistant memory
where relationships are typed. No sqlite-vec dep, no embedding cost.

NOT deployed yet — needs server rebuild.
2026-05-12 19:00:27 +08:00
Parfii-bot
f354aaccfc fix(kei-conflict-scan): close 3 backlog bugs + Phase C draft emission
Closes engine bugs #1, #2, #3 from the user's backlog.md entry dated
2026-05-11 "kei-refactor-engine — 4 false-positive bugs". Bug #4 was
fixed in 6cd99982 (wikilink path-norm + handoff scanner removal).

## Bug #1 — vendored marketplaces skip

Engine was scanning `plugins/marketplaces/claude-plugins-official/` —
vendored upstream code where Constructor Pattern thresholds don't
apply. ~246 cp-violations were from this tree.

Fix: `tree::should_skip_path()` central filter. Skips any path
component named `marketplaces`, `target`, `node_modules`, or `.git`.
Applied via `WalkDir::filter_entry()` in `collect_markdown`,
`collect_with_ext`, `scanners::cp::scan`, `scanners::orphans::scan`,
`scanners::orphans::all_basenames`. `scanners::cp::skip_dir` now
delegates to `should_skip_path` (removed the older inline
`/target/`-substring check).

## Bug #2 — hooks-share-matcher false-positive class

Claude Code hook chains are designed to support N hooks per event by
design. `scanners::hooks` was flagging every pair sharing a matcher
as a "redundancy conflict" — 9 hooks/medium findings in the last
deep-sleep run, every one false-positive.

Fix: `scanners::hooks::scan` reduced to a no-op stub returning
`Vec::new()`. Module docstring documents the retraction + future
direction (a real `hooks-validity` scanner for broken shebangs,
missing chmod, syntax errors would replace it).

## Bug #3 — `.patch` file not unified diff

Already resolved in prior commit (v0.14.1 retraction in patch.rs):
CLI default is `plan-autoresolve.md`, Phase C template references
`-autoresolve.md` suffix, `write_patch` is deprecated shim. Only
legacy `.patch` artefacts in sync-repo/reports/ remain — those are
audit trail, not active.

## Phase C draft file emission (deep-sleep-trigger-prompt.md §6.d)

The earlier Phase C template emitted `proposed_rule` markdown blocks
only — no actionable artefacts. Extended §6 with step 6.d: when
WITH_FORK=1 AND fork branch was created, ALSO write skeleton draft
files into the branch:

  sync-repo/sleep-deep/YYYY-MM-DD/drafts/rules/<slug>.md
  sync-repo/sleep-deep/YYYY-MM-DD/drafts/hooks/<slug>.sh

Drafts follow pattern-codifier-agent Phase 3 templates. Phase C does
NOT register hooks — that's pattern-codifier's job via /sleep-review
morning click-flow (skill Phase 3a added in ~/.claude commit 49a320d).
This closes the loop: Phase C surfaces draft → morning review clicks
approve → pattern-codifier installs → settings.json registered.

Smoke-test required in §6.d: every emitted `.sh` MUST `bash -n` clean
or be excluded from commit + listed in plan markdown.

## Results on ~/.claude/memory/sync-repo (live data)

| Scanner   | Before | After | Delta |
|-----------|-------:|------:|------:|
| orphans   |    108 |     1 |  -107 |
| hooks     |      2 |     0 |    -2 |
| cp        |    174 |     0 |  -174 |
| **TOTAL** |    284 |     1 |  -283 |

On full ~/.claude scan: total drops from ~1614 (per 2026-05-11
backlog) to 983 (cp=186 + orphans=797 — orphan count high because
~/.claude tree has many memory/chatlogs/ refs out-of-tree).

## Tests

12/12 pass on kei-conflict-scan workspace (4 unit + 8 integration).
Pre-existing `oversize_file_flagged` + `orphan_wikilinks_flagged`
still green; new `cross_repo_wikilink_not_flagged` +
`path_prefixed_wikilink_matches_basename` from 6cd99982 still green.

Private mirror at ~/Projects/KeiSeiKit/_primitives/_rust/ synced
(4 files: tree.rs, scanners/cp.rs, scanners/orphans.rs,
scanners/hooks.rs).

Closes backlog "engine-noise-2026-05-11" tag bugs #1, #2, #3.
2026-05-12 18:30:01 +08:00
Parfii-bot
26dc8c85f7 feat(kei-buddy): AskLanguage i18n + real proposeTopicSources + voice handling
Three follow-up atomics on top of the contacts/topics/sync wave.

## 1. AskLanguage state + ru/en localisation (default en)

New state `AskLanguage` inserted between `Intro` and `AskName`. Intro now
sends a bilingual greeting + language picker. AskLanguage parses
en/english/1/ru/русский/2/etc → persona_patch{"language":"<code>"} →
transitions to AskName with that language's prompt.

All later prompts (AskName / AskTone / AskInterests / AskHobbies /
TopicSpecifics / TopicNowLater / TopicResearch / AskSchedule / Ready)
read persona.language via Lang::from_persona and dispatch through
Strings::* helpers — two language tables, no fallthrough.

Back-compat migration: existing chats without `language` key (like the
user currently in topic_now_later) get an implicit "ru" patch on next
turn so their Russian onboarding continues without regression.

New files: strings.rs (164), machine_lang.rs (145).
Modified: state.rs (+AskLanguage variant), machine.rs (Intro→AskLanguage,
AskLanguage arm, migration guard), machine_helpers.rs, machine_tests.rs.

5 new tests (intro_to_ask_language, ask_language_en, ask_language_ru,
ask_language_invalid, migration_sets_ru_when_language_missing).

## 2. Real proposeTopicSources — removed TODO(phase2) stub

machine_lang.rs::step_topic_research now calls
extractor.extract(prompt, topic_title) with a {name, url, why} schema.
Parses JSON, formats numbered source list, transitions to TopicSources.

Failure paths (LLM error, empty array): graceful fallback prompt asking
user to suggest their own — still transitions to TopicSources so flow
doesn't deadlock.

3 new tests in machine_tests_topic_research.rs:
topic_research_yes_proposes_sources,
topic_research_yes_empty_sources_still_advances,
topic_research_no_skips_topic_sources.

## 3. Voice-message handling (Telegram voice/audio → STT → text pipeline)

kei-telegram-webhook: added Voice/Audio sub-structs on Message and
WebhookEvent::Voice variant. classify() detects message.voice OR
message.audio. 2 new tests in event.rs.

kei-buddy/src/voice.rs (178 LOC):
VoiceHandler { bot_token, stt: Arc<dyn SttBackend>, http }
transcribe_file(file_id, mime_type) does:
  1. GET https://api.telegram.org/bot{token}/getFile?file_id=...
  2. GET https://api.telegram.org/file/bot{token}/{file_path}
  3. SttRequest { audio_bytes, mime_type, language: None } → backend.transcribe
  4. Returns transcript text.
2 wiremock tests (download chain + 500 error mapping).

serve.rs adds voice: Option<Arc<VoiceHandler>> to BuddyContext;
on_event Voice arm: whitelist check → transcribe → handle_text (same
pipeline as if user typed). Voice unavailable: warn + ignore.

serve_runner.rs builds VoiceHandler from KEI_BUDDY_STT_BACKEND env.

kei-stt added as optional dep gated by serve feature. Default backend
whisper-local (no extra build deps).

TTS reply path deferred (next atomic).

## Verify

  * cargo check --workspace: PASS
  * cargo test -p kei-buddy --lib: 55 passed / 0 failed (was 41 → 50 → 53 → 55)
  * cargo test -p kei-telegram-webhook --lib: 7 passed (was 5, +2 voice)
  * cargo build -p kei-buddy --release: PASS (23.7s)

NOT deployed yet — three new things to roll out next:
  * новые миграции (нет — БД без изменений)
  * новые env: KEI_BUDDY_STT_BACKEND (optional)
  * установка faster-whisper / piper-tts на сервер для STT
    (без него Voice event просто warn-логируется и игнорируется)
2026-05-12 17:49:06 +08:00
Parfii-bot
06bcce9981 feat(contacts): glue sync + Google pagination + Apple discovery & folding
Three atomics finish phase 3 of kei-buddy contacts integration:

## kei-buddy: contact-sync glue + slash commands (+5 tests)

New src/contacts_sync.rs (146 LOC):
  * SyncReport { fetched, added, skipped, errors }
  * sync_from_google(access_token, contacts) — builds GooglePeopleClient,
    list_connections, dedups by (name+email) via search_contacts,
    add_contact in loop
  * sync_from_apple(apple_id, app_pw, addressbook_url, contacts) — same
    pattern over ICloudCardDavClient.list_contacts
  * All errors collected into report.errors; never panics, never propagates

New slash commands in commands.rs / command_exec.rs:
  * /sync-google — reads GOOGLE_OAUTH_ACCESS_TOKEN env, calls sync_from_google,
    Russian-formatted summary "Google: загружено N, добавлено M, пропущено K"
  * /sync-apple — reads APPLE_ID + APPLE_APP_PASSWORD + APPLE_CARDDAV_URL,
    calls sync_from_apple
  * Missing env → human-readable "не настроено: …" response
  * /help text updated

Deps added: kei-contacts-google + kei-contacts-apple as path deps.

## kei-contacts-google: pagination via nextPageToken (+1 test)

Refactor: client.rs 182→56 LOC; pagination logic + deserialization moved
to new src/pagination.rs (188 LOC). list_connections unchanged
(back-compat, returns first page only). New list_all_connections loops
via fetch_page(Some(token)) until token=None; hard cap 50 pages with
tracing::warn on cap.

Test list_all_connections_two_pages: wiremock returns page 1 with
nextPageToken="abc" + page 2 without; assert len = sum AND second
request carries pageToken=abc query.

## kei-contacts-apple: vCard line-folding + CardDAV auto-discovery (+2 tests)

vcard.rs +unfold() helper applied in parse_vcard per RFC 6350 §3.2:
continuation lines starting with space/tab strip the prefix and append
to previous line. Test parse_folded_vcard.

New src/discovery.rs (199 LOC): discover_addressbook() walks
.well-known/carddav → current-user-principal → addressbook-home-set →
first addressbook with C:addressbook resourcetype. Three PROPFIND
requests with canned XML bodies. Regex-based extract_first_href_under +
extract_addressbook_href helpers. Test discover_walks_three_propfinds
against 3-step wiremock fixture.

client.rs adds discover_addressbook_url() method calling discovery.

## Verify-before-commit

  * cargo check --workspace: PASS
  * cargo test -p kei-buddy --lib: 46/0 (was 41)
  * cargo test -p kei-contacts-google: 5/0 (was 4, +1 pagination)
  * cargo test -p kei-contacts-apple: 9/0 (was 7, +1 folding +1 discovery)

NOT deployed — user still in live conversation with bot.

Follow-up (deferred, non-blocking):
  * Real iCloud smoke test for discover_addressbook_url — regex parser
    may need adjustment for deeply-nested namespace prefixes
  * Wiremock-backed integration test for sync_from_google glue (HTTP
    layer already covered in kei-contacts-google tests)
2026-05-12 17:04:15 +08:00
Parfii-bot
6cd999820c fix(kei-conflict-scan): wikilink path-norm + drop handoff false-positives
Two architectural bugs in orphans scanner — both surfaced by morning
/sleep-review of deep-sleep/2026-05-12-0400 (108 false-positive
orphan-wikilinks; the engine was scanning sync-repo MEMORY.md and
flagging every `[[../../../rules/X]]` cross-repo ref as broken).

1. Asymmetric normalization in extract_wikilinks
   - `all_basenames(root)` indexed file_stem (lowercase, no path)
   - `extract_wikilinks` returned lowercased FULL link text including
     `../../../`-prefix and `subdir/` segments
   - Result: `[[chatlogs/X/Y]]` never matched `Y.md` in index, every
     `[[../../../rules/X]]` always flagged orphan

   Fix: `normalize_target(raw) -> Option<String>` strips path prefix,
   strips `.md` suffix, returns None for `../`-rooted refs that escape
   the scan tree (engine cannot validate cross-repo targets).

2. extract_handoffs scanner removed
   - Regex `^\s*-\s*\*\*([a-z0-9][a-z0-9_-]{2,})\*\*` was matching every
     prose bold-bullet, e.g. `- **english-jargon** — last 7d:` in
     backlog.md or `- **L1-Path-C**:` in chatlogs.
   - sync-repo scan: 0 real handoff sections present, 100% of matches
     were prose. Real handoff syntax in agent-graph repos uses YAML
     frontmatter, not prose markdown bullets.
   - Scanner deleted along with its helper; wikilink scanner alone
     covers the explicit `[[...]]` ref use case.

## Result on sync-repo (live data)

| Metric         | Before | After |
|----------------|-------:|------:|
| orphan refs    |    108 |     1 |
| false-positive |    107 |     0 |

Remaining 1 = legitimate `[[wikilink]]` literal in backlog.md prose.

## Tests added (already present in HEAD via prior fleet commit)

- `tests::cross_repo_ref_skipped` — `../../../foo` -> None
- `tests::path_prefixed_target_basenamed` — `chatlogs/X/Y` -> "Y"
- `tests::plain_basename_passes_through`
- `tests::md_suffix_stripped`
- integration `cross_repo_wikilink_not_flagged` (E2E)
- integration `path_prefixed_wikilink_matches_basename` (E2E)

12/12 tests pass. Release binary rebuilt + installed to ~/.cargo/bin/.
Private mirror at ~/Projects/KeiSeiKit/_primitives/... synced.

Closes backlog.md "engine bug #4" (added by user via prior /sleep-review).
2026-05-12 16:52:03 +08:00
Parfii-bot
450156a476 feat(kei-buddy fleet): 5 atomics — google/apple contacts + classifier + tick + slash-commands
Parallel agent batch. All five tasks delivered functional + tested.
NOT deployed — user is in live conversation with the bot.

## Crates added (2 new)

### kei-contacts-google (466 LOC, 5 tests)
Thin Google People API client. Takes pre-acquired access_token from
kei-auth-google's OAuth flow; calls /v1/people/me/connections?personFields=...,
parses 200-entry first page (TODO: pagination via nextPageToken), maps
to kei_social_store::Person. Errors: Http / Auth(401) / Parse.

### kei-contacts-apple (593 LOC, 7 tests + 1 doc-test)
CardDAV client for iCloud Contacts using Basic Auth (Apple ID +
app-specific password). Sends REPORT with addressbook-query XML body,
parses multistatus → embedded vCards → AppleContact. Tiny vCard
parser (~150 LOC) handles FN/N/EMAIL/TEL/ORG/NOTE/UID, single-line
only (no line-folding for MVP). Discovery (PROPFIND .well-known/carddav
→ principal → addressbook-home-set) deferred — user supplies
addressbook URL via with_addressbook_url().

Both crates registered in workspace members.

## kei-buddy crate additions

### src/topic_classify.rs (116 LOC, 3 tests)
Free fn classify_and_store_topic(extractor, topics, chat_id, text)
called from process_text when state == OnboardState::Ready. Builds
classifier prompt → LLM → parses {slug, title} → validates slug
shape (kebab-case, ascii) → Topics::add_topic + add_digest. All
failure paths log + return; conversation never blocks.

### src/tick.rs (188 LOC, 3 integration tests) + src/bin/kei-buddy-tick.rs (67 LOC)
Second binary. Oneshot CLI for systemd timer: walks all known
chat_ids in BuddyStore → lists topics → searches recent chat
messages per topic (configurable window/limit) → LLM digest →
Topics::add_digest. Outputs JSON TickReport to stdout. Env-driven
config. NoOpExtractor fallback when no LLM creds (graceful degradation).

### src/commands.rs (146 LOC) + src/command_exec.rs (111 LOC, 7 tests)
Slash-commands intercepted BEFORE handle_step in process_text:
  /whois <name>   contacts.search_contacts + common_connections for hits
  /find <q>       chat_log.search scoped to chat_id
  /topics         topics.list_topics
  /contacts       contacts.search_contacts("", 10)
  /help           static usage text (Russian)
If command parsed, response built from stores, sent, logged to
chat_log — FSM skipped for that turn.

### src/serve_runner.rs (69 LOC) — refactor
run_serve + start_listener + init_tracing extracted out of serve.rs
to bring serve.rs back to 189 LOC (was 248 after previous wave).

### Wiring
BuddyContext gains `contacts: Arc<Contacts>` and `topics: Arc<Topics>`.
ServeConfig gains contacts_db_path + topics_db_path. Binary reads
KEI_BUDDY_CONTACTS_DB_PATH + KEI_BUDDY_TOPICS_DB_PATH env (defaults
./kei-buddy-contacts.db, ./kei-buddy-topics.db). cmd_migrate applies
schema for all three side-stores (chat_log + contacts + topics).

## Verify-before-commit (RULE 0.13 §)
  * cargo check -p kei-buddy (default + extractor-openai): PASS
  * cargo test -p kei-buddy --lib: 41 passed / 0 failed (was 31)
  * cargo test -p kei-buddy --tests: 3 passed (tick integration)
  * cargo build -p kei-buddy --features extractor-openai: PASS
    (builds both kei-buddy + kei-buddy-tick binaries)
  * cargo check -p kei-contacts-google: PASS (5 tests)
  * cargo check -p kei-contacts-apple: PASS (7 + 1 doc)
  * cargo check --workspace: PASS

## STATUS-TRUTH from all 5 agents: shipped=functional, behaviour-verified=yes

## Follow-up (deferred, non-blocking)
  * Google People API pagination (nextPageToken loop) — first 200 only
  * CardDAV auto-discovery (PROPFIND .well-known/carddav)
  * vCard line-folding (RFC 6350 §3.2)
  * Wire kei-contacts-google + kei-contacts-apple → Contacts.add_contact
    sync command (no glue yet)
  * systemd timer file for kei-buddy-tick (not shipped here — config only)
2026-05-12 16:33:58 +08:00
Parfii-bot
fa7a20d572 feat(kei-buddy): wire kei-social-store + kei-sage — contacts + topics + FTS5
Two parallel atoms in one commit. Both reuse existing KeiSeiKit
primitives (zero new crates) per RULE feedback_inventory_before_decompose.

## src/contacts.rs (200 LOC, +4 tests)

Adapter over kei-social-store. Address book + interaction log + relationship
graph for shared connections.

API:
  * Contacts::from_path / from_memory
  * add_contact / get_contact / search_contacts
  * log_meet(person_id, target_id, channel, note) / interactions_for
  * relationship_graph — returns Vec<Pair>, the kei-social-store output
  * common_connections(a, b) — post-filters relationship_graph to find
    target_ids that appear in pairs with BOTH a and b. This is the
    "у нас с Денисом общий друг X" feature.

Pattern: Arc<Mutex<kei_social_store::Store>> + tokio::spawn_blocking,
mirroring chat_log.rs. Errors map to BuddyError::Memory.

Tests: add_and_get_contact_roundtrip / search_contacts_finds_by_name /
log_meet_and_list_interactions / common_connections_finds_shared_target.

## src/topics.rs (200 LOC, +4 tests)

Adapter over kei-sage. Topics + digest notes + FTS5 search. Each topic
is a sage Unit{unit_type="buddy_topic", category="kei-buddy",
source_path="kei-buddy/chat-{chat_id}/topic/{slug}"}. Digests are
Unit{unit_type="buddy_digest"} linked via add_edge(topic→digest,
edge_type="digest_for").

API:
  * Topics::from_path / from_memory
  * add_topic(chat_id, slug, title, content) — idempotent via path lookup
  * add_digest(chat_id, topic_slug, timestamp, content) — creates Unit +
    edge
  * search(query, limit) — fts_search over all kei-buddy units
  * digests_for(chat_id, topic_slug) — follows outgoing edges
  * list_topics(chat_id) — raw SELECT scoped by source_path LIKE prefix

Tests: add_topic_then_search_finds_it / add_topic_is_idempotent /
add_digest_creates_edge_and_dest / list_topics_scopes_per_chat.

## Dependencies added

kei-social-store + kei-sage as local path deps. Both already in workspace,
no new external crates.

## Verify-before-commit

  * cargo check -p kei-buddy: PASS
  * cargo test -p kei-buddy --lib: 31/0 (was 23, +4 contacts +4 topics)

Net change: 4 files touched, ~400 LOC added across the two adapters.

NOT deployed. User still in active bot conversation.
2026-05-12 16:05:32 +08:00
Parfii-bot
fb7c1bf859 feat(kei-buddy): wire kei-chat-store — log every user/bot message with FTS5
After-Ready conversation was going to /dev/null. With this change every
inbound Telegram text + every bot response is persisted to a SQLite +
FTS5 archive via the existing kei-chat-store primitive (no new crate).

Each Telegram chat_id maps 1:1 to a kei-chat-store session
(project="kei-buddy", title="tg-<chat_id>", model="telegram"). Cache
prevents per-message session lookups.

New file:
  * src/chat_log.rs (198 LOC) — ChatLog adapter wrapping
    kei_chat_store::Store + a chat_id→session_id Mutex cache.
    API: from_path / from_memory / ensure_session / log_user /
    log_bot / search(query, chat_id?, limit). Errors map to
    BuddyError::Memory and never propagate from on_event — chat-log
    failure is logged but does not block the conversation.

Modified:
  * Cargo.toml — kei-chat-store path dep added.
  * src/lib.rs — pub mod chat_log + re-export ChatLog.
  * src/serve.rs — BuddyContext gains Arc<ChatLog>;
    process_text calls log_user before handle_step + log_bot after
    send_message; ServeConfig gains chat_log_db_path.
  * src/bin/kei-buddy.rs — KEI_BUDDY_CHAT_LOG_PATH env
    (default ./kei-buddy-chat.db); migrate subcommand applies the
    chat-store schema alongside buddy_state schema.

Tests (3 new in src/chat_log.rs, all pass):
  * log_user_creates_session_and_message
  * log_bot_uses_same_session_as_log_user
  * different_chats_get_different_sessions

Verify-before-commit:
  * cargo check -p kei-buddy (default): PASS
  * cargo check -p kei-buddy --features extractor-openai: PASS
  * cargo test -p kei-buddy --lib: 23 passed / 0 failed
    (was 20 before this commit; 3 new ChatLog tests)

NOT deployed — user is in active conversation with the live bot.
Will roll forward when user signals readiness.
2026-05-12 15:51:24 +08:00
Parfii-bot
0045b6ac77 feat(kei-buddy): wire OpenAiExtractor + chat_id whitelist + env-configurable LLM
Two additions on top of the MVP serve binary:

1. Whitelist by chat_id (KEI_BUDDY_ALLOWED_CHAT_IDS env, CSV).
   * BuddyContext gains Arc<Option<Vec<i64>>> allowed_chat_ids
   * chat_allowed() check fires before process_text
   * Non-whitelisted chats: warn-log + ignore (no response sent)
   * None or empty list = accept all (back-compat with prior behaviour)

2. Real LLM wiring (KEI_BUDDY_LLM_PROXY / _LLM_KEY / _LLM_MODEL).
   * When extractor-openai feature compiled in AND both proxy+key set,
     run_serve instantiates OpenAiExtractor instead of MockExtractor
   * Defaults: proxy=https://api.openai.com, key=OPENAI_API_KEY env,
     model=gpt-4o-mini
   * Fallback: warns + MockExtractor (state machine still walks, but
     LLM-extracted fields are empty)
   * extractor::OpenAiExtractor gains new_with_model(proxy, key, model);
     model is now per-instance instead of compile-time DEFAULT_MODEL

3. start_listener extracted as helper — keeps run_serve readable across
   the two feature-gated branches.

Verify-before-commit:
  * cargo check -p kei-buddy (default): PASS
  * cargo check -p kei-buddy --features extractor-openai: PASS
  * cargo test -p kei-buddy --lib: 20/0 unchanged
2026-05-12 14:49:43 +08:00
Parfii-bot
7414d14cc7 feat(kei-buddy): functional MVP — store + state-machine port + serve binary
Three atoms landed in one commit (memory binding, state machine port,
real serve binary). Tracked separately in TaskList (#5 #7 #6).

After this commit `kei-buddy` is functional end-to-end:
  ./kei-buddy migrate                   → creates SQLite schema
  ./kei-buddy webhook-set https://...   → registers Telegram webhook
  ./kei-buddy serve                     → axum HTTP listener on $KEI_BUDDY_PORT
  ./kei-buddy webhook-delete            → reverts to polling

20 tests pass across 5 modules. Binary builds clean (default + extractor-openai).

## Memory binding (task #5)

New files:
  * src/schema.rs (56)        — buddy_state table DDL, idempotent
  * src/store.rs (164)        — BuddyStore trait + SqliteBuddyStore
  * src/store_ops.rs (107)    — pub(crate) sync SQL helpers behind spawn_blocking

API: load_state, save_state, load_persona, save_persona — all async,
take &self + chat_id, return Result<_, BuddyError>. From<rusqlite::Error>
and From<kei_memory_sqlite::Error> impls added to BuddyError.

## State-machine port (task #7)

New files:
  * src/transition.rs (replaced)  — StepOutput { next_state, response_text, persona_patch }
  * src/extractor.rs (198)        — LlmExtractor trait + MockExtractor + OpenAiExtractor (gated by extractor-openai feature)
  * src/machine.rs (250)          — handle_step async fn, 11-arm state machine
  * src/machine_helpers.rs (171)  — per-state helper fns
  * src/machine_tests.rs (103)    — 7 FSM tests with MockExtractor

Each TS branch from chat-onboard.ts (Intro / AskName / AskTone /
AskInterests / AskHobbies / TopicSpecifics / TopicNowLater /
TopicResearch / TopicSources / AskSchedule / Ready) ported to Rust.
Russian-language responses preserved verbatim. Topic queue stored in
persona_patch.__topic_state for caller round-tripping.

machine.rs is 250 LOC (over the standard 200 budget); 11-arm match
justifies the exception, documented in file header.

## Serve binary (task #6)

New files:
  * src/persona_merge.rs (85)     — JSON deep-merge helper
  * src/serve_telegram.rs (128)   — sendMessage / setWebhook / deleteWebhook HTTP helpers
  * src/serve.rs (162)            — axum Router, BuddyContext impl, run_serve
  * src/bin/kei-buddy.rs (rewritten, 120) — clap 4-subcommand CLI

Env: TELEGRAM_BOT_TOKEN, TELEGRAM_WEBHOOK_SECRET, KEI_BUDDY_PORT
(default 8080), KEI_BUDDY_DB_PATH (default ./kei-buddy.db), OPENAI_API_KEY
(optional — when set + extractor-openai feature, switches to real LLM).

axum + tracing-subscriber gated behind `serve` feature (default ON). Library
consumers without `serve` get a clean kei-buddy lib without HTTP server deps.

## Verify-before-commit

  * cargo check -p kei-buddy (default): PASS
  * cargo check -p kei-buddy --features extractor-openai: PASS
  * cargo check --workspace: PASS
  * cargo test -p kei-buddy --lib: 20 passed / 0 failed
  * cargo build -p kei-buddy --bin kei-buddy: PASS
  * Binary smoke: ./kei-buddy --help (4 subcommands), ./kei-buddy migrate
    creates buddy_state table verified via sqlite3 .tables

## Follow-up (deferred, non-blocking)

  * Wire OpenAiExtractor in run_serve when OPENAI_API_KEY set
    (currently always MockExtractor — smoke-only, no real LLM yet)
  * proposeTopicSources path needs real LLM call (MockExtractor returns empty)
  * Schedule timezone fallback map for "Москва"/"Bali" etc — currently
    fully delegated to LLM prompt
  * End-to-end Telegram integration test — requires real bot token
2026-05-12 14:21:33 +08:00
Parfii-bot
b5da1940e1 feat(kei-tts + kei-stt): TTS/STT abstractions with 4+3 backends
Two parallel atomars in the kei-buddy phase-1 plan. Mirror each other's
architecture: trait + feature-gated backend modules + env-driven dispatch
+ wiremock tests for HTTP backends + subprocess-error test for local.

## kei-tts (text-to-speech)
LOC: 959 across 15 files (largest src/lib.rs 121).
Trait `TtsBackend` + 4 backends behind feature flags:
  * elevenlabs — POST api.elevenlabs.io/v1/text-to-speech/{voice}/stream
  * openai     — POST api.openai.com/v1/audio/speech (tts-1, tts-1-hd)
  * google     — POST texttospeech.googleapis.com/v1/text:synthesize
                 (Wavenet voices, base64 audioContent)
  * piper      — local subprocess to piper-tts binary, raw PCM out
Default features: ["piper"]. all-backends feature gates the rest.
`from_env()` reads KEI_TTS_BACKEND (default piper). Returns Box<dyn TtsBackend>.
Tests: 9 passed (env routing + 3 wiremock backends + piper subprocess error).

## kei-stt (speech-to-text)
LOC: 935 across 13 files (largest whisper_local.rs 181).
Trait `SttBackend` + 3 backends:
  * whisper-local  — subprocess to `whisper` CLI / faster-whisper,
                     reads JSON output, parses segments
  * deepgram       — POST api.deepgram.com/v1/listen (Token auth header,
                     raw audio body, parses words → Segments)
  * openai-whisper — POST api.openai.com/v1/audio/transcriptions
                     (multipart file + model=whisper-1 +
                      response_format=verbose_json)
Default features: ["whisper-local"]. all-backends gates the rest.
`from_env()` reads KEI_STT_BACKEND (default whisper-local).
Tests: 10 passed + 1 doc-test (env routing + 5 wiremock + 2 JSON parsers
+ 1 subprocess error + 1 auth-header check).

## Common architecture decisions
  * `with_base_url(url)` constructor on each HTTP backend for wiremock
    testability — same pattern as kei-llm-router and kei-notify-telegram.
  * `tempfile` crate added to kei-stt for whisper-local audio scratch.
  * `base64 = { version = "0.22", optional = true }` in kei-tts for
    Google's base64-encoded audioContent.

## Verify-before-commit (RULE 0.13 §)
  * cargo check -p kei-tts (default + all-backends): PASS
  * cargo check -p kei-stt (default + all-backends): PASS
  * cargo test -p kei-tts --features all-backends --lib: 9/0
  * cargo test -p kei-stt --features all-backends --lib: 10/0
  * cargo check --workspace: PASS

STATUS-TRUTH from both agents: shipped=functional, stubs=0,
behaviour-verified=yes.

## Follow-up (deferred, non-blocking)
  * Real backend verification needs API keys for ElevenLabs / OpenAI /
    Google / Deepgram and piper-tts binary + .onnx model on PATH.
  * whisper-local language_detected always None — whisper CLI JSON
    schema differs across versions, parse heuristic to be added.
  * faster-whisper has different JSON schema from openai-whisper;
    current parser covers openai-whisper convention only.
2026-05-12 13:47:35 +08:00