Skip to content

feat(vault): seat_group cache columns + retrofit migrations#3

Open
Jakegogo wants to merge 43 commits into
develop-1.0.3from
feature/seat-group
Open

feat(vault): seat_group cache columns + retrofit migrations#3
Jakegogo wants to merge 43 commits into
develop-1.0.3from
feature/seat-group

Conversation

@Jakegogo

Copy link
Copy Markdown
Contributor

Part of the seat_group (席位组 / OAuth account pool) feature.

managed_virtual_keys_cache adds seat_group_id/group_accounts/routing_config; extra preserved across sync (fence test).

🤖 Generated with Claude Code

Jakegogo and others added 30 commits June 25, 2026 14:20
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
[aikey-cli] skip claim/delivery for group VKs during sync (no static material →
was a spurious 403 + "0 downloaded"); project seat_group_id + parsed
group_accounts into the internal team-records emit for the /user web.

Refs:
- CLI sync group-VK delivery 403 bugfix: https://github.com/aikeylabs/workflow/blob/3eb36d250c0f02ed28ba4753b6fa468d1fcf1b59/CI/bugfix/2026-06-26-cli-sync-group-vk-delivery-403.md
- seat-group / OAuth account pool requirement spec: https://github.com/aikeylabs/workflow/blob/3eb36d250c0f02ed28ba4753b6fa468d1fcf1b59/CI/requirements/2026-06-23-oauth-account-pool.md
… group-VK fixes

[aikey-cli] connectivity probe handles group VKs — empty VK-level provider_code
falls back to supported_providers, and group VKs are no longer skipped on absent
local ciphertext (material is proxy-side via channel ③).

Refs:
- group-VK connectivity-test 503 bugfix: https://github.com/aikeylabs/workflow/blob/9dbdc448e094f7719d9cceb2e4138fe68e015276/CI/bugfix/2026-06-26-vault-test-group-vk-and-error-code.md
- seat-group / OAuth pool requirement spec: https://github.com/aikeylabs/workflow/blob/9dbdc448e094f7719d9cceb2e4138fe68e015276/CI/requirements/2026-06-23-oauth-account-pool.md
… loopback probe hijack

[aikey-cli] group VKs carry no local key material by design — exempt them in
`aikey use` and key_material_reachable (was a misleading "key not delivered");
by-alias VK cache lookup now tries GROUP columns first (seat_group_id was always
None on the alias path); loopback probes bypass any external HTTP proxy
(NO_PROXY 127.0.0.1) so a non-loopback proxy can't hijack 127.0.0.1 to the wrong
aikey-proxy → false 401 (affects all VKs behind such a proxy).

Refs:
- group-VK + loopback-hijack + path-strip root-cause chain (§78+): https://github.com/aikeylabs/workflow/blob/6306efcd67b597c44e3c87a0e16da61bd6bd9514/CI/bugfix/2026-06-26-vault-test-group-vk-and-error-code.md
- seat-group / OAuth pool requirement spec: https://github.com/aikeylabs/workflow/blob/6306efcd67b597c44e3c87a0e16da61bd6bd9514/CI/requirements/2026-06-23-oauth-account-pool.md
…s / test names / trial migrate)

[aikey-cli] comment-only seat-group → oauth-group cleanup (field names already
renamed).

Refs:
- oauth_group rename plan (旧→新 mapping, P1–P7): https://github.com/aikeylabs/roadmap20260320/blob/d5280ecb78d50d2539da97123d8b9a6957e74eb2/技术实现/阶段7-企业生产版/20260626-oauth_group-重命名重构-分阶段实施计划.md
[aikey-cli] connectivity persistence/runtime updates

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… web double-slash white-screen)

