Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cff5ace
docs(rfc): specify bounded memory entry pagination
wutongyuonce Sep 28, 2026
65a27d7
feat(memory): track paged directory state
wutongyuonce Sep 28, 2026
a5b5a7e
feat(memory): query bounded entry directory
wutongyuonce Sep 28, 2026
ca6e2c1
feat(memory): gate directory queries on verified migration
wutongyuonce Sep 28, 2026
f64ce41
feat(server): add memory query migration command
wutongyuonce Sep 28, 2026
6189d69
feat(memory): expose bounded directory query
wutongyuonce Sep 28, 2026
d07ae36
perf(memory): batch directory backfill writes
wutongyuonce Sep 28, 2026
902ad65
docs(integrations): register memory query tool
wutongyuonce Sep 28, 2026
010304c
fix(integrations): align memory query support metadata
wutongyuonce Sep 28, 2026
b9f8ea6
docs(rfc): finalize memory pagination design
wutongyuonce Sep 28, 2026
0737559
merge: integrate master and refresh DSH operation bundle
wutongyuonce Sep 28, 2026
6eb0eb3
merge: resolve pagination conflicts with master
wutongyuonce Sep 28, 2026
616affb
feat(memory): add compact Atomic directory with unified migration
wutongyuonce Oct 12, 2026
5141307
refactor(memory): replace collection pagination with Atomic directory
wutongyuonce Oct 12, 2026
f5b06e8
fix(integrations): synchronize Atomic directory contracts and fixtures
wutongyuonce Oct 12, 2026
a479b19
refactor(memory): remove superseded directory repair entry point
wutongyuonce Oct 12, 2026
0406d4a
merge: synchronize Atomic directory with current master
wutongyuonce Oct 13, 2026
571253e
fix(memory): reject noninteger SQLite directory generations
wutongyuonce Oct 13, 2026
8321349
merge: synchronize directory contracts with Handoff and Kimi updates
wutongyuonce Oct 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
2 changes: 1 addition & 1 deletion desktop/src-tauri/src/transport/operations.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"contractSha256": "5c00096f12642263d6bc538f7271a1484e1f8a17660786b12563cac9f4d6b0ab",
"contractSha256": "271dee86394d08568133a68022bdbc36b3d75c91619e838643c65a9fb506752c",
"operations": {
"get_liveness": {
"method": "GET",
Expand Down
106 changes: 106 additions & 0 deletions desktop/ui/src/generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@

// Generated from openapi/powercontext.yaml. Do not edit.
export interface paths {
"/v1/atomic-memory/directory/query": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
/**
* Query compact current Atomic Memory references
* @description Returns current metadata and exact revisions without loading Memory text or history. Any directory or tag change invalidates continuation; restart from the first page on invalid_cursor or cursor_expired. Requires verified directory migration and writer composition, and a supported local authorization configuration. Production enablement is unavailable until the unified migration bundle manages the complete target. Unavailable codes include artifact_owner_pending, atomic_memory_directory_authorization_unavailable and atomic_memory_directory_unavailable; do not treat failure as an empty page.
*/
post: operations["query_atomic_memory_directory"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/v1/atomic-memory/list": {
parameters: {
query?: never;
Expand Down Expand Up @@ -4787,6 +4807,35 @@ export interface components {
items: components["schemas"]["AccessAuditEvent"][];
next_cursor: string | null;
};
QueryAtomicMemoryDirectoryRequest: {
scope_id: string;
/**
* @default [
* "active"
* ]
*/
states: components["schemas"]["AtomicMemoryState"][];
kind?: string | null;
/** @default [] */
tags: string[];
/** @default all */
tag_match: components["schemas"]["TagMatch"];
/** @default 50 */
limit: number;
cursor?: string | null;
};
AtomicMemoryDirectoryItem: {
artifact: components["schemas"]["ArtifactReference"];
kind: string;
state: components["schemas"]["AtomicMemoryState"];
state_version: number;
merged_into_id: string | null;
};
QueryAtomicMemoryDirectoryResponse: {
scope_id: string;
items: components["schemas"]["AtomicMemoryDirectoryItem"][];
next_cursor: string | null;
};
};
responses: {
/** @description The request query or pagination cursor is invalid. */
Expand Down Expand Up @@ -4927,6 +4976,63 @@ export interface components {
}
export type $defs = Record<string, never>;
export interface operations {
query_atomic_memory_directory: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody: {
content: {
"application/json": components["schemas"]["QueryAtomicMemoryDirectoryRequest"];
};
};
responses: {
/** @description Compact current directory page. */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["QueryAtomicMemoryDirectoryResponse"];
};
};
/** @description The cursor is invalid or its query, identity, policy or directory generation changed. */
400: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["ErrorResponse"];
};
};
401: components["responses"]["Unauthorized"];
403: components["responses"]["Forbidden"];
404: components["responses"]["NotFound"];
/** @description The cursor expired; restart from the first page. */
410: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["ErrorResponse"];
};
};
/** @description A compact item exceeds the response budget; no partial page is returned. */
413: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["ErrorResponse"];
};
};
422: components["responses"]["InvalidRequest"];
500: components["responses"]["InternalError"];
503: components["responses"]["Unavailable"];
};
};
list_atomic_memories: {
parameters: {
query?: never;
Expand Down
2 changes: 1 addition & 1 deletion desktop/ui/src/generated/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

// Generated from openapi/powercontext.yaml. Do not edit.
export const contractSha256 = "5c00096f12642263d6bc538f7271a1484e1f8a17660786b12563cac9f4d6b0ab";
export const contractSha256 = "271dee86394d08568133a68022bdbc36b3d75c91619e838643c65a9fb506752c";
export const operations = {
"get_liveness": {
"method": "GET",
Expand Down
46 changes: 46 additions & 0 deletions docs/en/docs/workflows/atomic-memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,52 @@ both accept `kind`, `tags` and `tag_match: "all" | "any"`, with limits of 1–10
[Artifact tag API](manage-artifact-tags.md) with `{type: "artifact", family: "atomic-memory", artifact_id: "M"}`
for new targets.

## Compact directory pagination

To browse memory identities and states, call `POST /v1/atomic-memory/directory/query`:

```json
{"scope_id": "S", "states": ["active"], "kind": "decision", "limit": 50}
```

The response contains `scope_id`, `items` and `next_cursor`. Each item contains only the exact `artifact` reference,
`kind`, `state`, `state_version` and `merged_into_id`; it loads no content, sources or history. Results are ordered by
`artifact.artifact_id` ascending. Read the returned exact revision when content is needed, so a later replacement
does not change the meaning of this page.

Keep the Scope, principal, filters and limit unchanged and copy `next_cursor` into the next request's `cursor`.
Creation, replacement, lifecycle changes, merge, restore or effective tag changes invalidate older cursors.
On `400 invalid_cursor` or `410 cursor_expired`, discard the continuation and restart at page one. This is a current
directory; it does not guarantee that a traversal completes during continuous writes. The default limit is 50 and
maximum is 100. `tags` accepts at most 16 labels of at most 64 characters each, with `tag_match: "all" | "any"`.
Every page rechecks access. Revocation prevents continuation; sharing one Artifact does not grant directory access.

The HTTP SDK and MCP operation is `query_atomic_memory_directory`; the Python scoped method is `query_directory`.
Unmanaged deployments return `503 atomic_memory_directory_unavailable`. The optional registered target supports
persistent SQLite, without an embedding extension, and the default business layout created by the current program.
Stop all writers and use the same deployment environment file:

This target uses the default evidence directory next to the database; a custom `--evidence-dir` is not supported.

```bash
powercontext server db-migrate plan --target atomic-memory-directory --env-file deployment.env
powercontext server db-migrate apply --target atomic-memory-directory --env-file deployment.env
powercontext server db-migrate verify --target atomic-memory-directory --env-file deployment.env
```

The explicit migration adds directory metadata and backfills only exact current Atomic content. Restart the matching
program after successful verification; normal startup does not add or backfill directory fields. Other modules retain
their existing initialization behavior. Completing legacy Memory migration alone does not establish this target's
readiness: unregistered archive objects, historical layouts and vector configurations are rejected. Inspection and
verification do not repair data. Interrupted maintenance retains its original recovery point and blocks business restart.

Directory access supports trusted local reads or native Builtin/Casbin Scope-read configurations on the same SQLite
database. Formal ownership is still checked across the entire Scope and is never guessed during migration. Unsupported access configurations
return `503 atomic_memory_directory_authorization_unavailable`. An item exceeding the response budget returns
`413 atomic_memory_directory_item_too_large`, without a partially successful page.
Restore only under stopped writes, with matching program/configuration and preserved recovery evidence. Rotate the
existing cursor signing secret before resuming service so cursors from before database restoration are rejected.

## Merge and restore

`POST /v1/atomic-memory/merges` requires at least two exact active input references and state versions, plus result content:
Expand Down
41 changes: 41 additions & 0 deletions docs/zh/docs/workflows/atomic-memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,47 @@ Atomic Memory 将每条事实、偏好或决策保存为独立的 `atomic-memory
`tag_match: "all" | "any"`,条数范围为 1–100。标签使用
[Artifact 标签接口](manage-artifact-tags.md),新目标为 `{type: "artifact", family: "atomic-memory", artifact_id: "M"}`。

## 紧凑目录分页

只需浏览记忆身份和状态时,调用 `POST /v1/atomic-memory/directory/query`:

```json
{"scope_id": "S", "states": ["active"], "kind": "decision", "limit": 50}
```

响应包含 `scope_id`、`items` 和 `next_cursor`。每项只有精确 `artifact` 引用、`kind`、`state`、
`state_version` 与 `merged_into_id`,不加载正文、来源或历史。按 `artifact.artifact_id` 升序返回;
需要正文时,用返回引用调用精确 revision 读取接口,避免将后来修订的正文当成本页结果。

保持 Scope、主体、筛选和 limit 相同,将 `next_cursor` 原样放入下一次请求的 `cursor`。
创建、修订、生命周期、合并、恢复或有效标签变化会使旧 cursor 失效;收到 `400 invalid_cursor`
或 `410 cursor_expired` 时丢弃续页位置,从第一页重新读取。这是当前目录,不保证持续写入期间完成一次遍历。
默认每页 50 项,最大 100;`tags` 最多 16 个、每个最多 64 字符,支持 `tag_match: "all" | "any"`。
每页重新检查权限,撤权后不能继续读取;单条 Artifact 分享不授予目录浏览权。

HTTP SDK 和 MCP 均使用 `query_atomic_memory_directory`;Python scoped 接口为 `query_directory`。
未纳管目录的部署返回 `503 atomic_memory_directory_unavailable`。首版可选目标支持持久 SQLite、无 embedding
向量扩展,以及当前程序创建的默认业务结构。停止所有写入者后,使用同一部署环境文件:

这个目标使用数据库旁的默认证据目录,首版不支持自定义 `--evidence-dir`。

```bash
powercontext server db-migrate plan --target atomic-memory-directory --env-file deployment.env
powercontext server db-migrate apply --target atomic-memory-directory --env-file deployment.env
powercontext server db-migrate verify --target atomic-memory-directory --env-file deployment.env
```

显式迁移增加目录字段,只回填当前 Atomic 精确正文中的类型。验证成功后启动匹配的新程序;普通启动不补目录
字段、不回填目录。其他模块保留原有初始化行为。旧 Memory 迁移成功不能单独证明这个目标就绪:未登记的归档
对象、历史结构和向量配置仍会被拒绝。检查和验证不修复数据;中断维护保留原恢复点,并阻止业务重新启动。

目录支持可信本地读取或同一 SQLite 数据库的原生 Builtin/Casbin Scope-read 配置。整个 Scope 的正式 owner
仍按共享规则检查,迁移不猜测或补造 owner。不支持的权限配置返回
`503 atomic_memory_directory_authorization_unavailable`。单项超出响应预算返回
`413 atomic_memory_directory_item_too_large`,不会返回部分成功页。
恢复数据库时保持停写,使用匹配的程序/配置并保留恢复证据;恢复后先轮换既有 cursor 签名密钥,再恢复服务,
让数据库恢复前的旧 cursor 全部失效。

## 合并与恢复

`POST /v1/atomic-memory/merges` 至少需要两个 active 输入的精确引用和状态版本,以及结果正文:
Expand Down
2 changes: 1 addition & 1 deletion evaluation/skills/skill-up/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ All relative config paths start at this project root, not at `evals/`.

Both arms receive the same `evals/fixtures/repos/powercontext-tool-contract/CLAUDE.md`: required/optional top-level
argument names, types and defaults derived from OpenAPI and its MCP specialization, including content CAS headers.
The mock catalog contains the Server's complete declared MCP tool subset (54 operations), verified against
The mock catalog contains the Server's complete declared MCP tool subset (55 operations), verified against
`src/powercontext/server/mcp.py` and the generated operation IDs. The successful save reply contains Atomic
`records`; empty search returns the current `text` mode and an empty `hits` list.
This fills the generic mock schemas' parameter information gap
Expand Down
2 changes: 2 additions & 0 deletions evaluation/skills/skill-up/evals/cases/empty-search.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ judge:
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__list_memory_entries}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__list_scopes}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__publish_artifact}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__query_atomic_memory_directory}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__query_code}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__record_task_outcome}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__reject_artifact_candidate}
Expand Down Expand Up @@ -99,6 +100,7 @@ judge:
- tool_not_called_in_turn: {turn: 2, name: mcp__powercontext__list_memory_entries}
- tool_not_called_in_turn: {turn: 2, name: mcp__powercontext__list_scopes}
- tool_not_called_in_turn: {turn: 2, name: mcp__powercontext__publish_artifact}
- tool_not_called_in_turn: {turn: 2, name: mcp__powercontext__query_atomic_memory_directory}
- tool_not_called_in_turn: {turn: 2, name: mcp__powercontext__query_code}
- tool_not_called_in_turn: {turn: 2, name: mcp__powercontext__record_task_outcome}
- tool_not_called_in_turn: {turn: 2, name: mcp__powercontext__reject_artifact_candidate}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ judge:
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__list_memory_entries}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__list_scopes}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__publish_artifact}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__query_atomic_memory_directory}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__query_code}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__record_task_outcome}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__reject_artifact_candidate}
Expand Down
1 change: 1 addition & 0 deletions evaluation/skills/skill-up/evals/cases/failed-save.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ judge:
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__list_memory_entries}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__list_scopes}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__publish_artifact}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__query_atomic_memory_directory}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__query_code}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__record_task_outcome}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__reject_artifact_candidate}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ judge:
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__list_memory_entries}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__list_scopes}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__publish_artifact}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__query_atomic_memory_directory}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__query_code}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__record_task_outcome}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__reject_artifact_candidate}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ judge:
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__list_memory_entries}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__list_scopes}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__publish_artifact}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__query_atomic_memory_directory}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__query_code}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__record_task_outcome}
- tool_not_called_in_turn: {turn: 1, name: mcp__powercontext__reject_artifact_candidate}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@
"publish_artifact": {
"error": "unexpected"
},
"query_atomic_memory_directory": {
"error": "unexpected"
},
"query_code": {
"error": "unexpected"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@
"publish_artifact": {
"error": "unexpected"
},
"query_atomic_memory_directory": {
"error": "unexpected"
},
"query_code": {
"error": "unexpected"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,11 @@ not replace those schemas or enforce their constraints. It contains no scenario
- Required: `idempotency_key: string`, `source: ArtifactAddress`, `target_scope_id: string`.
- Optional: (none).

## mcp__powercontext__query_atomic_memory_directory

- Required: `scope_id: string`.
- Optional: `cursor: string | null`, `kind: string | null`, `limit: integer = 50`, `states: array<"active" | "forgotten" | "merged" | "retired"> = ["active"]`, `tag_match: "all" | "any" = "all"`, `tags: array<string> = []`.

## mcp__powercontext__query_code

- Required: `operation: CodeStatusOperation | CodeChangesOperation | CodeMapOperation | CodeSearchOperation | CodeRelationOperation | CodeTestsOperation | CodeReadOperation`, `scope_id: string`.
Expand Down
3 changes: 3 additions & 0 deletions integrations/capabilities.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ capabilities = ["memory_read"]
id = "list_atomic_memories"
capabilities = ["memory_read"]
[[toolsets.tools]]
id = "query_atomic_memory_directory"
non_profile_reason = "Optional compact directory browsing requires the registered persistent SQLite directory migration and qualified Scope access; it is not a mandatory host profile operation."
[[toolsets.tools]]
id = "search_atomic_memory"
capabilities = ["memory_read"]
[[toolsets.tools]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"import_external_skill",
"list_artifact_candidates",
"list_atomic_memories",
"query_atomic_memory_directory",
"list_external_skills",
"list_managed_skills",
"list_memory_entries",
Expand Down
11 changes: 11 additions & 0 deletions integrations/dsh/plugins/powercontext/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,17 @@ function writeFailureConfirmation(error) {
//#endregion
//#region src/operations.generated.ts
const OPERATIONS$1 = {
query_atomic_memory_directory: {
method: "POST",
path: "/v1/atomic-memory/directory/query",
location: "body",
scopeMode: "current",
pathParameters: [],
queryParams: [],
headerParams: [],
successStatuses: [200],
emptyStatuses: []
},
list_atomic_memories: {
method: "POST",
path: "/v1/atomic-memory/list",
Expand Down
Loading
Loading