Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ac747d7
fix(memory): disable unsupported tool and skill extraction
chenjw Jul 18, 2026
b331304
refactor(memory): retire SessionCompressorV2
chenjw Jul 18, 2026
72e8060
docs: design service import cycle fix
chenjw Aug 10, 2026
f228f56
fix(import): break QueueFS service import cycle
chenjw Aug 10, 2026
d16b7a2
update
chenjw Aug 10, 2026
1fa876b
docs: design memory overview lock coverage fix
chenjw Aug 10, 2026
c2e0ea9
fix(memory): cover overview files in update leases
chenjw Aug 10, 2026
7ef798b
docs: design session commit default concurrency 50
chenjw Aug 11, 2026
938bfff
perf(queue): raise session commit concurrency to 50
chenjw Aug 11, 2026
cd15233
docs: revise session commit concurrency design
chenjw Aug 11, 2026
67068b4
docs: plan session commit default 8
chenjw Aug 11, 2026
6aebe5b
perf(queue): default session commit concurrency to 8
chenjw Aug 11, 2026
d91a9c9
fix(bot): disable cron during eval chat
chenjw Aug 11, 2026
9e55ae6
docs: design memory link lock stabilization
chenjw Aug 11, 2026
391224b
docs: plan memory link lock stabilization
chenjw Aug 11, 2026
f7ec8d2
fix(memory): stabilize link update lock coverage
chenjw Aug 11, 2026
963824b
docs: cover remapped post-group link locks
chenjw Aug 11, 2026
02b06a6
docs: design plain-content patch validation
chenjw Aug 11, 2026
4941002
docs: design first failing patch diagnostics
chenjw Aug 11, 2026
3633c7d
fix: report actual failing patch block
chenjw Aug 11, 2026
6e506e8
fix(memory): remap replacement links before locking
chenjw Aug 13, 2026
09ac204
fix(bot): include trusted identity in health probe
chenjw Aug 13, 2026
f01ade5
test: consolidate memory contract coverage
qin-ctx Aug 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion bot/tests/test_openviking_api_key_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -864,14 +864,21 @@ def test_validate_openviking_auth_allows_trusted_root(monkeypatch, capsys):
)

def _fake_probe(_server_url, path, *, headers=None):
if path == "/health":
assert headers == {
"X-OpenViking-Account": "acct",
"X-OpenViking-User": "admin",
"X-API-Key": "root-key",
}
return _auth_probe(data={"auth_mode": "trusted"})
if path == "/api/v1/system/status":
assert headers == {
"X-OpenViking-Account": "acct",
"X-OpenViking-User": "admin",
"X-API-Key": "root-key",
}
return _auth_probe(data={"status": "ok", "result": {"user": "admin"}})
return _auth_probe(data={"auth_mode": "trusted"})
raise AssertionError(f"unexpected auth probe path: {path}")

monkeypatch.setattr(config_loader_module, "_request_openviking_json", _fake_probe)