resolve_browse_base_url now trims a trailing slash from both the AIKEY_WEB_URL
override and the control_url fallback. A stored control_url like
`http://host:3000/` joined with the leading-slash deep-link path `/go/<alias>`
produced `http://host:3000//go/overview`; the browser reads the leading `//` as
a PROTOCOL-RELATIVE URL (`http://go/...`) and history.replaceState rejects it
with a cross-origin SecurityError → SPA white-screen. Normalizing at READ time
also fixes installs that already stored a trailing-slash control_url, with no
re-login or migration. This is the root-cause fix; aikey-control's
collapseLeadingSlashes is the front-end defense-in-depth. Adds regression test.

Refs:
- ak web double-slash SecurityError bugfix: https://github.com/aikeylabs/workflow/blob/2b72651400abb27b90684b500d736fa231c68d16/CI/bugfix/2026-06-30-ak-web-double-slash-securityerror.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ce of truth

[aikey-cli] rework merge_group_accounts_live so login_status/current_routed reflect the unified routed pick

Refs:
- Bugfix: route-drift (part 2, split-truth → poolroute): https://github.com/aikeylabs/workflow/blob/c957d8ae791377abba7b9300ab55cf1a61cdf5e3/CI/bugfix/20260701-分配引擎默认开启-修复加号后路由漂移.md
… usage audit

[aikey-cli] reboot PID-reuse lockout fix: pidfile-derived "running" states only produced when the configured port is actually held, so a leftover pidfile whose low PID got reused by a boot daemon falls to read-only OrphanedPort instead of locking the user out; +e2e reboot/PID-reuse cases

Refs:
- Bugfix: proxy orphaned lockout on reboot PID reuse: https://github.com/aikeylabs/workflow/blob/40eaa28ac5a7ed959fd7306f9c728b3fcc944e07/CI/bugfix/2026-07-01-proxy-orphaned-lockout-reboot-pid-reuse.md
…andoff, install entry unification

statusline: pool-account login-required line (reads proxy bypass state, offline-by-design); browser launch via rundll32 on Windows (cmd /c start splits URLs at '&')

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…degraded state surfaces to statusline

[aikey-cli] statusline surfaces a "control-plane sync degraded" row (reads proxy sync-health.json, renders live outage duration) + a group-login-required row at receipt-less exit points so a dead sync rail can't stay invisible

Refs:
- Spec: control-plane SyncRail framework design: https://github.com/aikeylabs/roadmap20260320/blob/ed04caeca75a932f53921f7a3c6ef12b8fa95780/技术实现/update/20260703-控制面同步框架SyncRail-技术方案.md
- Spec: group routing-override self-heal persistence + settings linkage: https://github.com/aikeylabs/roadmap20260320/blob/ed04caeca75a932f53921f7a3c6ef12b8fa95780/技术实现/update/20260703-组路由override自愈持久化与settings联动.md
- Spec: OAuth group-member login prompt (CLI + login_url): https://github.com/aikeylabs/roadmap20260320/blob/ed04caeca75a932f53921f7a3c6ef12b8fa95780/技术实现/update/20260703-OAuth组成员登录提示-CLI显示与login_url.md
…pe 503, pool cooldown persistence, chk_mpb_target guard

[aikey-cli] aikey service status / aikey web status commands + local-server probe; doctor/project feature coverage; account cache prune; README (en/zh)

Refs:
- Requirement: aikey service-status command: https://github.com/aikeylabs/workflow/blob/fe705f43504cb9eaf9d50516984561dce71d37bd/CI/requirements/2026-07-03-service-status-command.md
…egradation

[aikey-cli] group-VK usability by routed-account login state (RoutedState: LoggedIn/NeedsLogin/NoCandidate → active/needs_login/inactive, 待登录防呆); help-text reorg + `aikey status`; commands_service status_summary fix

Refs:
- Spec: 组VK待登录状态防呆: https://github.com/aikeylabs/roadmap20260320/blob/845685224f712c9c1e511f13f8d32d3032b8d567/技术实现/update/20260703-组VK待登录状态防呆.md
…+ cross-app menu fallback

[aikey-cli] Kimi CLI shell integration; codex probe-model self-healing (codex_last_model, self-heals across codex-cli upgrades); unified local-origin console; kimi receipt observability events

