@akagilnc/pi-workflow-roles 0.1.5241 → 0.1.5245
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/README.md +2 -3
- package/README.zh-CN.md +2 -3
- package/dist/acp-host/production-host.js +442 -3070
- package/dist/collector-config.js +0 -1
- package/dist/headless-host/production-host.js +443 -3071
- package/dist/migrate-book-topology.js +40 -107
- package/dist/package-contracts/collector-output.js +1 -90
- package/dist/package-contracts/terminating-tools.js +2 -2
- package/dist/packaged-role-registry.js +8 -11
- package/dist/public-cli/invocation.js +2 -22
- package/dist/public-cli/main.js +252 -698
- package/dist/public-cli/option-definitions.js +4 -19
- package/dist/public-cli/settlement.js +2 -79
- package/package.json +1 -1
- package/resources/collector-bot-handbook.md +4 -2
- package/src/collector-config.ts +0 -1
- package/src/collector-role.ts +40 -535
- package/src/collector-tool-schemas.ts +7 -117
- package/src/gh-api-runner.ts +102 -0
- package/src/package-contracts/collector-output.ts +15 -174
- package/src/package-contracts/terminating-tools.ts +0 -2
- package/src/packaged-role-registry.ts +7 -10
- package/src/public-cli/invocation.ts +3 -27
- package/src/public-cli/option-definitions.ts +4 -19
- package/src/public-cli/settlement.ts +3 -102
- package/src/role-runtime-dependencies.ts +0 -4
- package/src/role-runtime.ts +16 -98
- package/src/ticket-snapshot.ts +1 -1
- package/dist/collector-evidence.js +0 -319
- package/dist/collector-github.js +0 -725
- package/dist/collector-identity.js +0 -92
- package/dist/collector-ledger.js +0 -888
- package/dist/collector-target.js +0 -169
- package/src/collector-evidence.ts +0 -466
- package/src/collector-github.ts +0 -1032
- package/src/collector-handbook.ts +0 -194
- package/src/collector-identity.ts +0 -151
- package/src/collector-ledger.ts +0 -1186
- package/src/collector-target.ts +0 -207
|
@@ -2,136 +2,26 @@ import { Type, type Static } from "typebox";
|
|
|
2
2
|
import { openToolObject } from "./open-tool-schema.ts";
|
|
3
3
|
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.ts";
|
|
4
4
|
|
|
5
|
-
// #836 r16 class 3: execute() reads no params for this tool
|
|
6
|
-
// (src/collector-role.ts:431-459) — a closed root only rejects the role for
|
|
7
|
-
// saying more; no reader consumes extra fields.
|
|
8
|
-
export const collectorObserveArgsSchema = Type.Object({}, { additionalProperties: true });
|
|
9
|
-
// #836 r16 class 4: the regex was a real provider shape rejection ahead of the
|
|
10
|
-
// ledger's own identity check — src/collector-ledger.ts:911-972 already trims
|
|
11
|
-
// and enforces non-empty/lookup/marker/attemptKey identity on requestId once
|
|
12
|
-
// the tool receives it, so the duplicate pattern-based rejection is deleted
|
|
13
|
-
// (r15b decision, not reopened here).
|
|
14
|
-
export const collectorRequestArgsSchema = Type.Object({
|
|
15
|
-
requestId: Type.String({
|
|
16
|
-
minLength: 1,
|
|
17
|
-
description: "请求身份(配置 id 或角色判定的稳定 id;首尾无空白)",
|
|
18
|
-
}),
|
|
19
|
-
snapshotId: Type.String({ minLength: 1, description: "最新留存观察快照" }),
|
|
20
|
-
// #836 r16 class 3: body.minLength deleted — manifest-configured requestId
|
|
21
|
-
// ignores body, and the unknown-requestId non-empty condition is a real
|
|
22
|
-
// branch in src/collector-ledger.ts:922-951, not a provider shape gate.
|
|
23
|
-
body: Type.Optional(Type.String({
|
|
24
|
-
description: "角色判定的请求正文;request-manifest 未收录该 requestId 时必填",
|
|
25
|
-
})),
|
|
26
|
-
}, { additionalProperties: true });
|
|
27
|
-
/** UTF-8 byte ceiling per handbook body — keeps next activation materials inside context budget. */
|
|
28
|
-
export const COLLECTOR_HANDBOOK_MAX_BYTES = 64 * 1024;
|
|
29
|
-
export const collectorHandbookWriteArgsSchema = Type.Object({
|
|
30
|
-
scope: Type.Union([
|
|
31
|
-
Type.Literal("general"),
|
|
32
|
-
Type.Literal("repo"),
|
|
33
|
-
], { description: "general=通用手册;repo=当前仓库差异" }),
|
|
34
|
-
body: Type.String({
|
|
35
|
-
description: `手册全文(opaque 工作记忆;整份替换;UTF-8 至多 ${COLLECTOR_HANDBOOK_MAX_BYTES} 字节)`,
|
|
36
|
-
}),
|
|
37
|
-
}, { additionalProperties: true });
|
|
38
|
-
export const collectorReadArgsSchema = Type.Object({
|
|
39
|
-
evidenceId: Type.String({ minLength: 1, description: "observe 返回的材料证据 id(evidenceId)" }),
|
|
40
|
-
}, { additionalProperties: true });
|
|
41
|
-
export const collectorWaitArgsSchema = Type.Object({
|
|
42
|
-
durationMs: Type.Integer({
|
|
43
|
-
minimum: 1,
|
|
44
|
-
description: "等待毫秒;实际睡眠不超过剩余等待窗(#678;无包内单次任意上限)",
|
|
45
|
-
}),
|
|
46
|
-
}, { additionalProperties: true });
|
|
47
|
-
|
|
48
5
|
/**
|
|
49
|
-
* #
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*/
|
|
53
|
-
// #836 r16 class 3: startedAt.minLength deleted — an empty string is already
|
|
54
|
-
// handled as omission by src/collector-role.ts:586-597, so the provider
|
|
55
|
-
// nonblank gate is not load-bearing for any branch.
|
|
56
|
-
export const collectorOpenWaitWindowArgsSchema = Type.Object({
|
|
57
|
-
startedAt: Type.Optional(Type.String({
|
|
58
|
-
description: "等待窗起点 ISO 时间;新建 PR 用创建成功时刻;省略=现在(触发阶段结束)",
|
|
59
|
-
})),
|
|
60
|
-
}, { additionalProperties: true });
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* #676 A: role-decided target bind. The model judges task materials and submits
|
|
64
|
-
* the chosen PR and/or issue identity; runtime only performs online association
|
|
65
|
-
* for the role-chosen ticket — never scrapes task text to lock a target.
|
|
66
|
-
*/
|
|
67
|
-
export const collectorBindTargetArgsSchema = Type.Object({
|
|
68
|
-
prNumber: Type.Optional(Type.Unknown({
|
|
69
|
-
description: "本仓 PR 号(正整数)。",
|
|
70
|
-
})),
|
|
71
|
-
issueNumber: Type.Optional(Type.Unknown({
|
|
72
|
-
description: "本仓 issue 号(正整数)。",
|
|
73
|
-
})),
|
|
74
|
-
}, { additionalProperties: true });
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* #641 chain① / #676 C / ADR 0057: nested finding item declarations for model
|
|
78
|
-
* guidance. Host must not pure-shape-reject the envelope (第 0 条). Runtime binds
|
|
79
|
-
* resolvable evidence pointers only; unprojected content stays distinguishable.
|
|
80
|
-
*/
|
|
81
|
-
const collectorFindingItemDeclaration = (() => {
|
|
82
|
-
// Nested declarations for model guidance only — open required so host cannot
|
|
83
|
-
// pure-shape-reject missing optional fields (第 0 条 / ADR 0057 / #676 C).
|
|
84
|
-
const item = Type.Object(
|
|
85
|
-
{
|
|
86
|
-
evidenceId: Type.Unknown({
|
|
87
|
-
description: "observe 返回的材料指针(必填语义)",
|
|
88
|
-
}),
|
|
89
|
-
category: Type.Unknown({
|
|
90
|
-
description: "简短归类标签,不是摘要",
|
|
91
|
-
}),
|
|
92
|
-
summary: Type.Unknown({
|
|
93
|
-
description: "哪个 bot、什么问题的摘要;不誊抄正文",
|
|
94
|
-
}),
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
additionalProperties: true,
|
|
98
|
-
description:
|
|
99
|
-
"单条 finding 指针:evidenceId,以及可选 category、summary。",
|
|
100
|
-
},
|
|
101
|
-
);
|
|
102
|
-
(item as unknown as { required: string[] }).required = [];
|
|
103
|
-
return item;
|
|
104
|
-
})();
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Field declarations + descriptions are guidance for the model — host must not
|
|
108
|
-
* pure-shape-reject the envelope (第 0 条 / ADR 0057).
|
|
6
|
+
* #1088: LLM gathers evidence via host CLI and submits the receipt.
|
|
7
|
+
* Field declarations guide the model — host must not pure-shape-reject (第 0 条 / ADR 0057).
|
|
8
|
+
* Presence of `groups` remains the Collector terminal discriminator for settlement/analyst.
|
|
109
9
|
*/
|
|
110
10
|
export const collectorOutputBaseSchema = openToolObject(
|
|
111
11
|
Type.Object({
|
|
112
|
-
|
|
113
|
-
// Nested item declarations ride `items` for registration preservation (ADR 0057).
|
|
114
|
-
findings: Type.Unsafe({
|
|
12
|
+
groups: Type.Unsafe({
|
|
115
13
|
description:
|
|
116
|
-
"
|
|
117
|
-
items: collectorFindingItemDeclaration,
|
|
14
|
+
"按机器身份归组的出席、材料与逐条 findings(LLM 自行从宿主 CLI 取证整理;代码不代收不归并)。",
|
|
118
15
|
}),
|
|
119
16
|
unfinishedReasons: Type.Unknown({
|
|
120
|
-
description:
|
|
121
|
-
"未完成原因。",
|
|
17
|
+
description: "未完成原因;取证受限时如实报告。",
|
|
122
18
|
}),
|
|
123
19
|
}),
|
|
124
20
|
);
|
|
125
21
|
|
|
126
|
-
/** Runtime
|
|
22
|
+
/** Runtime does not enrich findings; the model submits the receipt and signals sole-final. */
|
|
127
23
|
export const collectorOutputArgsSchema = withTerminatingOutputDeclarations(
|
|
128
24
|
collectorOutputBaseSchema,
|
|
129
25
|
);
|
|
130
26
|
|
|
131
|
-
export type CollectorObserveArgs = Static<typeof collectorObserveArgsSchema>;
|
|
132
|
-
export type CollectorRequestArgs = Static<typeof collectorRequestArgsSchema>;
|
|
133
|
-
export type CollectorReadArgs = Static<typeof collectorReadArgsSchema>;
|
|
134
|
-
export type CollectorWaitArgs = Static<typeof collectorWaitArgsSchema>;
|
|
135
|
-
export type CollectorOpenWaitWindowArgs = Static<typeof collectorOpenWaitWindowArgsSchema>;
|
|
136
|
-
export type CollectorBindTargetArgs = Static<typeof collectorBindTargetArgsSchema>;
|
|
137
27
|
export type CollectorOutputArgs = Static<typeof collectorOutputArgsSchema>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
export type GhApiRunOptions = {
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type GhApiResponse = {
|
|
8
|
+
status: number;
|
|
9
|
+
headers: Record<string, string>;
|
|
10
|
+
bodyText: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type GhApiRunner = (
|
|
14
|
+
args: readonly string[],
|
|
15
|
+
options?: GhApiRunOptions,
|
|
16
|
+
) => Promise<GhApiResponse>;
|
|
17
|
+
|
|
18
|
+
export function createGhApiRunner(
|
|
19
|
+
options: {
|
|
20
|
+
spawnImpl?: typeof spawn;
|
|
21
|
+
env?: NodeJS.ProcessEnv;
|
|
22
|
+
} = {},
|
|
23
|
+
): GhApiRunner {
|
|
24
|
+
const spawnImpl = options.spawnImpl ?? spawn;
|
|
25
|
+
return async (args, runOptions = {}) => {
|
|
26
|
+
return await new Promise<GhApiResponse>((resolve, reject) => {
|
|
27
|
+
const signal = runOptions.signal;
|
|
28
|
+
if (signal?.aborted) {
|
|
29
|
+
reject(signal.reason ?? new Error("aborted"));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const child = spawnImpl("gh", args, {
|
|
33
|
+
env: options.env ?? process.env,
|
|
34
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
35
|
+
});
|
|
36
|
+
let stdout = "";
|
|
37
|
+
let stderr = "";
|
|
38
|
+
let settled = false;
|
|
39
|
+
const settle = (fn: () => void) => {
|
|
40
|
+
if (settled) return;
|
|
41
|
+
settled = true;
|
|
42
|
+
if (signal !== undefined) {
|
|
43
|
+
signal.removeEventListener("abort", onAbort);
|
|
44
|
+
}
|
|
45
|
+
fn();
|
|
46
|
+
};
|
|
47
|
+
const onAbort = () => {
|
|
48
|
+
try {
|
|
49
|
+
child.kill("SIGTERM");
|
|
50
|
+
} catch (error) {
|
|
51
|
+
settle(() => reject(error));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
settle(() => {
|
|
55
|
+
reject(signal?.reason ?? new Error("aborted"));
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
if (signal !== undefined) {
|
|
59
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
60
|
+
}
|
|
61
|
+
child.stdout.setEncoding("utf8").on("data", (chunk) => {
|
|
62
|
+
stdout += chunk;
|
|
63
|
+
});
|
|
64
|
+
child.stderr.setEncoding("utf8").on("data", (chunk) => {
|
|
65
|
+
stderr += chunk;
|
|
66
|
+
});
|
|
67
|
+
child.on("error", (error) => {
|
|
68
|
+
settle(() => reject(error));
|
|
69
|
+
});
|
|
70
|
+
child.on("close", (code, signal) => {
|
|
71
|
+
settle(() => {
|
|
72
|
+
// gh api --include prints: HTTP/ headers blank-line body
|
|
73
|
+
const match = stdout.match(/^HTTP\/[\d.]+\s+(\d+)[^\n]*\r?\n([\s\S]*?)\r?\n\r?\n([\s\S]*)$/);
|
|
74
|
+
if (match) {
|
|
75
|
+
const status = Number(match[1]);
|
|
76
|
+
const headerText = match[2] ?? "";
|
|
77
|
+
const bodyText = match[3] ?? "";
|
|
78
|
+
const headers: Record<string, string> = {};
|
|
79
|
+
for (const line of headerText.split(/\r?\n/)) {
|
|
80
|
+
const idx = line.indexOf(":");
|
|
81
|
+
if (idx === -1) continue;
|
|
82
|
+
const name = line.slice(0, idx).trim().toLowerCase();
|
|
83
|
+
const value = line.slice(idx + 1).trim();
|
|
84
|
+
headers[name] = value;
|
|
85
|
+
}
|
|
86
|
+
resolve({ status, headers, bodyText });
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (code === 0) {
|
|
90
|
+
resolve({ status: 200, headers: {}, bodyText: stdout });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const failure = new Error(
|
|
94
|
+
`gh api failed without a parseable HTTP response (code=${String(code)}): ${stderr || stdout}`,
|
|
95
|
+
{ cause: { code, signal, stderr, stdout } },
|
|
96
|
+
);
|
|
97
|
+
reject(failure);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -1,188 +1,29 @@
|
|
|
1
1
|
/** Package-owned Collector receipt leaf — no role registration surface. */
|
|
2
2
|
|
|
3
3
|
export const COLLECTOR_OUTPUT_TOOL = "ak_collector_output";
|
|
4
|
-
export const COLLECTOR_HOST = "github.com";
|
|
5
4
|
|
|
5
|
+
/**
|
|
6
|
+
* One identity group on the LLM-submitted receipt.
|
|
7
|
+
* #1088: code no longer builds ledger projections; the model submits groups.
|
|
8
|
+
*/
|
|
6
9
|
export type CollectorIdentityGroup = {
|
|
7
10
|
identity: Record<string, unknown> | null;
|
|
8
11
|
displayLogin?: string;
|
|
9
|
-
attendance
|
|
10
|
-
materials
|
|
11
|
-
findings
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export type CollectorRequestAttempt = {
|
|
15
|
-
attemptId: string;
|
|
16
|
-
requestId: string;
|
|
17
|
-
observedHead: string;
|
|
18
|
-
snapshotId: string;
|
|
19
|
-
marker: string;
|
|
20
|
-
body: string;
|
|
21
|
-
startedAt: string;
|
|
22
|
-
status: "started" | "succeeded" | "rejected" | "ambiguous_loss" | "recovered";
|
|
23
|
-
responseDiagnostics?: string;
|
|
24
|
-
commentEvidenceId?: string;
|
|
25
|
-
recoverySnapshotId?: string;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export type CollectorSnapshot = {
|
|
29
|
-
snapshotId: string;
|
|
30
|
-
observedAt: string;
|
|
31
|
-
completedAt: string;
|
|
32
|
-
completedMono: number;
|
|
33
|
-
host: "github.com";
|
|
34
|
-
repository: string;
|
|
35
|
-
prNumber: number;
|
|
36
|
-
prState: string;
|
|
37
|
-
headOid: string;
|
|
38
|
-
complete: boolean;
|
|
39
|
-
evidenceIds: string[];
|
|
40
|
-
pageDiagnostics: Array<Record<string, unknown>>;
|
|
41
|
-
normalizedByteLength: number;
|
|
12
|
+
attendance?: true;
|
|
13
|
+
materials?: Array<Record<string, unknown>>;
|
|
14
|
+
findings?: Array<Record<string, unknown>>;
|
|
42
15
|
};
|
|
43
16
|
|
|
44
17
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* never transcribed into the receipt.
|
|
18
|
+
* Collector receipt as submitted. Presence of `groups` is the terminal
|
|
19
|
+
* discriminator for settlement/analyst (#836: original object is the receipt).
|
|
48
20
|
*/
|
|
49
|
-
export type CollectorEvidenceRecord = {
|
|
50
|
-
evidenceId: string;
|
|
51
|
-
kind: string;
|
|
52
|
-
versionId: string;
|
|
53
|
-
contentDigest: string;
|
|
54
|
-
firstObservedAt: string;
|
|
55
|
-
githubId?: number;
|
|
56
|
-
authorLogin?: string;
|
|
57
|
-
htmlUrl?: string;
|
|
58
|
-
authoritativeTime?: string | null;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* #676 C: sole structured projection-fact type (builder + package leaf).
|
|
63
|
-
* Builder always emits the full object; package leaf keeps it optional so old
|
|
64
|
-
* volumes without the field stay readable — missing is unknown, never defaulted.
|
|
65
|
-
*/
|
|
66
|
-
export type CollectorSubmissionProjection = {
|
|
67
|
-
findingsSource: "absent" | "array" | "unreadable";
|
|
68
|
-
findingsProjectedCount: number;
|
|
69
|
-
findingsUnprojected: boolean;
|
|
70
|
-
unfinishedReasonsSource: "absent" | "array" | "unreadable";
|
|
71
|
-
unfinishedReasonsProjectedCount: number;
|
|
72
|
-
unfinishedReasonsUnprojected: boolean;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
21
|
export type CollectorReceipt = {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
* Absent/unreadable on old volumes is unknown — never defaulted to OPEN.
|
|
82
|
-
*/
|
|
22
|
+
groups: CollectorIdentityGroup[] | ReadonlyArray<Record<string, unknown>>;
|
|
23
|
+
unfinishedReasons?: unknown;
|
|
24
|
+
host?: string;
|
|
25
|
+
repository?: string;
|
|
26
|
+
prNumber?: number;
|
|
83
27
|
prState?: string;
|
|
84
|
-
manifestDigest
|
|
85
|
-
activationTime: string;
|
|
86
|
-
deadlineTime: string;
|
|
87
|
-
finalObservationTime: string;
|
|
88
|
-
finalSnapshotId: string;
|
|
89
|
-
targetHead: string;
|
|
90
|
-
groups: CollectorIdentityGroup[];
|
|
91
|
-
unfinishedReasons?: string[];
|
|
92
|
-
/**
|
|
93
|
-
* Distinguishes readable-empty from unprojected original submission content (#676 C).
|
|
94
|
-
* Package leaf may carry a partial projection when some sibling keys are unreadable —
|
|
95
|
-
* missing keys stay absent (never defaulted false/0/OPEN).
|
|
96
|
-
*/
|
|
97
|
-
submissionProjection?: Partial<CollectorSubmissionProjection>;
|
|
98
|
-
requestAttempts: CollectorRequestAttempt[];
|
|
99
|
-
snapshots: CollectorSnapshot[];
|
|
100
|
-
evidenceRecords: CollectorEvidenceRecord[];
|
|
28
|
+
manifestDigest?: string;
|
|
101
29
|
};
|
|
102
|
-
|
|
103
|
-
function safeGet(value: unknown, key: string): unknown {
|
|
104
|
-
if ((typeof value !== "object" && typeof value !== "function") || value === null) return undefined;
|
|
105
|
-
try { return (value as Record<string, unknown>)[key]; } catch { return undefined; }
|
|
106
|
-
}
|
|
107
|
-
function records(value: unknown): Record<string, unknown>[] {
|
|
108
|
-
return Array.isArray(value) ? value.filter((item): item is Record<string, unknown> => item !== null && typeof item === "object") : [];
|
|
109
|
-
}
|
|
110
|
-
function strings(value: unknown): string[] {
|
|
111
|
-
return Array.isArray(value) ? value.filter((item): item is string => typeof item === "string") : [];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** Settlement projection. Presence of the canonical groups array is the one Collector terminal discriminator. */
|
|
115
|
-
export function validateAcceptedCollectorReceipt(value: unknown): CollectorReceipt {
|
|
116
|
-
const rawGroups = safeGet(value, "groups");
|
|
117
|
-
if (!Array.isArray(rawGroups)) throw new Error("Collector receipt has no typed groups terminal discriminator");
|
|
118
|
-
const groups = records(rawGroups).map((group) => ({
|
|
119
|
-
identity: (safeGet(group, "identity") ?? null) as Record<string, unknown> | null,
|
|
120
|
-
...(typeof safeGet(group, "displayLogin") === "string" ? { displayLogin: safeGet(group, "displayLogin") as string } : {}),
|
|
121
|
-
attendance: true as const,
|
|
122
|
-
materials: records(safeGet(group, "materials")),
|
|
123
|
-
findings: records(safeGet(group, "findings")),
|
|
124
|
-
}));
|
|
125
|
-
const unfinishedRaw = safeGet(value, "unfinishedReasons");
|
|
126
|
-
const unfinishedReasons = strings(unfinishedRaw);
|
|
127
|
-
// #676 C: project readable submissionProjection fields independently — do not
|
|
128
|
-
// drop the whole block when one sibling key is the wrong type (left-拾遗1).
|
|
129
|
-
const submissionProjection = projectSubmissionProjection(safeGet(value, "submissionProjection"));
|
|
130
|
-
const prStateRaw = safeGet(value, "prState");
|
|
131
|
-
const prState = typeof prStateRaw === "string" ? prStateRaw : undefined;
|
|
132
|
-
return {
|
|
133
|
-
host: safeGet(value, "host") as "github.com",
|
|
134
|
-
repository: safeGet(value, "repository") as string,
|
|
135
|
-
prNumber: safeGet(value, "prNumber") as number,
|
|
136
|
-
...(prState === undefined ? {} : { prState }),
|
|
137
|
-
manifestDigest: safeGet(value, "manifestDigest") as string,
|
|
138
|
-
activationTime: safeGet(value, "activationTime") as string,
|
|
139
|
-
deadlineTime: safeGet(value, "deadlineTime") as string,
|
|
140
|
-
finalObservationTime: safeGet(value, "finalObservationTime") as string,
|
|
141
|
-
finalSnapshotId: safeGet(value, "finalSnapshotId") as string,
|
|
142
|
-
targetHead: safeGet(value, "targetHead") as string,
|
|
143
|
-
groups,
|
|
144
|
-
...(unfinishedReasons.length > 0 ? { unfinishedReasons } : {}),
|
|
145
|
-
...(submissionProjection === undefined ? {} : { submissionProjection }),
|
|
146
|
-
requestAttempts: records(safeGet(value, "requestAttempts")) as unknown as CollectorRequestAttempt[],
|
|
147
|
-
snapshots: records(safeGet(value, "snapshots")).map((snapshot) => ({
|
|
148
|
-
snapshotId: safeGet(snapshot, "snapshotId"), observedAt: safeGet(snapshot, "observedAt"), completedAt: safeGet(snapshot, "completedAt"), completedMono: safeGet(snapshot, "completedMono"), host: safeGet(snapshot, "host"), repository: safeGet(snapshot, "repository"), prNumber: safeGet(snapshot, "prNumber"), prState: safeGet(snapshot, "prState"), headOid: safeGet(snapshot, "headOid"), complete: safeGet(snapshot, "complete"), evidenceIds: strings(safeGet(snapshot, "evidenceIds")), pageDiagnostics: records(safeGet(snapshot, "pageDiagnostics")), normalizedByteLength: safeGet(snapshot, "normalizedByteLength"),
|
|
149
|
-
} as CollectorSnapshot)),
|
|
150
|
-
evidenceRecords: records(safeGet(value, "evidenceRecords")).map((record) => ({ evidenceId: safeGet(record, "evidenceId"), kind: safeGet(record, "kind"), versionId: safeGet(record, "versionId"), contentDigest: safeGet(record, "contentDigest"), firstObservedAt: safeGet(record, "firstObservedAt"), githubId: safeGet(record, "githubId"), authorLogin: safeGet(record, "authorLogin"), htmlUrl: safeGet(record, "htmlUrl"), authoritativeTime: safeGet(record, "authoritativeTime") } as CollectorEvidenceRecord)),
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/** Readable projection facts only; omit keys that are the wrong type — never fabricate defaults. */
|
|
155
|
-
function projectSubmissionProjection(
|
|
156
|
-
raw: unknown,
|
|
157
|
-
): Partial<CollectorSubmissionProjection> | undefined {
|
|
158
|
-
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return undefined;
|
|
159
|
-
const p = raw as Record<string, unknown>;
|
|
160
|
-
const out: Partial<CollectorSubmissionProjection> = {};
|
|
161
|
-
if (
|
|
162
|
-
p["findingsSource"] === "absent" ||
|
|
163
|
-
p["findingsSource"] === "array" ||
|
|
164
|
-
p["findingsSource"] === "unreadable"
|
|
165
|
-
) {
|
|
166
|
-
out.findingsSource = p["findingsSource"];
|
|
167
|
-
}
|
|
168
|
-
if (typeof p["findingsProjectedCount"] === "number") {
|
|
169
|
-
out.findingsProjectedCount = p["findingsProjectedCount"];
|
|
170
|
-
}
|
|
171
|
-
if (typeof p["findingsUnprojected"] === "boolean") {
|
|
172
|
-
out.findingsUnprojected = p["findingsUnprojected"];
|
|
173
|
-
}
|
|
174
|
-
if (
|
|
175
|
-
p["unfinishedReasonsSource"] === "absent" ||
|
|
176
|
-
p["unfinishedReasonsSource"] === "array" ||
|
|
177
|
-
p["unfinishedReasonsSource"] === "unreadable"
|
|
178
|
-
) {
|
|
179
|
-
out.unfinishedReasonsSource = p["unfinishedReasonsSource"];
|
|
180
|
-
}
|
|
181
|
-
if (typeof p["unfinishedReasonsProjectedCount"] === "number") {
|
|
182
|
-
out.unfinishedReasonsProjectedCount = p["unfinishedReasonsProjectedCount"];
|
|
183
|
-
}
|
|
184
|
-
if (typeof p["unfinishedReasonsUnprojected"] === "boolean") {
|
|
185
|
-
out.unfinishedReasonsUnprojected = p["unfinishedReasonsUnprojected"];
|
|
186
|
-
}
|
|
187
|
-
return Object.keys(out).length > 0 ? out : undefined;
|
|
188
|
-
}
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
COLLECTOR_OUTPUT_TOOL,
|
|
8
|
-
validateAcceptedCollectorReceipt,
|
|
9
8
|
type CollectorReceipt,
|
|
10
9
|
} from "./collector-output.ts";
|
|
11
10
|
import {
|
|
@@ -44,7 +43,6 @@ export {
|
|
|
44
43
|
JUDGE_OUTPUT_TOOL_NAME,
|
|
45
44
|
REVIEWER_OUTPUT_TOOL_NAME,
|
|
46
45
|
MERGER_OUTPUT_TOOL_NAME,
|
|
47
|
-
validateAcceptedCollectorReceipt,
|
|
48
46
|
validateReviewerIntent,
|
|
49
47
|
validateAcceptedWorkerDetails,
|
|
50
48
|
validateDoctorSubmissionShape,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/** Composition-root unique authoritative public-role records (#509 / #524). */
|
|
2
2
|
import { COLLECTOR_OUTPUT_TOOL } from "./package-contracts/collector-output.ts";
|
|
3
|
-
import { COLLECTOR_WAIT_TOOL } from "./collector-ledger.ts";
|
|
4
3
|
import { GATEKEEPER_OUTPUT_TOOL_NAME } from "./package-contracts/gatekeeper-output.ts";
|
|
5
4
|
import { NAVIGATOR_OUTPUT_TOOL_NAME } from "./package-contracts/navigator-output.ts";
|
|
6
5
|
import { JUDGE_OUTPUT_TOOL_NAME } from "./package-contracts/judge-output.ts";
|
|
@@ -232,23 +231,18 @@ export const PUBLIC_ROLE_RECORDS = [
|
|
|
232
231
|
],
|
|
233
232
|
},
|
|
234
233
|
// ak-collector-repo is GitHub owner/repo identity, not a local material path (#438).
|
|
234
|
+
// #1088: seat + request-manifest kept; code no longer collects — LLM uses host CLI.
|
|
235
235
|
{
|
|
236
236
|
role: "collector",
|
|
237
237
|
inCallAutoResume: false,
|
|
238
238
|
presentSettled: "always",
|
|
239
239
|
summonResume: false,
|
|
240
240
|
admission: "collect-target",
|
|
241
|
-
/** Public no_receipt projects a durable bind-target rejection. */
|
|
242
|
-
projectTargetBindRejection: true,
|
|
243
241
|
sameParent: "none",
|
|
244
242
|
phases: [null],
|
|
245
243
|
bareCommand: false,
|
|
246
244
|
outputTool: COLLECTOR_OUTPUT_TOOL,
|
|
247
|
-
settlement: "
|
|
248
|
-
/** #633: a prior wait-tool residual must not mask this attempt. */
|
|
249
|
-
residualScan: "current-attempt",
|
|
250
|
-
residualTool: COLLECTOR_WAIT_TOOL,
|
|
251
|
-
runnerFailure: "collector-known-first",
|
|
245
|
+
settlement: "sealed",
|
|
252
246
|
artifactFace: {
|
|
253
247
|
evidenceRole: true,
|
|
254
248
|
leaves: [
|
|
@@ -264,10 +258,13 @@ export const PUBLIC_ROLE_RECORDS = [
|
|
|
264
258
|
{ field: "repo", from: "repository.display", flag: "ak-collector-repo" },
|
|
265
259
|
{ field: "pr", from: "prNumber", text: true, flag: "ak-collector-pr" },
|
|
266
260
|
{ field: "requestManifestPath", flag: "ak-collector-request-manifest" },
|
|
267
|
-
{ field: "waitMs", from: "waitWindowMs", text: true, flag: "ak-collector-wait-ms" },
|
|
268
261
|
],
|
|
269
262
|
activationStage: "load-and-install",
|
|
270
|
-
sessionMaterials: [
|
|
263
|
+
sessionMaterials: [
|
|
264
|
+
"CLAUDE.md",
|
|
265
|
+
"souls/collector.md",
|
|
266
|
+
"resources/collector-bot-handbook.md",
|
|
267
|
+
],
|
|
271
268
|
},
|
|
272
269
|
{
|
|
273
270
|
role: "doctor",
|
|
@@ -69,7 +69,6 @@ import {
|
|
|
69
69
|
parseCollectorRepository,
|
|
70
70
|
type CollectorRepository,
|
|
71
71
|
} from "../collector-config.ts";
|
|
72
|
-
import { resolveCollectorTarget } from "../collector-target.ts";
|
|
73
72
|
import { ownerRepoFromGitHubRemoteUrl } from "./github-remote.ts";
|
|
74
73
|
import {
|
|
75
74
|
FixerPacketValidationError,
|
|
@@ -231,13 +230,11 @@ export type AdmittedFixerInvocation = AdmittedRoleInvocationBase & {
|
|
|
231
230
|
|
|
232
231
|
export type AdmittedCollectorInvocation = AdmittedRoleInvocationBase & {
|
|
233
232
|
readonly role: "collector";
|
|
234
|
-
/** Bound at admission when explicit
|
|
233
|
+
/** Bound at admission when explicit --pr; otherwise the LLM locates via host CLI. */
|
|
235
234
|
readonly prNumber?: number;
|
|
236
235
|
readonly repository: CollectorRepository;
|
|
237
236
|
readonly requestManifestPath?: string;
|
|
238
237
|
readonly manifestDigest: string;
|
|
239
|
-
/** Wait-window ms (#678 D4); default applied at role activate when omitted. */
|
|
240
|
-
readonly waitWindowMs?: number;
|
|
241
238
|
};
|
|
242
239
|
|
|
243
240
|
export type AdmittedDoctorInvocation = AdmittedRoleInvocationBase & {
|
|
@@ -899,7 +896,6 @@ type SeatArgvFields = {
|
|
|
899
896
|
prNumber?: number;
|
|
900
897
|
repo?: string;
|
|
901
898
|
requestManifestPath?: string;
|
|
902
|
-
waitWindowMs?: number;
|
|
903
899
|
issueNumber?: number;
|
|
904
900
|
runs?: string;
|
|
905
901
|
sourceRun?: string;
|
|
@@ -975,17 +971,6 @@ function assignPublicSeatOption(
|
|
|
975
971
|
case "request-manifest":
|
|
976
972
|
fields.requestManifestPath = requireOptionPath(taken.def.canonical, value);
|
|
977
973
|
return;
|
|
978
|
-
case "wait-ms": {
|
|
979
|
-
if (value === undefined || !/^[1-9]\d*$/.test(value.trim())) {
|
|
980
|
-
throw new CliUsageError("--wait-ms requires a positive safe-integer millisecond value");
|
|
981
|
-
}
|
|
982
|
-
const parsed = Number(value.trim());
|
|
983
|
-
if (!Number.isSafeInteger(parsed) || parsed < 1) {
|
|
984
|
-
throw new CliUsageError("--wait-ms requires a positive safe-integer millisecond value");
|
|
985
|
-
}
|
|
986
|
-
fields.waitWindowMs = parsed;
|
|
987
|
-
return;
|
|
988
|
-
}
|
|
989
974
|
case "issue": {
|
|
990
975
|
if (value === undefined || value.trim() === "") {
|
|
991
976
|
throw new CliUsageError("doctor --issue requires a positive integer");
|
|
@@ -1100,7 +1085,6 @@ export function parsePublicSeatArgv(
|
|
|
1100
1085
|
...(fields.prNumber === undefined ? {} : { prNumber: fields.prNumber }),
|
|
1101
1086
|
...(fields.repo === undefined ? {} : { repo: fields.repo }),
|
|
1102
1087
|
...(fields.requestManifestPath === undefined ? {} : { requestManifestPath: fields.requestManifestPath }),
|
|
1103
|
-
...(fields.waitWindowMs === undefined ? {} : { waitWindowMs: fields.waitWindowMs }),
|
|
1104
1088
|
...(fields.issueNumber === undefined ? {} : { issueNumber: fields.issueNumber }),
|
|
1105
1089
|
...(fields.runs === undefined ? {} : { runs: fields.runs }),
|
|
1106
1090
|
...(fields.sourceRun === undefined ? {} : { sourceRun: fields.sourceRun }),
|
|
@@ -1485,7 +1469,6 @@ export type PublicSeatParse = {
|
|
|
1485
1469
|
readonly prNumber?: number;
|
|
1486
1470
|
readonly repo?: string;
|
|
1487
1471
|
readonly requestManifestPath?: string;
|
|
1488
|
-
readonly waitWindowMs?: number;
|
|
1489
1472
|
readonly issueNumber?: number;
|
|
1490
1473
|
readonly runs?: string;
|
|
1491
1474
|
readonly sourceRun?: string;
|
|
@@ -1676,24 +1659,17 @@ export async function admitPublicRole(
|
|
|
1676
1659
|
attachmentPaths,
|
|
1677
1660
|
...project,
|
|
1678
1661
|
placedFields: async (placed) => {
|
|
1679
|
-
//
|
|
1680
|
-
const target = await resolveCollectorTarget({
|
|
1681
|
-
projectRoot,
|
|
1682
|
-
repository,
|
|
1683
|
-
...(explicitPrNumber === undefined ? {} : { explicitPrNumber }),
|
|
1684
|
-
});
|
|
1685
|
-
const prNumber = target.kind === "bound" ? target.prNumber : undefined;
|
|
1662
|
+
// #1088: explicit --pr only. Unbound PR is located by the LLM via host CLI.
|
|
1686
1663
|
let requestManifestPath: string | undefined;
|
|
1687
1664
|
if (manifestCanonicalJson !== undefined) {
|
|
1688
1665
|
requestManifestPath = join(placed.runDirectory, "request-manifest.json");
|
|
1689
1666
|
await writeFile(requestManifestPath, manifestCanonicalJson, "utf8");
|
|
1690
1667
|
}
|
|
1691
1668
|
return {
|
|
1692
|
-
...(
|
|
1669
|
+
...(explicitPrNumber === undefined ? {} : { prNumber: explicitPrNumber }),
|
|
1693
1670
|
repository: repository.canonical,
|
|
1694
1671
|
repositoryDisplay: repository.display,
|
|
1695
1672
|
...(requestManifestPath === undefined ? {} : { requestManifestPath }),
|
|
1696
|
-
...(parsed.waitWindowMs === undefined ? {} : { waitWindowMs: parsed.waitWindowMs }),
|
|
1697
1673
|
manifestDigest,
|
|
1698
1674
|
};
|
|
1699
1675
|
},
|
|
@@ -524,13 +524,12 @@ const COLLECTOR_OPTIONS = [
|
|
|
524
524
|
canonical: "--pr",
|
|
525
525
|
aliases: [],
|
|
526
526
|
valueMetavar: "number",
|
|
527
|
-
// #676 D1: optional when context uniquely determines the PR; ambiguous → require explicit.
|
|
528
527
|
required: false,
|
|
529
528
|
repeatable: false,
|
|
530
529
|
form: "option",
|
|
531
530
|
description: {
|
|
532
|
-
en: "Positive GitHub pull request number. Optional when
|
|
533
|
-
zh: "正整数 GitHub PR
|
|
531
|
+
en: "Positive GitHub pull request number. Optional when the Collector role locates the PR via host CLI from task materials.",
|
|
532
|
+
zh: "正整数 GitHub PR 号。可选;省略时由通进司经宿主 CLI 从任务材料自行定位。",
|
|
534
533
|
},
|
|
535
534
|
},
|
|
536
535
|
{
|
|
@@ -557,22 +556,8 @@ const COLLECTOR_OPTIONS = [
|
|
|
557
556
|
repeatable: false,
|
|
558
557
|
form: "option",
|
|
559
558
|
description: {
|
|
560
|
-
en: "Optional request manifest JSON path ({requests:[{id,body}]}).
|
|
561
|
-
zh: "可选 request manifest JSON 路径({requests:[{id,body}]}
|
|
562
|
-
},
|
|
563
|
-
},
|
|
564
|
-
{
|
|
565
|
-
id: "wait-ms",
|
|
566
|
-
owner: "collector",
|
|
567
|
-
canonical: "--wait-ms",
|
|
568
|
-
aliases: [],
|
|
569
|
-
valueMetavar: "ms",
|
|
570
|
-
required: false,
|
|
571
|
-
repeatable: false,
|
|
572
|
-
form: "option",
|
|
573
|
-
description: {
|
|
574
|
-
en: "Wait-window duration in milliseconds after the work step opens (default 600000 = 10 minutes). Does not require a code change to adjust.",
|
|
575
|
-
zh: "工作步骤开启后的等待窗时长(毫秒;默认 600000=十分钟)。改配置即可,无需改代码。",
|
|
559
|
+
en: "Optional request manifest JSON path ({requests:[{id,body}]}). Parsed for semantics and passed to the Collector as materials — not a machine collection directive.",
|
|
560
|
+
zh: "可选 request manifest JSON 路径({requests:[{id,body}]})。按语义解析后作为材料递给通进司,不是机器代收 finding 的指令。",
|
|
576
561
|
},
|
|
577
562
|
},
|
|
578
563
|
] as const satisfies readonly PublicOptionDefinition[];
|