Skip to content

Commit 32fc6f2

Browse files
committed
feat: add paged knowledge reads and verified VextJS integration
Support UTF-8 document and specification pages, streaming reads, shared snippet verification and actual cancellation. Add native VextJS provider/runtime examples, fixed-source compatibility CI and real service/browser validation entrypoints. Reduce FileAuthority allocations and duplicate builds with source/output fingerprints. Preserve release snapshot, retrieval invalidation, capacity retirement and external temporary-directory fixes.
1 parent 6e0b1fa commit 32fc6f2

74 files changed

Lines changed: 1978 additions & 226 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
cache: npm
2727
- run: npm ci
2828
- run: npm test
29-
- run: npm run test:package
29+
- run: npm run test:package:built
3030
- run: npm ci --ignore-scripts
3131
working-directory: examples/seed-mcp
3232
- run: npm test

‎.github/workflows/docs-ci.yml‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ jobs:
5454
package-lock.json
5555
website/package-lock.json
5656
- run: npm ci
57-
- run: npm run build
5857
- run: npm ci
5958
working-directory: website
6059
- run: npm run generate
@@ -88,13 +87,10 @@ jobs:
8887
package-lock.json
8988
website/package-lock.json
9089
- run: npm ci
91-
- run: npm run build
9290
- run: npm ci
9391
working-directory: website
9492
- run: npm run build
9593
working-directory: website
96-
- run: npm run check:build
97-
working-directory: website
9894

9995
docs-browser:
10096
runs-on: ubuntu-latest
@@ -110,9 +106,12 @@ jobs:
110106
package-lock.json
111107
website/package-lock.json
112108
- run: npm ci
113-
- run: npm run build
114109
- run: npm ci
115110
working-directory: website
111+
- uses: actions/cache@v4
112+
with:
113+
path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
114+
key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
116115
- run: npx playwright install --with-deps chromium firefox webkit
117116
- run: npm run build
118117
working-directory: website

‎.github/workflows/docs-deploy.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
- run: npm ci
4646
working-directory: website
4747
- run: npx playwright install --with-deps chromium
48-
- run: npm run check:deployment && npm run check:types && npm run build && npm run test:site && npm run check:build && npm run check:package
48+
- run: npm run check:deployment && npm run check:types && npm run build:built && npm run test:site && npm run check:package
4949
working-directory: website
5050
env:
5151
DEPLOYMENT_KIND: documentation

‎.github/workflows/release.yml‎

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
steps:
3636
- uses: actions/checkout@v7
3737
with:
38-
ref: ${{ github.event_name == 'workflow_dispatch' && github.sha || github.ref }}
38+
ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', github.event.inputs.release_tag) || github.ref }}
3939
fetch-depth: 0
4040
persist-credentials: false
4141
- id: target
@@ -73,7 +73,7 @@ jobs:
7373
steps:
7474
- uses: actions/checkout@v7
7575
with:
76-
ref: ${{ github.event_name == 'workflow_dispatch' && github.sha || github.ref }}
76+
ref: ${{ needs.freeze.outputs.release_commit }}
7777
persist-credentials: false
7878
- uses: actions/setup-node@v7
7979
with:
@@ -84,16 +84,16 @@ jobs:
8484
website/package-lock.json
8585
- run: npm ci
8686
- run: npm test
87-
- run: npm run test:package
88-
- run: npm run evaluate
87+
- run: npm run test:package:built
88+
- run: npm run evaluate:built
8989
- run: npm ci --ignore-scripts
9090
working-directory: examples/seed-mcp
9191
- run: npm test
9292
working-directory: examples/seed-mcp
9393
- run: npm ci
9494
working-directory: website
9595
- run: npx playwright install --with-deps chromium
96-
- run: npm run check
96+
- run: npm run check:built
9797
working-directory: website
9898

