* feat(compile): add memory consolidation mode via `--skill memory`
Add an in-process memory consolidation mode to `ov compile`. When `--skill`
is the sentinel value `memory`, CompileService runs the existing memory
framework (ConsolidationExtractContextProvider -> ExtractLoop -> MemoryUpdater)
directly inside OpenViking core to dedup/merge/split/compact an existing
memory-type directory in place, conforming to that type's schema. The default
skill path (VikingBot Wiki compile) is unchanged.
Highlights:
- New ConsolidationExtractContextProvider: agentic exploration with ls/search/read
tools seeded by a recursive listing; single schema inferred from --to; space
(self/peer) taken from the canonical --to URI so listing never depends on an
empty ctx.user_id.
- MemoryCompileRunner: session.commit-lite task shape (task_tracker + one
in-process asyncio.Task, no QueueFS re-delivery), bound to a root span so a
trace_id is recorded; result reports adds/updates/deletes (file URIs only,
memory_diff.json semantics) classified via read_file_contents.
- MemoryLsTool: add recursive listing (relative paths, 500-node cap with
truncation) and stop hiding subdirectories so subfoldered dirs are not
misreported as empty. Only the compile provider exposes ls, so session.commit
is unaffected.
- CLI: --from optional (required for normal mode, rejected for memory mode);
help gains a memory example.
- Fix a syntax regression in crates/ragfs/src/lock/provider.rs test module that
blocked `make build` (unrelated to compile; introduced by #4908).
- Docs: document the memory mode in ov-compile-design.md.
- Tests: unit tests for provider/runner/request validation; integration script
test_compile_memory_xiaomei.py with merge/split/dedup/preferences cases.
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* docs(compile): document memory consolidation mode
Add a dedicated user-facing page (zh + en) for `ov compile --skill memory`
covering when to use it, usage, parameters, behavior, and the adds/updates/
deletes result. Link it from the context-compilation overview. The VitePress
sidebar picks the new page up automatically from the directory listing.
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* fix(compile): honor language and cancellation
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory: migrate files when URI fields change
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory: materialize URI moves at apply time
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory: inherit source links on explicit merge
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* test(memory): cover rename conflict and streaming migration
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* test(compile): assert URI migration diff semantics
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* docs(compile): clarify cross-type link migration
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory: fail rename on target read errors
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory: preserve omitted URI identity fields
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory: rebase URI moves on latest source
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory: protect occupied empty rename targets
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory: reject case-only URI moves
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* docs(compile): document URI migration semantics
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory: validate explicit replacement targets
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory: reject case-only replacement moves
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory: rerender managed links after URI moves
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* test(compile): add repeatable Chinese URI rename case
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* test(compile): cover bidirectional URI renames
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory: remove empty directories after URI moves
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory(prompts): lowercase filename identity segments
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory: consolidate memories-root in one extract loop
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* docs(compile): document memory-root consolidation and failure semantics
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* test(compile): add memory_root and deterministic rename cases
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory(compile): honor Account templates and preserve merged duplicate links
- MemoryCompileRunner._consolidate now resolves the Account-level memory
template snapshot via resolve_account_memory_registry(), matching the
session.commit extract path, so consolidation no longer overwrites
operator-customized content templates with deployment defaults.
- _inherit_deleted_link_relations now tracks the deleted source URI for
each inherited link. Implicit rename targets only exclude contributions
copied from their own migration source, so links unique to a duplicate
merged into the same target (delete_replacements) are folded in and the
neighbor backlinks match.
- Regression tests cover the Account-template snapshot and a same-batch
rename+merge where only the duplicate holds a link to a third file.
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory: preserve rename sources through session commit queue
- ExtractLoop._updated_uri_for_existing_operation only considers an
identity-field change a rename when the merged value actually differs
from the current one, and returns the source URI unchanged when the
regenerated candidate matches it. Legacy paths whose new template
differs only in case (e.g. preferences user "Alice") no longer trip
the case-only migration guard on plain content updates.
- clone_operation_for_uri no longer drops old_memory_file_content when
the target URI differs from the source. The upstream
_materialize_uri_migrations still detects the mismatch and generates
a write-new + delete-old migration, but the queue clone keeps the
source content so the migration can inherit prior body and links
instead of turning a rename into an empty new record.
- python_protocol contract preamble drops the misleading "Unknown
business fields are ignored" clause; unknown fields raise at parse
time, so the note was inaccurate.
- Regression tests cover legacy case-only preferences updates going
through ExtractLoop and split_request_by_merge_group preserving the
rename source for the add + delete pair.
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* vikingbot(cli): pin openviking log stream to stderr before eager imports
vikingbot chat --eval / -e commits to a stdout JSON contract. Downstream
consumers like benchmark/locomo/vikingbot/run_eval.py parse stdout with
json.loads, so any log line on stdout breaks the parse and drops
token_usage and iteration to defaults (0). That is what caused
Total prompt tokens=0 and Avg iteration=0 in the LoCoMo summary.
Move the openviking / openviking_cli log redirection into a module-level
_preimport_redirect_openviking_logs_to_stderr() that runs before any
vikingbot.agent.* or openviking.* imports. Those imports call get_logger()
at module load time, which loads ov.conf and can emit warnings (e.g.
"Ignoring unknown config field") through openviking_cli's shared
QueueListener whose default output is stdout. Force the "stdout" listener
plus real StreamHandler pair into existence up front and rebind its
stream to stderr. Also move the in-chat() redirect ahead of ensure_config
and extend it to swap the shared stdout handler as a second-line guard.
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory(python-protocol): silently drop unknown-field edits
Extraction DSL programs occasionally reference a field name that does not
exist in the memory schema. Failing the whole program on this is
brittle: unrelated valid statements in the same commit are lost. Match
the tolerance kwargs already have on create()/set()/update() and treat
unknown-field attribute access as a compile-time no-op: return a
_FieldHandle flagged is_noop=True, and skip any .update()/.edit()/.drop()
chained on it plus the final _apply_field_handle. Sibling operations on
real fields keep applying.
Update the corresponding regression tests: replace the literal-`field`
placeholder rejection test with two new cases asserting the whole program
still commits and a real content.edit() still lands when a bogus field
appears in the same batch.
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* memory: tree-lock delete parents so overview cleanup covers empty dir rm
`generate_overview` recursively removes a memory directory once the last file
is deleted, but `_operation_tree_lock_paths` only tree-locked rename source
parents. Batches that plain-deleted the last file in a directory ran the
follow-up `rm -r` under a lease that did not cover the parent, and RAGFS
rejected the request with "pathlock lease ref does not cover the requested
operation". Add each delete's parent directory (unless a same-directory
rename replaces it) to the tree-lock set.
Co-authored-by: TRAE CLI <traecli@bytedance.com>
---------
Co-authored-by: TRAE CLI <traecli@bytedance.com>
DSH's plugin manager lists every bundle row, group rows included, but
resolves row state and toggles through the running-plugin inventory,
which skips group entries. The outer `@deepseek-ai/cordis-plugin-group`
row therefore always showed as disabled, and enabling it failed with
`unknown-plugin`.
The group only isolated the `openvikingMemory` service, which nothing
consumes, so the bundle now inserts the runtime row directly under the
same row id. Desktop toggles and id-targeted overrides keep applying,
and the docs show the id-targeted override form, which also applies to
the old nested layout.
The card resolves its text and icon through the package `exports` map, so a
manifest exporting only `.` left it with neither: export `./package.json` and
`./locale/*.json`, declare `icon: ./icon.svg`, ship both in `files`, and add
the English and Chinese dictionaries. The mark draws in one ink per colour
scheme (black on light surfaces, white on dark) rather than a fixed gradient,
so it stays legible on the card's dark background.
The peer ranges also admit the `0.1.7-rc` series from `rc.2` on, a pre-release
series semver otherwise rejects.
Version 0.5.7, as the repository's version-bump check requires.
* fix(pi): honor conversation capture filters in faithful mode
Apply the shared turn filter before capture decisions so configured rules cannot be bypassed by takeover or attached tool parts, while preserving tool values and behavior without applicable rules.
(cherry picked from commit e8fb6c9476)
Signed-off-by: Hao Zhe <haozhe4547@gmail.com>
* fix(opencode): wire captureFilters through config and the capture path
captureFilters (sed-style redaction rules, e.g. s/sk-.../[REDACTED-SK]/g)
was defined in the shared capture-utils library but never reached the
OpenCode capture path: lib/config.mjs had no mapping for the key and
memory-session's buildCapturePayload applied no filtering, so secrets
typed into OpenCode sessions were captured verbatim. The Codex plugin
applies the identical library correctly (issue #4984).
- map captureFilters in config (file key, OPENVIKING_CAPTURE_FILTERS env
override as JSON, non-string entries dropped)
- apply filterCaptureParts() to extracted parts in buildCapturePayload,
mirroring the Codex reference flow (role-level drops skip the turn;
an empty filtered result gates capture)
- cover config mapping, env override and validation in tests
(cherry picked from commit b5bd21930d)
Signed-off-by: Hao Zhe <haozhe4547@gmail.com>
* refactor(capture): share sanitized filter path across adapters
* test(capture): preserve mixed tool messages and cover OpenCode v2
* fix(capture): cap mixed text and preserve dated logs
* fix(capture): use shared shaping in Claude Code hooks
* fix(capture): sanitize fallback text once
---------
Signed-off-by: Hao Zhe <haozhe4547@gmail.com>
Co-authored-by: hemingzhe <hehesmilett@163.com>
Extract working-memory, archive, checkpoint, and tool-output
externalization logic from session.py into dedicated modules:
- working_memory.py: working-memory assembly and inline-image redaction
- archive_store.py: archive state/read and abstract extraction
- checkpoints.py: checkpoint planning and snapshots
- tool_output_externalizer.py: externalized tool result storage/hydration
Session delegates to these modules while preserving its public API.
Also aligns the session_auto_commit configuration with the split.
Co-authored-by: TRAE CLI <traecli@bytedance.com>
The provider ignored the host-provided agent_context, so sessions started
for scheduled cron jobs, delegated subagents, and flush forks recorded
turns and committed into OpenViking exactly like interactive sessions.
Fixed-prompt output landed in the memory bank as user context and every
scheduled run paid summarization plus extraction.
Read agent_context in initialize() (default "primary", so hosts that
predate the keyword keep the previous behavior) and skip sync_turn,
on_session_end, on_session_switch, and on_memory_write for the
non-primary set. Recall and prefetch are unchanged.
Fixes#5345
opencode awaits chat.message before persisting/broadcasting the user
message, so the serial session-inject -> recall waterfall stacks remote
latency onto message display (3-5s on the first message of a session
with a remote embedding endpoint; #5148). The two injections are
independent after hook entry; overlap them the way the dsh plugin
does (#4643).
Co-authored-by: ligjn <ligjn@users.noreply.github.com>
* feat(opencode-plugin): support the OpenCode v2 plugin API
OpenCode 2 does not run v1 hook plugins. Keep the existing server()
entrypoint and add setup() so one package serves both, adapting MCP,
recall, capture, and lifecycle events to the v2 shapes.
Refs #5226
* fix(opencode-plugin): align v2 lifecycle handling
* docs(opencode-plugin): document v2 support
* fix(opencode-plugin): preserve capture across v2 compaction
* fix(opencode-plugin): drop reasoning and keep state on failed v2 executions
v2 capture turned reasoning blocks into assistant text, so chain-of-thought
reached memory extraction and inflated pending tokens. v1 parts and the shared
capture filter drop reasoning; v2 now does the same.
A failed v2 execution ends one turn, not the session. Mapping it to v1's
session.error committed and deleted the session state, so a later capture
without a cursor resent every earlier turn. Failed executions now take the
same idle path as succeeded and interrupted ones.
* fix(opencode-plugin): scope v2 capture to the plugin location and persist its cursor
One OpenCode v2 service runs a plugin instance per location, and the plugin
event stream carries every location's events. Each instance therefore
captured and committed every session, storing each message once per open
project. Handle only lifecycle events whose session belongs to this
location, resolved from session.created or ctx.session.get because execution
events carry no envelope location.
The capture cursor lived only in memory, and the shared session state file
is rewritten by every instance, so a reloaded or evicted instance could
resend the transcript since the last compaction. Keep the cursor in plugin
storage and remove it when the session is deleted.
* docs(opencode-plugin): describe v2 commit points and location scoping
---------
Co-authored-by: Trent Telfer <4094016+ttelfer@users.noreply.github.com>
* docs: add Enterprise Deployment guides (checklist, install, operations)
Restore the bilingual Enterprise Deployment section that was drafted on
2026-09-22 but lost from the working tree before it was committed.
- Add zh/en guides 19-deployment-checklist, 20-private-deployment and
21-private-operations under Configuration & Deployment as a nested
sidebar group in docs-navigation.ts.
- Link the section from 03-deployment and the home deployment strip;
fix the Helm chart path (deploy/helm/openviking) and install flags.
- Register VPButton globally for the contact CTA on the install page.
- Keep the source/verification notes in docs/.vitepress/deployment-context.md
(excluded from site output and llms.txt).
* test(docs): expect the Enterprise Deployment group after Server Deployment
`list` was the only listing tool whose `uri` was required; `tree` and `glob`
already default to viking://, so a call without a uri failed validation
instead of listing the root.
When `edit` finds no match but the old_string would match after normalizing
CRLF/LF, the error now says so instead of only asking for a re-read.
Co-authored-by: wzc1753 <81469626+wzc1753@users.noreply.github.com>
Setup (--peer-role, the interactive prompt) and the installer
(--peer-role, OPENVIKING_PEER_ROLE, the interactive prompt) now reject
"person" and say it was renamed to "sender". Existing configs that still say
peer_role=person keep working: config parsing, the plugin manifest enum and
reads of the current config are unchanged.
* fix(openclaw): widen to unscoped recall when peer_role=sender has no sender
OpenClaw does not pass runtimeContext to context-engine assemble(), and cron,
heartbeat and webchat turns carry no sender. With peer_role=sender the plugin
threw "requires a sender identity" there, so auto-recall failed every turn and
tools without a sender errored.
Peer scoping is soft isolation: a missing sender now warns and continues as an
unscoped request (no X-OpenViking-Actor-Peer), like the MCP proxy (#5132).
memory_forget refuses in that state, since an unscoped search could pick and
delete another sender's memory. Capture already stored no peer_id when the
sender is missing and is unchanged.
* fix(openclaw): let memory_forget widen like find when the sender is missing
Peer scoping is soft isolation; forget follows the same unscoped fallback
as recall and find instead of refusing.
Cursor, TRAE, ZCode and Kimi Code capture through addAgentMessages and sent
their peer only as the X-OpenViking-Actor-Peer header. Session routes never
read that header (they use get_session_request_context), so messages landed
without a peer and their memories went to the user-level layer instead of
peers/<peer>. Claude Code and Codex already put peer_id in the body.
addAgentMessages now takes the peer and stamps it on payloads that do not
name one; the hook passes the same effective peer the header carries. When
peer mode is off the peer is empty and nothing is stamped.
Co-authored-by: somewhere1994 <108641179+somewhere1994@users.noreply.github.com>
`recall-core.mjs` reads `options.excludeUris` and forwards it as the search
request's `exclude_uris`, but the DSH runtime built its recall options without
that key, so no configuration could stop a subtree from being recalled. The
generated per-directory context files (`viking://user/<space>/skills`,
`viking://user/<space>/resources`, `viking://agent/skills`) came back as ordinary
hits and carry only boilerplate text — on a vague prompt, 3 of 7 returned entries
were these files. The only remedy was deleting the data.
Add a `recallExcludeUris` list knob to the shared config schema and pass
`cfg.recallExcludeUris` through as `excludeUris` from the DSH recall call, which
is the single place that builds those options. The schema entry lands in
`memory-plugin-shared/lib` and is propagated to the claude-code and codex copies
by `sync.mjs`; those two plugins are marked `committed: true` there because a
host installs them from a directory in this repository, so their vendored copies
belong in git. `recall-core.mjs` already caps the forwarded list at 200 entries
and omits the field entirely when the list is empty, so the default behaviour and
the request body are unchanged.
Validation, from `examples/dsh-memory-plugin` after
`node ../memory-plugin-shared/sync.mjs` and `npm install`:
`node --test *.test.mjs` 77 tests, 76 passed, 0 failed, 1 skipped. The new
`recallExcludeUris reaches the search request` failed before the change with
`exclude_uris` undefined in the request body and passes after it; the companion
case asserts no `exclude_uris` field is sent when the knob is unset.
`node --check` passes on all three changed source files.
`examples/claude-code-memory-plugin` fails 6 tests in
`scripts/auto-capture.test.mjs` on this Windows machine. Those tests spawn a real
subprocess that talks to a mock server on 127.0.0.1; they fail identically with
this change stashed and with a pristine checkout, so they are pre-existing and
environmental rather than caused by this change.
Co-authored-by: ydflow <314143294+ydflow@users.noreply.github.com>
ensure_checkout's SRC_ROOT assignment dies with the command substitution that
calls it, so install_lib_dir never sees the checkout under REPO_DIR. OpenCode
is the only host that reads the installer's JavaScript from there, so it alone
failed with "Installer runtime not found"; REPO_DIR is now a candidate.
Co-authored-by: wangyu134 <wangyu134@58.com>
* fix(pi): keep covered system messages during takeover
transformContext() sliced covered system messages off with the user
turns, dropping the model's tool declarations and instructions on
pi >= 0.86, where the leading system message carries the base prompt and
tools and later system messages carry mid-conversation tool changes,
section updates and appended instructions. The overview stands in for
the archived conversation only, never for the system state.
Keep every system message in [0, boundaryIdx) in front of the overview
in original order; leave a system message inside the retained tail where
it is. On 0.80.3 there are no system messages in the branch, so this
preserves nothing and the behaviour is unchanged. On 0.87 the host
reconciles declared tools against the executable set each request, so
keeping the declarations introduces no duplicate.
Regression tests assert tool loadout and system prompt across pi's real
merge (@earendil-works/pi-ai getCurrentTools/getCurrentSystemPrompt),
resolved from any pi install and skipped on pi < 0.86 where there is
nothing to preserve.
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* fix(pi): align takeover archives with trim boundaries
Sync and drain the current branch before committing, calculate keep_recent_count from the actual capture projection, and freeze the exact boundary while asynchronous archive summaries are pending. Only a successful archive with its own non-empty overview may advance context trimming.
Persist capture gaps caused by permanent delivery failures, block takeover across those gaps, and make native compaction fail open while retaining Pi’s first-kept boundary.
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* feat(pi): add takeover archive recovery hints
Persist the exact archive and history URIs and append a recovery footer after the bounded overview. Only advertise list/read when both tools are active; grep remains optional and captured history is not described as the full raw transcript.
Document the delivery and pending-archive behavior and bump the stable Pi extension to 0.4.1.
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* fix(pi): anchor the takeover boundary on a pi entry id
The boundary was frozen on getBranch() entries as a user-turn count plus
the fingerprint of the entry in front of it, then applied to the context
hook's messages. getBranch() also holds takeover's own ov-takeover state
entries, which sit in front of every next user turn, model changes and,
after a pi compaction, the compacted-away prefix; the context messages
hold none of them. The fingerprint disagreed on the very next request and
the boundary reset, so takeover committed archives but never trimmed.
After a pi compaction the turn count overshot the context as well.
Make the boundary the id of the entry the covered prefix ends at. Freeze
and apply it on pi's context projection of the branch (compaction-aware,
context edits applied) and map the first kept user entry onto the hook's
messages by timestamp. Messages between the boundary and that turn - a
run that went on after a keepRecentTurns 0 commit, a branch summary /tree
left at the boundary - are covered by no archive and stay. A boundary
outside the active context sends the full context and is kept for a
return to that branch. 0.4.0 state is adopted on the first context hook,
and the pending archive shrinks to the archive and its boundary entry.
Persist takeover state on transitions and at shutdown instead of every
turn, since each entry carries the overview.
With takeover on, startup drained the current session and never replayed
other sessions' queued entries. Drain this session first, then hand the
rest to the generic replay once none of this session's entries remain for
it to drop untracked.
tests/takeover-session-manager.test.mjs drives the core against pi's real
SessionManager. It fails on the previous head; it passes on pi 0.87.0,
and its state-entry, compaction and tree-navigation checks also pass on
0.80.3 and 0.86.1.
* fix(pi): keep takeover inside the host's handler budget
pi hosts cap every extension event handler at 30s; omp logs "handler
timed out after 30000ms", drops the result and lets the handler run on
(#5275). Takeover polled the archive summary inside turn_end - 15 reads
2s apart, 28s before any real work - and a summary that never came
(phase 2 failed, or Working Memory disabled on the server) left a pending
archive that repeated that wait on every later turn.
Nothing waits for a summary in turn_end any more. The commit's overview
is read once; a pending archive is read once per later turn_end and once
in before_agent_start, so a summary finished between prompts or pi -p
processes trims the next request. When the commit's task has ended, or
the server no longer knows it, and a last read still finds nothing, the
pending archive is dropped and its frozen token pressure spent. A pending
archive whose boundary left the active context is dropped without a
read.
Each handler gives takeover a 25s deadline from its start. The drain gets
the time the commit does not need, the commit gets the time one read does
not need, and with less than 10s left the commit waits for a later turn.
The compaction handler still polls, because pi needs its summary now,
but only until the deadline; when it hands compaction back to pi it no
longer resets the boundary, since pi may yet cancel or fail its own
compaction. The drain's default budget drops from 60s to 10s, as startup
replay runs inside before_agent_start too.
Refer to boundaries saved by 0.4.1 and earlier as count-based, since the
released 0.4.1 still writes them, and bump the extension to 0.4.2.
* fix(pi): tell a finished archive by the server's own markers
A pending archive is dropped once no summary can come any more. That was
decided through GET /api/v1/tasks/{id}, but task records expire and a
server replica may not know another's task. The server keeps an archive's
terminal state in the archive itself instead: .done, written last once
commit phase 2 completed (after the Working Memory when that is enabled,
recording working_memory_enabled=false when it is not), and .failed.json
once phase 2 failed for good. Read those markers, as #5320 does to decide
completion, and drop the task lookup.
Co-authored-by: cocolord <17559402+cocolord@users.noreply.github.com>
---------
Co-authored-by: cocolord <17559402+cocolord@users.noreply.github.com>
* feat(acl): default shared resources to inherited manager access
Use immutable user:* manage at the shared root and inherit permissions without
extra creator grants. Tolerate temporarily different ACL index snapshots.
* feat(acl): unify permissions under attrs and support creation attributes
* refactor(acl): accept top-level ACL on resource creation
* fix(acl): bind import permissions to authorized targets
Reject internal ingestion options from public resource arguments and defer
ACL authorization until the final import target has been resolved.
* refactor(acl): isolate import permissions from parser arguments
Build ingestion options from explicit public inputs and keep parser arguments out
of post-processing so they cannot supply internal ACL updates. Remove redundant
mkdir ACL handling and make permission snapshot selection easier to follow.
* fix(acl): release import locks after permission failures
Release locally acquired leases when source commit fails or is cancelled,
and ensure artifact cleanup cannot skip post-processing lock release.
* fix(docs): use CLI labels in ACL API references
* refactor(acl): restore dedicated permission interfaces
Restore standalone ACL HTTP, CLI and SDK operations while keeping attrs
focused on its existing attributes. Retain creation-time ACL support and
align the documentation with the final permission model and interfaces.
* fix(acl): authorize connector imports before creating watches
Reject ACL changes before reserving a Watch so denied requests cannot leave it stuck executing. Cover denial and submission cancellation in the existing Watch cleanup test.
* fix(web-studio): align ACL guidance and restriction status
Describe inherited permissions without creator privileges in both locales. Show access restriction only for restricted mode and verify the existing toggle flow against inherited management grants.
Add clear semantics across resource ingestion, content writes, reindexing, RNFV scalar planning, SDKs, CLI, and Web Studio. Treat empty replace tags as a no-op while preserving clear through durable queues and only updating vector scalars when the normalized value changes.
Co-authored-by: TRAE CLI <noreply@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>
* fix(hermes): mirror native memory replacements and removals
Port the provider implementation from NousResearch/hermes-agent#100187 at 32f75a9e6728a9a3d2f50a870dab3715a1f34fd7, which continues #85860. Keep the existing profile and connection-generation fixes, use relative imports and context-preserving workers, and cover the external loader and native-memory bridge.
* test(hermes): retain native memory mirror regression coverage
* fix(hermes): require committed entry identity and report indexing failures
* fix(hermes): clarify asynchronous add indexing status