@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.
Files changed (39) hide show
  1. package/README.md +2 -3
  2. package/README.zh-CN.md +2 -3
  3. package/dist/acp-host/production-host.js +442 -3070
  4. package/dist/collector-config.js +0 -1
  5. package/dist/headless-host/production-host.js +443 -3071
  6. package/dist/migrate-book-topology.js +40 -107
  7. package/dist/package-contracts/collector-output.js +1 -90
  8. package/dist/package-contracts/terminating-tools.js +2 -2
  9. package/dist/packaged-role-registry.js +8 -11
  10. package/dist/public-cli/invocation.js +2 -22
  11. package/dist/public-cli/main.js +252 -698
  12. package/dist/public-cli/option-definitions.js +4 -19
  13. package/dist/public-cli/settlement.js +2 -79
  14. package/package.json +1 -1
  15. package/resources/collector-bot-handbook.md +4 -2
  16. package/src/collector-config.ts +0 -1
  17. package/src/collector-role.ts +40 -535
  18. package/src/collector-tool-schemas.ts +7 -117
  19. package/src/gh-api-runner.ts +102 -0
  20. package/src/package-contracts/collector-output.ts +15 -174
  21. package/src/package-contracts/terminating-tools.ts +0 -2
  22. package/src/packaged-role-registry.ts +7 -10
  23. package/src/public-cli/invocation.ts +3 -27
  24. package/src/public-cli/option-definitions.ts +4 -19
  25. package/src/public-cli/settlement.ts +3 -102
  26. package/src/role-runtime-dependencies.ts +0 -4
  27. package/src/role-runtime.ts +16 -98
  28. package/src/ticket-snapshot.ts +1 -1
  29. package/dist/collector-evidence.js +0 -319
  30. package/dist/collector-github.js +0 -725
  31. package/dist/collector-identity.js +0 -92
  32. package/dist/collector-ledger.js +0 -888
  33. package/dist/collector-target.js +0 -169
  34. package/src/collector-evidence.ts +0 -466
  35. package/src/collector-github.ts +0 -1032
  36. package/src/collector-handbook.ts +0 -194
  37. package/src/collector-identity.ts +0 -151
  38. package/src/collector-ledger.ts +0 -1186
  39. package/src/collector-target.ts +0 -207
@@ -1,9 +1,7 @@
1
1
  /**
2
- * Collector business tools, soul/material assembly, and result projection.
3
- * Registration flags, activation barrier, resource assembly (no-skills / no-context),
4
- * sole-final seal, mode/fork gates, tool surface narrowing, and failInfrastructure
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
- COLLECTOR_READ_TOOL,
41
- COLLECTOR_REQUEST_TOOL,
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 { CollectorUnknownEvidenceError } from "./collector-identity.ts";
22
+ export { COLLECTOR_OUTPUT_TOOL };
62
23
 
63
- /** #676 A: role-chosen target could not bind uniquely — correctable, not host failure. */
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
- export const COLLECTOR_REQUIRED_TOOLS = [
83
- COLLECTOR_BIND_TARGET_TOOL,
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 bind from task materials.",
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: ${pr === undefined ? "未绑定" : String(pr)}`,
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
- `handbookGeneralSource: ${handbook.generalSource}`,
199
- `handbookRepoSource: ${handbook.repoSource}`,
78
+ `manifestDigest: ${activation.manifest.digest}`,
200
79
  "</collector_method>",
201
80
  ];
202
- // Opaque working memory only — no directional instructions (ADR 0073).
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
- general: handbook.general,
207
- repo: handbook.repo,
208
- generalSource: handbook.generalSource,
209
- repoSource: handbook.repoSource,
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("", "<collector_handbook>", payload, "</collector_handbook>");
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 business tools and material callback after lifecycle gates.
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
- activation: CollectorActivation,
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(ctx) {
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(activation, event) {
330
- // Business ledger facts only — allowed-tool / mode gates live on the envelope.
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: activation.ledger.fatalReason ?? "通进司致命状态",
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(toolCallId: string, params: OutputParams, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
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
- try {
665
- activation.ledger.beginOperational(COLLECTOR_OUTPUT_TOOL, toolCallId);
666
- activation.ledger.recordOutputCandidate();
667
- return {
668
- content: [],
669
- details: params,
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
  },