Refs:
- Bugfix: kimi-config-hooks-duplicate-key: https://github.com/aikeylabs/workflow/blob/dceaf38d54b507bfe2145c48fd3713aeea4f4ac9/CI/bugfix/2026-07-04-kimi-config-hooks-duplicate-key.md
- Spec: web统一origin本地网关: https://github.com/aikeylabs/roadmap20260320/blob/15c9ff4cb3ddafaf06a960c1fd1e02a344bda2cf/技术实现/update/20260703-web统一origin-本地网关方案.md
/v1/auth/cli/login/exchange returns {access_token,refresh_token,...} with no
status field; the CLI reused the poll DTO (status required) so serde failed with
'missing field status' and the copy-paste fallback login never wrote the vault.
Parse exchange with a dedicated statusless DTO, normalized to approved
client-side. Server protocol unchanged.

Bugfix: workflow/CI/bugfix/2026-07-06-cli-login-token-exchange-missing-status.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codex into OAuth account pool: mechanism + dynamic scheduling + pricing/login fixes
…ervice path)

launchd / systemd / the new Windows ScheduledTask all run exactly
`aikey proxy start --foreground` with no TTY, but this dispatch only
consulted the TTL-gated session cache (try_get) before falling into the
interactive prompt — so a boot-after-idle service start hung/errored
instead of unlocking. L9 fix A (try_get_unattended, 2026-06-11) was wired
into ensure_proxy_for_use only; this extends the same no-TTY raw-store
read + verify-then-invalidate discipline to the explicit command, and
fails fast with actionable guidance when no credential source exists
(previously the no-TTY prompt could hang a service task forever).

Env vars (AIKEY_MASTER_PASSWORD / AK_TEST_PASSWORD) keep precedence;
interactive behavior is byte-identical.

Found by 2026-07-07 Windows/Mac parity audit (P1-1 leg).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dows/Mac parity (alpha4)

[aikey-cli] binding-material guard + bind audit (WARN on write-fail); web unlock-state CLI side; profile-activation / ui-select / shell-integration adjustments

Refs:
- Spec: 绑定材料守卫 + Web 解锁态全量 sync: https://github.com/aikeylabs/roadmap20260320/blob/6dfff14/技术实现/update/20260706-绑定材料守卫与Web解锁态全量sync.md
- Designpattern: web-read-triggers-nonblocking-sync: https://github.com/aikeylabs/workflow/blob/260d6d4c52036d36c12bc8e360ad4861bc7d59cd/CI/designpattern/web-read-triggers-nonblocking-sync.md
Two unguarded stdin reads in `aikey login` hung forever when the process
was spawned without a terminal (script / service / web bridge):

1. The Control Panel URL prompt — now behaves like json_mode with no
   TTY: takes the default and says so, instead of reading a silent pipe.
2. The post-expiry "Paste token" fallback — the poll loop runs the full
   server-issued expires_in window (~1h), then `if !json_mode` dropped
   into read_line with no TTY check. This is the exact shape of the live
   `aikey login` process found hung >1h on the Windows box. Now gated on
   stdin being a terminal; the existing non-interactive error path
   ("Use --token for non-interactive login") takes over.

Parity audit 2026-07-07 P2-5. Interactive behavior unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dows/Mac parity (alpha4)

[aikey-cli] app install + watch command adjustments (Windows/Mac parity)
Live incident (2026-07-07, Windows box): a proxy started from an
admin/SSH (elevated) session is healthy, with pidfile + sidecar meta
correctly naming it — yet every NORMAL console's status /
ensure-running / wrapper preflight reported "orphaned (port held by
something we cannot manage)" and refused to route. Reproduced 100%:
same second, same pid — elevated probe says running(healthy),
Medium-IL probe says orphaned. Any customer who installs from an admin
PowerShell hits this on every regular terminal afterwards.

