@akagilnc/pi-workflow-roles 0.1.5241 → 0.1.5252

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.
Files changed (41) hide show
  1. package/README.md +2 -3
  2. package/README.zh-CN.md +2 -3
  3. package/dist/acp-host/production-host.js +482 -3141
  4. package/dist/collector-config.js +0 -1
  5. package/dist/headless-host/production-host.js +590 -3249
  6. package/dist/migrate-book-topology.js +40 -107
  7. package/dist/navigator-invocation-identity.js +0 -57
  8. package/dist/package-contracts/collector-output.js +1 -90
  9. package/dist/package-contracts/terminating-tools.js +2 -2
  10. package/dist/packaged-role-registry.js +8 -11
  11. package/dist/public-cli/invocation.js +2 -22
  12. package/dist/public-cli/main.js +256 -749
  13. package/dist/public-cli/option-definitions.js +4 -19
  14. package/dist/public-cli/settlement.js +12 -138
  15. package/package.json +1 -1
  16. package/resources/collector-bot-handbook.md +4 -2
  17. package/src/collector-config.ts +0 -1
  18. package/src/collector-role.ts +40 -535
  19. package/src/collector-tool-schemas.ts +7 -117
  20. package/src/gh-api-runner.ts +102 -0
  21. package/src/navigator-invocation-identity.ts +4 -121
  22. package/src/package-contracts/collector-output.ts +15 -174
  23. package/src/package-contracts/terminating-tools.ts +0 -2
  24. package/src/packaged-role-registry.ts +7 -10
  25. package/src/public-cli/invocation.ts +3 -27
  26. package/src/public-cli/option-definitions.ts +4 -19
  27. package/src/public-cli/settlement.ts +11 -177
  28. package/src/role-runtime-dependencies.ts +0 -4
  29. package/src/role-runtime.ts +39 -103
  30. package/src/ticket-snapshot.ts +1 -1
  31. package/dist/collector-evidence.js +0 -319
  32. package/dist/collector-github.js +0 -725
  33. package/dist/collector-identity.js +0 -92
  34. package/dist/collector-ledger.js +0 -888
  35. package/dist/collector-target.js +0 -169
  36. package/src/collector-evidence.ts +0 -466
  37. package/src/collector-github.ts +0 -1032
  38. package/src/collector-handbook.ts +0 -194
  39. package/src/collector-identity.ts +0 -151
  40. package/src/collector-ledger.ts +0 -1186
  41. package/src/collector-target.ts +0 -207
