feat: rule-based tool input compaction for auto-capture (~10x storage reduction) - #2874
feat: rule-based tool input compaction for auto-capture (~10x storage reduction)#2874lg320531124 wants to merge 305 commits into
Conversation
|
Nice implementation! Rule-based tool input compaction without LLM involvement is a smart approach — reduces cost and latency for auto-capture. The inline test functions avoid coupling to the full module. Tool input policies (full vs summary) are well-designed. |
4d80943 to
d4e525e
Compare
|
The I posted the full diagnosis on sibling PR #2900 (rebased onto latest This PR ( I don't have admin rights to rerun from a fork — could someone with access rerun the |
buildParts() (structured tool parts sent to OV as tool_input) bypassed compactToolInput, sending raw block.input objects while the prose extractAllTurns path already compacted Write/Edit/Bash/Task* to structural summaries. The ~10x storage reduction from volcengine#2874 only covered the inlined-text path; structured parts carried full Write content verbatim. Mirror the prose path: apply compactToolInput when cfg.toolInputCompaction !== false, fall back to formatToolInput otherwise. Non-summary tools (Read/Glob/Grep) keep full input. tool_input now a string (consistent with tool_output). Adds 6 buildParts regression tests asserting parity with the prose path for Write/Edit/Bash/Read, compaction=off fallback, and non-object input handling.
buildParts() (structured tool parts sent to OV as tool_input) bypassed compactToolInput, sending raw block.input objects while the prose extractAllTurns path already compacted Write/Edit/Bash/Task* to structural summaries. The ~10x storage reduction from volcengine#2874 only covered the inlined-text path; structured parts carried full Write content verbatim. Mirror the prose path: apply compactToolInput when cfg.toolInputCompaction !== false, fall back to formatToolInput otherwise. Non-summary tools (Read/Glob/Grep) keep full input. tool_input now a string (consistent with tool_output). Adds 6 buildParts regression tests asserting parity with the prose path for Write/Edit/Bash/Read, compaction=off fallback, and non-object input handling.
2583cd0 to
afe7c14
Compare
Co-authored-by: Yuanqing Zhao <2604121+yuanqingz@users.noreply.github.com>
Add multimodal image vectorization and image query support across the server, SDKs, and CLI.
* fix(memory): avoid nesting rendered links * fix(memory): cover spaces in link targets Reviewer (@fujiajie666) noted that _RELATIVE_LINK_RE rejected whitespace inside link targets, so an existing link like [Frank Ocean](entities/frank ocean.md) was not detected as a link span, causing the next render to wrap the inner 'Frank' and produce a nested broken link. - Loosen _RELATIVE_LINK_RE to \[^)]+\ so the target may contain spaces (and \%20\ percent-encoded forms) when matching. - Percent-encode spaces in generated link targets in render_links so the emitted markdown is portable across renderers. Tests cover: skip inside existing literal-space link, encode spaces in new targets, skip when existing target is %20-encoded, strip_links on both literal-space and %20-encoded targets, full round-trip, and idempotency on re-render. --------- Co-authored-by: AITree69 <292109083+AITree69@users.noreply.github.com>
中文:对 HTTP/HTTPS URL 使用 urlparse(url).path 提取扩展名,确保 video.mp4?signature=... 命中 UnderstandingAPI fast path。新增带签名视频 URL 的 ParserRouter 回归测试。 English: Parse HTTP/HTTPS URL paths before checking extensions so signed video URLs hit the UnderstandingAPI fast path. Add a ParserRouter regression test for signed video URLs. Co-authored-by: chenpengfei <chenpengfei@bytedance.com>
Disable recursive submodule fetching and avoid exposing remote Git errors to resource API callers.
Co-authored-by: huangruiteng <huangruiteng@bytedance.com>
…olcengine#3104) Commit 4d34025 added the required `sender_name` parameter to `BaseChannel._handle_message()` and updated `feishu.py`, but `slack.py` and `email.py` were not updated in the same change. This caused a `TypeError` on every inbound message in both channels. Because Slack SDK swallows exceptions in asyncio listener callbacks, the error was silent — the bot received events, added emoji reactions, but never called the LLM or sent any reply. Fix: pass `sender_name=sender_id` in SlackChannel and `sender_name=sender` in EmailChannel. Co-authored-by: scott.kim <scott@ScottMacBookPro.local> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…gine#3114) * fix(vectordb): only write content field for VikingDB backends * fix: update doc
Regenerate uv.lock metadata to match pyproject.toml after two upstream deps changes that left the lock unreconciled: - b596e26 (volcengine#2965): bump litellm pin <1.89.3 -> <1.90.3 - 47da6ce (volcengine#3037): merge bot-* extras into a single [bot] extra Metadata-only diff (13 insertions / 66 deletions) — no resolved package versions or hashes change. Confirmed by a clean `uv sync` leaving uv.lock unmodified. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ne#3685) * refactor(langchain): extract standalone integration package * fix(langchain): preserve optional legacy imports * fix(langchain): guard legacy submodule imports
* feat(ragfs): add structured lock tracing logs * feat(ragfs): add log async writer * fix(ragfs): add CRITICAL trans && add rust hook to receive rotate log file
…#3548) * fix(server): stop exporting raw query strings and buffering zip responses in observability Sweep findings: B-03, B-13. Prevent query secrets from reaching traces and keep ZIP responses streaming. (cherry picked from commit d8ac3dc) * fix(session): tolerate missing/corrupt archive in Phase-2 replay (NotFoundError / _ArchiveMessagesCorruptError) on a missing or corrupt archive messages.jsonl instead of returning []. That PR added skip-on- missing tolerance to the read path (_get_uncovered_archive_messages) and to resume_queued_commit, but not to the Phase-2 commit replay path (_prepare_phase2_archive_messages), which calls _read_archive_messages unguarded while rolling earlier failed archives into the current commit. Consequence: a terminally-failed earlier archive whose messages.jsonl is missing/corrupt (legacy "no messages" terminal data, or produced by volcengine#3417's own archive_read terminal path) makes every subsequent commit's Phase-2 extraction raise -> caught by _run_memory_extraction's except -> the current archive is terminal-failed too. Because the poisoned archive is only removed from replay once "covered" (which requires a later archive to complete), and no later archive can ever complete, the session's memory extraction is permanently poisoned. Raw messages are safe, but extraction is stuck. Fix: wrap the replay-loop _read_archive_messages call in the same tolerance _get_uncovered_archive_messages already uses -- skip + warn on not-found (_is_storage_not_found) and on _ArchiveMessagesCorruptError, re-raise real storage failures. The skipped archive stays in covered_failed so the current archive's .done marks it covered, clearing the poison permanently. Adds a regression test asserting the replay skips a failed archive with a missing messages.jsonl (and marks it covered) instead of raising, and that a real storage failure still propagates. Follow-up to volcengine#3417. (cherry picked from commit 5b8ec9e) * fix(client): align client surfaces without leaking memory metadata Reconstructs the client-parity work from upstream PR volcengine#3439 on current main and strips reserved memory metadata before line slicing in both embedded and HTTP reads. Based-on: 48b411d Co-authored-by: zhiheng.liu <zhiheng.liu@bytedance.com> * fix(index): propagate semantic vectorization failures safely Reconstructs upstream PR volcengine#3437 on current main, carries enqueue failures through SemanticDagExecutor, and drains the attempt's embedding tracker before retry-visible failure propagation. Based-on: 02387de Co-authored-by: zhiheng.liu <zhiheng.liu@bytedance.com> * fix(core): close privacy and embedding failure gaps * fix(memory): strip repeated metadata trailers * fix(core): close public memory visibility gaps * ci: skip embedding-dependent resource test without secrets --------- Co-authored-by: zhiheng.liu <zhiheng.liu@bytedance.com>
* feat: add audio and video understanding via VLM * docs: design media resource guards * fix: bound media staging concurrency * fix: cap unknown-size media staging * test: stage media in routing fake * test: exercise media staging callbacks * test: trim media understanding coverage * chore: 清理实现计划文档 * fix: 修复多凭证切换问题 --------- Co-authored-by: Qin Haojie <qinhaojie.exe@bytedance.com>
…olcengine#3706) Route the query() random-sampling branch through search_by_vector with a client-generated random vector (config.embedding.dimension) so every backend behaves consistently, instead of each backend's server-side search_by_random. This also makes Qdrant/OpenGauss truly random rather than a deterministic scroll/scan. Drop the raise_on_error path entirely per request: query(), the Collection wrapper, and HttpCollection.search_by_random no longer take raise_on_error, and delete() no longer requests it. As a result, HTTP filter-based deletion id lookups now return empty on non-200 instead of raising. Co-authored-by: TRAE CLI <noreply@bytedance.com>
volcengine#3704) * feat(config): add enable_watch_scheduler toggle for read-only replicas Add a top-level `enable_watch_scheduler` config flag (default true) and gate `WatchScheduler.start()` on it in service initialization. Read-only replicas that share a writer's data must not run the periodic watch/refresh background loop: it would execute writes against shared storage (vectordb / backups) and race the writer when persisting watch task state, which has no cross-process lock. The scheduler instance is still created and wired for on-demand read paths; only its background loop is skipped. Defaults to true, so existing single-instance deployments are unaffected. * fix * fix
GET /api/v1/fs/attrs read search tags with a bare `uri` equality filter
and no level constraint. Over a path-typed `uri` field this does not pin
an exact node, and it merged tags across all levels, so a directory whose
tags live on its L0/L1 summary records could read back tags from an
unrelated record instead of its own.
Mirror how ContentWriteCoordinator.set_tags writes tags: a directory
carries them on its L0/L1 records and a file on its L2 record. Query with
And([Eq("uri", uri), In("level", levels)]) so Eq compiles to an exact
path match (-d=0) and the level set matches the node kind.
Co-authored-by: TRAE CLI <noreply@bytedance.com>
…ngs (volcengine#3695) * feat(agent-evolution): record trajectory mapping in snapshot commits * feat(agent-evolution): apply global switch immediately * fix(snapshot): hide memory fields in visible history * fix(agent-evolution): preserve batch snapshot provenance * feat(agent-evolution): scope runtime settings by account * fix(agent-evolution): serialize experience snapshot finalization
The bytes_row STRING type uses a uint16 length prefix, capping a single
field at 65535 bytes. Add a new TEXT field type (enum value 9) that mirrors
STRING semantics (utf-8 str round-trip) but uses a uint32 length prefix,
lifting the per-field limit to ~4GB.
TEXT is added only at the physical bytes_row layer, across all serializers
that must stay byte-identical: the C++ engine (bytes_row.h/.cpp), the abi3
boundary (abi3_engine_backend.cpp, decoding to str not bytes), the pure
Python fallback (store/bytes_row.py), and the engine API (_python_api.py).
Existing types and the CandidateData.fields field are untouched, so old
on-disk data stays readable without reindex.
Fields opt into the new type via metadata={"field_type": FieldType.text}.
Add TestTextFieldType covering >65535-byte round-trips, py<->cpp cross
read/write, binary consistency, and metadata-based declaration.
Co-authored-by: TRAE CLI <noreply@bytedance.com>
* feat(integrations): add ZCode memory plugin Add examples/zcode-memory-plugin — a thin ZCode lifecycle adapter that reuses the shared memory-plugin-shared runtime for recall, capture, commit, and MCP proxy. No memory logic is duplicated. Key design decisions (see docs/design/zcode-memory-plugin-design.md): - Vendor shared runtime into scripts/shared/ via sync.mjs (self-contained plugin) - 4 hook events only (SessionStart, UserPromptSubmit, PreToolUse, Stop) — ZCode does not support PreCompact/SessionEnd/SubagentStart/SubagentStop - Output schema: ZCode-canonical keys only (no Claude-Code 'decision: approve') - Config-driven install: hooks + MCP merged into ~/.zcode/cli/config.json - install.sh wiring: detection, TUI, validation, install, uninstall Verified locally: - 22/22 node:test cases pass (turns parser + hook output schema) - sync.test.mjs passes - install → uninstall cycle: hooks/MCP correctly written and cleaned - URI guard denies viking:// paths with MCP redirect - Capture writes to OV session with zc- prefix Closes volcengine#3127 Related: volcengine#3442, volcengine#3544 * chore: remove non-essential files from PR, add .scratch to .gitignore - Remove .scratch/ working notes (local ticket files, not codebase artifacts) - Remove package.json and .gitignore from plugin dir (TRAE/Cursor don't have them) - Add .scratch/ to root .gitignore * fix(zcode): use verified ZCode field names + rollout fallback for capture - Update zcode-turns.mjs to probe responseText/responsePreview (verified from ZCode reverse-engineering in volcengine#3127 by @quinn-zenith) instead of the TRAE-inferred last_assistant_message - Add rollout file fallback: when stdin payload lacks user content (the known ZCode limitation), read ~/.zcode/cli/rollout/model-io-sess-*.jsonl to extract the last user+assistant pair from request.messages+response - Fix concurrent session isolation: normalize sessionId→session_id in zcode-hook.mjs before resolveNativeSessionId to prevent cwd-fallback collision when two ZCode windows run in the same directory - Add 2 new test cases for rollout fallback (14 turns tests total, 24 total) - All 24 tests pass * fix(zcode): address maintainer review blockers (config safety, MCP ownership, turnId) Addresses 3 blockers from @huangruiteng's review (CHANGES_REQUESTED): 1. Config safety: distinguish ENOENT from parse errors — malformed config.json now aborts instead of overwriting. Use backup+tmp+rename for atomic writes. 2. MCP ownership: only replace/delete mcp.servers.openviking entries tagged as openviking-memory. User-managed entries with the same name are preserved on install and untouched on uninstall. 3. TurnId-based dedup: rollout entries carry monotonic turnId — now used as the primary dedup key (capturedTurnIds set) instead of stableHash. extractUnseenRolloutTurns scans ALL unseen entries since lastTurnId, not just the last row — recovers missed turns after hook failure. Fail-closed when no turns are found. Also updates DESIGN.md to reflect verified field names (responseText/ responsePreview) and the turnId contract. 27/27 tests pass (was 24). Added 3 new rollout tests: incremental capture with lastTurnId, multi-entry scan, turnId propagation. * docs(zcode): update stale field name references in design spec Update test case descriptions to match verified field names (responseText/responsePreview instead of last_assistant_message) and add rollout fallback + turnId test coverage descriptions. * fix(zcode): dedup key includes role + first-capture returns all turns Fix two bugs found in code review pass 2: 1. Assistant turns silently dropped: user and assistant from the same rollout entry shared a turnId, so dedup via capturedTurnIds dropped the assistant. Fix: dedup key is now ${turnId}:${role}, not turnId alone. Regression test added. 2. First-capture data loss: when no lastKnownTurnId was set, only the last rollout entry was returned, losing prior turns. Fix: first-time capture now returns ALL entries. Also: add backup step to config atomic write (copyFileSync before tmp+rename), fix line width in zcode-turns.mjs, add 2 lifecycle tests (missed Stop recovery, user+assistant same turnId). 29/29 tests pass (was 27). * test(zcode): add concurrent session isolation tests Two new test cases addressing maintainer criterion 4 (concurrent sessions): 1. Two sessions read their own rollout files — verifies session A cannot see session B's content and vice versa (sentinel-based assertion) 2. Independent lastTurnId state per session — verifies incremental capture progresses independently when one session has prior state and another is fresh 31/31 tests pass (was 29). * fix(zcode): correct rollout file path pattern (model-io-<sessionId>) The rollout path used model-io-sess-${sessionId} but ZCode filenames are model-io-<sessionId> where sessionId already includes the sess_ prefix. This caused the rollout fallback to always miss the file and return empty, defeating capture entirely in production. Verified on live two-session ZCode setup: - Session A (sess_8c6ce483): 2 messages, 2 commits - Session B (sess_74759710): 2 messages, 2 commits - No cross-contamination between sessions 31/31 tests pass. Updated all test rollout filename patterns. * docs(zcode): fix stale rollout path in comments and DESIGN.md Comments referenced model-io-sess-<sessionId> but actual pattern is model-io-<sessionId> (fixed in code already, comments were stale). --------- Co-authored-by: woshiguanxiaoliang <woshiguanxiaoliang@noreply.gitcode.com>
…engine#3710) * feat(config): make external parse concurrency configurable * refactor(config): move external parse concurrency to queue workers --------- Co-authored-by: chenpengfei <chenpengfei@bytedance.com>
Use ZCode rollout logs as the authoritative incremental source, advance capture state only for the acknowledged prefix, and persist host turn identity with the OpenViking turn_id contract. Detach Stop writes, package ZCode in the TOS marketplace artifact, add end-to-end regressions, and move the integration docs under community plugins. Co-authored-by: TRAE CLI <noreply@bytedance.com>
volcengine#3730) Surface the vector store's search_tags on each matched context (returned under the "tags" key to match the tags filter param) and remove the result fields the retrieval pipeline never populates (category, match_reason, relations, overview). Co-authored-by: TRAE CLI <noreply@bytedance.com>
…e#3727) * feat(agent-evolution): track experience trajectory lineage * fix(agent-evolution): return matched trajectory records * fix(agent-evolution): stabilize lineage pagination
… mode="context" (volcengine#3534) * feat(retrieval): assemble auto-recall context server-side via /search mode="context" Auto-recall assembly lived in every harness plugin: each one searched per memory type, read hits back one by one, and stitched a context block with its own budget and degradation rules. The implementations drifted, and the shared weaknesses showed up in production injections — roughly half of the entries degraded to a bare URI plus a score, character budgets distorted up to 6x on CJK text, and adjacent turns re-injected the same memories. This moves assembly into the server as one round trip. /find stays an unchanged stateless primitive. /search gains mode="context" (mode="list" is the default and byte-identical to before), and /recall becomes a thin preset over the same kernel with its v1 field names folded onto the new contract. New assembly kernel under openviking/retrieve/context_assembler/: - Token budgeting with a CJK-aware estimate replaces the character budget. - detail="auto" fills breadth-first then deepens: every candidate gets a readable floor, then overview, then full for high-scoring entries. An oversized tier falls back to the previous one instead of being truncated, bounded by max_tokens / candidates * 2 per entry. - Overview extraction dispatches by source: memory files use their leading Summary section, code files reuse code_outline signatures, long documents use a heading tree plus first paragraph. - Directory hits start at overview and read their .overview.md sidecar, since directories carry no stored abstract; their full tier stays capped at overview. v1 injected the sidecar as if it were a whole file. - Quotas generalize beyond memory types to resources and skills, with purpose presets supplying ratios when quotas are absent. - dedup_turns keeps a per-session ledger at {session_uri}/.recall_log.json so every harness inherits cross-turn dedup; exclude_uris remains as the stateless fallback. - Rendering flattens to one <memory uri=... type=... score=... detail=...> element per entry. Every tier carries its URI, so the model can always drill down through the MCP read tool. - Query expansion and digest rewriting are opt-in and fail closed: both have timeout fuses, and a failed rewrite still returns the unrewritten block. Retrieval failures are counted into stats rather than silently yielding an empty block. Plugins now send one context request, falling back to /recall and then to raw find on older deployments, and cache that outcome so only the first turn pays for the probe. The tri-state recallRewrite knob chooses between local host-CLI compression and the server digest, and client-side settings move to a plugin section in ovcli.conf. * refactor(retrieval): give context tiers a per-category default The tier ladder assumed `abstract` is a cheap summary. For memory files it is not: the memory writer stores the whole stripped body in that scalar because it doubles as the embedding text, so `abstract` costs the same as `full` and the ladder runs `uri < overview < abstract = full`. Two of the model's properties fell out of that: exempting `abstract` from the per-entry cap let a single entry eat several times the budget, and `detail` — which only ever set a ceiling — collapsed to two distinguishable behaviours across its four values, since `auto` already allowed `full` for memory. Tiers now come from a per-category constant table that treats the storage shape as a given: `events` starts at overview (the one memory type whose `# Summary` extraction is a real compression) and may deepen to full on leftover budget; every other category is served at `abstract`, which for memory already is the complete file at zero read cost and for resources and skills is the generated 256-char summary. The table carries the note to move `events` back to `abstract` once the writer stores a separate summary scalar. Falling out of that: prefetch now reads only the candidates whose planned tier needs a body rather than every candidate, `detail` becomes a real pin (start and ceiling) and additionally accepts a per-category map, and `full_score_threshold` is gone — leftover budget is spent in score order instead of behind an absolute threshold the observed score band cannot support. `auto` is still accepted on the wire as a synonym for "unset". Assembly fixes found alongside: - Removing the abstract cap exemption would turn an oversized abstract into a bare URI, so it now falls back to overview first — for memory that is a cheaper substitute, not a step up. - Rewrite timeouts were reported as failures on Python 3.10, where `asyncio.TimeoutError` is a separate class from the builtin. - `stats.rewrite_usage` read `token_tracker` off `VLMConfig`, which has no such attribute; usage was structurally always null. It now reads the model instance's tracker and reports only when the call count moved by exactly one, since that tracker is shared. - A single malformed ledger record made every deduped recall in that session fail, and the file was never rewritten, so it could not heal. Records are now coerced on read and dropped on the next write, along with records left ahead of the clock by an archive rotation. - Entries served as a bare URI no longer enter the dedup cooldown: they lost to budget pressure, not to the reader having already seen them. - The render envelope only neutralised a literal `</memory>`, so a body could forge a sibling entry with its own uri, type and score. - Flat-mode gathering re-derived the category from the URI, reading `viking://resources/backup/memories/events/log.md` as an event. - Cooled and excluded URIs are compensated with extra rows, so a fully cooled bucket falls through to the next-best hits instead of coming back empty. - `/recall` quotas overlay the v1 bucket defaults again; `{"events": 5}` had started dropping the other three buckets. - The MCP `recall` signature sent its own defaults as if the caller had, which resolved a different profile than `POST /recall`; an unknown `detail` value raised `KeyError` through the whole call instead of degrading. * feat(codex): inject profile context on session start Reuse the shared profile builder for startup, clear, and resume hooks while preserving archive injection and orphan-session status output. Co-authored-by: TRAE CLI <noreply@bytedance.com> * fix(retrieval): raise rewrite timeout default to 30s * docs(agents): document low-latency recall settings * fix(codex): prefer luna as recall compressor fallback * refactor(plugins): unify recall compression setting * feat(plugins): enable recall compression by default * docs(agents): use absolute links in image docs * fix(retrieval): address context assembly review feedback Co-authored-by: TRAE CLI <noreply@bytedance.com> * test: trim redundant context assembly coverage * fix(retrieval): address second-round context assembly review - Drop the backticked `/search` from the deprecated-recall row in both API overviews. The reference checker scans the whole row after the method cell for backticked paths, so it read the description as a route named `POST /search` and Build Docs failed on an unknown, undocumented route. - Accept ovcli.conf's full field set in both Python readers. The file's schema belongs to the Rust CLI, which writes `root_api_key`, `output`, `echo_command`, `show_progress` and `verbose` and ignores unknown keys; the two Python readers had drifted into stricter subsets, so the shipped example already failed to load in both. Adding the new `plugin` section to a working ovcli.conf would have broken `ov doctor` and every SDK client the same way. - Return 400 from `mode="context"` for a request `mode="list"` also rejects. Retrieval validates query and image_url before searching, and the gather fuse swallowed that rejection along with genuine scope failures, so a body of `{"mode":"context"}` came back 200 with an empty block instead of the documented parameter error. Runtime failures still degrade into `stats.retrieval_errors`. - Let a context request that asks for a server-side digest outlast the server's rewrite fuse. The plugin's ordinary 15s request timeout is shorter than the 30s fuse, so a rewrite that finished inside its own budget was aborted client-side, discarding the whole response — including the uncompressed block the server returns when a rewrite fails — and falling back to `/recall`. The deadline is only extended when the body actually requests a rewrite, and `OPENVIKING_RECALL_CONTEXT_TIMEOUT_MS` / `plugin.recallContextTimeoutMs` pins it. * chore(plugins): sync shared modules into the zcode snapshot * fix(retrieval): align context quotas and plugin defaults Restore cross-domain coding recall, reuse authoritative actor resource scopes, and make bucket quotas the sole width control in purpose mode. Keep plugin defaults server-owned while preserving explicit legacy limit settings through quota conversion. Co-authored-by: TRAE CLI <noreply@bytedance.com> * fix(retrieval): preserve recall compatibility Restore the deprecated recall threshold default, distinguish successful empty rewrites from compressor failures, and document legacy quota floors across coding-agent plugins. Co-authored-by: TRAE CLI <noreply@bytedance.com> --------- Co-authored-by: TRAE CLI <noreply@bytedance.com> Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
…hots (volcengine#3742) * feat(agent-evolution): aggregate trajectory outcomes by experience * fix(agent-evolution): snapshot only visible experience changes * docs(api): document agent evolution queries
…engine#3738) * fix(compile): resolve pickle error and merge skill_name changes * fix
…ngleton 、cluster 、 sentinel (volcengine#3741)
Resolve import conflict in subagent-stop.mjs (keep both workspace-peer and compact-tool-input imports). Fix package.json test script to include scripts/__tests__/*.test.mjs so the compaction tests actually run.
… recalls (volcengine#3746) * fix(retrieval): honor tier ceilings and stop cooling unserved recalls Follow-up to volcengine#3534, from its post-merge review round. - The abstract-to-overview substitute now applies only to categories whose stored abstract is the whole file body. A resource or skill whose abstract is missing (`processing_mode=vectors_only`) or over the per-entry cap read its body and returned an overview instead, which for a short file is the body almost verbatim — crossing the opt-in deepening boundary those categories are documented to have, and doing it even under an explicit `detail="abstract"`. They now degrade to a bare URI and their body is never read. - A digest reporting `no_relevant` blanks `rendered`, so the client injects nothing, yet those URIs still entered the dedup ledger and were cooled for `dedup_turns` turns. That contradicted the ledger's own bare-URI grace rule and held memories back from the later turn they were relevant to. - Flat retrieval reaches built-in memory types outside the four named ones (`cases`, `patterns`, `tools`, `trajectories`, skill-usage memories) and reported them as an undeclared `memories` category that no tier or penalty table covered, so other-peer hits skipped the score penalty and callers could not pin their tier. The catch-all is now a declared category with both; it stays out of `quotas`, whose buckets it would overlap. Skill-usage memories also stop being misread as the `skills` category. - ZCode, OpenCode and pi own an OV session id but did not forward it, so their recalls silently ran without query expansion or cross-turn dedup. - The context-request deadline covered only the server's 30s rewrite fuse, but the pipeline is serial: expansion, retrieval and budgeting all precede it. 45s covers both fuses and the work between them. - `plugin` config scope and the `/recall` successor example now match what the code actually does. * fix(retrieval): make the context deadline and expansion opt-out reachable Forwarding a session id turns on server-side query expansion, an LLM call with its own 5s fuse, but neither the deadline that was supposed to cover it nor the switch that turns it off reached the two harnesses this PR newly enabled it for. - `contextRequestTimeoutMs()` now derives the deadline from the request body rather than from `cfg` plus a rewrite flag. The body is what states which server stages will run: a session takes the expansion fuse, `rewrite` takes the digest fuse, and a bare retrieval takes neither and keeps the caller's own budget. Reading `cfg` alone could not tell those apart. - OpenCode pinned `timeoutMs: 5000` after spreading the helper's options and pi ignored them entirely, so the helper's deadline was dead code in both. Their own budgets are now defaults rather than ceilings. OpenCode's 5s in particular was shorter than the expansion fuse it had just enabled, so a legal request would have been aborted client-side and dropped back to the path with neither dedup nor expansion. - OpenCode and pi read `OPENVIKING_RECALL_QUERY_EXPANSION` (and `recallQueryExpansion` in their own config files) and set the `configured` flag the shared body builder requires, so the documented opt-out exists where the cost was introduced. - The integration overview no longer implies every harness reads the same environment knobs, and describes the deadline as per-stage rather than rewrite-only.
Summary
Add rule-based tool input compaction to the Claude Code auto-capture pipeline.
compactToolInput()reduces Write/Edit/Bash/TaskCreate/TaskUpdate tool inputs to structural summaries instead of full file contents.Problem
Sampling 16 CC sessions shows 79% of session storage volume is full code content from Write (avg 11,575 chars) and Edit (avg 2,525 chars) tool inputs. This causes:
bge-large-zh-v1.5embeddings are dominated by code tokens instead of semantic contentSolution
No LLM involved — purely rule-based compaction:
Measured: 10.6x compression on realistic mixed session data.
Changes
auto-capture.mjs: AddcompactToolInput()with per-tool-type policies (TOOL_INPUT_POLICIES); modifyharvestContent()to use compaction when enabledconfig.mjs: AddtoolInputCompaction(default: true) andtoolInputMaxChars(default: 2000) config options with env var overrides__tests__/auto-capture-compaction.test.mjs: 21 regression tests covering all tool types, truncation, edge cases, and compression ratioConfiguration
OPENVIKING_TOOL_INPUT_COMPACTIONtrueOPENVIKING_TOOL_INPUT_MAX_CHARS2000Set
OPENVIKING_TOOL_INPUT_COMPACTION=falseto restore the old verbatim behavior.Test Plan
Closes #2875