homegraph 1.6.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +64 -14
- package/dist/db/queries.d.ts +4 -0
- package/dist/db/queries.js +15 -0
- package/dist/graph/evidence-paths.d.ts +2 -0
- package/dist/graph/evidence-paths.js +20 -3
- package/dist/mcp/arkts-evidence-packs.d.ts +17 -0
- package/dist/mcp/arkts-evidence-packs.js +218 -10
- package/dist/mcp/evidence-audit.d.ts +3 -0
- package/dist/mcp/evidence-audit.js +40 -0
- package/dist/mcp/implementation-context.d.ts +35 -0
- package/dist/mcp/implementation-context.js +232 -0
- package/dist/mcp/query-cache.d.ts +1 -15
- package/dist/mcp/query-cache.js +20 -11
- package/dist/mcp/request-evidence.d.ts +69 -0
- package/dist/mcp/request-evidence.js +297 -0
- package/dist/mcp/server-instructions.d.ts +2 -2
- package/dist/mcp/server-instructions.js +8 -8
- package/dist/mcp/tools.d.ts +18 -10
- package/dist/mcp/tools.js +129 -72
- package/dist/search/literal-evidence.js +11 -3
- package/dist/search/query-plan-provider.js +13 -5
- package/dist/search/query-plan.d.ts +3 -1
- package/dist/search/query-plan.js +11 -4
- package/dist/search/request-contract.d.ts +29 -0
- package/dist/search/request-contract.js +76 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,13 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
## [1.6.1] - 2026-09-28
|
|
14
|
+
|
|
15
|
+
### Improvements
|
|
16
|
+
|
|
17
|
+
- MCP default tools/list is now the product slim pair — `homegraph_explore`, `homegraph_project` — so tool schemas stop dominating host context. Full catalog via `HOMEGRAPH_MCP_TOOLS=all` (or `*`) or an explicit comma list (e.g. add `arkui_migrate` when needed). MCP initialize instructions and the explore tool description match the slim surface (no pointers to tools hidden by default) (Spec 0051).
|
|
18
|
+
- ArkTS explore can bundle verified render/navigation scope, used imports and types, explicit module exports and local dependency configuration with the located source. Attached SDK modules provide bounded method/parameter signatures, while control summaries distinguish icon appearance, click guards and direct enabled bindings. Whole-group budgets and source fingerprints preserve evidence boundaries; optional local pack audit supports error analysis. No additional model call or mandatory tool (Spec 0050).
|
|
19
|
+
|
|
13
20
|
## [1.6.0] - 2026-09-23
|
|
14
21
|
|
|
15
22
|
### Improvements
|
|
@@ -38,6 +45,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
38
45
|
|
|
39
46
|
### Improvements
|
|
40
47
|
|
|
48
|
+
- ArkTS explore can retain grounded page/literal/object targets and report request evidence separately from source completeness. Verified joint targets improve candidate ordering; a scoped enabled-binding check distinguishes actual modifiers from appearance-only controls while keeping runtime behavior unverified. Results honor source fingerprints and pack budgets, with independent target/behavior switches. Existing precise relationship and resource routes remain available (Spec 0049).
|
|
41
49
|
- Bump `arkanalyzer` to **1.0.94**.
|
|
42
50
|
- Harmony projects with a root `build-profile.json5` now default to **serial indexing** after ArkAnalyzer: one parse worker and no resolve/synthesis worker pool. On SceneBoard-scale repos this cut peak process-tree PrivateMemory from ~8.5GB to ~3.8GB with only ~6% more wall clock. Opt out with `HOMEGRAPH_HARMONY_SERIAL=0`; force on with `=1`. Explicit `CODEGRAPH_PARSE_WORKERS` still overrides parse concurrency (Spec 0037).
|
|
43
51
|
- MCP product index readiness is now five short states (`empty` / `fast` / `full` / `dirty` / `syncing`): tool replies use a one-line `HomeGraph status=…` footer (or that line alone when the tool cannot answer yet). Pending edits surface as `dirty` with path list instead of the long stale banner; write locks stay `syncing`. MCP initialize includes a one-line status glossary (Spec 0035).
|
|
@@ -274,3 +282,4 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
274
282
|
[1.5.8]: https://github.com/fujiaxin-coder/homegraph/releases/tag/v1.5.8
|
|
275
283
|
[1.5.10]: https://github.com/fujiaxin-coder/homegraph/releases/tag/v1.5.10
|
|
276
284
|
[1.6.0]: https://github.com/fujiaxin-coder/homegraph/releases/tag/v1.6.0
|
|
285
|
+
[1.6.1]: https://github.com/fujiaxin-coder/homegraph/releases/tag/v1.6.1
|
package/README.md
CHANGED
|
@@ -171,29 +171,76 @@ homegraph serve mcp # 启动 MCP 服务(一般由 Agent 自动拉
|
|
|
171
171
|
|
|
172
172
|
Agent 侧工具名前缀为 `homegraph_`。
|
|
173
173
|
|
|
174
|
-
|
|
174
|
+
### 默认暴露(下载即用)
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
未设置 `HOMEGRAPH_MCP_TOOLS` 时,`tools/list` **只注册下面 2 个工具**(为压缩宿主上下文;Spec 0051)。刚升级后若发现以前能用的 `homegraph_node` / `search` / `arkui_migrate` 等「不见了」,不是卸载坏了,而是默认不再暴露——实现仍在,按下方「如何打开更多工具」恢复即可。
|
|
177
|
+
|
|
178
|
+
| 工具 | 用途 |
|
|
179
|
+
|------|------|
|
|
180
|
+
| `homegraph_project` | 工程/模块地图与 Harmony skeleton、资源**路径**导航(无符号正文、无调用图) |
|
|
181
|
+
| `homegraph_explore` | 通用结构探索:相关符号源码、调用路径、usage/依赖/NAPI 关系、route/resource/capability 等图证据 |
|
|
177
182
|
|
|
178
|
-
|
|
183
|
+
### 默认不暴露(需配置才进 tools/list)
|
|
184
|
+
|
|
185
|
+
下列工具 **handler 仍在**,默认不出现在 `tools/list`;未配置时直接调用会返回 `disabled via HOMEGRAPH_MCP_TOOLS`。
|
|
179
186
|
|
|
180
187
|
| 工具 | 用途 |
|
|
181
188
|
|------|------|
|
|
182
|
-
| `homegraph_usages` |
|
|
183
|
-
| `homegraph_modules` |
|
|
184
|
-
| `homegraph_native` |
|
|
185
|
-
| `
|
|
186
|
-
| `
|
|
187
|
-
| `homegraph_node` | 读取单个符号或整个文件的源码(带行号)及调用关系;可替代 Read 读文件 |
|
|
189
|
+
| `homegraph_usages` | 精确用法清单:已命名 API / 成员 / 常量 / 字段的引用位置 |
|
|
190
|
+
| `homegraph_modules` | 模块拓扑:已命名模块之间的依赖或循环依赖 |
|
|
191
|
+
| `homegraph_native` | Native 边界:NAPI / native 导出与注册位置 |
|
|
192
|
+
| `homegraph_search` | 按名称快速搜索符号(仅位置,不含源码) |
|
|
193
|
+
| `homegraph_node` | 读取单个符号或整个文件的源码(带行号)及调用关系 |
|
|
188
194
|
| `homegraph_callers` / `homegraph_callees` | 查看调用方 / 被调用方 |
|
|
189
195
|
| `homegraph_impact` | 变更影响分析(重构前使用) |
|
|
190
196
|
| `homegraph_diff_impact` | 传入 unified diff(或 hunks),返回与变更行相交的符号及调用/影响证据包(代码审查用) |
|
|
191
197
|
| `homegraph_arkui_migrate` | 一次返回 ArkUI 组件迁移/状态语义快照(装饰器、状态字段、Provide/Consume 等) |
|
|
192
198
|
| `homegraph_files` | 已索引的文件树(支持 glob 过滤、按语言分组) |
|
|
193
199
|
| `homegraph_status` | 索引健康状态(调试用;含 WAL 大小等) |
|
|
194
|
-
| `homegraph_spec_match` | 将新需求描述与 Commit4Spec
|
|
195
|
-
| `homegraph_spec_find` | 根据文件路径反向查找关联的Spec |
|
|
196
|
-
| `homegraph_spec_trace` | 根据代码符号追溯回关联的Spec |
|
|
200
|
+
| `homegraph_spec_match` | 将新需求描述与 Commit4Spec 知识图谱做全文匹配 |
|
|
201
|
+
| `homegraph_spec_find` | 根据文件路径反向查找关联的 Spec |
|
|
202
|
+
| `homegraph_spec_trace` | 根据代码符号追溯回关联的 Spec |
|
|
203
|
+
|
|
204
|
+
默认两件套下:用法/依赖/NAPI 类问题优先用 `homegraph_explore`;读已知路径用宿主 Read/Grep;ArkUI 迁移请显式打开 `arkui_migrate`(或 `all`)。
|
|
205
|
+
|
|
206
|
+
### 如何打开更多 / 全部 MCP 工具
|
|
207
|
+
|
|
208
|
+
在 **MCP 服务配置的 `environment` 字段**里设置(与 `HOMEGRAPH_NO_DAEMON` 同级)。这是注入给 HomeGraph MCP **进程**的环境变量;**不要**指望只在 Windows「系统属性 → 环境变量」里添加——MCP 子进程通常读不到系统/用户变量。
|
|
209
|
+
|
|
210
|
+
**打开全部工具:**
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"mcpServers": {
|
|
215
|
+
"homegraph": {
|
|
216
|
+
"command": "homegraph",
|
|
217
|
+
"args": ["serve", "mcp"],
|
|
218
|
+
"environment": {
|
|
219
|
+
"HOMEGRAPH_NO_DAEMON": "1",
|
|
220
|
+
"HOMEGRAPH_MCP_TOOLS": "all"
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
`HOMEGRAPH_MCP_TOOLS=*` 与 `all` 等价。
|
|
228
|
+
|
|
229
|
+
**只打开需要的子集(更省上下文):**
|
|
230
|
+
|
|
231
|
+
```json
|
|
232
|
+
"environment": {
|
|
233
|
+
"HOMEGRAPH_MCP_TOOLS": "explore,project,node,search,arkui_migrate"
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
短名即可(`node` ≡ `homegraph_node`),逗号分隔。改完后**重启对应 Agent / MCP 服务**使配置生效。
|
|
238
|
+
|
|
239
|
+
**小仓注意:** 仅当设为 `all` / `*` 且索引文件数 **少于 500** 时,仍可能再裁到历史 TINY 核心集(`explore` / `search` / `node` / `diff_impact` / `project`)。小仓也要 `usages` / `arkui_migrate` 等时,请用**显式逗号列表**,不要只靠 `all`。
|
|
240
|
+
|
|
241
|
+
**跨项目查询:** 所有工具均支持可选参数 `projectPath`(绝对路径),用于在 monorepo 中查询子项目,或当 MCP 服务器根目录没有索引时指定目标项目。
|
|
242
|
+
|
|
243
|
+
**图谱数据源(`--sources` / `HOMEGRAPH_SOURCES`):** 控制 MCP 查询是否使用工程索引、OHOS SDK API 库,或两者。取值 `both`(默认)| `project` | `sdk` | `none`。CLI 优先于环境变量。评测示例:`homegraph serve mcp --path <app> --sources sdk`。`homegraph_status`(需先暴露该工具)会打印当前模式。
|
|
197
244
|
|
|
198
245
|
### 查询规划(实验性)
|
|
199
246
|
|
|
@@ -333,13 +380,16 @@ npm run cli
|
|
|
333
380
|
"homegraph": {
|
|
334
381
|
"type": "stdio",
|
|
335
382
|
"command": "homegraph",
|
|
336
|
-
"args": ["serve", "mcp"]
|
|
383
|
+
"args": ["serve", "mcp"],
|
|
384
|
+
"environment": {
|
|
385
|
+
"HOMEGRAPH_MCP_TOOLS": "all"
|
|
386
|
+
}
|
|
337
387
|
}
|
|
338
388
|
}
|
|
339
389
|
}
|
|
340
390
|
```
|
|
341
391
|
|
|
342
|
-
Cursor 等项目级配置写入 `./.cursor/mcp.json`,格式相同。推荐使用 `homegraph install` 自动完成。
|
|
392
|
+
不设 `HOMEGRAPH_MCP_TOOLS` 时默认只暴露 `explore` + `project`;需要全量或子集时按上一节「如何打开更多 / 全部 MCP 工具」配置。Cursor 等项目级配置写入 `./.cursor/mcp.json`,格式相同。推荐使用 `homegraph install` 自动完成。
|
|
343
393
|
|
|
344
394
|
---
|
|
345
395
|
|
package/dist/db/queries.d.ts
CHANGED
|
@@ -46,6 +46,10 @@ export declare class QueryBuilder {
|
|
|
46
46
|
/** Attach a versioned OHOS API db for federated symbol lookup. */
|
|
47
47
|
attachOhosApiDb(dbPath: string | null): void;
|
|
48
48
|
getOhosApiDbPath(): string | null;
|
|
49
|
+
/** Exact imported SDK module only. No cross-module bare-name fallback.
|
|
50
|
+
* A missing/oversized/ambiguous module returns no claim of signature coverage.
|
|
51
|
+
*/
|
|
52
|
+
getOhosApiModuleNodes(specifier: string): Node[];
|
|
49
53
|
private hasOhosApiAttached;
|
|
50
54
|
private mapOhosApiNodeRow;
|
|
51
55
|
/** Set the normalized project-name tokens used to down-weight non-discriminative
|
package/dist/db/queries.js
CHANGED
|
@@ -247,6 +247,21 @@ class QueryBuilder {
|
|
|
247
247
|
getOhosApiDbPath() {
|
|
248
248
|
return this.ohosApiDbPath;
|
|
249
249
|
}
|
|
250
|
+
/** Exact imported SDK module only. No cross-module bare-name fallback.
|
|
251
|
+
* A missing/oversized/ambiguous module returns no claim of signature coverage.
|
|
252
|
+
*/
|
|
253
|
+
getOhosApiModuleNodes(specifier) {
|
|
254
|
+
if (!this.ohosApiDbPath || !/^@(ohos|hms|kit)\.[A-Za-z0-9_.]+$/.test(specifier))
|
|
255
|
+
return [];
|
|
256
|
+
const stem = specifier.startsWith('@kit.') ? specifier.slice(5) : specifier;
|
|
257
|
+
const escaped = stem.replace(/[\\%_]/g, c => `\\${c}`);
|
|
258
|
+
const rows = this.db.prepare(`SELECT * FROM ohos_api.nodes WHERE
|
|
259
|
+
file_path = ? OR file_path = ? OR file_path LIKE ? ESCAPE '\\' OR file_path LIKE ? ESCAPE '\\'
|
|
260
|
+
ORDER BY file_path, start_line LIMIT 257`).all(`${stem}.d.ts`, `${stem}.d.ets`, `%/${escaped}.d.ts`, `%/${escaped}.d.ets`);
|
|
261
|
+
if (rows.length > 256 || new Set(rows.map(row => row.file_path)).size !== 1)
|
|
262
|
+
return [];
|
|
263
|
+
return rows.map(row => this.mapOhosApiNodeRow(row));
|
|
264
|
+
}
|
|
250
265
|
hasOhosApiAttached() {
|
|
251
266
|
return this.ohosApiDbPath != null;
|
|
252
267
|
}
|
|
@@ -53,6 +53,8 @@ export declare function isEvidencePathNode(n: Node): boolean;
|
|
|
53
53
|
export declare function completeEvidencePathCandidates(query: string, nodes: Node[], lookup: (qualifiedName: string, limit: number) => Node[], plan?: QueryPlan): Node[];
|
|
54
54
|
/** Resolve only actual candidate identities; a bare homonym never selects the first hit. */
|
|
55
55
|
export declare function resolveEvidencePathGoal(query: string, nodes: Node[], focusIds: Set<string>, plan?: QueryPlan): EvidencePathGoal;
|
|
56
|
+
/** Framework-labelled static UI wiring; never treat an arbitrary reference as rendering. */
|
|
57
|
+
export declare function isArkuiScopeEdge(edge: Edge): boolean;
|
|
56
58
|
export declare function evidenceEdgeCost(edge: Edge, goal: EvidencePathGoal): number | null;
|
|
57
59
|
export declare function evidenceEdgeKey(edge: Edge): string;
|
|
58
60
|
/** One bounded search per request. Each orientation follows directed edges throughout. */
|
|
@@ -4,6 +4,7 @@ exports.EVIDENCE_PATH_LIMITS = void 0;
|
|
|
4
4
|
exports.isEvidencePathNode = isEvidencePathNode;
|
|
5
5
|
exports.completeEvidencePathCandidates = completeEvidencePathCandidates;
|
|
6
6
|
exports.resolveEvidencePathGoal = resolveEvidencePathGoal;
|
|
7
|
+
exports.isArkuiScopeEdge = isArkuiScopeEdge;
|
|
7
8
|
exports.evidenceEdgeCost = evidenceEdgeCost;
|
|
8
9
|
exports.evidenceEdgeKey = evidenceEdgeKey;
|
|
9
10
|
exports.searchEvidencePaths = searchEvidencePaths;
|
|
@@ -108,11 +109,18 @@ function edgeSemantics(edge) {
|
|
|
108
109
|
|| ['callback', 'event-emitter', 'closure-collection', 'fn-pointer-dispatch', 'arkui-emitter', 'arkui-common-event', 'arkui-taskpool'].includes(String(by));
|
|
109
110
|
return { state, event };
|
|
110
111
|
}
|
|
112
|
+
/** Framework-labelled static UI wiring; never treat an arbitrary reference as rendering. */
|
|
113
|
+
function isArkuiScopeEdge(edge) {
|
|
114
|
+
const by = edge.metadata?.synthesizedBy;
|
|
115
|
+
return (edge.kind === 'references' || edge.kind === 'calls' || edge.kind === 'contains') && ((by === 'viewtree' && ['child-component', 'builder', 'builder-param'].includes(String(edge.metadata?.via)))
|
|
116
|
+
|| (by === 'arkui-ownership' && edge.metadata?.via === 'build') || by === 'arkui-child' || by === 'arkui-named-nav' || by === 'arkts-route-map' || by === 'arkui-route');
|
|
117
|
+
}
|
|
111
118
|
function evidenceEdgeCost(edge, goal) {
|
|
112
119
|
const { state, event } = edgeSemantics(edge);
|
|
113
120
|
let relevanceCost;
|
|
114
121
|
if (goal.kind === 'structure') {
|
|
115
|
-
if (!['extends', 'implements', 'overrides', 'instantiates'].includes(edge.kind)
|
|
122
|
+
if (!['extends', 'implements', 'overrides', 'instantiates'].includes(edge.kind)
|
|
123
|
+
&& !(process.env.HOMEGRAPH_ARKTS_IMPLEMENTATION_CONTEXT !== '0' && isArkuiScopeEdge(edge)))
|
|
116
124
|
return null;
|
|
117
125
|
relevanceCost = 0;
|
|
118
126
|
}
|
|
@@ -143,7 +151,7 @@ function searchEvidencePaths(graph, goal, options = {}) {
|
|
|
143
151
|
const neighbors = new Map();
|
|
144
152
|
const paths = [];
|
|
145
153
|
const missing = [];
|
|
146
|
-
const kinds = goal.kind === 'structure' ? ['extends', 'implements', 'overrides', 'instantiates'] : ['calls', 'references'];
|
|
154
|
+
const kinds = goal.kind === 'structure' ? ['extends', 'implements', 'overrides', 'instantiates', ...(process.env.HOMEGRAPH_ARKTS_IMPLEMENTATION_CONTEXT !== '0' ? ['calls', 'references', 'contains'] : [])] : ['calls', 'references'];
|
|
147
155
|
const alive = () => {
|
|
148
156
|
if (now() - started >= limits.milliseconds) {
|
|
149
157
|
hits.add('time');
|
|
@@ -179,7 +187,16 @@ function searchEvidencePaths(graph, goal, options = {}) {
|
|
|
179
187
|
stats.rowsRead += rows.length;
|
|
180
188
|
if (rows.length > limits.perNode)
|
|
181
189
|
hits.add('neighbors');
|
|
182
|
-
const valid = rows.slice(0, limits.perNode).
|
|
190
|
+
const valid = rows.slice(0, limits.perNode).map(e => {
|
|
191
|
+
if (goal.kind === 'structure' && e.kind === 'contains' && process.env.HOMEGRAPH_ARKTS_IMPLEMENTATION_CONTEXT !== '0') {
|
|
192
|
+
const from = node(e.source);
|
|
193
|
+
const to = node(e.target);
|
|
194
|
+
if (from && ['component', 'struct'].includes(from.kind) && to?.kind === 'method' && to.name === 'build') {
|
|
195
|
+
return { ...e, metadata: { ...e.metadata, synthesizedBy: 'arkui-ownership', via: 'build' } };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return e;
|
|
199
|
+
}).filter(e => evidenceEdgeCost(e, goal) !== null
|
|
183
200
|
&& node(e.source) && node(e.target)).sort((a, b) => evidenceEdgeCost(a, goal) - evidenceEdgeCost(b, goal)
|
|
184
201
|
|| evidenceEdgeKey(a).localeCompare(evidenceEdgeKey(b)));
|
|
185
202
|
adjacency.set(key, valid);
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { type SdkModule } from './implementation-context';
|
|
2
|
+
import { type RequestContract } from '../search/request-contract';
|
|
3
|
+
import { type RequestEvidence, type ControlEvidence } from './request-evidence';
|
|
1
4
|
import type HomeGraph from '../index';
|
|
2
5
|
import type { Edge, Node } from '../types';
|
|
3
6
|
import type { ExploreEmission } from './explore-session-state';
|
|
@@ -22,6 +25,15 @@ interface EvidencePack {
|
|
|
22
25
|
sources: string[];
|
|
23
26
|
priority: number;
|
|
24
27
|
gaps: EvidenceGap[];
|
|
28
|
+
context?: {
|
|
29
|
+
label: string;
|
|
30
|
+
notes: string[];
|
|
31
|
+
sdk?: {
|
|
32
|
+
module: string;
|
|
33
|
+
version: string;
|
|
34
|
+
nodes: Node[];
|
|
35
|
+
};
|
|
36
|
+
};
|
|
25
37
|
relation?: RelationEvidence;
|
|
26
38
|
path?: {
|
|
27
39
|
id: string;
|
|
@@ -36,6 +48,9 @@ export interface ArktsEvidenceResult {
|
|
|
36
48
|
version: 1 | 2;
|
|
37
49
|
scope: 'bounded_static_evidence';
|
|
38
50
|
status: 'complete' | 'partial' | 'empty';
|
|
51
|
+
requestEvidence?: RequestEvidence;
|
|
52
|
+
controls?: ControlEvidence[];
|
|
53
|
+
implementationContext?: Array<NonNullable<EvidencePack['context']>>;
|
|
39
54
|
selectedPacks: string[];
|
|
40
55
|
gaps: EvidenceGap[];
|
|
41
56
|
relations: NonNullable<EvidencePack['relation']>[];
|
|
@@ -62,6 +77,8 @@ export declare function buildArktsEvidencePacks(graph: Pick<HomeGraph, 'getNode'
|
|
|
62
77
|
maxChars: number;
|
|
63
78
|
maxFiles?: number;
|
|
64
79
|
pathSearch?: EvidencePathSearch;
|
|
80
|
+
requestContract?: RequestContract;
|
|
81
|
+
sdkModule?: (module: string) => SdkModule | undefined;
|
|
65
82
|
}): ArktsEvidenceResult | null;
|
|
66
83
|
export {};
|
|
67
84
|
//# sourceMappingURL=arkts-evidence-packs.d.ts.map
|
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.buildArktsEvidencePacks = buildArktsEvidencePacks;
|
|
37
|
+
const arkts_1 = require("../extraction/languages/arkts");
|
|
38
|
+
const implementation_context_1 = require("./implementation-context");
|
|
39
|
+
const path = __importStar(require("node:path"));
|
|
40
|
+
const request_contract_1 = require("../search/request-contract");
|
|
41
|
+
const request_evidence_1 = require("./request-evidence");
|
|
4
42
|
const node_crypto_1 = require("node:crypto");
|
|
5
43
|
const node_fs_1 = require("node:fs");
|
|
6
44
|
const utils_1 = require("../utils");
|
|
@@ -8,7 +46,7 @@ const evidence_rendering_1 = require("./evidence-rendering");
|
|
|
8
46
|
const explore_dedup_1 = require("./explore-dedup");
|
|
9
47
|
const evidence_paths_1 = require("../graph/evidence-paths");
|
|
10
48
|
const LIMITS = { nodes: 36, expand: 12, edges: 48, files: 8, fileBytes: 512 * 1024, bytes: 2 * 1024 * 1024 };
|
|
11
|
-
const
|
|
49
|
+
const BASE_DECLARATIONS = new Set(['class', 'struct', 'component', 'interface', 'method', 'function', 'property', 'field', 'constant', 'variable', 'enum']);
|
|
12
50
|
const CONTAINERS = new Set(['class', 'struct', 'component', 'interface', 'enum']);
|
|
13
51
|
const RELATIONS = new Set(['calls', 'references', 'extends', 'implements', 'instantiates']);
|
|
14
52
|
const inline = (s) => s.replace(/[`\r\n]/g, ' ');
|
|
@@ -22,16 +60,32 @@ const artifact = (n) => n.filePath.includes('@dummy') || n.name.startsWith('@dum
|
|
|
22
60
|
|| (n.name === 'constructor' && n.startLine === 1 && n.endLine === 1);
|
|
23
61
|
/** Consume already-located nodes. No text search, new planner call or recursive retrieval. */
|
|
24
62
|
function buildArktsEvidencePacks(graph, options) {
|
|
63
|
+
const contextEnabled = (0, implementation_context_1.implementationContextEnabled)();
|
|
64
|
+
const DECLARATIONS = contextEnabled ? new Set([...BASE_DECLARATIONS, 'type_alias', 'import', 'export', 'route', 'file']) : BASE_DECLARATIONS;
|
|
25
65
|
if (!options.nodes.some(n => local(n) && /\.ets$/i.test(n.filePath) && DECLARATIONS.has(n.kind)))
|
|
26
66
|
return null;
|
|
27
67
|
const search = options.pathSearch;
|
|
68
|
+
const targetChecks = (0, request_contract_1.accuracyTargetsEnabled)();
|
|
69
|
+
const behaviorChecks = (0, request_contract_1.accuracyCoverageEnabled)();
|
|
70
|
+
const inspector = options.requestContract && (targetChecks || behaviorChecks)
|
|
71
|
+
? (0, request_evidence_1.createRequestEvidenceInspector)(options.requestContract) : undefined;
|
|
72
|
+
const requestSources = new Map();
|
|
73
|
+
const requestUnit = (s) => {
|
|
74
|
+
let unit = requestSources.get(s);
|
|
75
|
+
if (!unit) {
|
|
76
|
+
unit = { filePath: s.node.filePath, start: s.start, source: s.source };
|
|
77
|
+
requestSources.set(s, unit);
|
|
78
|
+
}
|
|
79
|
+
return unit;
|
|
80
|
+
};
|
|
28
81
|
const requiredIds = search ? new Set([...search.goal.anchorIds, ...search.paths.flatMap(p => p.nodeIds)]) : options.focusIds;
|
|
29
82
|
const inputNodes = search && requiredIds.size ? [...requiredIds].flatMap(id => { const n = graph.getNode(id); return n ? [n] : []; }) : options.nodes;
|
|
30
83
|
const all = (0, evidence_rendering_1.canonicalSourceDeclarations)(inputNodes.filter(n => DECLARATIONS.has(n.kind) && local(n) && !artifact(n)));
|
|
31
84
|
if (!all.length)
|
|
32
85
|
return null;
|
|
86
|
+
const inputOrder = new Map(all.map((node, index) => [node.id, index]));
|
|
33
87
|
const candidates = all.sort((a, b) => Number(requiredIds.has(b.id)) - Number(requiredIds.has(a.id))
|
|
34
|
-
|| a.filePath.localeCompare(b.filePath) || a.startLine - b.startLine).slice(0, LIMITS.nodes);
|
|
88
|
+
|| (inspector && targetChecks ? inputOrder.get(a.id) - inputOrder.get(b.id) : a.filePath.localeCompare(b.filePath) || a.startLine - b.startLine)).slice(0, LIMITS.nodes);
|
|
35
89
|
const focus = candidates.filter(n => requiredIds.has(n.id));
|
|
36
90
|
// A named owning type provides scope for its named members, not another
|
|
37
91
|
// callable endpoint. Do not invent a missing Type→method call obligation.
|
|
@@ -46,11 +100,11 @@ function buildArktsEvidencePacks(graph, options) {
|
|
|
46
100
|
let bytesRead = 0;
|
|
47
101
|
const root = (0, node_fs_1.realpathSync)(options.projectRoot);
|
|
48
102
|
const maxFiles = Math.max(1, Math.min(LIMITS.files, options.maxFiles ?? LIMITS.files));
|
|
49
|
-
const readSource = (node) => {
|
|
103
|
+
const readSource = (node, snapshot = false) => {
|
|
50
104
|
const previous = nodeSources.get(node.id);
|
|
51
105
|
if (previous)
|
|
52
106
|
return previous;
|
|
53
|
-
if (!local(node) || artifact(node) || !DECLARATIONS.has(node.kind)) {
|
|
107
|
+
if ((!local(node) && !(contextEnabled && (snapshot || node.kind === 'route' || (!node.filePath.startsWith('ohos-sdk:') && /\.d\.(?:ts|ets)$/.test(node.filePath))))) || artifact(node) || !DECLARATIONS.has(node.kind)) {
|
|
54
108
|
const failure = { gap: { target: label(node), reason: 'unavailable', nextAnchor: location(node) } };
|
|
55
109
|
nodeSources.set(node.id, failure);
|
|
56
110
|
return failure;
|
|
@@ -90,7 +144,7 @@ function buildArktsEvidencePacks(graph, options) {
|
|
|
90
144
|
const content = buffer.subarray(0, bytes).toString('utf8');
|
|
91
145
|
const indexed = graph.getFile(node.filePath);
|
|
92
146
|
// Hash equality, not mtime: edits can retain timestamps and invalidate ranges.
|
|
93
|
-
if (bytes !== size || !indexed || indexed.contentHash !== (0, node_crypto_1.createHash)('sha256').update(content).digest('hex'))
|
|
147
|
+
if (bytes !== size || (!snapshot && (!indexed || indexed.contentHash !== (0, node_crypto_1.createHash)('sha256').update(content).digest('hex'))))
|
|
94
148
|
file.reason = 'stale';
|
|
95
149
|
else
|
|
96
150
|
file = { content, lines: content.split('\n'), fingerprint: (0, explore_dedup_1.fileFingerprint)(content) };
|
|
@@ -102,6 +156,14 @@ function buildArktsEvidencePacks(graph, options) {
|
|
|
102
156
|
}
|
|
103
157
|
fileCache.set(node.filePath, file);
|
|
104
158
|
}
|
|
159
|
+
if ((snapshot || node.kind === 'file') && file.lines)
|
|
160
|
+
node = { ...node, endLine: file.lines.length };
|
|
161
|
+
if (node.kind === 'import' && file.lines) {
|
|
162
|
+
const head = file.lines.slice(node.startLine - 1, node.startLine + 20).join('\n');
|
|
163
|
+
const importText = head.match(/^\s*import\s+[\s\S]*?\bfrom\s*['"][^'"\r\n]+['"][^\S\r\n]*;?/);
|
|
164
|
+
if (importText)
|
|
165
|
+
node = { ...node, endLine: node.startLine + importText[0].split('\n').length - 1 };
|
|
166
|
+
}
|
|
105
167
|
const reason = file.reason ?? (!Number.isInteger(node.startLine) || !Number.isInteger(node.endLine)
|
|
106
168
|
|| node.startLine < 1 || node.endLine < node.startLine || node.endLine > file.lines.length
|
|
107
169
|
|| !file.lines.slice(node.startLine - 1, node.endLine).join('\n').trim() ? 'invalid_range' : undefined);
|
|
@@ -117,13 +179,13 @@ function buildArktsEvidencePacks(graph, options) {
|
|
|
117
179
|
const id = `${node.filePath}:${start}-${node.endLine}`;
|
|
118
180
|
if (!sources.has(id))
|
|
119
181
|
sources.set(id, { id, node, start, end: node.endLine,
|
|
120
|
-
source: file.lines.slice(start - 1, node.endLine).join('\n'), fingerprint: file.fingerprint });
|
|
182
|
+
source: file.lines.slice(start - 1, node.endLine).join('\n'), fingerprint: file.fingerprint, ...(snapshot ? { snapshot: true } : {}) });
|
|
121
183
|
const result = { id };
|
|
122
184
|
nodeSources.set(node.id, result);
|
|
123
185
|
return result;
|
|
124
186
|
};
|
|
125
187
|
const dependencies = (nodes) => {
|
|
126
|
-
const resolved = nodes.map(readSource);
|
|
188
|
+
const resolved = nodes.map(n => readSource(n));
|
|
127
189
|
return { sources: [...new Set(resolved.flatMap(s => s.id ? [s.id] : []))],
|
|
128
190
|
gaps: resolved.flatMap(s => s.gap ? [s.gap] : []) };
|
|
129
191
|
};
|
|
@@ -134,6 +196,104 @@ function buildArktsEvidencePacks(graph, options) {
|
|
|
134
196
|
if (all.length > candidates.length)
|
|
135
197
|
globalGaps.push({ target: `${all.length - candidates.length} additional candidates`,
|
|
136
198
|
reason: 'scope_limit', nextAnchor: location(all[candidates.length]) });
|
|
199
|
+
const configNode = (file) => ({ id: `context-config:${file}`, kind: 'constant', name: file, qualifiedName: file,
|
|
200
|
+
filePath: file, startLine: 1, endLine: 1, startColumn: 0, endColumn: 0, updatedAt: 0, language: 'yaml' });
|
|
201
|
+
const manifestCache = new Map();
|
|
202
|
+
const nearestManifest = (file) => {
|
|
203
|
+
if (manifestCache.has(file))
|
|
204
|
+
return manifestCache.get(file);
|
|
205
|
+
let dir = path.posix.dirname(file.replace(/\\/g, '/'));
|
|
206
|
+
let found;
|
|
207
|
+
for (let depth = 0; depth < 12; depth++) {
|
|
208
|
+
const manifest = path.posix.join(dir, 'oh-package.json5');
|
|
209
|
+
const absolute = (0, utils_1.validatePathWithinRoot)(root, manifest);
|
|
210
|
+
try {
|
|
211
|
+
if (absolute && (0, node_fs_1.statSync)(absolute).isFile()) {
|
|
212
|
+
found = configNode(manifest);
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch { /* absent */ }
|
|
217
|
+
if (dir === '.')
|
|
218
|
+
break;
|
|
219
|
+
dir = path.posix.dirname(dir);
|
|
220
|
+
}
|
|
221
|
+
manifestCache.set(file, found);
|
|
222
|
+
return found;
|
|
223
|
+
};
|
|
224
|
+
const configValue = (node) => {
|
|
225
|
+
const r = readSource(node, true);
|
|
226
|
+
if (!r.id)
|
|
227
|
+
return undefined;
|
|
228
|
+
try {
|
|
229
|
+
const value = (0, arkts_1.parseJson5Minimal)(sources.get(r.id).source);
|
|
230
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
const moduleBinding = (importer, module) => {
|
|
237
|
+
const manifest = nearestManifest(importer);
|
|
238
|
+
if (!manifest)
|
|
239
|
+
return undefined;
|
|
240
|
+
const config = configValue(manifest);
|
|
241
|
+
const deps = config?.dependencies;
|
|
242
|
+
if (!deps || typeof deps !== 'object' || Array.isArray(deps))
|
|
243
|
+
return undefined;
|
|
244
|
+
const dep = deps[module];
|
|
245
|
+
if (typeof dep !== 'string' || !/^file:\.{1,2}\//.test(dep))
|
|
246
|
+
return undefined;
|
|
247
|
+
const depRoot = path.posix.normalize(path.posix.join(path.posix.dirname(manifest.filePath), dep.slice(5)));
|
|
248
|
+
if (depRoot === '..' || depRoot.startsWith('../') || path.posix.isAbsolute(depRoot))
|
|
249
|
+
return undefined;
|
|
250
|
+
const targetManifest = configNode(path.posix.join(depRoot, 'oh-package.json5'));
|
|
251
|
+
const target = configValue(targetManifest);
|
|
252
|
+
if (!target)
|
|
253
|
+
return undefined;
|
|
254
|
+
const entry = typeof target.main === 'string' ? path.posix.join(depRoot, target.main) : depRoot;
|
|
255
|
+
const relativePath = './' + path.posix.relative(path.posix.dirname(importer), entry);
|
|
256
|
+
return { relativePath, witnesses: [manifest, targetManifest] };
|
|
257
|
+
};
|
|
258
|
+
const contextGroups = contextEnabled ? (0, implementation_context_1.collectImplementationContext)(graph, focus.length ? focus : candidates, node => { const r = readSource(node); return r.id ? sources.get(r.id)?.source : undefined; }, options.sdkModule, moduleBinding) : [];
|
|
259
|
+
for (const group of contextGroups) {
|
|
260
|
+
const deps = dependencies(group.nodes);
|
|
261
|
+
packs.push({ id: group.id, label: group.label, priority: 130, ...deps,
|
|
262
|
+
context: { label: group.label, notes: [...group.notes, ...group.missing.map(m => `Unresolved: ${m}`)], ...(group.sdk ? { sdk: group.sdk } : {}) } });
|
|
263
|
+
for (const missing of group.missing)
|
|
264
|
+
globalGaps.push({ target: missing, reason: 'unindexed_connection', nextAnchor: missing });
|
|
265
|
+
}
|
|
266
|
+
// Only ancestor manifests of the located code; no workspace traversal or credential files.
|
|
267
|
+
const configs = new Set();
|
|
268
|
+
for (const node of contextEnabled && contextGroups.some(g => g.id.startsWith('impl-import:')) ? (focus.length ? focus : candidates).slice(0, 3) : []) {
|
|
269
|
+
let dir = path.posix.dirname(node.filePath.replace(/\\/g, '/'));
|
|
270
|
+
for (let depth = 0; depth < 12; depth++) {
|
|
271
|
+
const manifest = path.posix.join(dir, 'oh-package.json5');
|
|
272
|
+
const absolute = (0, utils_1.validatePathWithinRoot)(root, manifest);
|
|
273
|
+
try {
|
|
274
|
+
if (absolute && (0, node_fs_1.statSync)(absolute).isFile()) {
|
|
275
|
+
configs.add(manifest);
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch { /* missing ancestor manifest */ }
|
|
280
|
+
if (dir === '.')
|
|
281
|
+
break;
|
|
282
|
+
dir = path.posix.dirname(dir);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (configs.size)
|
|
286
|
+
configs.add('build-profile.json5');
|
|
287
|
+
for (const config of configs) {
|
|
288
|
+
const node = { id: `context-config:${config}`, kind: 'constant', name: config, qualifiedName: config,
|
|
289
|
+
filePath: config, startLine: 1, endLine: 1, startColumn: 0, endColumn: 0, updatedAt: Date.now(), language: 'yaml' };
|
|
290
|
+
const dep = readSource(node, true);
|
|
291
|
+
if (dep.id)
|
|
292
|
+
packs.push({ id: node.id, label: `module/version configuration ${config}`, priority: 125, sources: [dep.id], gaps: [],
|
|
293
|
+
context: { label: `module/version configuration ${config}`, notes: ['Current configuration snapshot; declared dependencies do not prove installation or export availability.'] } });
|
|
294
|
+
else if (dep.gap && dep.gap.reason !== 'unavailable')
|
|
295
|
+
globalGaps.push(dep.gap);
|
|
296
|
+
}
|
|
137
297
|
const candidateIds = new Set(candidates.map(n => n.id));
|
|
138
298
|
const edges = [];
|
|
139
299
|
const edgeKeys = new Set();
|
|
@@ -158,6 +318,16 @@ function buildArktsEvidencePacks(graph, options) {
|
|
|
158
318
|
edges.push(edge);
|
|
159
319
|
}
|
|
160
320
|
}
|
|
321
|
+
for (const group of contextGroups)
|
|
322
|
+
for (const edge of group.edges) {
|
|
323
|
+
if (edge.kind === 'imports')
|
|
324
|
+
continue;
|
|
325
|
+
const key = (0, evidence_paths_1.evidenceEdgeKey)(edge);
|
|
326
|
+
if (!edgeKeys.has(key)) {
|
|
327
|
+
edgeKeys.add(key);
|
|
328
|
+
edges.push(edge);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
161
331
|
if (search) {
|
|
162
332
|
const fallbackNeighbors = search.missing.length || search.goal.anchorIds.length < 2 ? search.neighbors : [];
|
|
163
333
|
for (const edge of [...search.paths.flatMap(p => p.edges), ...fallbackNeighbors]) {
|
|
@@ -194,7 +364,7 @@ function buildArktsEvidencePacks(graph, options) {
|
|
|
194
364
|
for (const [i, edge] of edges.entries()) {
|
|
195
365
|
const from = graph.getNode(edge.source);
|
|
196
366
|
const to = graph.getNode(edge.target);
|
|
197
|
-
if (!from || !to || !local(from) || !local(to)) {
|
|
367
|
+
if (!from || !to || (!local(from) && from.kind !== 'route') || (!local(to) && to.kind !== 'route')) {
|
|
198
368
|
globalGaps.push({ target: `${from ? label(from) : edge.source} ${edge.kind} ${to ? label(to) : edge.target}`,
|
|
199
369
|
reason: 'unavailable', nextAnchor: from ? location(from) : edge.source });
|
|
200
370
|
continue;
|
|
@@ -267,6 +437,19 @@ function buildArktsEvidencePacks(graph, options) {
|
|
|
267
437
|
globalGaps.push({ target: `connecting path in the bounded graph slice: ${label(relationFocus[0])} → ${label(disconnected[0])}`,
|
|
268
438
|
reason: 'unindexed_connection', nextAnchor: location(disconnected[0]) });
|
|
269
439
|
}
|
|
440
|
+
// Rank already-read, hash-verified declarations by joint targets in the same file.
|
|
441
|
+
// Never discard a dependent helper merely because it has no UI label.
|
|
442
|
+
if (inspector && targetChecks) {
|
|
443
|
+
const byFile = new Map();
|
|
444
|
+
for (const unit of sources.values()) {
|
|
445
|
+
const list = byFile.get(unit.node.filePath) ?? [];
|
|
446
|
+
list.push(requestUnit(unit));
|
|
447
|
+
byFile.set(unit.node.filePath, list);
|
|
448
|
+
}
|
|
449
|
+
const scores = new Map([...byFile].map(([file, units]) => [file, inspector.score(units)]));
|
|
450
|
+
for (const pack of packs)
|
|
451
|
+
pack.priority += 30 * Math.max(0, ...pack.sources.map(id => scores.get(sources.get(id).node.filePath) ?? 0));
|
|
452
|
+
}
|
|
270
453
|
const selected = [];
|
|
271
454
|
const rejectedGap = (p) => p.gaps.length ? p.gaps : [{ target: p.label, reason: 'budget',
|
|
272
455
|
nextAnchor: sources.get(p.sources[0] ?? '') ? location(sources.get(p.sources[0]).node) : p.label }];
|
|
@@ -281,6 +464,12 @@ function buildArktsEvidencePacks(graph, options) {
|
|
|
281
464
|
return units.filter(s => !units.some(other => other.id !== s.id && other.node.filePath === s.node.filePath
|
|
282
465
|
&& other.start <= s.start && other.end >= s.end)).sort((a, b) => a.node.filePath.localeCompare(b.node.filePath) || a.start - b.start);
|
|
283
466
|
};
|
|
467
|
+
const controlCache = new Map();
|
|
468
|
+
const controlsFor = (chosen) => unitsFor(chosen).flatMap(unit => {
|
|
469
|
+
if (!controlCache.has(unit))
|
|
470
|
+
controlCache.set(unit, (0, request_evidence_1.inspectControlEvidence)([requestUnit(unit)]));
|
|
471
|
+
return controlCache.get(unit);
|
|
472
|
+
}).slice(0, 8);
|
|
284
473
|
const relationsFor = (chosen) => [...new Map(chosen.flatMap(p => p.path?.relations ?? (p.relation ? [p.relation] : []))
|
|
285
474
|
.map(r => [JSON.stringify(r), r])).values()];
|
|
286
475
|
const stopFor = (chosen) => search?.stopReason === 'supported'
|
|
@@ -291,6 +480,16 @@ function buildArktsEvidencePacks(graph, options) {
|
|
|
291
480
|
'Complete declarations preserve internal branches. Enclosing callers and runtime order/value flow are not proven. Retrieval is not task completion.'];
|
|
292
481
|
if (search)
|
|
293
482
|
lines.push(`Path goal: ${search.goal.kind}; search direction: ${search.goal.direction}; stop: ${stopFor(chosen)}. Static paths do not prove runtime behavior.`);
|
|
483
|
+
if (inspector)
|
|
484
|
+
lines.push(...(0, request_evidence_1.renderRequestEvidence)(inspector.inspect(unitsFor(chosen).map(requestUnit)), targetChecks, behaviorChecks));
|
|
485
|
+
if ((0, implementation_context_1.controlEvidenceEnabled)())
|
|
486
|
+
lines.push(...(0, request_evidence_1.renderControlEvidence)(controlsFor(chosen)));
|
|
487
|
+
for (const pack of chosen.filter(p => p.context)) {
|
|
488
|
+
lines.push(`**Implementation context: ${inline(pack.context.label)}**`, ...pack.context.notes.map(n => `- ${inline(n)}`));
|
|
489
|
+
const sdk = pack.context.sdk;
|
|
490
|
+
if (sdk)
|
|
491
|
+
lines.push(`Indexed SDK signatures for ${inline(sdk.module)}; database version ${inline(sdk.version)}. Not source bodies or device validation.`, ...sdk.nodes.map(n => `- ${inline(n.filePath)}:${n.startLine} ${inline(n.signature || n.name)}${n.docstring ? ` — ${inline(n.docstring).slice(0, 500)}${n.docstring.length > 500 ? ' [documentation excerpt; remaining constraints not shown]' : ''}` : ''}`));
|
|
492
|
+
}
|
|
294
493
|
const chosenPaths = chosen.filter(p => p.path);
|
|
295
494
|
if (chosenPaths.length)
|
|
296
495
|
lines.push('**Directed paths (all source dependencies provided)**', ...chosenPaths.map(p => `- ${inline(p.label)}`));
|
|
@@ -304,7 +503,7 @@ function buildArktsEvidencePacks(graph, options) {
|
|
|
304
503
|
for (const unit of unitsFor(chosen)) {
|
|
305
504
|
// Fence longer than any source backtick run: source-like guidance cannot escape.
|
|
306
505
|
const fence = '`'.repeat(Math.max(3, ...Array.from(unit.source.matchAll(/`+/g), m => m[0].length + 1)));
|
|
307
|
-
lines.push(`### ${inline(unit.node.filePath)}:${unit.start}-${unit.end} — complete declaration; file-fingerprint=${unit.fingerprint}`, fence + unit.node.language, unit.source.split('\n').map((line, i) => `${unit.start + i}\t${line}`).join('\n'), fence);
|
|
506
|
+
lines.push(`### ${inline(unit.node.filePath)}:${unit.start}-${unit.end} — ${unit.snapshot ? 'current configuration snapshot' : unit.node.kind === 'file' ? 'complete indexed file' : 'complete declaration'}; file-fingerprint=${unit.fingerprint}`, fence + unit.node.language, unit.source.split('\n').map((line, i) => `${unit.start + i}\t${line}`).join('\n'), fence);
|
|
308
507
|
}
|
|
309
508
|
if (gaps.length) {
|
|
310
509
|
lines.push('**Gaps — inspect only the missing evidence needed for the task**', ...gaps.slice(0, 6).map(g => `- ${g.reason}: ${inline(g.target)}. Next anchor: ${inline(g.nextAnchor)}.`));
|
|
@@ -341,11 +540,20 @@ function buildArktsEvidencePacks(graph, options) {
|
|
|
341
540
|
const locatedNodes = candidates.filter(n => units.some(s => s.node.filePath === n.filePath && s.start <= n.startLine && s.end >= n.endLine))
|
|
342
541
|
.map(n => ({ id: n.id, name: n.name, qualifiedName: n.qualifiedName, filePath: n.filePath, startLine: n.startLine }));
|
|
343
542
|
const status = !units.length ? 'empty' : gaps.length ? 'partial' : 'complete';
|
|
543
|
+
const checkedRequest = inspector?.inspect(units.map(requestUnit));
|
|
544
|
+
const requestEvidence = checkedRequest ? { ...checkedRequest, targets: targetChecks ? checkedRequest.targets : [],
|
|
545
|
+
behaviors: behaviorChecks ? checkedRequest.behaviors : [] } : undefined;
|
|
344
546
|
return { text, emission: { projectRoot: options.projectRoot, query: options.query, files,
|
|
345
547
|
sourceBytes: files.reduce((sum, f) => sum + f.bytes, 0), responseBytes: text.length, locatedNodes,
|
|
346
548
|
evidenceStatus: status, partial: status !== 'complete', coveredObligations: selected.map(p => p.label),
|
|
347
549
|
uncoveredObligations: gaps.map(g => `${g.reason}: ${g.target}`), nextAnchor: gaps[0]?.nextAnchor },
|
|
348
|
-
metadata: {
|
|
550
|
+
metadata: { ...((0, implementation_context_1.controlEvidenceEnabled)() ? { controls: controlsFor(selected) } : {}),
|
|
551
|
+
...(contextEnabled ? { implementationContext: selected.flatMap(p => p.context ? [{ ...p.context,
|
|
552
|
+
...(p.context.sdk ? { sdk: { ...p.context.sdk, nodes: p.context.sdk.nodes.map(n => ({
|
|
553
|
+
id: n.id, name: n.name, qualifiedName: n.qualifiedName, kind: n.kind, filePath: n.filePath,
|
|
554
|
+
startLine: n.startLine, endLine: n.endLine, startColumn: n.startColumn, endColumn: n.endColumn,
|
|
555
|
+
language: n.language, updatedAt: n.updatedAt, signature: n.signature,
|
|
556
|
+
})) } } : {}) }] : []) } : {}), ...(requestEvidence ? { requestEvidence } : {}), version: search ? 2 : 1, scope: 'bounded_static_evidence', status, selectedPacks: selected.map(p => p.id), gaps,
|
|
349
557
|
relations: relationsFor(selected), ...(search ? { pathSearch: { goal: search.goal, stopReason: stopFor(selected),
|
|
350
558
|
limitsHit: search.limitsHit, stats: search.stats, paths: search.paths.map(p => ({ id: p.id, nodeIds: p.nodeIds, cost: p.cost,
|
|
351
559
|
evidence: selected.some(c => c.path?.id === p.id) ? 'provided' : 'partial' })) } } : {}) } };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Opt-in local evidence only, never an answer cache. Logging cannot break retrieval. */
|
|
2
|
+
export declare function recordEvidencePack(projectRoot: string, input: unknown, output: unknown): 'written' | 'disabled' | 'limit' | 'unavailable';
|
|
3
|
+
//# sourceMappingURL=evidence-audit.d.ts.map
|