@akagilnc/pi-workflow-roles 0.1.3236 → 0.1.3249
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/dist/grok/production-host.js +229 -48
- package/dist/package-contracts/collector-output.js +1 -1
- package/dist/package-contracts/terminating-infrastructure.js +12 -1
- package/dist/public-cli/main.js +25 -5
- package/package.json +1 -1
- package/souls/collector.md +1 -1
- package/src/collector-identity.ts +128 -81
- package/src/collector-ledger.ts +101 -19
- package/src/collector-receipt.ts +42 -7
- package/src/collector-role.ts +89 -9
- package/src/collector-tool-schemas.ts +25 -2
- package/src/host-contracts.ts +7 -1
- package/src/package-contracts/collector-output.ts +10 -2
- package/src/package-contracts/terminating-infrastructure.ts +12 -0
- package/src/public-cli/settlement.ts +16 -0
- package/src/role-runtime.ts +2 -0
- package/src/submission-ledger.ts +16 -10
|
@@ -1,29 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
type GitHubMachineIdentity,
|
|
4
|
-
type GitHubPullRequestReaction,
|
|
5
|
-
type GitHubReview,
|
|
6
|
-
type GitHubReviewComment,
|
|
1
|
+
import type {
|
|
2
|
+
GitHubMachineIdentity,
|
|
7
3
|
} from "./collector-github.ts";
|
|
8
4
|
import type { CollectorEvidenceRecord, HeadRelation } from "./collector-evidence.ts";
|
|
9
|
-
|
|
10
|
-
export type GitHubIdentityMaterial = GitHubReview | GitHubIssueComment | GitHubReviewComment | GitHubPullRequestReaction;
|
|
5
|
+
import { CorrectableSubmissionError } from "./submission-correctable-error.ts";
|
|
11
6
|
|
|
12
7
|
export type CollectorMaterialRef = {
|
|
13
8
|
kind: "review" | "issue_comment" | "review_comment" | "reaction";
|
|
14
9
|
id: number;
|
|
15
|
-
/** Original review body, retained as uninterpreted material. */
|
|
16
|
-
body?: string;
|
|
17
10
|
/** Receipt-local immutable source reference. */
|
|
18
11
|
evidenceId?: string;
|
|
19
12
|
headRelation?: HeadRelation | "unbound";
|
|
20
13
|
};
|
|
21
14
|
|
|
15
|
+
/**
|
|
16
|
+
* #641 chain①: a receipt finding is a model-classified unit (splitting stays
|
|
17
|
+
* with the collector LLM) whose source pointer resolves against the stored
|
|
18
|
+
* evidence and whose machine locator (repo/PR/comment id/url/author/kind/时间)
|
|
19
|
+
* is enriched by the runtime from the same record — never transcribed bodies.
|
|
20
|
+
*/
|
|
22
21
|
export type CollectorFinding = {
|
|
23
|
-
identity: GitHubMachineIdentity;
|
|
22
|
+
identity: GitHubMachineIdentity | null;
|
|
24
23
|
source: CollectorMaterialRef;
|
|
25
|
-
category
|
|
26
|
-
|
|
24
|
+
category?: string;
|
|
25
|
+
pointer: {
|
|
26
|
+
repository: string;
|
|
27
|
+
prNumber: number;
|
|
28
|
+
commentId: number;
|
|
29
|
+
htmlUrl?: string;
|
|
30
|
+
authorLogin?: string;
|
|
31
|
+
kind: CollectorMaterialRef["kind"];
|
|
32
|
+
authoritativeTime?: string | null;
|
|
33
|
+
};
|
|
27
34
|
};
|
|
28
35
|
|
|
29
36
|
export type CollectorIdentityGroup = {
|
|
@@ -35,12 +42,10 @@ export type CollectorIdentityGroup = {
|
|
|
35
42
|
materials: CollectorMaterialRef[];
|
|
36
43
|
};
|
|
37
44
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return "issue_comment";
|
|
43
|
-
}
|
|
45
|
+
export type ExtractedCollectorIdentityGroup = CollectorIdentityGroup & {
|
|
46
|
+
attendance: true;
|
|
47
|
+
findings: CollectorFinding[];
|
|
48
|
+
};
|
|
44
49
|
|
|
45
50
|
function identityKey(identity: GitHubMachineIdentity | null): string {
|
|
46
51
|
if (identity === null) return "unassigned";
|
|
@@ -61,60 +66,10 @@ function mergeMachineIdentity(
|
|
|
61
66
|
return observed.userType < current.userType ? observed : current;
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const groups = new Map<string, CollectorIdentityGroup>();
|
|
69
|
-
for (const material of materials) {
|
|
70
|
-
const identity = material.machineIdentity ?? null;
|
|
71
|
-
const key = identityKey(identity);
|
|
72
|
-
let group = groups.get(key);
|
|
73
|
-
if (group === undefined) {
|
|
74
|
-
group = {
|
|
75
|
-
identity,
|
|
76
|
-
...(material.userLogin === null ? {} : { displayLogin: material.userLogin }),
|
|
77
|
-
materials: [],
|
|
78
|
-
};
|
|
79
|
-
groups.set(key, group);
|
|
80
|
-
} else {
|
|
81
|
-
group.identity = mergeMachineIdentity(group.identity, identity);
|
|
82
|
-
}
|
|
83
|
-
const kind = materialKind(material);
|
|
84
|
-
group.materials.push({
|
|
85
|
-
kind,
|
|
86
|
-
id: material.id,
|
|
87
|
-
...(kind === "review" && "body" in material ? { body: material.body } : {}),
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
return [...groups.values()];
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const CODEX_USER_ID = 199175422;
|
|
94
|
-
const CODERABBIT_USER_ID = 136622811;
|
|
95
|
-
export type ExtractedCollectorIdentityGroup = CollectorIdentityGroup & {
|
|
96
|
-
attendance: true;
|
|
97
|
-
findings: CollectorFinding[];
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
/** Extract attendance and provider findings without using login/display text as identity. */
|
|
101
|
-
export function extractGitHubIdentityGroups(materials: readonly GitHubIdentityMaterial[]): ExtractedCollectorIdentityGroup[] {
|
|
102
|
-
const groups = groupGitHubMaterialsByIdentity(materials);
|
|
103
|
-
const byKey = new Map(groups.map((group) => [identityKey(group.identity), group]));
|
|
104
|
-
for (const group of groups) {
|
|
105
|
-
group.attendance = true;
|
|
106
|
-
group.findings = [];
|
|
107
|
-
}
|
|
108
|
-
for (const material of materials) {
|
|
109
|
-
const identity = material.machineIdentity ?? null;
|
|
110
|
-
const group = byKey.get(identityKey(identity))!;
|
|
111
|
-
if (identity === null) continue;
|
|
112
|
-
const source = { kind: materialKind(material), id: material.id };
|
|
113
|
-
if (source.kind === "review_comment" && "body" in material && (identity.userId === CODEX_USER_ID || identity.userId === CODERABBIT_USER_ID)) {
|
|
114
|
-
group.findings!.push({ identity, source, category: "inline", body: material.body });
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return groups as ExtractedCollectorIdentityGroup[];
|
|
69
|
+
function headRelationFor(record: CollectorEvidenceRecord, targetHead: string): HeadRelation | "unbound" {
|
|
70
|
+
return record.commitOid === undefined || record.commitOid === null
|
|
71
|
+
? "unbound"
|
|
72
|
+
: record.commitOid === targetHead ? "current" : "prior";
|
|
118
73
|
}
|
|
119
74
|
|
|
120
75
|
/** Receipt adapter consuming the typed facts retained by transport normalization. */
|
|
@@ -131,11 +86,8 @@ export function extractCollectorEvidenceIdentityGroups(
|
|
|
131
86
|
const source: CollectorMaterialRef = {
|
|
132
87
|
kind,
|
|
133
88
|
id: record.githubId,
|
|
134
|
-
...(kind === "review" && record.body !== undefined ? { body: record.body } : {}),
|
|
135
89
|
evidenceId: record.evidenceId,
|
|
136
|
-
headRelation: record
|
|
137
|
-
? "unbound"
|
|
138
|
-
: record.commitOid === targetHead ? "current" : "prior",
|
|
90
|
+
headRelation: headRelationFor(record, targetHead),
|
|
139
91
|
};
|
|
140
92
|
const key = identityKey(identity);
|
|
141
93
|
let group = groups.get(key);
|
|
@@ -152,10 +104,105 @@ export function extractCollectorEvidenceIdentityGroups(
|
|
|
152
104
|
group.identity = mergeMachineIdentity(group.identity, identity);
|
|
153
105
|
}
|
|
154
106
|
group.materials.push(source);
|
|
155
|
-
if (identity === null || record.body === undefined) continue;
|
|
156
|
-
if (kind === "review_comment" && (identity.userId === CODEX_USER_ID || identity.userId === CODERABBIT_USER_ID)) {
|
|
157
|
-
group.findings.push({ identity, source: { ...source }, category: "inline", body: record.body });
|
|
158
|
-
}
|
|
159
107
|
}
|
|
160
108
|
return [...groups.values()];
|
|
161
109
|
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* #641 chain①: pointer-open failures are model misuse, not host failures. The
|
|
113
|
+
* seat rejects them as correctable so the model can retry with a stored
|
|
114
|
+
* evidenceId — on Pi and Grok/ACP alike (第 0 条: 模型提交方式可纠正).
|
|
115
|
+
*/
|
|
116
|
+
export class CollectorUnknownEvidenceError extends CorrectableSubmissionError {
|
|
117
|
+
constructor(evidenceId: string) {
|
|
118
|
+
super(`未在本局已观测材料中找到 evidenceId ${evidenceId};请用 observe 返回的指针重试。`);
|
|
119
|
+
this.name = "CollectorUnknownEvidenceError";
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* #641 chain①: any malformed findings submission is a model misuse the model
|
|
125
|
+
* can correct and resubmit — a branded correctable rejection on every supported
|
|
126
|
+
* engine, never a round infrastructure failure.
|
|
127
|
+
*/
|
|
128
|
+
export class CollectorFindingsValidationError extends CorrectableSubmissionError {
|
|
129
|
+
constructor(message: string) {
|
|
130
|
+
super(message);
|
|
131
|
+
this.name = "CollectorFindingsValidationError";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* #641 chain①: turn model-submitted finding pointer refs into receipt findings.
|
|
137
|
+
* Each pointer must resolve to a stored text-bearing evidence record; the
|
|
138
|
+
* machine locator is enriched from the same record so receipt and volume agree
|
|
139
|
+
* (指针可解析、开卷相符) by construction. Throws branded correctable
|
|
140
|
+
* (non-fatal, model-visible) errors for unresolvable or mis-typed findings.
|
|
141
|
+
*/
|
|
142
|
+
export function enrichCollectorFindings(input: {
|
|
143
|
+
candidate: unknown;
|
|
144
|
+
records: readonly CollectorEvidenceRecord[];
|
|
145
|
+
groups: readonly ExtractedCollectorIdentityGroup[];
|
|
146
|
+
targetHead: string;
|
|
147
|
+
repository: string;
|
|
148
|
+
prNumber: number;
|
|
149
|
+
}): void {
|
|
150
|
+
const candidate = input.candidate;
|
|
151
|
+
if (candidate === undefined || candidate === null) return;
|
|
152
|
+
if (typeof candidate !== "object" || Array.isArray(candidate)) {
|
|
153
|
+
throw new CollectorFindingsValidationError("通进司交件参数必须为对象");
|
|
154
|
+
}
|
|
155
|
+
const rawFindings = (candidate as { findings?: unknown }).findings;
|
|
156
|
+
if (rawFindings === undefined) return;
|
|
157
|
+
if (!Array.isArray(rawFindings)) {
|
|
158
|
+
throw new CollectorFindingsValidationError("通进司 findings 必须为数组");
|
|
159
|
+
}
|
|
160
|
+
const byEvidenceId = new Map(input.records.map((record) => [record.evidenceId, record]));
|
|
161
|
+
for (const raw of rawFindings) {
|
|
162
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
163
|
+
throw new CollectorFindingsValidationError("通进司 finding 必须为对象");
|
|
164
|
+
}
|
|
165
|
+
const evidenceId = (raw as { evidenceId?: unknown }).evidenceId;
|
|
166
|
+
if (typeof evidenceId !== "string" || evidenceId.length === 0) {
|
|
167
|
+
throw new CollectorFindingsValidationError("通进司 finding 缺少可解析的 evidenceId 指针");
|
|
168
|
+
}
|
|
169
|
+
const record = byEvidenceId.get(evidenceId);
|
|
170
|
+
if (record === undefined) {
|
|
171
|
+
throw new CollectorUnknownEvidenceError(evidenceId);
|
|
172
|
+
}
|
|
173
|
+
if (record.kind !== "review" && record.kind !== "issue_comment" && record.kind !== "review_comment") {
|
|
174
|
+
throw new CollectorFindingsValidationError(`通进司 finding 指针指向不可承 finding 的证据种类 ${record.kind}`);
|
|
175
|
+
}
|
|
176
|
+
if (record.githubId === undefined) {
|
|
177
|
+
throw new CollectorFindingsValidationError(`通进司 finding 指针证据 ${evidenceId} 缺少 GitHub id`);
|
|
178
|
+
}
|
|
179
|
+
const category = (raw as { category?: unknown }).category;
|
|
180
|
+
if (category !== undefined && (typeof category !== "string" || category.trim().length === 0)) {
|
|
181
|
+
throw new CollectorFindingsValidationError("通进司 finding category 必须为非空字符串");
|
|
182
|
+
}
|
|
183
|
+
const identity = record.machineIdentity ?? null;
|
|
184
|
+
const group = input.groups.find((candidateGroup) => identityKey(candidateGroup.identity) === identityKey(identity));
|
|
185
|
+
if (group === undefined) {
|
|
186
|
+
throw new CollectorFindingsValidationError(`通进司 finding 指针证据 ${evidenceId} 无归属身份组`);
|
|
187
|
+
}
|
|
188
|
+
group.findings.push({
|
|
189
|
+
identity,
|
|
190
|
+
source: {
|
|
191
|
+
kind: record.kind,
|
|
192
|
+
id: record.githubId,
|
|
193
|
+
evidenceId: record.evidenceId,
|
|
194
|
+
headRelation: headRelationFor(record, input.targetHead),
|
|
195
|
+
},
|
|
196
|
+
...(category === undefined ? {} : { category: category.trim() }),
|
|
197
|
+
pointer: {
|
|
198
|
+
repository: input.repository,
|
|
199
|
+
prNumber: input.prNumber,
|
|
200
|
+
commentId: record.githubId,
|
|
201
|
+
...(record.htmlUrl === undefined ? {} : { htmlUrl: record.htmlUrl }),
|
|
202
|
+
...(record.authorLogin === undefined ? {} : { authorLogin: record.authorLogin }),
|
|
203
|
+
kind: record.kind,
|
|
204
|
+
authoritativeTime: record.authoritativeTime ?? null,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
package/src/collector-ledger.ts
CHANGED
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
type CollectorClock,
|
|
17
17
|
type CollectorEvidenceRecord,
|
|
18
18
|
type CollectorSnapshot,
|
|
19
|
+
type HeadRelation,
|
|
20
|
+
type WindowRelation,
|
|
19
21
|
} from "./collector-evidence.ts";
|
|
20
22
|
import {
|
|
21
23
|
buildCollectorRequestBody,
|
|
@@ -26,22 +28,92 @@ import {
|
|
|
26
28
|
import {
|
|
27
29
|
collectorObserveArgsSchema,
|
|
28
30
|
collectorOutputArgsSchema,
|
|
31
|
+
collectorReadArgsSchema,
|
|
29
32
|
collectorRequestArgsSchema,
|
|
30
33
|
collectorWaitArgsSchema,
|
|
31
34
|
} from "./collector-tool-schemas.ts";
|
|
32
35
|
import { COLLECTOR_OUTPUT_TOOL } from "./package-contracts/collector-output.ts";
|
|
33
36
|
|
|
34
37
|
export const COLLECTOR_OBSERVE_TOOL = "ak_collector_observe";
|
|
38
|
+
export const COLLECTOR_READ_TOOL = "ak_collector_read";
|
|
35
39
|
export const COLLECTOR_REQUEST_TOOL = "ak_collector_request";
|
|
36
40
|
export const COLLECTOR_WAIT_TOOL = "ak_collector_wait";
|
|
37
41
|
export { COLLECTOR_OUTPUT_TOOL };
|
|
38
42
|
|
|
39
43
|
export const COLLECTOR_OPERATIONAL_TOOLS = [
|
|
40
44
|
COLLECTOR_OBSERVE_TOOL,
|
|
45
|
+
COLLECTOR_READ_TOOL,
|
|
41
46
|
COLLECTOR_REQUEST_TOOL,
|
|
42
47
|
COLLECTOR_WAIT_TOOL,
|
|
43
48
|
] as const;
|
|
44
49
|
|
|
50
|
+
/**
|
|
51
|
+
* #641 chain①: observe context projects at most this many bytes of each
|
|
52
|
+
* material body; the full body stays reachable via the record pointer
|
|
53
|
+
* (evidenceId + htmlUrl) and is preserved in the volume, never unconditionally
|
|
54
|
+
* transcribed into model context or receipt.
|
|
55
|
+
*/
|
|
56
|
+
export const COLLECTOR_OBSERVE_BODY_HEAD_BYTES = 512;
|
|
57
|
+
|
|
58
|
+
function utf8SafePrefix(text: string, maxBytes: number): string {
|
|
59
|
+
if (Buffer.byteLength(text, "utf8") <= maxBytes) return text;
|
|
60
|
+
const bytes = Buffer.from(text, "utf8");
|
|
61
|
+
let end = maxBytes;
|
|
62
|
+
while (end > 0 && (bytes[end]! & 0xc0) === 0x80) end -= 1;
|
|
63
|
+
return `${bytes.subarray(0, end).toString("utf8")}…`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type ObserveEvidenceEntry = {
|
|
67
|
+
evidenceId: string;
|
|
68
|
+
kind: string;
|
|
69
|
+
authorLogin?: string | undefined;
|
|
70
|
+
state?: string | undefined;
|
|
71
|
+
body?: string | undefined;
|
|
72
|
+
commitOid?: string | null | undefined;
|
|
73
|
+
htmlUrl?: string | undefined;
|
|
74
|
+
path?: string | undefined;
|
|
75
|
+
line?: number | null | undefined;
|
|
76
|
+
side?: string | null | undefined;
|
|
77
|
+
authoritativeTime?: string | null | undefined;
|
|
78
|
+
windowRelation?: WindowRelation | undefined;
|
|
79
|
+
pullRequestReviewId?: number | null | undefined;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export type ObserveModelView = {
|
|
83
|
+
snapshotId: string;
|
|
84
|
+
observedAt: string;
|
|
85
|
+
completedAt: string;
|
|
86
|
+
prState: string;
|
|
87
|
+
headOid: string;
|
|
88
|
+
complete: boolean;
|
|
89
|
+
evidence: ObserveEvidenceEntry[];
|
|
90
|
+
requestAttempts: Array<{
|
|
91
|
+
attemptId: string;
|
|
92
|
+
requestId: string;
|
|
93
|
+
observedHead: string;
|
|
94
|
+
status: string;
|
|
95
|
+
marker: string;
|
|
96
|
+
recoverySnapshotId?: string | undefined;
|
|
97
|
+
}>;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* #641 chain① model-context projection: identical shape to the volume view,
|
|
102
|
+
* with every material body bounded to a head preview. Full bodies remain
|
|
103
|
+
* pointer-reachable (evidenceId/htmlUrl) and stay in the ledger volume
|
|
104
|
+
* (collector-side; provider-visible observe details carry only this bounded
|
|
105
|
+
* projection), never unconditionally transcribed into model context.
|
|
106
|
+
*/
|
|
107
|
+
export function projectObserveContextView(modelView: ObserveModelView): ObserveModelView {
|
|
108
|
+
return {
|
|
109
|
+
...modelView,
|
|
110
|
+
evidence: modelView.evidence.map((entry) =>
|
|
111
|
+
entry.body === undefined
|
|
112
|
+
? entry
|
|
113
|
+
: { ...entry, body: utf8SafePrefix(entry.body, COLLECTOR_OBSERVE_BODY_HEAD_BYTES) }),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
45
117
|
export type CollectorOperationalTool = (typeof COLLECTOR_OPERATIONAL_TOOLS)[number];
|
|
46
118
|
|
|
47
119
|
export type CollectorRequestAttempt = {
|
|
@@ -117,7 +189,7 @@ export type CollectorLedger = {
|
|
|
117
189
|
signal?: AbortSignal,
|
|
118
190
|
): Promise<{
|
|
119
191
|
snapshot: CollectorSnapshot;
|
|
120
|
-
|
|
192
|
+
contextView: ObserveModelView;
|
|
121
193
|
}>;
|
|
122
194
|
|
|
123
195
|
request(
|
|
@@ -157,6 +229,8 @@ export function collectorToolArgumentsValid(
|
|
|
157
229
|
switch (name) {
|
|
158
230
|
case COLLECTOR_OBSERVE_TOOL:
|
|
159
231
|
return Value.Check(collectorObserveArgsSchema, args);
|
|
232
|
+
case COLLECTOR_READ_TOOL:
|
|
233
|
+
return Value.Check(collectorReadArgsSchema, args);
|
|
160
234
|
case COLLECTOR_REQUEST_TOOL:
|
|
161
235
|
return Value.Check(collectorRequestArgsSchema, args);
|
|
162
236
|
case COLLECTOR_WAIT_TOOL:
|
|
@@ -566,7 +640,7 @@ export function createCollectorLedger(config: CollectorConfigState): CollectorLe
|
|
|
566
640
|
attempts,
|
|
567
641
|
});
|
|
568
642
|
|
|
569
|
-
return { snapshot, modelView };
|
|
643
|
+
return { snapshot, contextView: projectObserveContextView(modelView) };
|
|
570
644
|
},
|
|
571
645
|
|
|
572
646
|
async request(input, transport, clock, signal) {
|
|
@@ -780,12 +854,35 @@ export function createCollectorLedger(config: CollectorConfigState): CollectorLe
|
|
|
780
854
|
return ledger;
|
|
781
855
|
}
|
|
782
856
|
|
|
857
|
+
/**
|
|
858
|
+
* #641 chain①: single authoritative evidence-record projection shared by the
|
|
859
|
+
* observe model view and the pointer-open read tool.
|
|
860
|
+
*/
|
|
861
|
+
export function projectEvidenceEntryView(record: CollectorEvidenceRecord): ObserveEvidenceEntry {
|
|
862
|
+
return {
|
|
863
|
+
evidenceId: record.evidenceId,
|
|
864
|
+
kind: record.kind,
|
|
865
|
+
authorLogin: record.authorLogin,
|
|
866
|
+
state: record.state,
|
|
867
|
+
body: record.body,
|
|
868
|
+
commitOid: record.commitOid,
|
|
869
|
+
htmlUrl: record.htmlUrl,
|
|
870
|
+
path: record.path,
|
|
871
|
+
// Single display fallback: current line, else originalLine.
|
|
872
|
+
line: record.line ?? record.originalLine,
|
|
873
|
+
side: record.side,
|
|
874
|
+
authoritativeTime: record.authoritativeTime,
|
|
875
|
+
windowRelation: record.windowRelation,
|
|
876
|
+
pullRequestReviewId: record.pullRequestReviewId,
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
|
|
783
880
|
function buildObserveModelView(input: {
|
|
784
881
|
snapshot: CollectorSnapshot;
|
|
785
882
|
records: readonly CollectorEvidenceRecord[];
|
|
786
883
|
requesterLogin: string | undefined;
|
|
787
884
|
attempts: readonly CollectorRequestAttempt[];
|
|
788
|
-
}):
|
|
885
|
+
}): ObserveModelView {
|
|
789
886
|
const relevant = input.records;
|
|
790
887
|
return {
|
|
791
888
|
snapshotId: input.snapshot.snapshotId,
|
|
@@ -794,22 +891,7 @@ function buildObserveModelView(input: {
|
|
|
794
891
|
prState: input.snapshot.prState,
|
|
795
892
|
headOid: input.snapshot.headOid,
|
|
796
893
|
complete: input.snapshot.complete,
|
|
797
|
-
evidence: relevant.map((record) => (
|
|
798
|
-
evidenceId: record.evidenceId,
|
|
799
|
-
kind: record.kind,
|
|
800
|
-
authorLogin: record.authorLogin,
|
|
801
|
-
state: record.state,
|
|
802
|
-
body: record.body,
|
|
803
|
-
commitOid: record.commitOid,
|
|
804
|
-
htmlUrl: record.htmlUrl,
|
|
805
|
-
path: record.path,
|
|
806
|
-
// Single display fallback: current line, else originalLine.
|
|
807
|
-
line: record.line ?? record.originalLine,
|
|
808
|
-
side: record.side,
|
|
809
|
-
authoritativeTime: record.authoritativeTime,
|
|
810
|
-
windowRelation: record.windowRelation,
|
|
811
|
-
pullRequestReviewId: record.pullRequestReviewId,
|
|
812
|
-
})),
|
|
894
|
+
evidence: relevant.map((record): ObserveEvidenceEntry => projectEvidenceEntryView(record)),
|
|
813
895
|
requestAttempts: input.attempts.map((attempt) => ({
|
|
814
896
|
attemptId: attempt.attemptId,
|
|
815
897
|
requestId: attempt.requestId,
|
package/src/collector-receipt.ts
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import { COLLECTOR_HOST } from "./collector-config.ts";
|
|
2
2
|
import type { CollectorClock, CollectorEvidenceRecord, CollectorSnapshot } from "./collector-evidence.ts";
|
|
3
3
|
import type { CollectorLedger, CollectorRequestAttempt } from "./collector-ledger.ts";
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
enrichCollectorFindings,
|
|
6
|
+
extractCollectorEvidenceIdentityGroups,
|
|
7
|
+
type ExtractedCollectorIdentityGroup,
|
|
8
|
+
} from "./collector-identity.ts";
|
|
9
|
+
import { validateAcceptedCollectorReceipt, type CollectorEvidenceRecord as ReceiptEvidenceRecord } from "./package-contracts/collector-output.ts";
|
|
6
10
|
|
|
7
11
|
export { validateAcceptedCollectorReceipt };
|
|
8
12
|
|
|
13
|
+
/**
|
|
14
|
+
* #641 chain① receipt-local evidence projection: machine identity, integrity
|
|
15
|
+
* digest and resolvable pointer fields only. Bodies/raw stay in the volume
|
|
16
|
+
* (observe details + GitHub pointer); they are never transcribed into the receipt.
|
|
17
|
+
*/
|
|
18
|
+
export type CollectorReceiptEvidenceRecord = ReceiptEvidenceRecord;
|
|
19
|
+
|
|
9
20
|
export type CollectorReceipt = {
|
|
10
21
|
host: "github.com";
|
|
11
22
|
repository: string;
|
|
@@ -19,15 +30,15 @@ export type CollectorReceipt = {
|
|
|
19
30
|
groups: ExtractedCollectorIdentityGroup[];
|
|
20
31
|
requestAttempts: CollectorRequestAttempt[];
|
|
21
32
|
snapshots: CollectorSnapshot[];
|
|
22
|
-
evidenceRecords:
|
|
33
|
+
evidenceRecords: CollectorReceiptEvidenceRecord[];
|
|
23
34
|
};
|
|
24
35
|
|
|
25
36
|
function fail(message: string): never { throw new Error(message); }
|
|
26
37
|
|
|
27
|
-
/** Runtime output
|
|
38
|
+
/** Runtime output assembles findings pointers from the model submission into the typed receipt. */
|
|
28
39
|
export function buildCollectorReceipt(
|
|
29
40
|
ledger: CollectorLedger,
|
|
30
|
-
|
|
41
|
+
candidateRaw: unknown,
|
|
31
42
|
clock?: CollectorClock,
|
|
32
43
|
): CollectorReceipt {
|
|
33
44
|
ledger.assertNotFatal();
|
|
@@ -46,7 +57,7 @@ export function buildCollectorReceipt(
|
|
|
46
57
|
if (finalSnapshot === undefined || !finalSnapshot.complete) fail("Collector final snapshot is incomplete");
|
|
47
58
|
if (finalSnapshot.prState !== "OPEN") fail("Collector final snapshot PR state is not OPEN");
|
|
48
59
|
|
|
49
|
-
const evidenceRecords = [...ledger.allEvidence()];
|
|
60
|
+
const evidenceRecords: CollectorEvidenceRecord[] = [...ledger.allEvidence()];
|
|
50
61
|
const snapshots = [...ledger.allSnapshots()];
|
|
51
62
|
const evidenceIndex = new Map(evidenceRecords.map((record) => [record.evidenceId, record]));
|
|
52
63
|
const snapshotIndex = new Map(snapshots.map((snapshot) => [snapshot.snapshotId, snapshot]));
|
|
@@ -59,6 +70,16 @@ export function buildCollectorReceipt(
|
|
|
59
70
|
}
|
|
60
71
|
|
|
61
72
|
const groups = extractCollectorEvidenceIdentityGroups(evidenceRecords, finalSnapshot.headOid);
|
|
73
|
+
// #641 chain①: the collector LLM submits findings as pointer refs; the runtime
|
|
74
|
+
// enriches each with the machine locator from the same stored record.
|
|
75
|
+
enrichCollectorFindings({
|
|
76
|
+
candidate: candidateRaw,
|
|
77
|
+
records: evidenceRecords,
|
|
78
|
+
groups,
|
|
79
|
+
targetHead: finalSnapshot.headOid,
|
|
80
|
+
repository: ledger.config.repository.canonical,
|
|
81
|
+
prNumber: ledger.config.prNumber,
|
|
82
|
+
});
|
|
62
83
|
for (const group of groups) {
|
|
63
84
|
if (group.attendance !== true) fail("Collector group lacks attendance");
|
|
64
85
|
for (const material of group.materials) {
|
|
@@ -82,6 +103,20 @@ export function buildCollectorReceipt(
|
|
|
82
103
|
groups,
|
|
83
104
|
requestAttempts: [...ledger.requestAttempts()],
|
|
84
105
|
snapshots,
|
|
85
|
-
evidenceRecords,
|
|
106
|
+
evidenceRecords: evidenceRecords.map(toReceiptEvidenceRecord),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function toReceiptEvidenceRecord(record: CollectorEvidenceRecord): CollectorReceiptEvidenceRecord {
|
|
111
|
+
return {
|
|
112
|
+
evidenceId: record.evidenceId,
|
|
113
|
+
kind: record.kind,
|
|
114
|
+
versionId: record.versionId,
|
|
115
|
+
contentDigest: record.contentDigest,
|
|
116
|
+
firstObservedAt: record.firstObservedAt,
|
|
117
|
+
...(record.githubId === undefined ? {} : { githubId: record.githubId }),
|
|
118
|
+
...(record.authorLogin === undefined ? {} : { authorLogin: record.authorLogin }),
|
|
119
|
+
...(record.htmlUrl === undefined ? {} : { htmlUrl: record.htmlUrl }),
|
|
120
|
+
...(record.authoritativeTime === undefined ? {} : { authoritativeTime: record.authoritativeTime }),
|
|
86
121
|
};
|
|
87
122
|
}
|