9999
package:
@@ -108,15 +108,14 @@ jobs:
108108
steps:
109109
- uses: actions/checkout@v7
110110
with:
111-
ref: ${{ github.event_name == 'workflow_dispatch' && github.sha || github.ref }}
111+
ref: ${{ needs.freeze.outputs.release_commit }}
112112
persist-credentials: false
113113
- uses: actions/setup-node@v7
114114
with:
115115
node-version: "24"
116116
registry-url: "https://registry.npmjs.org"
117117
- run: npm install --global npm@11.15.0
118118
- run: npm ci
119-
- run: npm run build
120119
- id: pack
121120
name: Build the immutable package artifact
122121
shell: bash
@@ -232,7 +231,7 @@ jobs:
232231
steps:
233232
- uses: actions/checkout@v7
234233
with:
235-
ref: ${{ github.event_name == 'workflow_dispatch' && github.sha || github.ref }}
234+
ref: ${{ needs.freeze.outputs.release_commit }}
236235
persist-credentials: false
237236
- uses: actions/setup-node@v7
238237
with:
@@ -254,7 +253,7 @@ jobs:
254253
echo "npm latest is $latest, not $PACKAGE_VERSION" >&2
255254
exit 1
256255
}
257-
- run: npm run build
256+
- run: npm run build:built
258257
working-directory: website
259258
- uses: actions/configure-pages@v5
260259
- uses: actions/upload-pages-artifact@v4
@@ -306,7 +305,7 @@ jobs:
306305
steps:
307306
- uses: actions/checkout@v7
308307
with:
309-
ref: ${{ github.event_name == 'workflow_dispatch' && github.sha || github.ref }}
308+
ref: ${{ needs.freeze.outputs.release_commit }}
310309
persist-credentials: false
311310
- uses: actions/setup-node@v7
312311
with:

‎.github/workflows/vextjs-ci.yml‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: VextJS fixed-source compatibility
2+
3+
on:
4+
push:
5+
paths: ['src/**', 'examples/**', 'scripts/**', 'test/**', 'package*.json', 'tsconfig*.json', '.github/workflows/vextjs-ci.yml']
6+
pull_request:
7+
paths: ['src/**', 'examples/**', 'scripts/**', 'test/**', 'package*.json', 'tsconfig*.json', '.github/workflows/vextjs-ci.yml']
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read
12+
13+
jobs:
14+
native-and-session:
15+
runs-on: ${{ matrix.os }}
16+
timeout-minutes: 20
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
os: [ubuntu-latest, windows-latest]
21+
node: ['20.19.0', '22.12.0', '24.19.0']
22+
env:
23+
VEXT_SOURCE_COMMIT: ef926649926e17543562b8169982fa1786285ecf
24+
VEXT_ROOT: ${{ runner.temp }}/capability-graph-vextjs-fixed
25+
CG_ARTIFACTS_DIR: ${{ github.workspace }}/../capability-graph-artifacts
26+
steps:
27+
- uses: actions/checkout@v7
28+
with:
29+
persist-credentials: false
30+
- uses: actions/setup-node@v7
31+
with:
32+
node-version: ${{ matrix.node }}
33+
cache: npm
34+
- run: npm ci
35+
- run: npm run build && npm run build:tests
36+
- name: Fetch and verify the fixed framework source outside the project
37+
shell: bash
38+
run: |
39+
set -euo pipefail
40+
mkdir -p "$VEXT_ROOT"
41+
git -C "$VEXT_ROOT" init --quiet
42+
git -C "$VEXT_ROOT" remote add origin https://github.com/devcodex-labs/vextjs.git
43+
git -C "$VEXT_ROOT" fetch --depth=1 origin "$VEXT_SOURCE_COMMIT"
44+
git -C "$VEXT_ROOT" checkout --detach FETCH_HEAD
45+
test "$(git -C "$VEXT_ROOT" rev-parse HEAD)" = "$VEXT_SOURCE_COMMIT"
46+
- name: Install the framework's lockfile and build its actual CLI
47+
shell: bash
48+
run: npm ci --ignore-scripts --prefix "$VEXT_ROOT" && npm --prefix "$VEXT_ROOT" run build
49+
- name: Verify native catalog, retrieval and actual TCP Sessions
50+
shell: bash
51+
run: |
52+
set -euo pipefail
53+
mkdir -p "$CG_ARTIFACTS_DIR/verification"
54+
node scripts/validation/vextjs-compatibility.mjs "$VEXT_ROOT" "$VEXT_SOURCE_COMMIT" > "$CG_ARTIFACTS_DIR/verification/vextjs-compatibility.json"
55+
- uses: actions/upload-artifact@v4
56+
if: always()
57+
with:
58+
name: vextjs-${{ matrix.os }}-node-${{ matrix.node }}
59+
path: ${{ env.CG_ARTIFACTS_DIR }}/verification/
60+
retention-days: 7

