@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.
- package/README.md +2 -3
- package/README.zh-CN.md +2 -3
- package/dist/acp-host/production-host.js +482 -3141
- package/dist/collector-config.js +0 -1
- package/dist/headless-host/production-host.js +590 -3249
- package/dist/migrate-book-topology.js +40 -107
- package/dist/navigator-invocation-identity.js +0 -57
- 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 +256 -749
- package/dist/public-cli/option-definitions.js +4 -19
- package/dist/public-cli/settlement.js +12 -138
- 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/navigator-invocation-identity.ts +4 -121
- 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 +11 -177
- package/src/role-runtime-dependencies.ts +0 -4
- package/src/role-runtime.ts +39 -103
- 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
package/src/collector-role.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Collector business
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* exit live on the shared envelope (ADR 0018 / #676 E). Role keeps materials,
|
|
6
|
-
* business tools, ledger facts, and projection only.
|
|
2
|
+
* Collector business: soul/material assembly and sole submission tool.
|
|
3
|
+
* #1088: code no longer observes/requests/merges GitHub findings — the LLM
|
|
4
|
+
* uses host CLI tools. Envelope owns host-surface + construction seatbelt.
|
|
7
5
|
*/
|
|
8
6
|
import type { RoleHost, HostContext, HostToolResult } from "./host-contracts.ts";
|
|
9
7
|
import type { Static } from "typebox";
|
|
@@ -17,78 +15,16 @@ import {
|
|
|
17
15
|
type CollectorRepository,
|
|
18
16
|
} from "./collector-config.ts";
|
|
19
17
|
import {
|
|
20
|
-
COLLECTOR_DEFAULT_WAIT_WINDOW_MS,
|
|
21
|
-
createSystemCollectorClock,
|
|
22
|
-
type CollectorClock,
|
|
23
|
-
} from "./collector-evidence.ts";
|
|
24
|
-
import {
|
|
25
|
-
createGhApiRunner,
|
|
26
|
-
listPullRequestNumbersByTicket,
|
|
27
|
-
type CollectorGitHubTransport,
|
|
28
|
-
} from "./collector-github.ts";
|
|
29
|
-
import {
|
|
30
|
-
createCollectorHandbookStore,
|
|
31
|
-
resolveCollectorHandbookRoot,
|
|
32
|
-
type CollectorHandbookStore,
|
|
33
|
-
} from "./collector-handbook.ts";
|
|
34
|
-
import {
|
|
35
|
-
COLLECTOR_BIND_TARGET_TOOL,
|
|
36
|
-
COLLECTOR_HANDBOOK_WRITE_TOOL,
|
|
37
|
-
COLLECTOR_OBSERVE_TOOL,
|
|
38
|
-
COLLECTOR_OPEN_WAIT_WINDOW_TOOL,
|
|
39
18
|
COLLECTOR_OUTPUT_TOOL,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
COLLECTOR_WAIT_TOOL,
|
|
43
|
-
projectEvidenceEntryView,
|
|
44
|
-
type CollectorConfigState,
|
|
45
|
-
type CollectorLedger,
|
|
46
|
-
} from "./collector-ledger.ts";
|
|
47
|
-
|
|
48
|
-
import {
|
|
49
|
-
collectorBindTargetArgsSchema,
|
|
50
|
-
collectorHandbookWriteArgsSchema,
|
|
51
|
-
collectorObserveArgsSchema,
|
|
52
|
-
collectorOpenWaitWindowArgsSchema,
|
|
53
|
-
collectorOutputArgsSchema,
|
|
54
|
-
collectorReadArgsSchema,
|
|
55
|
-
collectorRequestArgsSchema,
|
|
56
|
-
collectorWaitArgsSchema,
|
|
57
|
-
} from "./collector-tool-schemas.ts";
|
|
58
|
-
import { CorrectableSubmissionError, isCorrectableExecuteError } from "./submission-correctable-error.ts";
|
|
59
|
-
import { CollectorUnknownEvidenceError } from "./collector-identity.ts";
|
|
19
|
+
} from "./package-contracts/collector-output.ts";
|
|
20
|
+
import { collectorOutputArgsSchema } from "./collector-tool-schemas.ts";
|
|
60
21
|
|
|
61
|
-
export {
|
|
22
|
+
export { COLLECTOR_OUTPUT_TOOL };
|
|
62
23
|
|
|
63
|
-
|
|
64
|
-
export class CollectorTargetBindError extends CorrectableSubmissionError {
|
|
65
|
-
constructor(message: string) {
|
|
66
|
-
super(message);
|
|
67
|
-
this.name = "CollectorTargetBindError";
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export {
|
|
72
|
-
COLLECTOR_BIND_TARGET_TOOL,
|
|
73
|
-
COLLECTOR_HANDBOOK_WRITE_TOOL,
|
|
74
|
-
COLLECTOR_OBSERVE_TOOL,
|
|
75
|
-
COLLECTOR_OPEN_WAIT_WINDOW_TOOL,
|
|
76
|
-
COLLECTOR_OUTPUT_TOOL,
|
|
77
|
-
COLLECTOR_READ_TOOL,
|
|
78
|
-
COLLECTOR_REQUEST_TOOL,
|
|
79
|
-
COLLECTOR_WAIT_TOOL,
|
|
80
|
-
};
|
|
24
|
+
export const COLLECTOR_REQUIRED_TOOLS = [COLLECTOR_OUTPUT_TOOL] as const;
|
|
81
25
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
COLLECTOR_OBSERVE_TOOL,
|
|
85
|
-
COLLECTOR_READ_TOOL,
|
|
86
|
-
COLLECTOR_REQUEST_TOOL,
|
|
87
|
-
COLLECTOR_OPEN_WAIT_WINDOW_TOOL,
|
|
88
|
-
COLLECTOR_WAIT_TOOL,
|
|
89
|
-
COLLECTOR_HANDBOOK_WRITE_TOOL,
|
|
90
|
-
COLLECTOR_OUTPUT_TOOL,
|
|
91
|
-
] as const;
|
|
26
|
+
/** Host tools that grant construction seat privileges — blocked for 收证席 (ADR 0064). */
|
|
27
|
+
export const COLLECTOR_CONSTRUCTION_TOOLS = Object.freeze(["write", "edit"] as const);
|
|
92
28
|
|
|
93
29
|
/** Envelope-owned transport flags (ADR 0018 / #676 E). */
|
|
94
30
|
export const COLLECTOR_TRANSPORT_FLAGS = Object.freeze([
|
|
@@ -104,7 +40,7 @@ export const COLLECTOR_TRANSPORT_FLAGS = Object.freeze([
|
|
|
104
40
|
name: "ak-collector-pr",
|
|
105
41
|
definition: Object.freeze({
|
|
106
42
|
description:
|
|
107
|
-
"Optional positive safe-integer pull request number for Collector. Omit when the role will
|
|
43
|
+
"Optional positive safe-integer pull request number for Collector. Omit when the role will locate the PR via host CLI.",
|
|
108
44
|
type: "string" as const,
|
|
109
45
|
}),
|
|
110
46
|
}),
|
|
@@ -112,157 +48,78 @@ export const COLLECTOR_TRANSPORT_FLAGS = Object.freeze([
|
|
|
112
48
|
name: "ak-collector-request-manifest",
|
|
113
49
|
definition: Object.freeze({
|
|
114
50
|
description:
|
|
115
|
-
"Path to the Collector v1 request manifest JSON file.",
|
|
116
|
-
type: "string" as const,
|
|
117
|
-
}),
|
|
118
|
-
}),
|
|
119
|
-
Object.freeze({
|
|
120
|
-
name: "ak-collector-wait-ms",
|
|
121
|
-
definition: Object.freeze({
|
|
122
|
-
description:
|
|
123
|
-
`Collector wait-window duration in milliseconds (default ${COLLECTOR_DEFAULT_WAIT_WINDOW_MS}). Caller-configurable; opens at a work step, not session start (#678 D4).`,
|
|
51
|
+
"Path to the Collector v1 request manifest JSON file (caller guidance; not a machine collection directive).",
|
|
124
52
|
type: "string" as const,
|
|
125
53
|
}),
|
|
126
54
|
}),
|
|
127
55
|
] as const);
|
|
128
56
|
|
|
129
|
-
const observeSchema = collectorObserveArgsSchema;
|
|
130
|
-
const readSchema = collectorReadArgsSchema;
|
|
131
|
-
const requestSchema = collectorRequestArgsSchema;
|
|
132
|
-
const openWaitWindowSchema = collectorOpenWaitWindowArgsSchema;
|
|
133
|
-
const waitSchema = collectorWaitArgsSchema;
|
|
134
|
-
const bindSchema = collectorBindTargetArgsSchema;
|
|
135
|
-
const handbookWriteSchema = collectorHandbookWriteArgsSchema;
|
|
136
57
|
const outputSchema = collectorOutputArgsSchema;
|
|
137
|
-
|
|
138
|
-
type RequestParams = Static<typeof requestSchema>;
|
|
139
|
-
type ReadParams = Static<typeof readSchema>;
|
|
140
|
-
type OpenWaitWindowParams = Static<typeof openWaitWindowSchema>;
|
|
141
|
-
type WaitParams = Static<typeof waitSchema>;
|
|
142
|
-
type BindParams = Static<typeof bindSchema>;
|
|
143
|
-
type HandbookWriteParams = Static<typeof handbookWriteSchema>;
|
|
144
58
|
type OutputParams = Static<typeof outputSchema>;
|
|
145
59
|
|
|
146
|
-
function parseWaitWindowMsFlag(raw: unknown): number {
|
|
147
|
-
if (raw === undefined || raw === null || raw === "") return COLLECTOR_DEFAULT_WAIT_WINDOW_MS;
|
|
148
|
-
if (typeof raw === "number" && Number.isSafeInteger(raw) && raw >= 1) return raw;
|
|
149
|
-
if (typeof raw === "string" && /^[1-9]\d*$/.test(raw.trim())) {
|
|
150
|
-
const value = Number(raw.trim());
|
|
151
|
-
if (Number.isSafeInteger(value) && value >= 1) return value;
|
|
152
|
-
}
|
|
153
|
-
throw new Error("Collector --ak-collector-wait-ms must be a positive safe-integer millisecond string");
|
|
154
|
-
}
|
|
155
|
-
|
|
156
60
|
export type CollectorRoleDependencies = {
|
|
157
61
|
loadSoul(): Promise<string>;
|
|
158
|
-
createTransport(): CollectorGitHubTransport;
|
|
159
|
-
createClock?(): CollectorClock;
|
|
160
|
-
createLedger(config: CollectorConfigState, clock: CollectorClock, ctx: HostContext): CollectorLedger;
|
|
161
|
-
/** Optional packaged seed for first-use general handbook (#677). */
|
|
162
|
-
loadHandbookSeed?(): Promise<string>;
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
export type CollectorRoleHostActions = {
|
|
166
|
-
failInfrastructure(error: unknown, ctx: HostContext, toolCallId?: string): never;
|
|
167
62
|
};
|
|
168
63
|
|
|
169
64
|
export type CollectorActivation = {
|
|
170
65
|
soul: string;
|
|
171
66
|
repository: CollectorRepository;
|
|
67
|
+
prNumber: number | undefined;
|
|
172
68
|
manifest: CollectorManifest;
|
|
173
|
-
ledger: CollectorLedger;
|
|
174
|
-
transport: CollectorGitHubTransport;
|
|
175
|
-
clock: CollectorClock;
|
|
176
|
-
handbook: CollectorHandbookStore;
|
|
177
|
-
/** Loaded at activate; refreshed after handbook write for same-session materials. */
|
|
178
|
-
handbookView: {
|
|
179
|
-
general: string;
|
|
180
|
-
repo: string;
|
|
181
|
-
generalSource: "book" | "seed" | "empty";
|
|
182
|
-
repoSource: "book" | "empty";
|
|
183
|
-
generalPath: string;
|
|
184
|
-
repoPath: string;
|
|
185
|
-
};
|
|
186
69
|
};
|
|
187
70
|
|
|
188
71
|
function buildMethodContext(activation: CollectorActivation): string {
|
|
189
|
-
const pr = activation.ledger.config.prNumber;
|
|
190
|
-
const handbook = activation.handbookView;
|
|
191
72
|
const lines = [
|
|
192
73
|
"<collector_method>",
|
|
193
74
|
`host: github.com`,
|
|
194
75
|
`repository: ${activation.repository.canonical}`,
|
|
195
|
-
`prNumber: ${
|
|
196
|
-
`waitWindowMs: ${String(activation.ledger.config.waitWindowMs)}`,
|
|
76
|
+
`prNumber: ${activation.prNumber === undefined ? "未绑定" : String(activation.prNumber)}`,
|
|
197
77
|
`requests: ${JSON.stringify(activation.manifest.requests.map((request) => ({ id: request.id })))}`,
|
|
198
|
-
`
|
|
199
|
-
`handbookRepoSource: ${handbook.repoSource}`,
|
|
78
|
+
`manifestDigest: ${activation.manifest.digest}`,
|
|
200
79
|
"</collector_method>",
|
|
201
80
|
];
|
|
202
|
-
|
|
203
|
-
// JSON + `<` → \u003c keeps bodies from forging the delivery close tag (#677).
|
|
204
|
-
if (handbook.general.length > 0 || handbook.repo.length > 0) {
|
|
81
|
+
if (activation.manifest.requests.length > 0) {
|
|
205
82
|
const payload = JSON.stringify({
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
repository: activation.repository.canonical,
|
|
83
|
+
requests: activation.manifest.requests.map((request) => ({
|
|
84
|
+
id: request.id,
|
|
85
|
+
body: request.requestBody,
|
|
86
|
+
})),
|
|
211
87
|
}).replaceAll("<", "\\u003c");
|
|
212
|
-
lines.push("", "<
|
|
88
|
+
lines.push("", "<collector_request_manifest>", payload, "</collector_request_manifest>");
|
|
213
89
|
}
|
|
214
90
|
return lines.join("\n");
|
|
215
91
|
}
|
|
216
92
|
|
|
217
|
-
function parsePositiveTicket(raw: unknown, label: string): number | undefined {
|
|
218
|
-
if (raw === undefined || raw === null) return undefined;
|
|
219
|
-
if (typeof raw === "number" && Number.isSafeInteger(raw) && raw >= 1) return raw;
|
|
220
|
-
if (typeof raw === "string" && /^[1-9]\d*$/.test(raw.trim())) {
|
|
221
|
-
const value = Number(raw.trim());
|
|
222
|
-
if (Number.isSafeInteger(value)) return value;
|
|
223
|
-
}
|
|
224
|
-
throw new CollectorTargetBindError(`通进司绑定 ${label} 须为正安全整数`);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
93
|
/**
|
|
228
|
-
* Shared envelope installs
|
|
94
|
+
* Shared envelope installs the submission tool and material callback after lifecycle gates.
|
|
229
95
|
* Role module does not self-hook events, setActiveTools, or mode/fork checks (ADR 0018 / #676 E).
|
|
230
96
|
*/
|
|
231
97
|
export function createCollectorRoleRuntime(
|
|
232
98
|
pi: RoleHost,
|
|
233
99
|
dependencies: CollectorRoleDependencies,
|
|
234
|
-
hostActions: CollectorRoleHostActions,
|
|
235
100
|
): {
|
|
236
|
-
/** Business activation only: soul, flags→config, ledger. Envelope owns lifecycle gates. */
|
|
237
101
|
activate(ctx: HostContext): Promise<CollectorActivation>;
|
|
238
|
-
/** Business material assembly — envelope hangs this on before_agent_start. */
|
|
239
102
|
assembleMaterials(activation: CollectorActivation, baseSystemPrompt: string): string;
|
|
240
|
-
/** Business operational bookkeeping callbacks for shared tool_call / tool_result seams. */
|
|
241
103
|
onToolCall(
|
|
242
|
-
|
|
104
|
+
_activation: CollectorActivation,
|
|
243
105
|
event: { toolName: string; toolCallId: string },
|
|
244
106
|
): { block: true; reason: string } | undefined;
|
|
245
|
-
onToolResult(activation: CollectorActivation, event: { toolCallId: string }): void;
|
|
246
|
-
/** Register business tools once. Envelope owns uniqueness + setActiveTools. */
|
|
247
107
|
registerBusinessTools(getActivation: () => CollectorActivation | undefined): void;
|
|
248
108
|
} {
|
|
249
109
|
let toolsRegistered = false;
|
|
250
110
|
|
|
251
111
|
return {
|
|
252
|
-
async activate(
|
|
112
|
+
async activate(_ctx) {
|
|
253
113
|
const soul = (await dependencies.loadSoul()).trim();
|
|
254
114
|
if (soul.length === 0) throw new Error("Collector soul is empty");
|
|
255
115
|
|
|
256
|
-
// Flags registered by the shared envelope (COLLECTOR_TRANSPORT_FLAGS).
|
|
257
116
|
const repoFlag = pi.getFlag("ak-collector-repo");
|
|
258
117
|
const prFlag = pi.getFlag("ak-collector-pr");
|
|
259
118
|
const requestManifestFlag = pi.getFlag("ak-collector-request-manifest");
|
|
260
|
-
const waitMsFlag = pi.getFlag("ak-collector-wait-ms");
|
|
261
119
|
if (typeof repoFlag !== "string" || repoFlag.trim().length === 0) {
|
|
262
120
|
throw new Error("Collector requires --ak-collector-repo");
|
|
263
121
|
}
|
|
264
122
|
const repository = parseCollectorRepository(repoFlag);
|
|
265
|
-
// #676 A: PR may be unbound — role binds via ak_collector_bind_target from materials.
|
|
266
123
|
let prNumber: number | undefined;
|
|
267
124
|
if (typeof prFlag === "string" && prFlag.trim().length > 0) {
|
|
268
125
|
prNumber = parseCollectorPrNumber(prFlag);
|
|
@@ -272,45 +129,12 @@ export function createCollectorRoleRuntime(
|
|
|
272
129
|
const manifest = typeof requestManifestFlag === "string" && requestManifestFlag.trim().length > 0
|
|
273
130
|
? await loadCollectorManifest(requestManifestFlag)
|
|
274
131
|
: emptyCollectorManifest();
|
|
275
|
-
const waitWindowMs = parseWaitWindowMsFlag(waitMsFlag);
|
|
276
|
-
|
|
277
|
-
const clock = dependencies.createClock?.() ?? createSystemCollectorClock();
|
|
278
|
-
const transport = dependencies.createTransport();
|
|
279
|
-
const ledger = dependencies.createLedger(
|
|
280
|
-
{ repository, prNumber, manifest, waitWindowMs },
|
|
281
|
-
clock,
|
|
282
|
-
ctx,
|
|
283
|
-
);
|
|
284
|
-
|
|
285
|
-
// #677: handbook under admitted book topology (session path → books/<key>/collector-handbook).
|
|
286
|
-
const sessionPath = ctx.sessionManager?.getSessionFile?.()
|
|
287
|
-
?? ctx.sessionManager?.getSessionDir?.();
|
|
288
|
-
if (typeof sessionPath !== "string" || sessionPath.length === 0) {
|
|
289
|
-
throw new Error("通进司手册要求 session 路径位于 books/<bookKey>/");
|
|
290
|
-
}
|
|
291
|
-
const placement = resolveCollectorHandbookRoot(sessionPath);
|
|
292
|
-
const seedGeneral = dependencies.loadHandbookSeed === undefined
|
|
293
|
-
? undefined
|
|
294
|
-
: (await dependencies.loadHandbookSeed()).trim();
|
|
295
|
-
const handbook = createCollectorHandbookStore({
|
|
296
|
-
ledgerHome: placement.ledgerHome,
|
|
297
|
-
handbookRoot: placement.root,
|
|
298
|
-
repositoryCanonical: repository.canonical,
|
|
299
|
-
...(seedGeneral === undefined || seedGeneral.length === 0
|
|
300
|
-
? {}
|
|
301
|
-
: { seedGeneral }),
|
|
302
|
-
});
|
|
303
|
-
const handbookView = await handbook.read();
|
|
304
132
|
|
|
305
133
|
return {
|
|
306
134
|
soul,
|
|
307
135
|
repository,
|
|
136
|
+
prNumber,
|
|
308
137
|
manifest,
|
|
309
|
-
ledger,
|
|
310
|
-
transport,
|
|
311
|
-
clock,
|
|
312
|
-
handbook,
|
|
313
|
-
handbookView,
|
|
314
138
|
};
|
|
315
139
|
},
|
|
316
140
|
|
|
@@ -326,360 +150,41 @@ export function createCollectorRoleRuntime(
|
|
|
326
150
|
].join("\n");
|
|
327
151
|
},
|
|
328
152
|
|
|
329
|
-
onToolCall(
|
|
330
|
-
|
|
331
|
-
if (activation.ledger.fatal) {
|
|
153
|
+
onToolCall(_activation, event) {
|
|
154
|
+
if ((COLLECTOR_CONSTRUCTION_TOOLS as readonly string[]).includes(event.toolName)) {
|
|
332
155
|
return {
|
|
333
156
|
block: true,
|
|
334
|
-
reason:
|
|
157
|
+
reason: `通进司为收证席,禁用施工工具 ${event.toolName}`,
|
|
335
158
|
};
|
|
336
159
|
}
|
|
337
|
-
if (event.toolName === COLLECTOR_OUTPUT_TOOL) {
|
|
338
|
-
activation.ledger.beginOperational(COLLECTOR_OUTPUT_TOOL, event.toolCallId);
|
|
339
|
-
}
|
|
340
160
|
return undefined;
|
|
341
161
|
},
|
|
342
162
|
|
|
343
|
-
onToolResult(activation, event) {
|
|
344
|
-
activation.ledger.completeOperational(event.toolCallId);
|
|
345
|
-
},
|
|
346
|
-
|
|
347
163
|
registerBusinessTools(getActivation) {
|
|
348
164
|
if (toolsRegistered) return;
|
|
349
165
|
toolsRegistered = true;
|
|
350
166
|
|
|
351
|
-
pi.registerTool({
|
|
352
|
-
name: COLLECTOR_BIND_TARGET_TOOL,
|
|
353
|
-
label: "通进司认票绑定",
|
|
354
|
-
description: "绑定本仓唯一 PR 目标。",
|
|
355
|
-
promptSnippet: "绑定角色判定的 issue/PR 目标",
|
|
356
|
-
parameters: bindSchema,
|
|
357
|
-
async execute(toolCallId: string, params: BindParams, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
358
|
-
const activation = getActivation();
|
|
359
|
-
if (activation === undefined) throw new Error("通进司未激活");
|
|
360
|
-
try {
|
|
361
|
-
activation.ledger.beginOperational(COLLECTOR_BIND_TARGET_TOOL, toolCallId);
|
|
362
|
-
const prNumber = parsePositiveTicket(params.prNumber, "prNumber");
|
|
363
|
-
const issueNumber = parsePositiveTicket(params.issueNumber, "issueNumber");
|
|
364
|
-
if (prNumber === undefined && issueNumber === undefined) {
|
|
365
|
-
throw new CollectorTargetBindError(
|
|
366
|
-
"通进司绑定须由角色判定 prNumber 与/或 issueNumber",
|
|
367
|
-
);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
let bound = prNumber;
|
|
371
|
-
let associated: readonly number[] = [];
|
|
372
|
-
if (issueNumber !== undefined) {
|
|
373
|
-
associated = await listPullRequestNumbersByTicket(createGhApiRunner(), {
|
|
374
|
-
owner: activation.repository.owner,
|
|
375
|
-
repo: activation.repository.repo,
|
|
376
|
-
ticketNumber: issueNumber,
|
|
377
|
-
});
|
|
378
|
-
if (bound === undefined && associated.length === 1) {
|
|
379
|
-
bound = associated[0]!;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
if (bound === undefined) {
|
|
383
|
-
return {
|
|
384
|
-
content: [{
|
|
385
|
-
type: "text" as const,
|
|
386
|
-
text: JSON.stringify({
|
|
387
|
-
bound: false,
|
|
388
|
-
repository: activation.repository.canonical,
|
|
389
|
-
prNumber: prNumber ?? null,
|
|
390
|
-
issueNumber: issueNumber ?? null,
|
|
391
|
-
associated,
|
|
392
|
-
}),
|
|
393
|
-
}],
|
|
394
|
-
details: {
|
|
395
|
-
bound: false,
|
|
396
|
-
repository: activation.repository.canonical,
|
|
397
|
-
prNumber: prNumber ?? null,
|
|
398
|
-
issueNumber: issueNumber ?? null,
|
|
399
|
-
associated,
|
|
400
|
-
},
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
activation.ledger.bindTarget(bound);
|
|
405
|
-
activation.ledger.completeOperational(toolCallId);
|
|
406
|
-
return {
|
|
407
|
-
content: [{
|
|
408
|
-
type: "text" as const,
|
|
409
|
-
text: `目标已绑定:${activation.repository.canonical}#${bound}`,
|
|
410
|
-
}],
|
|
411
|
-
details: {
|
|
412
|
-
bound: true,
|
|
413
|
-
repository: activation.repository.canonical,
|
|
414
|
-
prNumber: bound,
|
|
415
|
-
issueNumber: issueNumber ?? null,
|
|
416
|
-
associated,
|
|
417
|
-
},
|
|
418
|
-
};
|
|
419
|
-
} catch (error) {
|
|
420
|
-
if (isCorrectableExecuteError(error)) throw error;
|
|
421
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
422
|
-
} finally {
|
|
423
|
-
try {
|
|
424
|
-
activation.ledger.completeOperational(toolCallId);
|
|
425
|
-
} catch {
|
|
426
|
-
// already completed or not begun
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
},
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
pi.registerTool({
|
|
433
|
-
name: COLLECTOR_OBSERVE_TOOL,
|
|
434
|
-
label: "通进司观察",
|
|
435
|
-
description: "抓取配置目标的 GitHub PR 证据。",
|
|
436
|
-
promptSnippet: "抓取配置目标 PR 证据",
|
|
437
|
-
parameters: observeSchema,
|
|
438
|
-
async execute(toolCallId: string, _params: unknown, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
439
|
-
const activation = getActivation();
|
|
440
|
-
if (activation === undefined) throw new Error("通进司未激活");
|
|
441
|
-
try {
|
|
442
|
-
activation.ledger.beginOperational(COLLECTOR_OBSERVE_TOOL, toolCallId);
|
|
443
|
-
const { snapshot, contextView } = await activation.ledger.observe(
|
|
444
|
-
activation.transport,
|
|
445
|
-
activation.clock,
|
|
446
|
-
signal,
|
|
447
|
-
);
|
|
448
|
-
activation.ledger.completeOperational(toolCallId);
|
|
449
|
-
return {
|
|
450
|
-
content: [{
|
|
451
|
-
type: "text" as const,
|
|
452
|
-
text: JSON.stringify(contextView),
|
|
453
|
-
}],
|
|
454
|
-
details: contextView,
|
|
455
|
-
};
|
|
456
|
-
} catch (error) {
|
|
457
|
-
if (isCorrectableExecuteError(error)) throw error;
|
|
458
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
459
|
-
}
|
|
460
|
-
},
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
pi.registerTool({
|
|
464
|
-
name: COLLECTOR_READ_TOOL,
|
|
465
|
-
label: "通进司开卷",
|
|
466
|
-
description: "按 evidenceId 开卷读取一条已观测材料的全量正文与指针。",
|
|
467
|
-
promptSnippet: "按指针开卷读材料",
|
|
468
|
-
parameters: readSchema,
|
|
469
|
-
async execute(toolCallId: string, params: ReadParams, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
470
|
-
const activation = getActivation();
|
|
471
|
-
if (activation === undefined) throw new Error("通进司未激活");
|
|
472
|
-
try {
|
|
473
|
-
activation.ledger.beginOperational(COLLECTOR_READ_TOOL, toolCallId);
|
|
474
|
-
const record = activation.ledger.getEvidence(params.evidenceId);
|
|
475
|
-
if (
|
|
476
|
-
record === undefined ||
|
|
477
|
-
(record.kind !== "review" && record.kind !== "issue_comment" && record.kind !== "review_comment" && record.kind !== "reaction") ||
|
|
478
|
-
typeof record.body !== "string"
|
|
479
|
-
) {
|
|
480
|
-
throw new CollectorUnknownEvidenceError(params.evidenceId);
|
|
481
|
-
}
|
|
482
|
-
const material = projectEvidenceEntryView(record);
|
|
483
|
-
activation.ledger.completeOperational(toolCallId);
|
|
484
|
-
return {
|
|
485
|
-
content: [{
|
|
486
|
-
type: "text" as const,
|
|
487
|
-
text: JSON.stringify(material),
|
|
488
|
-
}],
|
|
489
|
-
details: material,
|
|
490
|
-
};
|
|
491
|
-
} catch (error) {
|
|
492
|
-
if (isCorrectableExecuteError(error)) throw error;
|
|
493
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
494
|
-
}
|
|
495
|
-
},
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
pi.registerTool({
|
|
499
|
-
name: COLLECTOR_REQUEST_TOOL,
|
|
500
|
-
label: "通进司请求",
|
|
501
|
-
description: "发一次请求。",
|
|
502
|
-
promptSnippet: "发一次评审请求",
|
|
503
|
-
parameters: requestSchema,
|
|
504
|
-
async execute(toolCallId: string, params: RequestParams, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
505
|
-
const activation = getActivation();
|
|
506
|
-
if (activation === undefined) throw new Error("通进司未激活");
|
|
507
|
-
try {
|
|
508
|
-
activation.ledger.beginOperational(COLLECTOR_REQUEST_TOOL, toolCallId);
|
|
509
|
-
const details = await activation.ledger.request(
|
|
510
|
-
{
|
|
511
|
-
requestId: params.requestId,
|
|
512
|
-
snapshotId: params.snapshotId,
|
|
513
|
-
...(typeof params.body === "string" ? { body: params.body } : {}),
|
|
514
|
-
},
|
|
515
|
-
activation.transport,
|
|
516
|
-
activation.clock,
|
|
517
|
-
signal,
|
|
518
|
-
);
|
|
519
|
-
activation.ledger.completeOperational(toolCallId);
|
|
520
|
-
return {
|
|
521
|
-
content: [{
|
|
522
|
-
type: "text" as const,
|
|
523
|
-
text: `请求尝试已记录:request ${params.requestId}`,
|
|
524
|
-
}],
|
|
525
|
-
details,
|
|
526
|
-
};
|
|
527
|
-
} catch (error) {
|
|
528
|
-
if (isCorrectableExecuteError(error)) throw error;
|
|
529
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
530
|
-
}
|
|
531
|
-
},
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
pi.registerTool({
|
|
535
|
-
name: COLLECTOR_HANDBOOK_WRITE_TOOL,
|
|
536
|
-
label: "通进司手册写入",
|
|
537
|
-
description: "写入通用手册或当前仓库差异全文(整份替换)。",
|
|
538
|
-
promptSnippet: "更新 bot 手册",
|
|
539
|
-
parameters: handbookWriteSchema,
|
|
540
|
-
async execute(toolCallId: string, params: HandbookWriteParams, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
541
|
-
const activation = getActivation();
|
|
542
|
-
if (activation === undefined) throw new Error("通进司未激活");
|
|
543
|
-
try {
|
|
544
|
-
activation.ledger.beginOperational(COLLECTOR_HANDBOOK_WRITE_TOOL, toolCallId);
|
|
545
|
-
// scope/body shape authority = collectorHandbookWriteArgsSchema (host parameters).
|
|
546
|
-
const details = await activation.handbook.write(params.scope, params.body);
|
|
547
|
-
activation.handbookView = await activation.handbook.read();
|
|
548
|
-
activation.ledger.completeOperational(toolCallId);
|
|
549
|
-
return {
|
|
550
|
-
content: [{
|
|
551
|
-
type: "text" as const,
|
|
552
|
-
text: `手册已写入:${details.scope}`,
|
|
553
|
-
}],
|
|
554
|
-
details: {
|
|
555
|
-
scope: details.scope,
|
|
556
|
-
path: details.path,
|
|
557
|
-
byteLength: details.byteLength,
|
|
558
|
-
generalSource: activation.handbookView.generalSource,
|
|
559
|
-
repoSource: activation.handbookView.repoSource,
|
|
560
|
-
},
|
|
561
|
-
};
|
|
562
|
-
} catch (error) {
|
|
563
|
-
if (isCorrectableExecuteError(error)) throw error;
|
|
564
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
565
|
-
} finally {
|
|
566
|
-
try {
|
|
567
|
-
activation.ledger.completeOperational(toolCallId);
|
|
568
|
-
} catch {
|
|
569
|
-
// already completed or not begun
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
},
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
pi.registerTool({
|
|
576
|
-
name: COLLECTOR_OPEN_WAIT_WINDOW_TOOL,
|
|
577
|
-
label: "通进司开启等待窗",
|
|
578
|
-
description:
|
|
579
|
-
"在工作步骤上开启等待窗:新建并自动触发的 PR 传创建成功时刻;已有 PR 在触发阶段结束后省略 startedAt(=现在)。窗长由使用方配置,默认十分钟;开启后不因 PR 更新重置。",
|
|
580
|
-
promptSnippet: "按工作步骤开启等待窗",
|
|
581
|
-
parameters: openWaitWindowSchema,
|
|
582
|
-
async execute(toolCallId: string, params: OpenWaitWindowParams, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
583
|
-
const activation = getActivation();
|
|
584
|
-
if (activation === undefined) throw new Error("通进司未激活");
|
|
585
|
-
try {
|
|
586
|
-
activation.ledger.beginOperational(COLLECTOR_OPEN_WAIT_WINDOW_TOOL, toolCallId);
|
|
587
|
-
let startedAt: Date | undefined;
|
|
588
|
-
if (typeof params.startedAt === "string" && params.startedAt.trim().length > 0) {
|
|
589
|
-
const parsed = new Date(params.startedAt);
|
|
590
|
-
if (Number.isNaN(parsed.getTime())) {
|
|
591
|
-
throw new Error("通进司等待窗 startedAt 须为有效 ISO 时间");
|
|
592
|
-
}
|
|
593
|
-
startedAt = parsed;
|
|
594
|
-
}
|
|
595
|
-
activation.ledger.openWaitWindow(
|
|
596
|
-
activation.clock,
|
|
597
|
-
startedAt === undefined ? undefined : { startedAt },
|
|
598
|
-
);
|
|
599
|
-
activation.ledger.completeOperational(toolCallId);
|
|
600
|
-
return {
|
|
601
|
-
content: [{
|
|
602
|
-
type: "text" as const,
|
|
603
|
-
text: "等待窗已开启",
|
|
604
|
-
}],
|
|
605
|
-
details: {
|
|
606
|
-
activationTime: activation.ledger.activationTime?.toISOString(),
|
|
607
|
-
deadlineTime: activation.ledger.deadlineTime?.toISOString(),
|
|
608
|
-
waitWindowMs: activation.ledger.config.waitWindowMs,
|
|
609
|
-
},
|
|
610
|
-
};
|
|
611
|
-
} catch (error) {
|
|
612
|
-
if (isCorrectableExecuteError(error)) throw error;
|
|
613
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
614
|
-
} finally {
|
|
615
|
-
try {
|
|
616
|
-
activation.ledger.completeOperational(toolCallId);
|
|
617
|
-
} catch {
|
|
618
|
-
// already completed or not begun
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
},
|
|
622
|
-
});
|
|
623
|
-
|
|
624
|
-
pi.registerTool({
|
|
625
|
-
name: COLLECTOR_WAIT_TOOL,
|
|
626
|
-
label: "通进司等待",
|
|
627
|
-
description: "等待。实际睡眠不超过剩余等待窗。",
|
|
628
|
-
promptSnippet: "等待窗内等待",
|
|
629
|
-
parameters: waitSchema,
|
|
630
|
-
async execute(toolCallId: string, params: WaitParams, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
631
|
-
const activation = getActivation();
|
|
632
|
-
if (activation === undefined) throw new Error("通进司未激活");
|
|
633
|
-
try {
|
|
634
|
-
activation.ledger.beginOperational(COLLECTOR_WAIT_TOOL, toolCallId);
|
|
635
|
-
const details = await activation.ledger.wait(
|
|
636
|
-
params,
|
|
637
|
-
activation.clock,
|
|
638
|
-
signal,
|
|
639
|
-
);
|
|
640
|
-
activation.ledger.completeOperational(toolCallId);
|
|
641
|
-
return {
|
|
642
|
-
content: [{
|
|
643
|
-
type: "text" as const,
|
|
644
|
-
text: `已等待 ${String((details as { effectiveMs: number }).effectiveMs)}ms`,
|
|
645
|
-
}],
|
|
646
|
-
details,
|
|
647
|
-
};
|
|
648
|
-
} catch (error) {
|
|
649
|
-
if (isCorrectableExecuteError(error)) throw error;
|
|
650
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
651
|
-
}
|
|
652
|
-
},
|
|
653
|
-
});
|
|
654
|
-
|
|
655
167
|
pi.registerTool({
|
|
656
168
|
name: COLLECTOR_OUTPUT_TOOL,
|
|
657
169
|
label: "通进司输出",
|
|
658
170
|
description: "提交通进司回执。",
|
|
659
171
|
promptSnippet: "提交通进司回执",
|
|
660
172
|
parameters: outputSchema,
|
|
661
|
-
async execute(
|
|
173
|
+
async execute(
|
|
174
|
+
_toolCallId: string,
|
|
175
|
+
params: OutputParams,
|
|
176
|
+
_signal: AbortSignal | undefined,
|
|
177
|
+
_onUpdate: unknown,
|
|
178
|
+
_ctx: HostContext,
|
|
179
|
+
): Promise<HostToolResult<unknown>> {
|
|
662
180
|
const activation = getActivation();
|
|
663
181
|
if (activation === undefined) throw new Error("通进司未激活");
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
terminate: true as const,
|
|
671
|
-
};
|
|
672
|
-
} catch (error) {
|
|
673
|
-
if (
|
|
674
|
-
error instanceof Error &&
|
|
675
|
-
(error as { collectorFatal?: boolean }).collectorFatal === true
|
|
676
|
-
) {
|
|
677
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
678
|
-
}
|
|
679
|
-
throw error;
|
|
680
|
-
} finally {
|
|
681
|
-
activation.ledger.completeOperational(toolCallId);
|
|
682
|
-
}
|
|
182
|
+
// #1088 / #836: record as submitted — runtime does not merge findings.
|
|
183
|
+
return {
|
|
184
|
+
content: [],
|
|
185
|
+
details: params,
|
|
186
|
+
terminate: true as const,
|
|
187
|
+
};
|
|
683
188
|
},
|
|
684
189
|
});
|
|
685
190
|
},
|