Root cause: Windows process_alive treated EVERY OpenProcess failure as
"process dead", but ERROR_ACCESS_DENIED means "exists, no permission"
(an elevated process's DACL grants only Administrators+SYSTEM; the
filtered token's Administrators group is deny-only). The unix branch
already handles the analogous EPERM as alive — the Windows port simply
dropped that semantic. Layer 1 then took classify_dead_pid → port held
by the "dead" pid → OrphanedPort/PortHeldByExternal, whose hint sent
users hunting a nonexistent rogue listener.

Fix, in three aligned pieces:
- process_alive (Windows): ERROR_ACCESS_DENIED → alive (EPERM parity).
- New proxy_proc::process_open_denied() — one neutral interface, per-OS
  bodies (EPERM / ERROR_ACCESS_DENIED) — so the decision tree can tell
  "identity UNVERIFIABLE (permission)" from "verified NOT ours".
- New OrphanReason::OwnershipUnverifiablePermission + quorum path:
  when identity is unverifiable by permission but the readable evidence
  agrees (meta parses ∧ meta.pid == pidfile pid ∧ port owner == pid ∧
  /health 200) classify Running — status shows the truth and preflight
  routes again. Anything short of the quorum → OrphanedPort with an
  elevation-aware hint (manage/stop from an elevated terminal, then
  `aikey proxy start` here), never the misleading external-holder text.
  birth_token stays uncompared (that is the very permission denied);
  kill paths remain physically blocked by the same OS permission, so
  the weaker classification cannot make Layer 2 signal a stranger.

Fences: hint-wording pins, quorum-miss classification, unix EPERM
alignment probe against pid 1, open-denied-false-for-self.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… rules

The regression pins from the 2026-07-01 fix caught it immediately: the
new open-denied path classified a recycled PRIVILEGED pid (init/launchd
after reboot — exactly an open-denied pid) with a FREE port as
OrphanedPort, re-introducing the start/stop lockout. Rework
classify_permission_denied_pid to be port-occupancy first, mirroring
classify_alive_unowned_pid: port free → Crashed (recoverable); held by
a third pid → PortHeldByExternal naming the real listener; only when
the denied pid itself holds the port does the elevated-proxy quorum
run. Fences updated + new arm test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lateral sweep of the 2026-07-07 window-flash class: this was the only
in-tree spawn that both omits window suppression and creates a
LONG-LIVED console-subsystem child (aikey-proxy). If the spawning aikey
process ever runs console-less (web-bridge / doctor auto-restart /
service contexts), the proxy would materialize a persistent terminal
window on the desktop. CREATE_NO_WINDOW composes with the existing
CREATE_NEW_PROCESS_GROUP and — unlike the deliberately-rejected
DETACHED_PROCESS — keeps handle inheritance, so the stderr→startup-log
redirection is unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lateral sweep of the P2-5 hang class (unguarded io::stdin().read_line on
a silent-but-open stdin) found two live HANG-RISK sites; both now bail
cleanly when stdin is not a terminal:

1. ui_select fallback_select / fallback_multi_select — the fallback is
   entered precisely when stderr is NOT a tty, then read stdin
   unconditionally. A scripted `aikey use` / `aikey auth login` with no
   explicit argument hung forever. Returns Cancelled with an actionable
   hint; piped-selection was never a supported contract (all documented
   automation passes explicit args, and interactive pickers elsewhere
   are already gated on stdin tty — see `aikey add`).

2. prompt_display_identity — the device_code OAuth flow reaches this
   prompt with no earlier tty-gated read to fail first. Skips (display
   name is optional; --alias remains the non-interactive path).

Found by the 2026-07-07 lateral sweep; interactive behavior unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The vault deliberately has no migration ledger — schema convergence =
replaying the whole idempotent baseline (CREATE TABLE IF NOT EXISTS /
guarded ALTER / INSERT OR IGNORE). Several of those statements are real
write attempts that take SQLite's write lock even when they no-op, and
the pre-dispatch hook ran the replay on EVERY command. Humans never
noticed; the web console's `_internal` bridge children (several per
vault-page load + background polls) queued on that write lock — live
Windows measurements: 52ms solo vs 1.2-3.3s under normal background
concurrency, 2.7-7.2s at the endpoint, i.e. "the vault page crawls".