‎README.md‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,9 @@ npm run evaluate
150150

151151
`npm run demo:http` 运行真实 Node.js HTTP 文档读取、能力词项召回、知识检索、过期拒绝和重建恢复,再请求业务路由。示例不调用模型;数据库与真实框架接入仍需分别实现和验证。
152152

153-
`npm run evaluate:capacity` 执行可复用的真实文件容量验证。默认 1 万节点、2 个 Provider、10 并发,混合目录/详情/八类邻居/选择/正文/规范查询,并交错定义更新与 reload,验证关闭时已 pin 的读取完成和新查询拒绝。扩大验证可运行 `npm run evaluate:capacity -- --nodes 100000 --providers 2 --concurrency 100 --operations 3000 --duration-ms 120000 --reload-every 1000`;持续时间是负载下限,等待中的 reload/读取会延长总耗时。临时来源执行后删除,JSON 报告保留在同级产物目录。延迟和采样内存只说明本机本次运行,不是生产容量承诺。
153+
`npm run evaluate:capacity` 执行可复用的真实文件容量验证。默认 1 万节点、2 个 Provider、10 并发:先完整遍历全局 Catalog,核对跨 Provider 的全部身份、无遗漏和重复;再强制每个 Provider 至少两轮 reload,验证旧 previous 退出可查询槽位,同时已 pin 的查询仍能读取原视图。随后混合目录/详情/八类邻居/选择/正文/规范查询,交错定义更新与 reload,最后验证关闭时读取完成和新查询拒绝。报告分别记录 `catalogCoverage`、`retirementChecks`、负载中的 `providerQueries` 与各阶段耗时;全局 Catalog 的负载计数按页面实际出现的 Provider 统计。
154+
155+
扩大验证可运行 `npm run evaluate:capacity -- --nodes 100000 --providers 2 --concurrency 100 --operations 3000 --duration-ms 120000 --reload-every 1000 --reload-rounds 2`。操作数至少为 `7 × Provider 数`;`--reload-rounds` 可增大但不能少于两轮,`--reload-every 0` 只关闭负载期间的交错 reload。持续时间是负载下限,完整遍历、强制 reload 及等待中的读取会增加总耗时。临时来源执行后删除,JSON 报告保留在同级产物目录。延迟和采样内存只说明本机本次运行,不是生产容量承诺。
154156

155157
容量工具每类延迟最多保留 4096 个样本,报告标明分位数来自全量还是抽样;计数和最大值持续累计,内存统计也使用固定大小,避免验证工具自身随运行时长持续增长。
156158

@@ -172,3 +174,9 @@ npm run evaluate
172174
## 许可证
173175

174176
Apache-2.0
177+
178+
大文档使用 `readDocumentPage` / `readSpecificationPage`,模型决定范围与是否续读。默认 32 KiB 是页大小,文档总大小不设准入门槛;全文与本页哈希分开,内容变化拒绝旧游标。旧全文接口保持完整返回语义,超过单次响应容量时改用分页。
179+
180+
VextJS 的固定原生 schema 2 接入与实际验证入口见 [examples/vextjs](examples/vextjs/README.md)。原生目录角色与来源保留,运行快照保持 partial/unknown/unverified;默认回归与外部框架、数据库、浏览器证据分开。
181+
182+
CI 的 `test:package:built`、`evaluate:built` 和 website `build:built` 先核对源码及全部输出指纹;普通命令仍从当前源构建。共享 dist 的构建必须串行,临时报告、服务数据和浏览器输出在仓库外。