@@ -1,194 +0,0 @@
1
- /**
2
- * Collector bot handbook — opaque working memory under the book topology.
3
- * Runtime stores and delivers UTF-8 text only; never parses free text into
4
- * code state rules (parent #673 D3 / #677).
5
- *
6
- * Placement reuses the machine ledger home (ADR 0048) when the admitted
7
- * session already sits under books/<bookKey>/ — no parallel persistence frame.
8
- */
9
- import { readFile } from "node:fs/promises";
10
- import { join, sep } from "node:path";
11
-
12
- import { writeFileAtomically } from "./atomic-write.ts";
13
- import {
14
- activationBookDirectory,
15
- assertLedgerFileInsideHome,
16
- ensureRealDirectoryTree,
17
- resolveActivationLedgerHomeForPath,
18
- } from "./activation-ledger-topology.ts";
19
- import { COLLECTOR_HANDBOOK_MAX_BYTES } from "./collector-tool-schemas.ts";
20
-
21
- export type CollectorHandbookScope = "general" | "repo";
22
-
23
- export type CollectorHandbookRead = {
24
- readonly general: string;
25
- readonly repo: string;
26
- readonly generalSource: "book" | "seed" | "empty";
27
- readonly repoSource: "book" | "empty";
28
- readonly generalPath: string;
29
- readonly repoPath: string;
30
- };
31
-
32
- export type CollectorHandbookWriteResult = {
33
- readonly scope: CollectorHandbookScope;
34
- readonly path: string;
35
- readonly byteLength: number;
36
- };
37
-
38
- export type CollectorHandbookStore = {
39
- readonly root: string;
40
- readonly repositoryCanonical: string;
41
- read(): Promise<CollectorHandbookRead>;
42
- write(scope: CollectorHandbookScope, body: string): Promise<CollectorHandbookWriteResult>;
43
- };
44
-
45
- export type CollectorHandbookPlacement = {
46
- readonly ledgerHome: string;
47
- readonly bookKey: string;
48
- readonly root: string;
49
- };
50
-
51
- /**
52
- * Derive handbook root from an admitted session/run path under
53
- * `.ak-roles/books/<bookKey>/...`. Fails closed when topology is absent.
54
- */
55
- export function resolveCollectorHandbookRoot(sessionPath: string): CollectorHandbookPlacement {
56
- if (typeof sessionPath !== "string" || sessionPath.trim().length === 0) {
57
- throw new Error("通进司手册要求非空 session 路径,且位于 books/<bookKey>/");
58
- }
59
- // Platform path segments only — never rewrite non-separator characters (POSIX `\\` is literal).
60
- const segments = sessionPath.split(sep);
61
- let bookKey: string | undefined;
62
- for (let i = 0; i + 3 < segments.length; i += 1) {
63
- if (segments[i] === ".ak-roles" && segments[i + 1] === "books") {
64
- const candidate = segments[i + 2]!;
65
- // Require a following segment so bookKey is a true path component under books/.
66
- if (candidate.length > 0) {
67
- bookKey = candidate;
68
- break;
69
- }
70
- }
71
- }
72
- if (bookKey === undefined) {
73
- throw new Error(
74
- `通进司手册要求 session 位于 books/<bookKey>/;收到 ${sessionPath}`,
75
- );
76
- }
77
- if (bookKey === "." || bookKey === "..") {
78
- throw new Error(`通进司手册拒绝不安全 bookKey ${JSON.stringify(bookKey)}`);
79
- }
80
- const ledgerHome = resolveActivationLedgerHomeForPath(sessionPath);
81
- const root = join(activationBookDirectory(ledgerHome, bookKey), "collector-handbook");
82
- return { ledgerHome, bookKey, root };
83
- }
84
-
85
- /** Flat repo file name under handbook/repos/ — avoids nested owner/repo dirs. */
86
- function collectorHandbookRepoFileName(repositoryCanonical: string): string {
87
- if (!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*$/.test(repositoryCanonical)) {
88
- throw new Error(
89
- `通进司手册仓库文件要求规范 owner/repo,收到 ${JSON.stringify(repositoryCanonical)}`,
90
- );
91
- }
92
- return `${repositoryCanonical.replaceAll("/", "__")}.md`;
93
- }
94
-
95
- export function createCollectorHandbookStore(input: {
96
- readonly ledgerHome: string;
97
- readonly handbookRoot: string;
98
- readonly repositoryCanonical: string;
99
- readonly seedGeneral?: string;
100
- }): CollectorHandbookStore {
101
- const generalPath = join(input.handbookRoot, "general.md");
102
- const repoDir = join(input.handbookRoot, "repos");
103
- const repoPath = join(repoDir, collectorHandbookRepoFileName(input.repositoryCanonical));
104
-
105
- /**
106
- * ADR 0038: confine at the real read seam. Parent dirs must be physical under
107
- * ledger home; the leaf must not be a pre-existing symlink (even in-home).
108
- */
109
- /** Sole UTF-8 budget seam — write and read share COLLECTOR_HANDBOOK_MAX_BYTES. */
110
- const assertHandbookBudget = (body: string, label: string): number => {
111
- const byteLength = Buffer.byteLength(body, "utf8");
112
- if (byteLength > COLLECTOR_HANDBOOK_MAX_BYTES) {
113
- throw new Error(
114
- `通进司手册${label} UTF-8 至多 ${COLLECTOR_HANDBOOK_MAX_BYTES} 字节,收到 ${byteLength}`,
115
- );
116
- }
117
- return byteLength;
118
- };
119
-
120
- const readOptional = async (path: string, parentDir: string): Promise<string | undefined> => {
121
- ensureRealDirectoryTree(input.ledgerHome, parentDir);
122
- assertLedgerFileInsideHome(path, input.ledgerHome);
123
- try {
124
- const body = await readFile(path, "utf8");
125
- assertHandbookBudget(body, "正文");
126
- return body;
127
- } catch (error) {
128
- if (isNotFound(error)) return undefined;
129
- throw error;
130
- }
131
- };
132
-
133
- return {
134
- root: input.handbookRoot,
135
- repositoryCanonical: input.repositoryCanonical,
136
- async read() {
137
- const bookGeneral = await readOptional(generalPath, input.handbookRoot);
138
- const bookRepo = await readOptional(repoPath, repoDir);
139
- if (bookGeneral !== undefined) {
140
- return {
141
- general: bookGeneral,
142
- repo: bookRepo ?? "",
143
- generalSource: "book",
144
- repoSource: bookRepo === undefined ? "empty" : "book",
145
- generalPath,
146
- repoPath,
147
- };
148
- }
149
- const seed = input.seedGeneral;
150
- if (typeof seed === "string" && seed.length > 0) {
151
- assertHandbookBudget(seed, "正文");
152
- return {
153
- general: seed,
154
- repo: bookRepo ?? "",
155
- generalSource: "seed",
156
- repoSource: bookRepo === undefined ? "empty" : "book",
157
- generalPath,
158
- repoPath,
159
- };
160
- }
161
- return {
162
- general: "",
163
- repo: bookRepo ?? "",
164
- generalSource: "empty",
165
- repoSource: bookRepo === undefined ? "empty" : "book",
166
- generalPath,
167
- repoPath,
168
- };
169
- },
170
- async write(scope, body) {
171
- // body string shape = collectorHandbookWriteArgsSchema; byte budget is business (UTF-8).
172
- const byteLength = assertHandbookBudget(body, "正文");
173
- ensureRealDirectoryTree(input.ledgerHome, input.handbookRoot);
174
- const path = scope === "general" ? generalPath : repoPath;
175
- if (scope === "repo") {
176
- ensureRealDirectoryTree(input.ledgerHome, repoDir);
177
- }
178
- assertLedgerFileInsideHome(path, input.ledgerHome);
179
- await writeFileAtomically(path, body);
180
- return {
181
- scope,
182
- path,
183
- byteLength,
184
- };
185
- },
186
- };
187
- }
188
-
189
- function isNotFound(error: unknown): boolean {
190
- return typeof error === "object"
191
- && error !== null
192
- && "code" in error
193
- && (error as { code?: unknown }).code === "ENOENT";
194
- }
@@ -1,151 +0,0 @@
1
- import type {
2
- GitHubMachineIdentity,
3
- } from "./collector-github.ts";
4
- import type { CollectorEvidenceRecord, HeadRelation } from "./collector-evidence.ts";
5
- import { CorrectableSubmissionError } from "./submission-correctable-error.ts";
6
-
7
- export type CollectorMaterialRef = {
8
- kind: "review" | "issue_comment" | "review_comment" | "reaction";
9
- id: number;
10
- /** Receipt-local immutable source reference. */
11
- evidenceId?: string;
12
- headRelation?: HeadRelation | "unbound";
13
- };
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
- */
21
- export type CollectorFinding = {
22
- identity: GitHubMachineIdentity | null;
23
- source: CollectorMaterialRef;
24
- /** Short classification label; not the finding summary. */
25
- category?: string;
26
- /** Finding summary for the caller; not a body transcription. */
27
- summary?: string;
28
- pointer: {
29
- repository: string;
30
- prNumber: number;
31
- commentId: number;
32
- htmlUrl?: string;
33
- authorLogin?: string;
34
- kind: CollectorMaterialRef["kind"];
35
- authoritativeTime?: string | null;
36
- /** Corresponding commit when the evidence carries one. */
37
- commitOid?: string | null;
38
- };
39
- };
40
-
41
- export type CollectorIdentityGroup = {
42
- identity: GitHubMachineIdentity | null;
43
- /** Human-readable metadata only; never participates in grouping. */
44
- displayLogin?: string;
45
- attendance?: true;
46
- findings?: CollectorFinding[];
47
- materials: CollectorMaterialRef[];
48
- };
49
-
50
- export type ExtractedCollectorIdentityGroup = CollectorIdentityGroup & {
51
- attendance: true;
52
- findings: CollectorFinding[];
53
- };
54
-
55
- function identityKey(identity: GitHubMachineIdentity | null): string {
56
- if (identity === null) return "unassigned";
57
- // GitHub omits App metadata on some surfaces (notably review comments).
58
- // The stable user ID is the grouping identity; richer observed structure is
59
- // merged below and must not split one actor across transport surfaces.
60
- return String(identity.userId);
61
- }
62
-
63
- function mergeMachineIdentity(
64
- current: GitHubMachineIdentity | null,
65
- observed: GitHubMachineIdentity | null,
66
- ): GitHubMachineIdentity | null {
67
- if (current === null) return observed;
68
- if (observed === null) return current;
69
- if (current.appId === undefined && observed.appId !== undefined) return observed;
70
- if (current.appId !== undefined && observed.appId === undefined) return current;
71
- return observed.userType < current.userType ? observed : current;
72
- }
73
-
74
- function headRelationFor(record: CollectorEvidenceRecord, targetHead: string): HeadRelation | "unbound" {
75
- return record.commitOid === undefined || record.commitOid === null
76
- ? "unbound"
77
- : record.commitOid === targetHead ? "current" : "prior";
78
- }
79
-
80
- /** Receipt adapter consuming the typed facts retained by transport normalization. */
81
- export function extractCollectorEvidenceIdentityGroups(
82
- records: readonly CollectorEvidenceRecord[],
83
- targetHead: string,
84
- ): ExtractedCollectorIdentityGroup[] {
85
- const groups = new Map<string, ExtractedCollectorIdentityGroup>();
86
- for (const record of records) {
87
- if (record.kind !== "review" && record.kind !== "issue_comment" && record.kind !== "review_comment" && record.kind !== "reaction") continue;
88
- if (record.githubId === undefined) continue;
89
- const identity = record.machineIdentity ?? null;
90
- const kind = record.kind;
91
- const source: CollectorMaterialRef = {
92
- kind,
93
- id: record.githubId,
94
- evidenceId: record.evidenceId,
95
- headRelation: headRelationFor(record, targetHead),
96
- };
97
- const key = identityKey(identity);
98
- let group = groups.get(key);
99
- if (group === undefined) {
100
- group = {
101
- identity,
102
- ...(record.authorLogin === undefined ? {} : { displayLogin: record.authorLogin }),
103
- attendance: true,
104
- findings: [],
105
- materials: [],
106
- };
107
- groups.set(key, group);
108
- } else {
109
- group.identity = mergeMachineIdentity(group.identity, identity);
110
- }
111
- group.materials.push(source);
112
- }
113
- return [...groups.values()];
114
- }
115
-
116
- /**
117
- * #641 chain①: pointer-open failures are model misuse, not host failures. The
118
- * seat rejects them as correctable so the model can retry with a stored
119
- * evidenceId — on Pi and Grok/ACP alike (第 0 条: 模型提交方式可纠正).
120
- */
121
- export class CollectorUnknownEvidenceError extends CorrectableSubmissionError {
122
- constructor(evidenceId: string) {
123
- super(`未在本局已观测材料中找到 evidenceId ${evidenceId};请用 observe 返回的指针重试。`);
124
- this.name = "CollectorUnknownEvidenceError";
125
- }
126
- }
127
-
128
- /**
129
- * #676 D6: non-OPEN targets keep collected materials and must not fire new review
130
- * requests. Bounce the request as correctable so the seat can still seal output.
131
- */
132
- export class CollectorNonOpenRequestError extends CorrectableSubmissionError {
133
- constructor(prState: string) {
134
- super(`通进司请求要求 OPEN 状态的 PR 快照;当前为 ${prState},不再触发新评审,请直接交回已有材料`);
135
- this.name = "CollectorNonOpenRequestError";
136
- }
137
- }
138
-
139
- /** #678: wait window not open or already ended — bounce without latching fatal so materials still seal. */
140
- export class CollectorWaitWindowClosedError extends CorrectableSubmissionError {
141
- constructor(action: "request" | "wait" | "wait-before-open") {
142
- super(
143
- action === "request"
144
- ? "通进司请求不在资格截止前"
145
- : action === "wait-before-open"
146
- ? "通进司等待需要先在工作步骤开启等待窗(新建 PR 用创建成功时刻;已有 PR 在触发阶段结束后开启)"
147
- : "通进司等待不在资格截止前",
148
- );
149
- this.name = "CollectorWaitWindowClosedError";
150
- }
151
- }