The schema can only change when the binary changes, so convergence is
now once-per-build instead of once-per-command:

- config['schema.replayed_by'] records "<version>:<git revision>" of the
  build that last replayed (revision included so dirty dev builds
  sharing a version string still re-replay after rebuild).
- Pre-dispatch does a READ-ONLY probe of that marker (no write lock);
  match → skip, anything else (legacy vault, other build, unreadable) →
  full replay + stamp. First contact of every existing install replays
  once and then fast-paths.
- `aikey db upgrade` still force-replays unconditionally (the manual
  self-heal escape hatch) and stamps; `aikey db rollback` clears the
  marker so the next command re-converges, preserving pre-marker
  semantics for same-binary rollbacks.

Trade-off (user-approved 2026-07-07): content self-heal drops from
every-command to once-per-build; hand-damaged vault rows between
upgrades now need `aikey db upgrade`. Installer stays responsible for
server DBs only — vault stays CLI-owned because it doesn't exist at
install time and binaries can arrive without the installer (online
update / rollback / manual replacement).

Fences: marker lifecycle (replay → SkippedFresh → foreign-marker replay
→ clear re-arms) + legacy no-marker vault converges once then skips.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Michael and others added 13 commits July 7, 2026 02:25
…r comment

`aikey env` stopped listing the codex/kimi configs it had injected. Root
cause is a reader/writer truth-source split: the injectors migrated to
structural toml_edit writes that emit NO `# BEGIN aikey` marker comments
(kimi keyed on the hook-command tail, codex_merge/codex_remove keyed on
our keys), but injected_provider_toml_paths still detected by marker
text. Live trigger: codex CLI re-serializes config.toml on its own
writes and drops ALL comments — on 2026-07-06 it washed out the legacy
marker while every aikey key survived, and the listing went blank.

Detection now keys on the same semantic identity the REMOVE paths use:
- codex: codex_content_has_aikey mirrors codex_remove's predicates
  ([model_providers.aikey] / model_provider=="aikey" / openai_base_url
  ending in /openai); a lockstep test pins the equivalence so display
  ("wired") and `aikey unuse` ("what gets stripped") can never disagree
  again.
- kimi: KIMI_HOOK_COMMAND_MARKER (the established semantic key).
- AIKEY_BEGIN still honored for legacy never-rewritten files.

Verified live against the real ~/.codex/config.toml that reproduced the
report. Note: write/remove paths were already structural — this was a
display-only blindness, no stale-config risk.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Completes the 2026-07-07 marker-blindness fix class for the kimi side.
Three readers each had their own AIKEY_BEGIN comment-marker checks, which
go blind once kimi CLI re-serializes its config (dropping all comments):

- `aikey env` listing (fixed in the previous commit, now routed through
  the shared predicate),
- `kimi_status` / `aikey statusline status` — reported "not wired" on a
  wired install,
- `uninstall_kimi_hook`'s gate — the real correctness bug: it returned
  NothingToRemove and SKIPPED removal entirely while the hook stayed
  wired (the structural removal underneath keys on the hook-command
  tail and would have worked fine).

New kimi_content_has_aikey (hook-command tail + both legacy markers) is
the single truth source for all three; kimi_status gains a pure core
(kimi_content_status) so the semantics are unit-testable. One test
guards the shared predicate plus the exact-command leg.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n WAL

Live Windows regression of the marker fix itself: opening a WAL database
with SQLITE_OPEN_READ_ONLY depends on adopting the -shm mapping — the
same fragile step behind the Go readers' CANTOPEN(14) class. The RO
probe failed routinely on the live box, every command fell through to
the full replay, and the write-lock convoy came right back (measured
with the marker binary deployed: `_internal rules` median 1.5s, min
53ms ≈ solo-replay cost — the fast path was never taken, while `aikey
version` held a rock-steady 25-31ms floor).