Expand Down
15 changes: 10 additions & 5 deletions bot/vikingbot/cli/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ def chat(
# Use unified default session ID
if session_id is None:
session_id = get_or_create_machine_id()
cron = prepare_cron(bus, quiet=is_single_turn)
cron = None if eval else prepare_cron(bus, quiet=is_single_turn)
channels = prepare_agent_channel(
config,
bus,
Expand All @@ -878,7 +878,7 @@ async def run():
try:
if is_single_turn:
# Single-turn mode: run channels and agent, exit after response
task_cron = asyncio.create_task(cron.start())
task_cron = asyncio.create_task(cron.start()) if cron is not None else None
task_channels = asyncio.create_task(channels.start_all())
task_agent = asyncio.create_task(agent_loop.run())

Expand All @@ -890,15 +890,20 @@ async def run():
# Cancel all other tasks
for task in pending:
task.cancel()
task_cron.cancel()
if task_cron is not None:
task_cron.cancel()
task_agent.cancel()

# Wait for cancellation
await asyncio.gather(task_cron, task_agent, return_exceptions=True)
background_tasks = [task_agent]
if task_cron is not None:
background_tasks.append(task_cron)
await asyncio.gather(*background_tasks, return_exceptions=True)
else:
# Interactive mode: run forever
tasks = []
tasks.append(cron.start())
if cron is not None:
tasks.append(cron.start())
tasks.append(channels.start_all())
tasks.append(agent_loop.run())

Expand Down
7 changes: 7 additions & 0 deletions bot/vikingbot/config/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,13 @@ def validate_openviking_auth(config: Config) -> None:
api_key = getattr(ov_server, "api_key", None)
if api_key:
headers["X-API-Key"] = api_key
if auth_mode == "trusted":
headers["X-OpenViking-Account"] = str(
getattr(ov_server, "account_id", "") or "default"
).strip()
headers["X-OpenViking-User"] = str(
getattr(ov_server, "admin_user_id", "") or "default"
).strip()

health = _request_openviking_json(server_url, "/health", headers=headers)
if not health.ok:
Expand Down
2 changes: 1 addition & 1 deletion docs/design/memory-link-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -1889,7 +1889,7 @@ PPR 传播(多种子叠加,按 3.2.7.4 配置表):
| `memory_updater.py` | `apply_operations()` 中链接分发(from→links, to→backlinks 写入 `.relations.json`)+ 行号修正 |
| `memory_type_registry.py` | `_parse_memory_type()` 解析 `link_enabled` 字段;支持 `dream_tasks` 配置 |
| `tools.py` | 搜索 API 新增 PPR 后处理层(6.1.1);prefetch 扩展 PPR(6.1.2) |
| `session/compressor_v2.py` | `_create_relations()` 使用新 `link()` 签名 |
| `session/compressor_v3.py` | 通过共享 `MemoryUpdater` 应用并持久化 memory links |
| `session/session.py` | `_run_memory_extraction()` 使用新 `link()` 签名 |
| `server/routers/relations.py` | `LinkRequest` 扩展 `direction`/`link_type`/`weight` 等字段 |
| `sdk/python/openviking_sdk/client.py` | `link()` 方法签名扩展 |
Expand Down
41 changes: 22 additions & 19 deletions docs/design/session-memory-extraction-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,21 @@ runs configured memory extraction, but skips the archive summary.

| Group | Types | Target |
| --- | --- | --- |
| Long-term memory extraction | Enabled registry schemas with `stage: user` | Self and peer |
| Execution memory extraction | Execution-derived schemas, currently `trajectories`, `experiences` | Self only |
| Session skills | `SESSION_SKILL_MEMORY_TYPE` output | Self only |
| User-memory extraction | Enabled registry schemas with `stage: user`, including `cases` | Self and peer, subject to schema policy |
| Case-driven training | `trajectories`, `experiences` | Self only |
| Executable session skills | Optional output of case-driven training | Self only |

Memory schemas default to `stage: user` and `peer_enabled: true`. Set
`stage: agent` for schemas that are extracted only by the execution-memory
providers. Set `peer_enabled: false` for user-stage schemas that should ignore
`peer_id` and `ranges` peer targets and remain under the current user space
(for example `cases`).
`peer_enabled: false` for user-stage schemas that should ignore `peer_id` and
`ranges` peer targets and remain under the current user space (for example
`cases`). Execution-derived types are not exposed to the ordinary user-memory
extractor.

Trajectory/experience extraction is controlled by `memory_types`: omitted or
`null` allows both, while an explicit list must include those names. Session
skill extraction also requires self memory to be enabled and only runs when the
execution memory extraction phase has work.
`SessionCompressorV3.extract_long_term_memories` is the only public extraction
entry. It trains trajectories, experiences, and optional executable session
skills only when ordinary extraction produces at least one case. An explicit
execution-only `memory_types` policy does not invoke ordinary extraction, so it
cannot create a case and does not trigger training.

## Commit Flow

Expand All @@ -52,13 +53,14 @@ Implemented in `openviking/session/session.py`:
4. If peer memory is enabled, collect safe `message.peer_id` values from the
archived batch into `allowed_peer_ids`.
5. Start archive summary generation.
6. If long-term memory extraction is enabled and allowed by `memory_types`, call
`SessionCompressorV2.extract_long_term_memories` once with the full archived
6. Remove execution-derived types from the schema whitelist passed to ordinary
extraction. If enabled user-memory types remain, call
`SessionCompressorV3.extract_long_term_memories` once with the full archived
batch, `allow_self_memory`, and `allowed_peer_ids`.
7. If trajectory/experience extraction has work, call
`SessionCompressorV2.extract_execution_memories` once with the full archived
batch. When session skill extraction is enabled, it runs inside this execution
phase instead of starting a separate phase by itself.
7. V3 applies ordinary memory operations and collects extracted `cases`. When
at least one case exists, V3 runs streaming training for trajectories and
experiences and, when enabled, an executable session skill. With no case,
all three training outputs are skipped.

The current flow does not build separate buckets such as
`self_identity_messages`, `self_experience_messages`,
Expand Down Expand Up @@ -99,8 +101,9 @@ are initialized only when `allow_self_memory` is true.

## Practical Invariants

- Long-term extraction sees the full archived batch once.
- V3 user-memory extraction sees the full archived batch once.
- The extractor may emit self and peer operations in the same response.
- Final write targets are decided per operation by the isolation handler.
- Peer writes require safe peer IDs observed in the archived batch.
- `trajectories`, `experiences`, and session skills never write peer memory.
- `trajectories`, `experiences`, and executable session skills are trained only
from an extracted case and never write peer memory.
2 changes: 1 addition & 1 deletion docs/en/api/05-sessions.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Create a new session. Sessions are containers for conversations, storing message
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| session_id | str | No | None | Session ID. Creates new session with auto-generated ID if None |
| memory_policy | object | No | None | Default memory extraction policy for the session. Optional `self` and `peer` switches control write targets, optional `working_memory.enabled=false` skips archive summaries, and optional top-level `memory_types` limits extraction to specific enabled memory schemas. Use JSON booleans for every `enabled` value. Legacy boolean-like values remain accepted temporarily (including string `"false"`, which is parsed as false) but emit a deprecation warning. When `memory_types` is omitted or `null`, all enabled memory schemas are allowed. Invalid shapes or unknown memory types are rejected with `InvalidArgumentError`. |
| memory_policy | object | No | None | Default memory extraction policy for the session. Optional `self` and `peer` switches control write targets, optional `working_memory.enabled=false` skips archive summaries, and optional top-level `memory_types` limits extraction to specific enabled memory schemas. Including `experiences` automatically activates `cases` and `trajectories`; without `experiences`, explicitly supplied `cases` and `trajectories` are ignored. Use JSON booleans for every `enabled` value. Legacy boolean-like values remain accepted temporarily (including string `"false"`, which is parsed as false) but emit a deprecation warning. When `memory_types` is omitted or `null`, all enabled memory schemas are allowed. Invalid shapes or unknown memory types are rejected with `InvalidArgumentError`. |
| auto_commit_policy | object | No | None | Optional auto-commit policy (see table below). Any provided fields are validated, clamped to their bounds, and merged over the defaults; the effective policy is returned in the response `result.auto_commit_policy` and persisted into session metadata. If no policy is provided, auto commit is disabled unless `memory.session_auto_commit.default_enabled=true`. The policy can later be partially updated or disabled through `update_session_config()`. |

`auto_commit_policy` fields (all optional; omitted fields fall back to the defaults when a policy is present):
Expand Down
4 changes: 2 additions & 2 deletions docs/en/concepts/02-context-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ Memories are durable knowledge learned from interactions and task execution. The
| **cases** | `user/memories/cases/` | Task cases used for training and evaluation |
| **trajectories** | `user/memories/trajectories/` | Reusable task-execution trajectories |
| **experiences** | `user/memories/experiences/` | Reusable experience distilled from execution outcomes |
| **tools** | `user/memories/tools/` | Tool usage knowledge and best practices |
| **skills** | `user/memories/skills/` | Skill-execution knowledge and workflow strategies |

The `user/...` entries above are current-user short paths. The server resolves them to `viking://user/{user_id}/...`. When the memory policy permits Peer memory, supported types may instead be written under `viking://user/{user_id}/peers/{peer_id}/memories/...`. Applications can extend or adjust memory types with custom templates.

The schema-defined `memories/tools/` and `memories/skills/` types are disabled. They are separate from standalone Skills stored under `viking://user/{user_id}/skills/{skill_name}/SKILL.md`, which remain supported.

### Usage

```python
Expand Down
7 changes: 6 additions & 1 deletion docs/en/concepts/06-extraction.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,14 @@ await client.add_skill({
# Memory auto-extracted from session
await session.commit()

# Flow: SessionCompressorV2 → ExtractLoop → MemoryUpdater → SemanticQueue
# Flow: SessionCompressorV3 → ExtractLoop → MemoryUpdater → SemanticQueue
```

V3 has one extraction entry. It first extracts enabled user-memory schemas,
including `cases`. Trajectory, experience, and optional executable session-skill
training runs only when that extraction produces at least one case. A session
with no case therefore produces none of those execution-derived artifacts.

## Related Documents

- [Architecture Overview](./01-architecture.md) - System architecture
Expand Down
8 changes: 4 additions & 4 deletions docs/en/concepts/08-session.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ Unfinished tasks

After a session is committed, OpenViking uses the conversation and active memory policy to extract information that can improve future interactions. It stores the result in the current user's memory space. When a conversation involves a stable Peer, relevant memories can also be stored in that Peer's space.

OpenViking includes memory types such as `profile`, `preferences`, `entities`, `events`, `identity`, `soul`, `cases`, `trajectories`, `experiences`, `tools`, and `skills`, and supports custom types for application-specific needs. See [Context Types](./02-context-types.md) for the complete purpose and path mapping.
OpenViking includes memory types such as `profile`, `preferences`, `entities`, `events`, `identity`, `soul`, `cases`, `trajectories`, and `experiences`, and supports custom types for application-specific needs. See [Context Types](./02-context-types.md) for the complete purpose and path mapping.

Within `memory_policy.memory_types`, `experiences` enables the complete Agent Evolution pipeline and automatically activates `cases` and `trajectories`. If `experiences` is absent, explicitly supplied `cases` and `trajectories` entries are ignored without an error.

### Extraction Flow

Expand Down Expand Up @@ -249,9 +251,7 @@ viking://user/memories/
├── events/
├── cases/
├── trajectories/
├── experiences/
├── tools/
└── skills/
└── experiences/
```

`viking://user/sessions/{session_id}` is accepted as a short form relative to
Expand Down
2 changes: 1 addition & 1 deletion docs/en/configuration/01-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ This setting controls queue-job concurrency. It is separate from `vlm.media.max_

| Field | Type | Default | Description |
|---|---|---:|---|
| `max_concurrent` | integer | `4` | Number of SessionCommit jobs consumed concurrently; must be greater than `0`; requires a server restart after changes |
| `max_concurrent` | integer | `8` | Number of SessionCommit jobs consumed concurrently; must be greater than `0`; requires a server restart after changes |

## HTTP Server Settings

Expand Down
3 changes: 0 additions & 3 deletions docs/en/guides/07-operation-telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,17 +270,14 @@ This group appears on memory-extraction flows such as `session.commit`.
| `summary.memory.extract.duration_ms` | Total duration of the memory-extraction flow |
| `summary.memory.extract.candidates.total` | Total extracted candidates before final actions |
| `summary.memory.extract.candidates.standard` | Standard memory candidates |
| `summary.memory.extract.candidates.tool_skill` | Tool or skill candidates |
| `summary.memory.extract.actions.created` | Number of newly created memories |
| `summary.memory.extract.actions.merged` | Number of merges into existing memories |
| `summary.memory.extract.actions.deleted` | Number of deleted old memories |
| `summary.memory.extract.actions.skipped` | Number of skipped candidates |
| `summary.memory.extract.stages.prepare_inputs_ms` | Time spent preparing extraction inputs |
| `summary.memory.extract.stages.llm_extract_ms` | Time spent in the LLM extraction call |
| `summary.memory.extract.stages.normalize_candidates_ms` | Time spent parsing and normalizing candidates |
| `summary.memory.extract.stages.tool_skill_stats_ms` | Time spent aggregating tool or skill stats |
| `summary.memory.extract.stages.profile_create_ms` | Time spent creating or updating profile memory |
| `summary.memory.extract.stages.tool_skill_merge_ms` | Time spent merging tool or skill memories |
| `summary.memory.extract.stages.dedup_ms` | Time spent deduplicating candidates |
| `summary.memory.extract.stages.create_memory_ms` | Time spent creating new memories |
| `summary.memory.extract.stages.merge_existing_ms` | Time spent merging into existing memories |
Expand Down
Loading