‎examples/seed-mcp/src/server.ts‎

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { readFile } from "node:fs/promises";
2-
import path from "node:path";
31
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
42
import { z } from "zod";
53
import { CapabilityGraph, CapabilityGraphError, parseQualifiedId } from "@devcodex/capability-graph";
@@ -51,6 +49,14 @@ export function createSeedServer(graph: CapabilityGraph, providerRoot: string):
5149
...scope, selected: z.array(id), knowledgeIds: z.array(z.string()).optional(), roles: z.array(z.string()).optional(),
5250
locales: z.array(z.string()).optional(),
5351
} }, (query) => call(() => graph.readDocuments(query)));
52+
const bodyPage = { knowledgeId: z.string(), startOffset: z.number().int().nonnegative().optional(),
53+
maxBytes: z.number().int().positive().optional(), cursor: z.string().optional(), requiredStaticRevision: z.string().optional() };
54+
server.registerTool("example_read_document_page", { description: "Read a document page. For large sources, decide whether to continue with nextCursor. Full-document and page hashes are separate.", inputSchema: {
55+
...bodyPage, capability: id,
56+
} }, (query, extra) => call(() => graph.readDocumentPage({ ...query, signal: extra.signal })));
57+
server.registerTool("example_read_specification_page", { description: "Read one Specification document page and optionally continue with its version-bound cursor.", inputSchema: {
58+
...bodyPage, providerId: z.string(),
59+
} }, (query, extra) => call(() => graph.readSpecificationPage({ ...query, signal: extra.signal })));
5460
server.registerTool("example_read_specification", { description: "Explicitly read selected Specification documents from one Provider.", inputSchema: {
5561
providerId: z.string(), knowledgeIds: z.array(z.string()).optional(), locales: z.array(z.string()).optional(),
5662
requiredStaticRevision: z.string().optional(),
@@ -66,8 +72,11 @@ export function createSeedServer(graph: CapabilityGraph, providerRoot: string):
6672
...scope, project: z.string(), environment: z.string(), instanceOf: id.optional(), instanceId: z.string().optional(),
6773
requiredRuntimeRevision: z.string().optional(), cursor: z.string().optional(), limit: z.number().int().positive().optional(),
6874
} }, (query) => call(() => graph.queryRuntime(query)));
69-
server.registerResource("provider-specification", "seed://provider/specification", { mimeType: "text/markdown" }, async (uri) => ({
70-
contents: [{ uri: uri.href, mimeType: "text/markdown", text: await readFile(path.join(providerRoot, "PROVIDER.md"), "utf8") }],
71-
}));
75+
server.registerResource("provider-specification", "seed://provider/specification", { mimeType: "text/markdown" }, async (uri) => {
76+
const batch = await graph.forProvider("seed.http").readSpecification({ knowledgeIds: ["SPEC-01"] });
77+
const slot = batch.results[0];
78+
if (!slot || !slot.ok) throw new CapabilityGraphError(slot && !slot.ok ? slot.error.code : "CG_NOT_FOUND", { nextAction: "repair_source" });
79+
return { contents: [{ uri: uri.href, mimeType: "text/markdown", text: slot.value.text }] };
80+
});
7281
return server;
7382
}