Probe now uses a default-mode connection: no -shm adoption, still
SELECT-only, and WAL readers never block on writers so it remains
contention-free; busy_timeout(1s) guards the non-WAL edge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two fixes from the 2026-07-07 encoding sweep (zh-CN Windows focus):

1. New crate::strip_bom applied at the three read→serde sites
   (install-state.json via detect_edition, project config, global
   config). Shipped PS 5.1 trial installers wrote install-state.json
   WITH a UTF-8 BOM; serde rejects BOM at byte 0 and the `.ok()?`
   swallowed the error — a whole trial install silently read as "not
   installed". Writers are being fixed, but BOM'd files already exist on
   customer machines, so readers must tolerate them forever. Regression
   test: detect_edition_tolerates_utf8_bom.

2. H2 guard in the PowerShell $PROFILE wiring: never byte-append UTF-8
   onto a profile that isn't valid UTF-8 (UTF-16LE profiles are common on
   PS 5.1 via `>` redirection). Appending corrupted the file — PS decodes
   it all as UTF-16, the block turns to garbage, every new session can
   throw — and since the marker check can't read UTF-16 either, repeated
   runs kept appending. Now fails safe with a one-line conversion command
   and re-run guidance. (Full decode-splice-rewrite is a follow-up; the
   installer's Get-Content/Set-Content path was already safe.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ot sync

THE vault-page latency root cause, pinned by startup profiling on the
live Windows box: `std::thread::spawn` itself cost ~1.2s there —
endpoint AV (Norton) intercepts OS thread creation. Every short-lived
`_internal` bridge child called try_background_snapshot_sync, whose
in-process SYNC_IN_PROGRESS flag is useless across processes, so every
bridge call paid the 1.2s BEFORE the sync's own sync_version fast-check
could even run. (Earlier suspects — schema replay write-lock convoy,
RO-probe WAL failure — were contributors at most: profiler showed
7ms→1683ms across this single call.)

Fix per user decision (2026-07-07): KEEP the implicit async sync — it
exists so OAuth-pool material changes propagate promptly — and gate the
thread spawn on a cross-process debounce file
(~/.aikey/run/snapshot-sync-last-attempt, 30s window). Hot path is a
sub-ms stat+read; one process per window pays the spawn and runs the
existing sync_version-compare logic unchanged. State file follows
enhancement-not-dependency: missing/corrupt/future-clock all read as
stale (sync runs); write failures ignored.

Live A/B on the Windows box (deployed + verified):
- `_internal rules`: 1.1-1.5s per call → 57-103ms after first-in-window
- /api/user/vault/status+list concurrent (browser shape): 4.6-5.5s → 264-270ms

Fence: debounce lifecycle (fresh/suppressed/elapsed/corrupt/clock-skew).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
[aikey-cli] shell-integration adjustment (Windows/Mac parity)
…r worked there

Evidence-based revision of the earlier "keep it for OAuth-pool
freshness" decision, with the freshness objection factually resolved:

1. A bridge child exits ~50ms after dispatch, killing the detached sync
   thread before its first HTTP round-trip completes — the implicit
   sync from `_internal` has NEVER delivered a sync. It only paid the
   thread-creation cost: ~1.2s under endpoint AV (Norton, profiled
   live), i.e. the entire vault-page latency.
2. Web freshness is genuinely delivered by the server-side explicit
   trigger (CRUDHandlers.triggerBackgroundSnapshotSync → `_internal
   query snapshot_sync`, which runs the sync inside the handler and
   completes) — unaffected. Interactive commands keep the implicit
   thread+debounce path.
3. A detached worker PROCESS was built, measured, and rejected: AV
   taxes spawning our unsigned binary the same way (~1.4s — the earlier
   "process spawn is ~30ms" datum was for signed cmd.exe and does not
   transfer), plus the parent inherited waits on the worker's 21s
   connect timeout when the team server is unreachable.

Live A/B on the Windows box after deploy:
- first-hit `_internal rules` (debounce cleared): 1.2-1.4s → 52-54ms
- cold-open status+list concurrent (the exact user scenario of opening
  the vault page after idle): 4.6-5.5s → 291-325ms

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
aikey use writes the shell hook into $PROFILE.CurrentUserAllHosts, but
the path was hardcoded as <home>\Documents\... — while PowerShell itself
derives $PROFILE from the FOLDERID_Documents KNOWN FOLDER, which can be
redirected (Parallels/VMware shared profiles, OneDrive folder
redirection, roaming profiles). On such machines the hook landed in a
profile PowerShell never reads: active.env vars silently reached no
shell, and the post-use hint (. $PROFILE.CurrentUserAllHosts) died with
CommandNotFoundException (Parallels box, 2026-07-08: interactive
Documents = C:\Mac\Home\Documents).

Compat layer only (user decision): documents_known_folder() asks
SHGetKnownFolderPath(FOLDERID_Documents) — the same API PowerShell's own
resolution bottoms out in — and its two profile candidates go FIRST.
The hardcoded <home>\Documents candidates are kept unchanged as
fallbacks (API failure; hooks written by older builds must stay
findable). On non-redirected machines the known folder equals
<home>\Documents and dedup collapses the list to exactly the old one —
zero behavior change for machines that already work.

The value is read live per invocation on purpose: on Parallels it is
session-dependent (interactive sessions see the redirected path, SSH
sessions the local default); resolving in the same session the user's
shells run in is what makes write and read locations agree.

Verification: cargo check --target x86_64-pc-windows-gnu green; host
cargo test --lib shell_integration 149 passed; deployed to the Parallels
test box via make restart-personal-win (binary hash matched).
Interactive acceptance (aikey use -> new window sees OPENAI_API_KEY) is
tracked in workflow/CI/bugfix/2026-07-08-powershell-profile-documents-redirect.md.

Co-authored-by: Michael <jake@michael.MBP>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…n direct-jump + 信创 OS support

[aikey-cli] main.rs adjustment
…v1-128k default

[aikey-cli] moonshot-v1-128k default (8k breaks kimi-cli: system prompt ~12.4K > 8192); commands_account/proxy + profile-activation test; provider_registry.yaml

Refs:
- Spec: moonshot 默认模型 8k→128k: https://github.com/aikeylabs/roadmap20260320/blob/051ecf5/技术实现/update/20260708-moonshot-默认模型-8k-改-128k.md
…flight) + egress two-lane refinement

