Commit Graph
387 Commits
Author SHA1 Message Date
Baokaiandjlcbk 040920b10d feat(claude-code-plugin): 为短生命周期 Coding 工具增加本地 Pending Queue (#2421)
* feat(claude-code-plugin): add local pending queue for offline resilience

When the OpenViking server is temporarily unreachable, write operations
(addMessage, commitSession) now serialize payloads to a local pending
queue at ~/.openviking/pending/. On the next session-start, the queue
is replayed when the server is healthy again.

Key changes:
- New pending-queue.mjs module with enqueue/replay/cleanup/dedup
- Health gate moved after transcript parsing (auto-capture, subagent-stop)
- Retryable failures (network/5xx/408/429) auto-enqueue locally
- Non-retryable failures (401/403/404/422) warn and skip
- Atomic file writes via temp+rename, restrictive permissions (0o700/0o600)
- SHA-256 prefix-based dedup to avoid duplicate queue entries
- Replay stops on first retryable addMessage failure to preserve ordering
- Subagent commit intent generated even when all turns go to pending queue

Addresses reviewer feedback on PR #2421.

* test(claude-code-plugin): cover pending queue replay

---------

Co-authored-by: jlcbk <jlcbk@users.noreply.github.com>
2026-06-12 11:33:06 +08:00
marchpureandhaoxingjun 2dbee8e33d add OpenViking read tools to OpenClaw plugin (#2551)
* add ov_list tool to openclaw plugin

* declare ov_list in OpenClaw plugin manifest

* add OpenViking read tools to OpenClaw plugin

---------

Co-authored-by: haoxingjun <haoxingjun@bytedance.com>
2026-06-11 23:51:47 +08:00
Qin Haojie e06671b351 feat(session): 将 session 存储到 user 命名空间 (#2556)
* feat(session): store sessions in user namespace

* fix(session): tolerate legacy commit body fields
2026-06-11 14:33:00 +08:00
Zayn Jarvis c780c8336e Add ov-add-paper skill example (#2548)
* Add ov-add-paper skill example

* Harden ov-add-paper ingestion

* Keep ov-add-paper hardening in skill

* docs: simplify ov-add-paper ingest flow
2026-06-10 22:17:46 +08:00
Mingjian Que ba8c8ec058 fix(openclaw-plugin): stop capturing tool placeholders (#2534) 2026-06-10 16:20:34 +08:00
Qin Haojie a6fc0424bc fix(session): apply memory type policy whitelist (#2530)
* fix(session): apply memory type policy whitelist

Restore top-level memory_types filtering for session memory extraction and validate it against enabled registry schemas. Ensure initialization and peer-aware smoke coverage honor the whitelist.

* fix(session): scope session skills to execution memory policy

* refactor(session): remove per-commit memory policy
2026-06-10 14:54:24 +08:00
Qin Haojie 669f379591 fix(test): anonymize openclaw auto-recall fixture (#2541) 2026-06-10 14:39:57 +08:00
Mingjian Que 74102b78f0 fix(openclaw): use user namespace for experience recall (#2537) 2026-06-10 14:13:53 +08:00
Mingjian Que 5854dbfdc2 Restrict auto recall to transformContext assemble (#2532) 2026-06-10 14:08:10 +08:00
Mingjian Que 790daaf885 feat(openclaw): inject agent experience memories (#2281) 2026-06-10 10:43:41 +08:00
Evo 58ff0290b2 fix(opencode-plugin): fall back to session mapping when memcommit gets empty session_id (#2412) (#2488)
memcommit used `??` so an empty-string session_id bypassed the
current-session fallback and hit the 'No OpenViking session' error.
Match sibling memsearch's truthiness fallback so '' falls back like a
missing arg (session_id is always a non-empty UUID when set).
2026-06-08 20:47:50 +08:00
Evo 43c7f456e0 fix(openclaw-plugin): drop duplicate viking://user/memories find in auto-recall (regression from #2236) (#2484) 2026-06-08 20:01:55 +08:00
MaojiaSheng 6fe739b766 fix(skills): update skills example (#2495)
* fix(skills): update skills example

* fix: remove unexpected dir
2026-06-08 12:02:56 +08:00
t0saki 7237ac611c fix(plugins): skip shell aliases when wrapping extra launch commands (#2471)
Adding a shell-alias name (e.g. `cc` from `alias cc=claude`) to
OPENVIKING_CC_WRAP_EXTRA / OPENVIKING_CODEX_WRAP_EXTRA broke the wrapper:
bash expands the alias mid-eval and clobbers the base `claude`/`codex`
function (so `command cc` ends up running the C compiler), while zsh
aborts with a parse error on every shell start. Guard the wrapper-defining
loop to skip names that are already shell aliases — an alias already
routes through the base wrapper once it expands, so it needs no function.
Also reject heads starting with `-`, which `alias`/`command` would
otherwise misparse as an option.

Also document the custom-launch-command feature and the alias guidance:
- 8 agent-integration docs (en/zh main + CDN cards): brief install note,
  plus two troubleshooting rows (wrapper-not-sourced, alias gap)
- claude/codex plugin READMEs (+ README_CN, which was missing the section
  entirely): wrap the real target command, never the alias name
2026-06-06 12:40:14 +08:00
t0saki 9a329c2e05 feat(plugins): wrap custom launch commands; align codex installer UX with claude (#2464)
Wrap extra launch commands besides `claude` / `codex`:
- wrapper.sh (both plugins): factor credential injection into a helper and
  read $OPENVIKING_{CC,CODEX}_WRAP_EXTRA — a ';'-separated list of extra launch
  commands (e.g. a custom alias `claude-w`, or a multi-word launcher `ccr code`
  / `aiden x claude`). Single-word entries always inject; multi-word entries
  inject only when the leading args match the sub-command, so other uses of
  that command pass through untouched. Portable to bash and zsh (manual
  parameter-expansion splitting, no unquoted word splitting).
- install.sh (both plugins): collect the list (interactive prompt or the
  $OPENVIKING_{CC,CODEX}_WRAP_EXTRA env var), normalize it, and persist it in
  the rc marker block; reuse the existing value on re-run.

Codex installer UX parity with the claude-code installer:
- TTY-aware colored step output (info/warn/err/ask/heading).
- Interactive ovcli.conf setup: reuse existing / choose self-hosted vs
  Volcengine Cloud / enter URL+key, written via node (no jq dependency) and
  merged so extra fields (account/user) are preserved.
- Degrades to non-interactive (existing config / env vars) when stdin is not
  a TTY (e.g. `curl | bash`). All codex-specific install logic (marketplace,
  config.toml, cache, hooks.json, .mcp.json rendering) is unchanged.

READMEs document wrapping extra launch commands.
2026-06-05 18:15:26 +08:00
Zayn Jarvis 6b3d261b61 docs: remove stale agent header references (#2462) 2026-06-05 17:27:07 +08:00
yangxinxin-7andClaude Sonnet 4.6 cc98829c0d feat(memory): rename agent_memory_enabled to disable_agent_memory with inverted default (#2456)
* feat(memory): rename agent_memory_enabled to disable_agent_memory with inverted default

Agent memory (trajectory/experience extraction) is now on by default.
Use `disable_agent_memory: true` in ov.conf to opt out.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(memory): add backward compat for deprecated agent_memory_enabled config field

Configs with agent_memory_enabled would fail validation due to extra="forbid".
Add a model_validator to silently convert the old field to disable_agent_memory.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* revert: remove unnecessary backward compat for agent_memory_enabled

No existing users, no migration needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(memory): keep agent_memory_enabled name, change default to true

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: gitignore integration test tmp dirs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: restore RUN_AGENT_MEMORY_TESTS guard for agent memory e2e

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 15:34:45 +08:00
Mingjian Que e5c8208dcb Make OpenClaw auto-recall timeout configurable (#2448) 2026-06-05 14:05:54 +08:00
Qin Haojie ff258768c2 feat(memory): 引入 User/Peer 记忆隔离模型 (#2236)
* feat(memory): introduce user and peer memory isolation

Unify agent-scoped memory behavior into user-owned memory spaces, add peer_id compatibility for session and retrieval paths, and wire memory_policy through session commit flows.

* feat(memory): align session identity around peer IDs

* feat(search): pass peer id through retrieval

* refactor(memory): remove agent identity from integrations

* fix(memory): isolate peer identity from self extraction

* fix(tau2): provision benchmark user configs

* fix(auth): allow admin keys to access data APIs

* fix(openclaw): enable peer memory policy for peer roles

* fix(openclaw): resolve sender for peer recall

* refactor(session): simplify memory extraction routing

* refactor(ov-cli): reduce formatting-only diff

* refactor(message): remove unused message helpers

* refactor(retrieval): simplify peer target resolution

* refactor(namespace): remove deprecated agent namespace policy

* fix(agent): propagate peer id through integrations

* fix(auth): align integration clients with api-key mode
2026-06-05 10:55:48 +08:00
MaojiaSheng 4405059c13 fix(cli): Select a configuration to use: (#2441)
1. user (http://localhost:1933)
  2. jiahui_test5
  3. serverless (https://api.vikingdb.cn-beijing.volces.com/openviking)
  4. devbox (http://10.37.194.3:1933)
  5. bak (https://api.vikingdb.cn-beijing.volces.com/openviking)
  6. minio (http://localhost:1933)

Enter    '<number>' to use a configuration (e.g. '2')
Or enter 'del<number>' to delete a configuration (e.g. 'del2')
Press Enter without input to cancel
[?2004h
> 
[?2004l

Cancelled. reports invalid skip
2026-06-04 20:15:37 +08:00
MaojiaSheng 57b2e2a00e fix(openclaw-plugin): authenticate auto-recall health precheck (#2384)
Reuse the OpenViking client for auto-recall prechecks so remote /health probes carry the same auth and tenant headers as normal requests, preventing false recall skips behind authenticated gateways.
2026-06-02 16:07:38 +08:00
Mingjian Que e42dd12741 docs(openclaw): document explicit memory writes (#2323) 2026-06-01 16:58:41 +08:00
EurakaxunandEurekaxun ab855a43b0 fix: add cjk-aware token estimation (#2348)
Co-authored-by: Eurekaxun <eurekaxun@163.com>
2026-06-01 14:12:27 +08:00
t0saki e8acebbb46 fix(cc-memory-plugin): record tool calls/results as structured tool parts (#2340)
* fix(cc-memory-plugin): record tool calls/results as structured tool parts

auto-capture.mjs and subagent-stop.mjs inlined tool_use input and
tool_result output into the message content (`[tool: NAME]\n{input}` /
`[tool result]`) and sent content-only, so OV stored each turn as a
single text part and could not separate calls from results. Tool results
were additionally dropped entirely (TOOL_RESULT_MAX_CHARS=0).

Emit structured `tool` parts (tool_id / tool_name / tool_input /
tool_output / tool_status) and send via parts-mode, which
lib/ov-session.addMessage already supports. tool_result blocks are
labelled with the matching call name (looked up by tool_use_id) and
their output is captured bounded to 2000 chars. The legacy inlined
`text` is kept solely to drive the unchanged capture heuristics
(length / keyword), so capture decisions are unaffected.

* chore(cc-memory-plugin): bump version to 0.2.2

* fix(cc-memory-plugin): make installer update an already-installed plugin

install_modern() used marketplace add + plugin install, both no-ops on an
existing install — re-running the installer after a version bump left the
old cached copy in place. Detect the already-present case and use
marketplace update + plugin update instead, which re-sync the catalog from
source and apply the new version (restart required).
2026-05-31 18:03:56 +08:00
AutoCoder 9e99c8f7db Add URI to autorecall memories (#2321) 2026-05-30 16:42:58 +08:00
Mingjian Que edb82d56fd fix(openclaw-plugin): report memory_store zero extraction (#2307) 2026-05-29 19:46:51 +08:00
Evo 156afc4af7 docs(openclaw-plugin): list openviking_tool_result_read/search/list in plugin tool tables (#2058) (#2295)
* docs(openclaw-plugin): list openviking_tool_result read/search/list in tool table (#2058)

* docs(openclaw-plugin): 工具表补充 openviking_tool_result read/search/list (#2058)
2026-05-29 15:10:29 +08:00
Qin Haojie bb22bac435 fix(security): remove stale critical dependency locks (#2242) 2026-05-26 18:10:01 +08:00
AutoCoder 1d631cd38d Rename search tool to ov_search to avoid conflict with existing OpenClaw tool name (#2235) 2026-05-26 11:58:15 +08:00
chenjwandAiden 82225006fb Feat/searchable template (#2193)
* feat: replace searchable memory fields with embedding templates

Use memory-type embedding templates for vectorization, share template rendering with content serialization, and fall back to plain content when embedding rendering cannot be resolved.

🤖 Generated with [Aiden x Claude Code]

Co-Authored-By: Aiden

* fix(memory): rename role-id isolation config flag

Use role_id_memory_isolation_enabled consistently across config, handler, and tests so the rename matches current prepare_messages behavior.

🤖 Generated with [Aiden x Claude Code]

Co-Authored-By: Aiden

* auto-commit before eval 20260522_190730

* auto-commit before eval 20260522_194846

* auto-commit before eval 20260522_200235

* auto-commit before eval 20260522_200720

* update

* auto-commit before eval 20260525_013620
2026-05-25 13:57:26 +08:00
DuTao 96431c17b4 feat(skill): Add extracting skills from session commit processing (#2182)
* add skill extract

* add skill extract

* add skill extract

* add skill extract

* add skill extract

* add skill extract
2026-05-22 16:48:33 +08:00
LinQiang391andCursor 87039cac4c docs(openclaw): align plugin docs with ClawHub standard install experience (#2150)
Use explicit clawhub: prefix across all install paths (README, INSTALL, INSTALL-ZH, INSTALL-AGENT, SKILL.md) since bare specs resolve to npm on current OpenClaw. Restructure ClawHub README with Quick Start first screen, How It Works, Tools table, Data Flow and Privacy section. Move engineering details into collapsible section. Demote ov-install to fallback. Fix ov-install params, OpenClaw min version, and parameter table. Allow images in ClawHub bundle.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 20:15:23 +08:00
Yuan Shenheng a27c18eee9 fix(examples): wait before quickstart preview (#2167) 2026-05-21 17:11:32 +08:00
HaotianChen616 f39b030926 feat: externalize oversized session tool results (#2058)
Squashed commits:

- 28e175c8 feat: externalize oversized session tool results
- 73d6461d feat: coalesce OpenClaw tool results by turn
- 3bf4a0c2 fix: apply tool result config and filtered listing
- a5bc0582 [bugfix] extractNewTurnMessages会错误过滤掉没有text block的assistant toolCall,用[toolCall: toolName]占位符确保消息传入
- 1d76827b style: format tool result compression changes
- 495b5623 fix: preserve textless toolUse turns
- 399f2dc3 feat: expose tool result access tools
- 43b48dd7 fix: honor min preview chars for source reads
- 6eaaf54f fix: split aggregated tool result messages
- f8ad98c3 fix: hydrate tool outputs for extraction
- f9031458 fix: improve tool descriptions for tool result access tools
- ae3bcd33 fix: hydrate source-read tool outputs
- 8976f8ce test: add tool result compression bench cases
2026-05-21 14:36:10 +08:00
agent f62b640ee3 feat: add ov_dream OpenClaw skill for OpenViking sync (#2136)
* feat: add ov dream skill

* feat: ov dream

* feat: ov dream

* fix: bug

* fix

* fix

* docs: add ov dream openclaw install prompt

* docs: rename ov dream install guide

* docs: rename ov lite install guide

* docs: clarify ov lite install source

* docs: make ov lite install guide stable
2026-05-20 12:05:56 +08:00
CuSO41108andqin-ctx 1071b8bb53 fix(opencode): preserve pending messages on session error (#2094)
* fix(opencode): preserve pending messages on session error

* fix(opencode): preserve pending messages during error cleanup

* fix(opencode): simplify pending cleanup handling

---------

Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
2026-05-18 14:14:25 +08:00
LinQiang391andLinQiang391 933ece4acb docs(openclaw): use canonical OpenViking plugin package (#2099)
Co-authored-by: LinQiang391 <linqiang391@users.noreply.github.com>
2026-05-18 10:55:01 +08:00
LinQiang391andLinQiang391 8ec1e21a82 fix(openclaw): support npm plugin installs in setup helper (#2072)
Co-authored-by: LinQiang391 <linqiang391@users.noreply.github.com>
2026-05-15 17:50:52 +08:00
yepper 9ebfd59342 feat(metrics): add vikingbot feedback observability (#2037)
* feat(metrics): add vikingbot feedback observability

* fix(bot): align feedback observability contracts

* fix(metrics): decouple feedback collector bootstrap
2026-05-15 17:30:24 +08:00
t0saki 42484bf91d fix(plugin/codex): default-on assistant capture, recommend env vars over ov.conf for tuning (#2065)
Two related fixes to plugin tuning ergonomics:

1. `captureAssistantTurns` defaults to true (mirrors claude-code-memory-plugin).
   A memory plugin that only captures the user side of every turn extracts
   half the conversation and produces noticeably worse memories. Operators
   who want the old user-only behavior can still set
   `OPENVIKING_CAPTURE_ASSISTANT_TURNS=0` or `codex.captureAssistantTurns=false`.

2. README + agent-integrations docs (zh+en) now recommend `OPENVIKING_*`
   environment variables in shell rc as the primary way to tune the plugin.
   The previous docs claimed the tuning block lived in `ovcli.conf`, but
   `scripts/config.mjs` only reads `codex.*` from `ov.conf` — and `ov.conf`
   is server-scope, so per-machine plugin tuning doesn't belong there
   anyway. The legacy `ov.conf` path is acknowledged and kept working for
   backward compat, but de-emphasized.
2026-05-15 13:07:33 +08:00
Qin Haojie 77b604a641 fix(storage): 优化路径锁与语义刷新并发 (#2029)
* fix(storage): refine path lock semantic refresh concurrency

Use exact path locks for source commits, tree locks only for lifecycle and schema scopes, and coalesce derived semantic writes to avoid stale summary overwrites under concurrent resource and memory updates.

* chore: split benchmark changes into separate PR

* chore: keep semantic refresh design notes out of docs

* style: format lock changes

* fix: preserve resource lifecycle locks

* Revert "fix: preserve resource lifecycle locks"

This reverts commit d2fb274f85.

* fix(resource): simplify lifecycle locking

* fix(queuefs): consolidate semantic sidecar writes
2026-05-14 20:44:28 +08:00
Zayn Jarvis ace6efcbe2 fix(codex): derive ov session ids from codex sessions (#2039) 2026-05-14 19:20:05 +08:00
AutoCoderandClaude Sonnet 4.6 5271baff1b chore(openclaw-plugin): remove unused code and redundant guards (#2050)
Drop dead exports/helpers with no callers (sysEnv, waitForHealth, estimateTokens, etc.) and remove redundant conditions and stale comments.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 19:19:26 +08:00
Zayn Jarvis a7d27920cd fix(plugin/codex): simplify commit hook messages (#2036) 2026-05-14 13:04:01 +08:00
Zayn Jarvis aa93690d2f fix(plugin/codex): raise auto-recall score threshold (#2033) 2026-05-14 12:26:27 +08:00
t0saki 527d68d352 refactor(plugin/{codex,claude-code}): extract installer wrapper to checked-in file (#2026)
* refactor(plugin/codex): move shell wrapper to standalone rc file

The installer-emitted codex() wrapper had grown to ~60 lines of shell
function body inlined as a marker-delimited block inside the user's
~/.zshrc / ~/.bashrc. Every upgrade required the awk-strip-and-append
dance, which had a known edge case (rc with begin-marker but no
end-marker) we'd already had to harden against, and the inline noise
was hostile to anyone reading their own rc.

This commit switches to the standard pyenv / nvm / fnm pattern:

- Wrapper body lives in its own file at ~/.openviking/codex-plugin.rc.sh
  (path overridable via OPENVIKING_CODEX_WRAPPER_RC). Full overwrite on
  every install — no marker logic inside the wrapper file itself.

- The user's shell rc gets a single one-line source hook, still wrapped
  in marker comments for cleanup-on-uninstall:

    # >>> openviking-codex-plugin >>>
    [ -f "$HOME/.openviking/codex-plugin.rc.sh" ] && . "..."
    # <<< openviking-codex-plugin <<<

  Since the content of this block never changes across installs, the
  marker-replacement logic only triggers the legacy-cleanup path once
  when upgrading from a pre-rc-split install that inlined the full
  wrapper.

User-visible improvements:

- ~/.zshrc OV-plugin block: ~70 lines → 3 lines.
- `cat ~/.openviking/codex-plugin.rc.sh` shows the wrapper directly.
- Uninstall is just `rm ~/.openviking/codex-plugin.rc.sh` + delete the
  3-line block — no awk required.
- Upgrades touch the rc file at most once (to install the source hook);
  subsequent installs only rewrite the wrapper file.

Verified end-to-end: stale install with the old inline wrapper got the
3-line source hook substituted in place; `source ~/.zshrc && type codex`
showed the new wrapper loaded from the standalone file.

* refactor(plugin/codex): source wrapper from repo path, drop heredoc dance

Follow-up to the rc-split commit. Instead of embedding the wrapper body
as a heredoc inside install.sh and writing it to a copy under
~/.openviking/, the wrapper now lives as its own checked-in file at
examples/codex-memory-plugin/setup-helper/wrapper.sh. The user's shell
rc sources that file directly from the cloned plugin checkout (the path
the installer already manages via git fetch + reset --hard).

What this buys:

- Wrapper diffs are real diffs — code review sees `+ codex() { ... }`
  rather than `+ heredoc lines inside install.sh that produce
  ~/.openviking/codex-plugin.rc.sh`.
- No copy step in the installer means no installer code path for "did
  the user accidentally edit ~/.openviking/codex-plugin.rc.sh?" or "is
  the copied file in sync with what the installer would produce now?"
- Updates ride for free on `git pull` / the installer's existing
  fetch+reset. No "re-run installer to refresh the wrapper" step.
- Uninstall is just `rm ~/.openviking/openviking-repo` (or just leave it
  — the source hook will silently no-op when the file is gone, since
  it's gated with `[ -f ... ] && .`).

Installer shrinks from ~420 lines (with the inline heredoc) to ~340.
The wrapper is unchanged content-wise; this commit only moves where it
lives.

* refactor(plugin/claude-code): move shell wrapper to standalone rc file

The installer-emitted claude() wrapper had been inlined as a
marker-delimited block in the user's ~/.zshrc / ~/.bashrc. Every upgrade
required the awk-strip-and-append dance, the inline noise was hostile
to anyone reading their own rc, and there was a known footgun: if the
END marker got hand-deleted from the rc, the next install's awk-strip
would drop everything from the BEGIN marker to EOF.

Switch to the standard pyenv / nvm / fnm pattern, mirroring what the
codex-memory-plugin installer now does (see #2023):

- Wrapper body lives in its own file at ~/.openviking/claude-plugin.rc.sh
  (path overridable via OPENVIKING_CLAUDE_WRAPPER_RC). Full overwrite on
  every install — no marker logic inside the wrapper file itself.

- The user's shell rc gets a single one-line source hook, still
  marker-wrapped for clean uninstall:

    # >>> openviking claude-code memory plugin >>>
    [ -f "$HOME/.openviking/claude-plugin.rc.sh" ] && . "..."
    # <<< openviking claude-code memory plugin <<<

  Content is constant across installs, so the marker-replacement logic
  only triggers the legacy-cleanup path once (when upgrading from a
  pre-rc-split install that inlined the full claude() function body).

User-visible improvements:

- ~/.zshrc OV-plugin block: ~16 lines of wrapper body → 3 lines.
- Wrapper body is a real file you can `cat` / `diff` / restore from
  source control; no need to re-run the installer to inspect it.
- Uninstall: `rm ~/.openviking/claude-plugin.rc.sh` + delete the 3-line
  marker block.
- The END-marker corruption footgun is gone, since the marker block
  content is bytestring-stable and the awk-strip only runs when both
  markers are present anyway.

No behavior change to the wrapper itself (still pulls url/api_key from
ovcli.conf via jq).

* refactor(plugin/claude-code): extract wrapper to checked-in setup-helper/wrapper.sh

Squash-style follow-up to the previous rc-split commit on this branch:
now that the wrapper lives in its own file conceptually, just check it
in at examples/claude-code-memory-plugin/setup-helper/wrapper.sh and
have the user's shell rc source it directly from the cloned plugin
checkout. No copy step, no heredoc dance in install.sh.

Why this is better than the previous approach (wrapper body embedded as
a heredoc in install.sh, written to a copy in ~/.openviking):

- Wrapper is a real reviewable file. Diffs show `+ claude() { ... }`,
  not "+ heredoc lines that produce the wrapper".
- Updates ride on the installer's existing `git fetch + reset --hard`
  step — no separate "re-run installer to refresh the copy" path.
- One less source of truth (no $HOME copy that can drift from the
  installer's intent).
- Uninstall: `rm ~/.openviking/openviking-repo`; the source hook in the
  rc silently no-ops via `[ -f ... ] && .`.

The previous commit on this branch already shrunk the rc block from
~16 inline lines to 3 (marker + source hook + marker). This commit just
moves the wrapper body from "embedded in installer" to "checked into the
repo at a stable path", with no behavior change to the wrapper itself.
2026-05-13 22:02:51 +08:00
t0saki c73a32f270 fix(plugin/codex): allow empty api_key (unauthenticated local OV) (#2023)
* fix(plugin/codex): allow empty api_key (unauthenticated local OV)

Reported: with an ovcli.conf that has no `api_key` (typical local OV
without auth), the plugin would not start cleanly. Root cause: .mcp.json
ships with `bearer_token_env_var: "OPENVIKING_API_KEY"`, and when that
env var resolves to an empty string at codex launch (because ovcli.conf
has no key), Codex interprets it as "auth configured but not provided"
and falls back to its OAuth dance — which then fails against an OV that
doesn't speak OAuth.

Hook side is unaffected: scripts/config.mjs already gates the Bearer
header on `if (cfg.apiKey)`, so empty api_key → no Authorization header
sent → OV accepts in unauth mode. Verified end-to-end with auto-recall
against `http://127.0.0.1:1933` and an empty-key ovcli.conf.

Fix: at install time, detect whether ANY api_key is configured (env or
ovcli.conf) and conditionally render `.mcp.json` *with or without*
`bearer_token_env_var`:

  - api_key present → keep `bearer_token_env_var: "OPENVIKING_API_KEY"`
  - api_key absent  → drop the field entirely (Codex will then just hit
                      OV without Authorization and treat 200 as success)

Implementation uses node (already required) to read/edit the cached
.mcp.json as proper JSON rather than sed, so we don't have to worry
about field-position-dependent regexes.

Installer footer now also reports the resolved auth mode so the user
sees `MCP auth: Bearer (OPENVIKING_API_KEY)` vs `MCP auth: none
(unauthenticated)` at the end of the run.

env_http_headers stays in both modes — identity headers
(X-OpenViking-Account / User / Agent) are independent of auth and OV
accepts empty values (defaults to "default").

* fix(plugin/codex): support runtime OPENVIKING_CLI_CONFIG_FILE swap

Reported: setting OPENVIKING_CLI_CONFIG_FILE=ovcli-local.conf (a config
without api_key, for benchmark-memory isolation) and running codex fails
with:

  Environment variable OPENVIKING_API_KEY for MCP server 'openviking-memory'
  is empty

Two issues stacked on top of each other:

1. Codex 0.130 hard-fails MCP startup when bearer_token_env_var resolves
   to an EMPTY env var (confirmed empirically — not OAuth fallback, just
   a startup error).

2. The previous codex() wrapper exported `OPENVIKING_API_KEY=""` via the
   inline-prefix syntax `OPENVIKING_API_KEY="${...:-${...:-}}" codex`,
   which sets the variable to an empty string when no key is resolvable.
   So even my prior fix (don't render bearer_token_env_var when no key
   at install time) didn't help users who install with one conf and run
   with another via OPENVIKING_CLI_CONFIG_FILE.

Fix is two parts:

a) Build the env prefix dynamically into a bash array, skipping any
   OPENVIKING_* whose resolved value is empty. So an empty api_key
   produces no OPENVIKING_API_KEY at all in codex's env — neither
   set-to-empty nor set-to-something.

b) Have the wrapper re-render the cached .mcp.json's bearer_token_env_var
   on every codex launch based on the currently-active ovcli.conf. The
   idempotent fast-path skips writing when the desired state already
   matches. This makes swapping configs at runtime (typical benchmark
   isolation workflow) work without re-running the installer.

The wrapper now uses `env "${_env_args[@]}" codex "$@"` instead of the
inline-prefix form for the same reason — proper handling of conditional
env-var presence.

Manual setup snippets in README + docs (en/zh) updated to the same
empty-aware pattern; the cache-rendering bit is left to the installer-
emitted wrapper since it's noisy and only needed when actually swapping
configs.

Validated with synthetic test:

  ovcli-local.conf (no api_key)
    → env passed to codex: URL=..., ACCOUNT=..., USER=..., AGENT_ID=codex
      (no OPENVIKING_API_KEY at all)
    → cache .mcp.json rewritten to drop bearer_token_env_var

  ovcli.conf (with api_key)
    → env passed to codex: URL=..., API_KEY=..., ACCOUNT=..., USER=..., AGENT_ID=codex
    → cache .mcp.json rewritten to re-add bearer_token_env_var

  Idempotent: re-render with same hasKey state does not bump file mtime.

* fix(plugin/codex): wrapper also re-renders cache .mcp.json URL

Previously the codex() wrapper only re-rendered bearer_token_env_var
based on the active ovcli.conf, but the cached .mcp.json URL stayed
whatever was baked at install time. Result: swapping
OPENVIKING_CLI_CONFIG_FILE to a config that points at a different OV
server (e.g. localhost) would still hit the install-time URL —
typically the remote production OV — and fail auth.

Reported in testing:

  OPENVIKING_CLI_CONFIG_FILE=ovcli-local.conf codex
  # ovcli-local.conf: { "url": "http://127.0.0.1:1933" }
  # cache .mcp.json still says url=https://ov-dev.tosaki.top/mcp
  # Codex hits remote ov-dev with no bearer → 401 → "Not logged in" OAuth dance

Fix: the rewrite block now also patches s.url from the conf-resolved
URL (`${_ov_url%/}/mcp`, or `$OPENVIKING_MCP_URL` if explicitly set).
Same idempotent fast-path — only writes when something actually changed.

Tested both directions:
  ovcli-local.conf (no key, localhost)
    → cache .mcp.json: url=http://127.0.0.1:1933/mcp, no bearer field
    → env passed to codex: no OPENVIKING_API_KEY
    → /mcp: Auth: None, tools list populated
  ovcli.conf (with key, remote)
    → cache .mcp.json: url=https://ov-dev.tosaki.top/mcp, bearer present
    → /mcp: Auth: Bearer token, tools list populated
2026-05-13 21:40:30 +08:00
t0saki b0076110ae refactor(plugin/codex): switch MCP from local stdio to OV /mcp directly (#2022)
* refactor(plugin/codex): switch MCP from local stdio server to OV /mcp (http)

Codex 0.130 supports streamable-HTTP MCP servers with bearer auth via
`bearer_token_env_var` in `.mcp.json` (and per-header env binding via
`env_http_headers`). OpenViking server has exposed `/mcp` natively since
1.27, so the local stdio MCP middleman (`src/memory-server.ts` +
`servers/memory-server.js` + the npm-ci runtime bootstrap) is dead weight:
the model now gets a strictly larger tool set (search, store, read, list,
grep, glob, forget, add_resource, health — vs the previous recall/store/
forget/health) by talking to OV directly, and the plugin loses its only
build/dependency surface.

What changed

- `.mcp.json`: switched to `url` + `bearer_token_env_var: "OPENVIKING_API_KEY"`
  + `env_http_headers` for the multi-tenant identity headers. URL is a
  `__OPENVIKING_MCP_URL__` placeholder; installer renders it from ovcli.conf
  / `OPENVIKING_URL` at install time. API key never lands on disk in the
  cached .mcp.json — it's pulled from process env at codex launch.

- `setup-helper/install.sh`: resolves the OV /mcp URL (OPENVIKING_MCP_URL >
  OPENVIKING_URL/mcp > ovcli.conf.url/mcp > localhost), renders the
  .mcp.json placeholder into the cached copy, and appends a `codex()` shell
  function wrapper to the user's rc that promotes ovcli.conf fields into
  env vars before exec'ing codex (mirrors the claude-code-memory-plugin
  pattern; needed because Codex reads OPENVIKING_API_KEY from process env
  at MCP launch, not from any file).

- Deleted: `src/memory-server.ts`, `servers/memory-server.js`, `tsconfig.json`,
  `package.json`, `package-lock.json`, `scripts/bootstrap-runtime.mjs`,
  `scripts/runtime-common.mjs`, `scripts/start-memory-server.mjs`. Net
  ~2400 lines removed. Hook scripts remain zero-dep .mjs running on
  Codex's bundled Node 22.

- README + docs/{en,zh}/agent-integrations/04-codex.md: rewritten to
  describe the new architecture. The MCP tools list and protocol details
  are now referenced via a link to docs/{en,zh}/guides/06-mcp-integration.md
  rather than duplicated in the plugin docs.

- Plugin version: 0.4.1 → 0.5.0.

Validation

Verified end-to-end on Codex 0.130 against `ov-dev.tosaki.top`:

  /mcp
  🔌 MCP Tools
    • openviking-memory
      • Auth: Bearer token
      • Tools: add_resource, forget, glob, grep, health, list, read, search, store

`openviking-memory.health` returned `OpenViking is healthy ... storage: VikingFS`;
Stop hook reported `appended 2 turn(s) to OpenViking session <id>`.

Notes

- `.mcp.json` headers that don't have a corresponding env var (e.g. user
  didn't set `OPENVIKING_USER`) are simply not sent — `env_http_headers`
  silently omits missing vars per Codex's MCP runtime.
- Rotating the API key now just needs `codex` restart (env re-reads from
  ovcli.conf via the wrapper). URL changes still need a re-install since
  the URL is baked into the cached .mcp.json.
- The shell function wrapper has a marker-delimited block so re-running
  the installer replaces it in place rather than appending duplicates.

* review(plugin/codex): address copilot feedback on installer + docs

1. Switch the codex() shell-function wrapper from jq to node. The installer
   already hard-requires node 22+, while jq is not always present; the old
   wrapper would silently fall through to `command codex` with no env
   injection when jq was missing, which caused Codex to start with no
   Bearer token, OV to return 401, and Codex to drop into its OAuth
   fallback. Now there is a single tool dependency for both the installer
   and the wrapper it emits.

2. Marker-replacement is now defensive: rewrite-in-place only triggers
   when BOTH the BEGIN and END markers exist in the rc. If only BEGIN
   is present (manual edit / corruption), warn and append a fresh block
   instead of awk-dropping everything from BEGIN to EOF.

3. When no rc is detected, omit the `source $RC` line from the final
   "Next:" hint and tell the user to paste the snippet manually instead
   of printing `source ` with a trailing space.

4. Docs (README + 04-codex.md zh/en): use the full env var names
   (OPENVIKING_API_KEY / OPENVIKING_ACCOUNT / OPENVIKING_USER /
   OPENVIKING_AGENT_ID) instead of `_ACCOUNT` / `_USER` shorthand;
   update the manual-setup snippets to the node-based wrapper.

The wrapper body is now defined once and reused for both the appended-to-rc
path and the manual-paste path, so the two cannot drift.
2026-05-13 21:08:57 +08:00
t0saki 8034abc158 docs(plugin/codex): dedicated agent-integrations page (zh+en) + fix MCP startup (#2019)
* docs(plugin/codex): add dedicated agent-integrations page + fix MCP startup

Follow-up to #1957. Lifts Codex out of `04-other-plugins.md` into its own
`04-codex.md` (en + zh) with full install steps, configuration, hook
behavior, and troubleshooting — mirrors the shape of `02-claude-code.md`.

Renumbers `04-other-plugins.md` → `05-` and `05-langchain-langgraph.md`
→ `06-`. Overview tables in both locales updated; cross-refs fixed.

Also fixes two install/runtime bugs surfaced while validating the fresh
installer flow against the merged PR:

1. **Stale repo clone**: `setup-helper/install.sh` previously skipped the
   clone if `~/.openviking/openviking-repo` already existed, so a user
   who installed before #1957 merged ended up with a pre-PR plugin
   checkout (no `scripts/`, no `servers/memory-server.js`). The installer
   now `git fetch + reset --hard` an existing checkout to `$REPO_REF`
   (default `main`), matching the claude-code installer pattern.

2. **`${CODEX_PLUGIN_ROOT}` not expanded in `.mcp.json`**: Codex 0.130
   does not substitute env vars in `.mcp.json` `args`/`env` and does not
   always inject `CODEX_PLUGIN_ROOT` into MCP child env. The literal
   string `${CODEX_PLUGIN_ROOT}` was being passed to node, which then
   tried to resolve `${CODEX_PLUGIN_ROOT}/scripts/start-memory-server.mjs`
   against codex's cwd and failed with `MODULE_NOT_FOUND`. Fix:
   - `.mcp.json`: `args: ["scripts/start-memory-server.mjs"]` + `cwd: "."`
     (matches the syntax 0.1.0 used, which Codex does honor)
   - `scripts/runtime-common.mjs`: derive plugin root from
     `import.meta.url` as a fallback so the launcher works regardless of
     whether `CODEX_PLUGIN_ROOT` is set in the spawn env

Bumps plugin to 0.4.1 (package.json + plugin.json + lockfile) since the
runtime-common.mjs change invalidates the install-state hash and forces
a re-install of node_modules into the per-user runtime data root.

* fix(plugin/codex): hooks.json must use relative paths, not ${CODEX_PLUGIN_ROOT}

Same root cause as the .mcp.json fix in the previous commit: Codex 0.130
does not expand ${CODEX_PLUGIN_ROOT} in hooks.json `command` strings. The
shell that runs the hook sees the literal ${CODEX_PLUGIN_ROOT} and expands
it to "" (or leaves it literal), so node tries to load `/scripts/...mjs`
and exits 1.

Symptom in the chat UI:
  • SessionStart hook (failed)  error: hook exited with code 1
  • UserPromptSubmit hook (failed)
  • Stop hook (failed)

Fix: use `./scripts/<name>.mjs` paths, matching the pattern Codex's own
bundled plugins (e.g. figma) use. Codex's hook dispatcher resolves these
relative to the plugin root (where hooks.json lives).

The MCP launcher fix from the prior commit already handles the same class
of bug for .mcp.json; this catches the hooks path.

* fix(plugin/codex): hooks.json needs absolute paths rendered at install time

Previous fix (relative ./scripts/...) was based on the figma example but
empirically does not work on Codex 0.130: the hook subprocess runs with
cwd = user's cwd (not plugin root) and CODEX_PLUGIN_ROOT is NOT injected
into the env. So both ${CODEX_PLUGIN_ROOT}/scripts/foo.mjs and
./scripts/foo.mjs resolve to the wrong absolute path and node exits 1.

Verified with a probe shell script wired into hooks.json:
  argv: /tmp/codex-hook-probe.sh SessionStart
  cwd: /Users/<user>
  CODEX_PLUGIN_ROOT: <unset>
  CODEX_PLUGIN_DATA: <unset>

(The "Under-development features are incomplete" banner Codex prints when
plugin_hooks is enabled is real - the hook env wiring is unfinished in
0.130.)

Fix: keep the source hooks.json as a template (uses __OPENVIKING_PLUGIN_ROOT__
placeholder) and have install.sh sed-render the cache copy with the
absolute $CACHE_DIR path on every install. The cached hooks.json is now
fully self-contained absolute-path commands; the repo's checked-in copy
stays portable.

.mcp.json is unaffected: Codex 0.130 does honor the `cwd: "."` field for
MCP servers, so relative args resolve against plugin root there.

* fix(plugin/codex): bump UserPromptSubmit timeout to 15s

Empirically the auto-recall hook can take 0.8s–4s end-to-end (depending on
result count and remote OV latency), and Codex 0.130 sometimes adds 4-5s
of spawn overhead before our script even starts. The original 8s budget
was borderline and produced spurious "hook timed out after 8s" UI errors
on slow paths even when the recall would have succeeded.

15s matches the auto-recall internal timeoutMs default (config.mjs:186)
and gives enough headroom for spawn-time variance without holding the
user's input noticeably longer in the worst case.

* fix(plugin/codex): installer accepts OPENVIKING_REPO_BRANCH as alias

Per review feedback: the claude-code installer uses OPENVIKING_REPO_BRANCH
for the same purpose. Aliasing both names lets users reuse one env var
across installers without remembering which plugin uses which name.

Precedence: OPENVIKING_REPO_REF > OPENVIKING_REPO_BRANCH > "main".
2026-05-13 20:33:36 +08:00
e92180a7e1 feat(plugin/codex): add lifecycle hooks (recall, capture, pre-compact) to codex-memory-plugin (#1957)
* feat(plugin/codex): add lifecycle hooks (recall, capture, pre-compact)

Brings the codex-memory-plugin to feature parity with the claude-code-memory-plugin
by wiring the four Codex lifecycle hooks via `hooks.json`:

- SessionStart  -> bootstrap-runtime.mjs (npm ci into ${CODEX_PLUGIN_DATA}/runtime)
- UserPromptSubmit -> auto-recall.mjs (search OV, inject via hookSpecificOutput.additionalContext)
- Stop -> auto-capture.mjs (incremental transcript capture + last_assistant_message commit)
- PreCompact -> pre-compact-capture.mjs (full transcript -> single OV session -> commit)

Differences from the Claude Code plugin baked into the scripts:

- Codex output schema does not allow `decision: "approve"`; no-op is `{}`
- Stop/PreCompact only support `systemMessage`, not `additionalContext`
- Plugin envs are CODEX_PLUGIN_ROOT / CODEX_PLUGIN_DATA
- Config section is `codex` (was `claude_code`); config file defaults to
  `~/.openviking/ovcli.conf`, falling back to legacy `~/.openviking/ov.conf`

Other changes:

- src/memory-server.ts now reads ovcli.conf-style configs (top-level `url`,
  `api_key`, `account`, `user`, `agent_id`) so the plugin works against
  hosted OpenViking deployments out of the box. Env-var-only operation
  (OPENVIKING_URL set, no config file) is also supported.
- .mcp.json points at scripts/start-memory-server.mjs, which boots the same
  runtime the hooks use, so the MCP path benefits from npm-ci bootstrap.
- README rewritten with architecture diagram, validation SOP, configuration
  reference, and a Codex-vs-Claude-Code differences table.

Validated end-to-end against an OpenViking deployment:

- Auto-recall returns ranked memories with full content and emits
  hookSpecificOutput.additionalContext.
- Auto-capture (last_assistant_message path) creates a session, commits, and
  the OV pipeline extracts events + preferences within ~60s.
- Pre-compact-capture posts a full 4-turn transcript to one OV session,
  commits with archived=true, and produces structured leaf memories
  (preferences, events, entities) under viking://user/<user>/memories/.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(plugin/codex): drop SessionStart, split Stop=add_message vs PreCompact=commit

Codex's `Stop` hook fires per turn, not at session end, so committing per-Stop
over-fragments memory extraction. And codex re-fires `SessionStart` on short
reconnects, so registering an `npm ci` bootstrap there reinstalls the runtime
unnecessarily.

This change keeps one long-lived OpenViking session per codex `session_id`
across all `Stop` invocations, and only triggers the OV memory extractor on
`PreCompact` (or via an idle-sweep best-effort commit when codex exits without
compacting).

- hooks.json: drop SessionStart entry; keep UserPromptSubmit/Stop/PreCompact
- scripts/session-state.mjs (new): per-codex-session state under
  ~/.openviking/codex-plugin-state/, tracks ovSessionId + capturedTurnCount
- scripts/auto-capture.mjs (Stop): incremental add_message only, idle-sweep at
  the tail to commit stale codex sessions (default IDLE_TTL=30 min, override
  with OPENVIKING_CODEX_IDLE_TTL_MS)
- scripts/pre-compact-capture.mjs (PreCompact): catch-up append + commit the
  long-lived OV session, then null out ovSessionId so the next Stop opens a
  fresh OV session for the post-compact half
- MCP runtime install stays lazy in start-memory-server.mjs (already there);
  no SessionStart hook means short reconnects don't re-trigger npm ci
- VERIFICATION.md: end-to-end SOP against a live OV server (~3 min)
- bump plugin to 0.3.0

Verified end-to-end against ov.zaynjarvis.com:
  Stop adds turns idempotently and incrementally; PreCompact commits to
  history/archive_001/ with extractor producing memories under
  viking://user/<user>/memories/profile.md after ~30 s; post-compact Stop
  opens a fresh OV session; idle-sweep commits stale state files.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(plugin/codex): replace idle-sweep with SessionStart(source=clear) commit

Per Zayn's followup ("非必要不要加 idle commit"): drop the idle-sweep added
in the previous commit and use codex's actual context-disappearing signal —
SessionStart with source=clear — to commit orphaned sessions.

Codex hook signal map:
- /compact         → PreCompact      ✅ commit (already)
- /clear           → SessionStart(source=clear) for the NEW session_id;
                     the prior transcript is orphaned. Now committed.
- /new             → SessionStart(source=startup); ambiguous with fresh
                     codex startup, so we don't act on it.
- /resume / short reconnect → SessionStart(source=resume|startup); no-op
                     to avoid corrupting still-active sessions.
- SIGTERM/Ctrl+C/exit → no hook fires. Documented as a known gap; users
                     should /compact before /exit if they want commit.

Changes:
- new scripts/session-start-commit.mjs: gates internally on source=clear,
  iterates listStates(), and commits any state file whose codexSessionId
  != the new SessionStart session_id, then clears that state file
- hooks/hooks.json: re-register SessionStart pointing at the new script
  (timeout 30s)
- scripts/auto-capture.mjs: remove sweepIdleSessions() and
  IDLE_TTL_MS env handling; Stop is now strictly add_message
- README/VERIFICATION.md: update arch diagram, replace idle-sweep step
  with SessionStart(source=clear) verify (positive + negative paths),
  add "Known gap: SIGTERM/exit are silent" section
- bump to 0.3.1

Verified end-to-end against ov.zaynjarvis.com:
  Stop add+idempotent ✓
  SessionStart source=startup → {} ✓
  SessionStart source=resume → {} ✓
  SessionStart source=clear → committed prior OV session, history/archive_001/
  appeared, profile.md gained "Favorite snack: dark chocolate" within 30 s.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore(plugin/codex): SessionStart matcher = "clear" (native dispatcher gate)

Codex's hooks dispatcher matches the SessionStart hook's `matcher` field
against the SessionStart `source` value. Setting matcher to "clear" means
codex won't even spawn our script on `source=startup` or `source=resume`
(short reconnects); we previously gated this in-script. The internal
source check in session-start-commit.mjs is kept as defense-in-depth.

Source: codex-rs/hooks/src/events/session_start.rs `select_handlers(...,
matcher_input: Some(request.source.as_str()))` and
codex-rs/hooks/src/events/common.rs `is_exact_matcher` — "clear" is
all-alphanumeric so it's matched as exact equality, not regex.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(plugin/codex): active-window heuristic + idle-TTL sweep at SessionStart (v0.4.0)

Source of truth: examples/codex-memory-plugin/DESIGN.md (added in this commit).

Behavioral changes:

- SessionStart matcher widens from `clear` to `clear|startup`. Both sources
  run the same active-window heuristic; `resume` is a hard no-op (still fires
  on short reconnects).
- Heuristic (DESIGN.md §3): count state files (excluding new session_id) within
  ACTIVE_WINDOW_MS (default 2 min). 0 → noop, 1 → commit it (just-ended
  session), ≥2 → skip and rely on idle TTL. Tunable via
  OPENVIKING_CODEX_ACTIVE_WINDOW_MS.
- Idle-TTL sweep returns at the tail of session-start-commit.mjs only (not
  every Stop). Default IDLE_TTL_MS = 30 min via OPENVIKING_CODEX_IDLE_TTL_MS.
  Catches SIGTERM/Ctrl+C/`/exit` orphans and the ≥2-active skip path.
- Stop hook deliberately does NOT sweep — state-write-on-every-turn already
  gives us the freshness signal. Marker comment added.
- Stop hook adds post-compact transcript-shrink defense: if
  allTurns.length < state.capturedTurnCount, reset capturedTurnCount = 0.
- Commit-on-failure preserves state everywhere (PreCompact, heuristic,
  idle sweep). A non-2xx /commit no longer clears ovSessionId; the next
  sweep retries.
- session-state.mjs saveState now uses atomic write (tmpfile + rename) for
  crash safety. listStates ignores the brief `<id>.json.tmp` window.

Bump: package.json + .codex-plugin/plugin.json → 0.4.0.

Docs: README "How It Works" gained a DESIGN.md pointer and rewrites the
SessionStart section to reflect heuristic + idle TTL. VERIFICATION.md step 6
now exercises all four heuristic branches (0/1/≥2 active, idle TTL, resume).

Phase-2 resume context inject documented in DESIGN.md but explicitly out of
scope here.

Verified locally with synthetic stdin tests against a fake OV server:
1-active commit, ≥2-active skip, idle TTL sweep, resume noop,
unreachable-server keeps state.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(plugin/codex): align config loading with claude-code plugin

Addresses three review points on PR #1957:

1. Honor OPENVIKING_CLI_CONFIG_FILE for the ovcli.conf override path
   (matches the convention used by `ov` CLI and claude-code-memory-plugin).
   OPENVIKING_CONFIG_FILE stays as the ov.conf override; for backward
   compat it still works when pointed at an ovcli-shaped file.

2. Strict env-first priority for every connection / identity field
   (baseUrl, apiKey, account, user, agentId). Env vars now win over
   ovcli.conf, which wins over ov.conf's codex.* block / server.*,
   which wins over built-in defaults.

3. Unify hook and MCP-server config loading: src/memory-server.ts now
   imports loadConfig from scripts/config.mjs (relative path stays
   valid post-compile because servers/ and scripts/ are siblings),
   eliminating the divergent account/user/agentId fallback chains
   the PR-Agent reviewer flagged.

Auth header: emit Authorization: Bearer (primary, required by OpenViking
Cloud) plus the legacy X-API-Key during the transition window. All six
fetch sites updated (4 hook scripts + memory-server.ts + compiled
servers/memory-server.js).

README: document the new resolution chain, OPENVIKING_CLI_CONFIG_FILE,
OPENVIKING_BEARER_TOKEN alias, and the Authorization: Bearer migration.

* docs(plugin/codex): put installation first

* fix(plugin/codex): harden runtime and capture paths

* docs(plugin/codex): align local marketplace name

* docs(plugin/codex): add one-line installer

* fix(plugin/codex): support branch installer testing

* fix(plugin/codex): keep installer env surface stable

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: zhengxiao.wu <zhengxiao.wu@bytedance.com>
2026-05-13 19:25:30 +08:00