‎examples/seed-mcp/test/server.test.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async function connected(run: (client: Client, graph: CapabilityGraph) => Promis
2828
}
2929
test("MCP: initialize and enumerate query tools, not one tool per capability", async () => connected(async (client) => {
3030
const list = await client.listTools();
31-
assert.equal(list.tools.length, 12);
31+
assert.equal(list.tools.length, 14);
3232
assert.ok(list.tools.every((tool) => tool.name.startsWith("example_")));
3333
assert.equal(JSON.stringify(list).includes("D-02"), false);
3434
}));
@@ -45,6 +45,10 @@ test("MCP: static and document queries exactly match the same Core API", async (
4545
await graph.readSpecification({ providerId: "seed.http", knowledgeIds: ["SPEC-01"] })],
4646
["example_read_documents", { selected: [canonical("route.validation"), canonical("schema.request")] },
4747
await graph.readDocuments({ selected: [canonical("route.validation"), canonical("schema.request")] })],
48+
["example_read_document_page", { capability: canonical("route.validation"), knowledgeId: "D-02", maxBytes: 20 },
49+
await graph.readDocumentPage({ capability: canonical("route.validation"), knowledgeId: "D-02", maxBytes: 20 })],
50+
["example_read_specification_page", { providerId: "seed.http", knowledgeId: "SPEC-01", maxBytes: 20 },
51+
await graph.readSpecificationPage({ providerId: "seed.http", knowledgeId: "SPEC-01", maxBytes: 20 })],
4852
];
4953
for (const [name, args, expected] of checks) {
5054
const actual = await client.callTool({ name, arguments: args });
@@ -108,7 +112,7 @@ test("MCP: actual stdio child serves requests and exits on close", async (contex
108112
let pid: number | null = null;
109113
try {
110114
await client.connect(transport); pid = transport.pid; assert.ok(pid);
111-
assert.equal((await client.listTools()).tools.length, 12);
115+
assert.equal((await client.listTools()).tools.length, 14);
112116
const result = payload(await client.callTool({ name: "example_list_catalog", arguments: {} }));
113117
assert.equal((result.items as unknown[]).length, 5);
114118
} finally { await client.close(); await transport.close(); }

‎examples/seed-runtime/README.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
`knowledge-reader.ts` 限制来源 origin、并发数、总时限和原始响应字节,拒绝重定向与压缩;超时及关闭都会销毁请求并等待连接关闭。`text-retrieval.ts` 是确定性的内存词项索引,使用有限缓存,按已选 targets 重查内容身份;零命中也需要有效证据。静态定义、正文或分块配置变更后,由接入方显式重建或调用 invalidate,Core 不自动刷新。
1010

11-
分块保留 UTF-8 BOM 和补充字符,偏移对应原始正文的字节。缓存按选择做 LRU 淘汰;即便正文和静态修订相同,切换知识根目录仍须失效旧映射。来源读取失败不会伪装成缓存成功,能力索引重建超容量也不会发布半个索引。这些边界分别由根 `test/http-retrieval.test.ts` 和 `test/text-retrieval.test.ts` 通过真实来源验收。
11+
分块保留 UTF-8 BOM 和补充字符,偏移对应原始正文的字节。缓存按选择做 LRU 淘汰;即便正文和静态修订相同,切换知识根目录仍须失效旧映射。每次检索固定读取预算、分块和停用词配置;invalidate 按 Provider 标记在途请求,旧请求可以完成原来的快照,但不能在失效后回填旧缓存或覆盖新缓存。无关 Provider 的失效保持隔离,查询结束时释放在途标记。来源读取失败不会伪装成缓存成功,能力索引重建超容量也不会发布半个索引。这些边界分别由根 `test/http-retrieval.test.ts` 和 `test/text-retrieval.test.ts` 通过真实来源验收。
1212

1313
这条路径使用零次模型调用,证明本机 HTTP 和检索链路,不证明语义搜索质量或 Agent 任务成功率。临时 Provider 位于仓库同级 `capability-graph-artifacts`,结束后关闭 Reader、graph、HTTP 服务并清理自建目录。正式回归位于根 `test/http-retrieval.test.ts`。
1414

@@ -35,3 +35,5 @@ node dist-test/examples/seed-runtime/main.js '{"project":"sample-app","environme
3535
## 验证
3636

3737
`test/real-runtime.test.ts` 启动独立子进程,实际请求业务路由并沿 API 完成目录、关系、实例和知识流程;随后核对项目/环境隔离、源变化、旧构建、失联/恢复及分页。进程 PID 与端口在测试诊断中输出,结束后验证进程不存在且监听端口可重新绑定。IPC 注册只供这个可控制参考应用演示动态注册,不暴露为 HTTP 管理入口。
38+
39+
Reader 可实现 `stream`:Core 计算全文哈希,页只保留所选范围,单块大小有界。HTTP 参考支持无 Range 来源、完整转移期限和 AbortSignal 终止真实请求。TextKnowledgeRetriever 用 scan 消费大正文;configure 的 pageBytes 是页大小,maxDocumentBytes 保留为旧名称。仅提供 read 的旧宿主仍使用有界全文回退。

0 commit comments

Comments
 (0)