[aikey-cli] proxy lifecycle fixes — early-SIGTERM kills startup, port-drift blocked by OrphanedPort preflight, WARN on drift-meta write-fail; proxy status shows start time

Refs:
- Bugfix: early-sigterm-kills-proxy-startup: https://github.com/aikeylabs/workflow/blob/8a922c0/CI/bugfix/2026-07-08-early-sigterm-kills-proxy-startup.md
- Bugfix: port-drift-blocked-by-orphanedport-preflight: https://github.com/aikeylabs/workflow/blob/8a922c0/CI/bugfix/2026-07-08-port-drift-blocked-by-orphanedport-preflight.md
- Spec: proxy-status 显示启动时间: https://github.com/aikeylabs/roadmap20260320/blob/1b5f896/技术实现/update/20260708-proxy-status-显示启动时间.md
…r; Windows hook fixes

[aikey-cli] warn (stderr) when CLI configs still route through aikey but the hook/env channel is unwired (codex would fail on missing OPENAI_API_KEY); hook.bash bootstrap + hook.ps1 ASCII fence; shell_integration unix/windows + hook_op

Refs:
- Spec: hook 接线可见性升级: https://github.com/aikeylabs/roadmap20260320/blob/ea17cea9b20a733104d868098bfa1a1953b15864/技术实现/update/20260710-hook接线可见性升级.md
- Requirement: hook-wiring-visibility: https://github.com/aikeylabs/workflow/blob/847b502ac807ed85ddb7e051328a95120fef7566/CI/requirements/2026-07-10-hook-wiring-visibility.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants