@akagilnc/pi-workflow-roles 0.1.3749 → 0.1.3771
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/CLAUDE.md +4 -0
- package/README.md +3 -2
- package/README.zh-CN.md +3 -2
- package/dist/acp-host/production-host.js +1389 -751
- package/dist/collector-config.js +0 -1
- package/dist/collector-github.js +199 -2
- package/dist/collector-identity.js +128 -41
- package/dist/collector-ledger.js +38 -9
- package/dist/collector-receipt.js +19 -7
- package/dist/collector-role.js +330 -369
- package/dist/collector-target.js +169 -0
- package/dist/collector-tool-schemas.js +51 -14
- package/dist/package-contracts/collector-output.js +32 -0
- package/dist/package-contracts/terminating-infrastructure.js +13 -12
- package/dist/pi/role-turn-host.js +1 -2
- package/dist/public-cli/github-remote.js +45 -0
- package/dist/public-cli/invocation.js +53 -54
- package/dist/public-cli/main.js +615 -148
- package/dist/public-cli/option-definitions.js +6 -4
- package/dist/public-cli/run-lifecycle.js +3 -3
- package/dist/public-cli/settlement.js +83 -6
- package/dist/role-runtime.js +137 -7
- package/dist/submission-correctable-error.js +24 -0
- package/extensions/role-runtime.ts +0 -1
- package/package.json +1 -1
- package/souls/coder.md +11 -6
- package/souls/fixer.md +10 -9
- package/src/acp-host/role-envelope.ts +8 -22
- package/src/collector-config.ts +0 -1
- package/src/collector-github.ts +236 -2
- package/src/collector-identity.ts +148 -40
- package/src/collector-ledger.ts +48 -10
- package/src/collector-receipt.ts +33 -14
- package/src/collector-role.ts +376 -450
- package/src/collector-target.ts +207 -0
- package/src/collector-tool-schemas.ts +62 -15
- package/src/host-contracts.ts +2 -1
- package/src/package-contracts/collector-output.ts +72 -0
- package/src/package-contracts/terminating-infrastructure.ts +24 -13
- package/src/pi/role-turn-host.ts +1 -2
- package/src/public-cli/cli.ts +10 -1
- package/src/public-cli/collector-run.ts +3 -2
- package/src/public-cli/github-remote.ts +45 -0
- package/src/public-cli/invocation.ts +60 -59
- package/src/public-cli/option-definitions.ts +6 -4
- package/src/public-cli/run-lifecycle.ts +2 -2
- package/src/public-cli/settlement.ts +82 -6
- package/src/role-runtime.ts +166 -13
- package/src/submission-correctable-error.ts +38 -0
package/dist/collector-role.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { emptyCollectorManifest, loadCollectorManifest, parseCollectorPrNumber, parseCollectorRepository, } from "./collector-config.js";
|
|
2
2
|
import { createSystemCollectorClock, } from "./collector-evidence.js";
|
|
3
|
-
import {
|
|
3
|
+
import { createGhApiRunner, listPullRequestNumbersByTicket, } from "./collector-github.js";
|
|
4
|
+
import { COLLECTOR_BIND_TARGET_TOOL, COLLECTOR_OBSERVE_TOOL, COLLECTOR_OUTPUT_TOOL, COLLECTOR_READ_TOOL, COLLECTOR_REQUEST_TOOL, COLLECTOR_WAIT_TOOL, projectEvidenceEntryView, } from "./collector-ledger.js";
|
|
4
5
|
import { buildCollectorReceipt, } from "./collector-receipt.js";
|
|
5
|
-
import { collectorObserveArgsSchema, collectorOutputArgsSchema, collectorReadArgsSchema, collectorRequestArgsSchema, collectorWaitArgsSchema, } from "./collector-tool-schemas.js";
|
|
6
|
+
import { collectorBindTargetArgsSchema, collectorObserveArgsSchema, collectorOutputArgsSchema, collectorReadArgsSchema, collectorRequestArgsSchema, collectorWaitArgsSchema, } from "./collector-tool-schemas.js";
|
|
6
7
|
import { COLLECTOR_ACCEPTED_TEXT } from "./package-contracts/collector-output.js";
|
|
7
8
|
import { CorrectableSubmissionError, isCorrectableExecuteError } from "./submission-correctable-error.js";
|
|
8
9
|
import { CollectorUnknownEvidenceError } from "./collector-identity.js";
|
|
@@ -18,122 +19,133 @@ export class CollectorNormalCompletionDeclarationError extends CorrectableSubmis
|
|
|
18
19
|
this.name = "CollectorNormalCompletionDeclarationError";
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
-
export
|
|
22
|
+
/** #676 A: role-chosen target could not bind uniquely — correctable, not host failure. */
|
|
23
|
+
export class CollectorTargetBindError extends CorrectableSubmissionError {
|
|
24
|
+
constructor(message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "CollectorTargetBindError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export { COLLECTOR_BIND_TARGET_TOOL, COLLECTOR_OBSERVE_TOOL, COLLECTOR_OUTPUT_TOOL, COLLECTOR_READ_TOOL, COLLECTOR_REQUEST_TOOL, COLLECTOR_WAIT_TOOL, };
|
|
23
30
|
export const COLLECTOR_REQUIRED_TOOLS = [
|
|
31
|
+
COLLECTOR_BIND_TARGET_TOOL,
|
|
24
32
|
COLLECTOR_OBSERVE_TOOL,
|
|
25
33
|
COLLECTOR_READ_TOOL,
|
|
26
34
|
COLLECTOR_REQUEST_TOOL,
|
|
27
35
|
COLLECTOR_WAIT_TOOL,
|
|
28
36
|
COLLECTOR_OUTPUT_TOOL,
|
|
29
37
|
];
|
|
38
|
+
/** Envelope-owned transport flags (ADR 0018 / #676 E). */
|
|
39
|
+
export const COLLECTOR_TRANSPORT_FLAGS = Object.freeze([
|
|
40
|
+
Object.freeze({
|
|
41
|
+
name: "ak-collector-repo",
|
|
42
|
+
definition: Object.freeze({
|
|
43
|
+
description: "GitHub owner/repo target for Collector (github.com only; conservative ASCII grammar).",
|
|
44
|
+
type: "string",
|
|
45
|
+
}),
|
|
46
|
+
}),
|
|
47
|
+
Object.freeze({
|
|
48
|
+
name: "ak-collector-pr",
|
|
49
|
+
definition: Object.freeze({
|
|
50
|
+
description: "Optional positive safe-integer pull request number for Collector. Omit when the role will bind from task materials.",
|
|
51
|
+
type: "string",
|
|
52
|
+
}),
|
|
53
|
+
}),
|
|
54
|
+
Object.freeze({
|
|
55
|
+
name: "ak-collector-request-manifest",
|
|
56
|
+
definition: Object.freeze({
|
|
57
|
+
description: "Path to the Collector v1 request manifest JSON file.",
|
|
58
|
+
type: "string",
|
|
59
|
+
}),
|
|
60
|
+
}),
|
|
61
|
+
]);
|
|
30
62
|
const observeSchema = collectorObserveArgsSchema;
|
|
31
63
|
const readSchema = collectorReadArgsSchema;
|
|
32
64
|
const requestSchema = collectorRequestArgsSchema;
|
|
33
65
|
const waitSchema = collectorWaitArgsSchema;
|
|
66
|
+
const bindSchema = collectorBindTargetArgsSchema;
|
|
34
67
|
const outputSchema = collectorOutputArgsSchema;
|
|
35
|
-
/** Sole-final at the output execution seam (same shape as other terminating roles). */
|
|
36
68
|
function buildMethodContext(activation) {
|
|
69
|
+
const pr = activation.ledger.config.prNumber;
|
|
37
70
|
return [
|
|
38
71
|
"<collector_method>",
|
|
39
72
|
`host: github.com`,
|
|
40
73
|
`repository: ${activation.repository.canonical}`,
|
|
41
|
-
`prNumber: ${
|
|
74
|
+
`prNumber: ${pr === undefined ? "unbound — call ak_collector_bind_target with the role-decided issue/PR before observe" : String(pr)}`,
|
|
42
75
|
`requests: ${JSON.stringify(activation.manifest.requests.map((request) => ({ id: request.id })))}`,
|
|
43
76
|
"</collector_method>",
|
|
44
77
|
].join("\n");
|
|
45
78
|
}
|
|
79
|
+
function parsePositiveTicket(raw, label) {
|
|
80
|
+
if (raw === undefined || raw === null)
|
|
81
|
+
return undefined;
|
|
82
|
+
if (typeof raw === "number" && Number.isSafeInteger(raw) && raw >= 1)
|
|
83
|
+
return raw;
|
|
84
|
+
if (typeof raw === "string" && /^[1-9]\d*$/.test(raw.trim()))
|
|
85
|
+
return Number(raw.trim());
|
|
86
|
+
throw new CollectorTargetBindError(`ak_collector_bind_target ${label} must be a positive safe integer`);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Shared envelope installs business tools and material callback after lifecycle gates.
|
|
90
|
+
* Role module does not self-hook events, setActiveTools, or mode/fork checks (ADR 0018 / #676 E).
|
|
91
|
+
*/
|
|
46
92
|
export function createCollectorRoleRuntime(pi, dependencies, hostActions) {
|
|
47
|
-
let activation;
|
|
48
|
-
let inputCount = 0;
|
|
49
|
-
let lifecycleRegistered = false;
|
|
50
93
|
let toolsRegistered = false;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
type: "string",
|
|
63
|
-
});
|
|
64
|
-
const ensureLifecycle = () => {
|
|
65
|
-
if (lifecycleRegistered)
|
|
66
|
-
return;
|
|
67
|
-
lifecycleRegistered = true;
|
|
68
|
-
pi.on("input", (event, ctx) => {
|
|
69
|
-
if (activation === undefined) {
|
|
70
|
-
// role not active
|
|
71
|
-
return { action: "continue" };
|
|
72
|
-
}
|
|
73
|
-
if (inputCount >= 1) {
|
|
74
|
-
activation.ledger.latchFatal("通进司已拒绝后续输入");
|
|
75
|
-
if (process.exitCode === undefined || process.exitCode === 0) {
|
|
76
|
-
process.exitCode = 1;
|
|
77
|
-
}
|
|
78
|
-
console.error("Collector rejected later input");
|
|
79
|
-
return { action: "handled" };
|
|
80
|
-
}
|
|
81
|
-
inputCount += 1;
|
|
82
|
-
return {
|
|
83
|
-
action: "transform",
|
|
84
|
-
text: COLLECTOR_FIXED_KICKOFF,
|
|
85
|
-
images: [],
|
|
86
|
-
};
|
|
87
|
-
});
|
|
88
|
-
pi.on("before_agent_start", (event, ctx) => {
|
|
89
|
-
if (activation === undefined)
|
|
90
|
-
return;
|
|
91
|
-
// Detectable ambient instruction resources on the supported prompt surface.
|
|
92
|
-
const options = event.systemPromptOptions;
|
|
93
|
-
if (options.skills && options.skills.length > 0) {
|
|
94
|
-
hostActions.failInfrastructure(activation.ledger.latchFatal("通进司检测到系统提示中的环境 skills"), ctx);
|
|
95
|
-
}
|
|
96
|
-
if (options.contextFiles && options.contextFiles.length > 0) {
|
|
97
|
-
hostActions.failInfrastructure(activation.ledger.latchFatal("通进司检测到系统提示中的环境 context files"), ctx);
|
|
98
|
-
}
|
|
99
|
-
if (typeof options.appendSystemPrompt === "string" &&
|
|
100
|
-
options.appendSystemPrompt.trim().length > 0) {
|
|
101
|
-
hostActions.failInfrastructure(activation.ledger.latchFatal("通进司检测到 appendSystemPrompt 漂移"), ctx);
|
|
94
|
+
return {
|
|
95
|
+
async activate(ctx) {
|
|
96
|
+
const soul = (await dependencies.loadSoul()).trim();
|
|
97
|
+
if (soul.length === 0)
|
|
98
|
+
throw new Error("Collector soul is empty");
|
|
99
|
+
// Flags registered by the shared envelope (COLLECTOR_TRANSPORT_FLAGS).
|
|
100
|
+
const repoFlag = pi.getFlag("ak-collector-repo");
|
|
101
|
+
const prFlag = pi.getFlag("ak-collector-pr");
|
|
102
|
+
const requestManifestFlag = pi.getFlag("ak-collector-request-manifest");
|
|
103
|
+
if (typeof repoFlag !== "string" || repoFlag.trim().length === 0) {
|
|
104
|
+
throw new Error("Collector requires --ak-collector-repo");
|
|
102
105
|
}
|
|
103
|
-
|
|
104
|
-
|
|
106
|
+
const repository = parseCollectorRepository(repoFlag);
|
|
107
|
+
// #676 A: PR may be unbound — role binds via ak_collector_bind_target from materials.
|
|
108
|
+
let prNumber;
|
|
109
|
+
if (typeof prFlag === "string" && prFlag.trim().length > 0) {
|
|
110
|
+
prNumber = parseCollectorPrNumber(prFlag);
|
|
105
111
|
}
|
|
106
|
-
if (
|
|
107
|
-
|
|
108
|
-
activation.ledger.recordActivation(activation.clock);
|
|
112
|
+
else if (typeof prFlag === "number") {
|
|
113
|
+
prNumber = parseCollectorPrNumber(prFlag);
|
|
109
114
|
}
|
|
115
|
+
const manifest = typeof requestManifestFlag === "string" && requestManifestFlag.trim().length > 0
|
|
116
|
+
? await loadCollectorManifest(requestManifestFlag)
|
|
117
|
+
: emptyCollectorManifest();
|
|
118
|
+
const clock = dependencies.createClock?.() ?? createSystemCollectorClock();
|
|
119
|
+
const transport = dependencies.createTransport();
|
|
120
|
+
const ledger = dependencies.createLedger({ repository, prNumber, manifest }, clock, ctx);
|
|
110
121
|
return {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
"",
|
|
118
|
-
buildMethodContext(activation),
|
|
119
|
-
].join("\n"),
|
|
122
|
+
soul,
|
|
123
|
+
repository,
|
|
124
|
+
manifest,
|
|
125
|
+
ledger,
|
|
126
|
+
transport,
|
|
127
|
+
clock,
|
|
120
128
|
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
129
|
+
},
|
|
130
|
+
assembleMaterials(activation, baseSystemPrompt) {
|
|
131
|
+
return [
|
|
132
|
+
baseSystemPrompt,
|
|
133
|
+
"",
|
|
134
|
+
"<collector_soul>",
|
|
135
|
+
activation.soul,
|
|
136
|
+
"</collector_soul>",
|
|
137
|
+
"",
|
|
138
|
+
buildMethodContext(activation),
|
|
139
|
+
].join("\n");
|
|
140
|
+
},
|
|
141
|
+
onToolCall(activation, event) {
|
|
142
|
+
// Business ledger facts only — allowed-tool / mode gates live on the envelope.
|
|
125
143
|
if (activation.ledger.fatal) {
|
|
126
144
|
return {
|
|
127
145
|
block: true,
|
|
128
146
|
reason: activation.ledger.fatalReason ?? "通进司致命状态",
|
|
129
147
|
};
|
|
130
148
|
}
|
|
131
|
-
if (!COLLECTOR_REQUIRED_TOOLS.includes(event.toolName)) {
|
|
132
|
-
return {
|
|
133
|
-
block: true,
|
|
134
|
-
reason: `通进司禁用工具 ${event.toolName}`,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
149
|
if (event.toolName === COLLECTOR_OUTPUT_TOOL) {
|
|
138
150
|
activation.ledger.beginOperational(COLLECTOR_OUTPUT_TOOL, event.toolCallId);
|
|
139
151
|
}
|
|
@@ -145,297 +157,246 @@ export function createCollectorRoleRuntime(pi, dependencies, hostActions) {
|
|
|
145
157
|
};
|
|
146
158
|
}
|
|
147
159
|
return undefined;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (activation === undefined)
|
|
151
|
-
return;
|
|
160
|
+
},
|
|
161
|
+
onToolResult(activation, event) {
|
|
152
162
|
activation.ledger.completeOperational(event.toolCallId);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (
|
|
163
|
+
},
|
|
164
|
+
registerBusinessTools(getActivation) {
|
|
165
|
+
if (toolsRegistered)
|
|
156
166
|
return;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
167
|
+
toolsRegistered = true;
|
|
168
|
+
pi.registerTool({
|
|
169
|
+
name: COLLECTOR_BIND_TARGET_TOOL,
|
|
170
|
+
label: "通进司认票绑定",
|
|
171
|
+
description: "角色判定任务材料后绑定本仓唯一 PR 目标。可提交 prNumber 或 issueNumber(线上关联唯一 PR);显式 --pr 已绑定时无需再调。多义或无法确定时会正确驳回,要求调用方明确 --pr。",
|
|
172
|
+
promptSnippet: "绑定角色判定的 issue/PR 目标",
|
|
173
|
+
parameters: bindSchema,
|
|
174
|
+
async execute(toolCallId, params, _signal, _onUpdate, ctx) {
|
|
175
|
+
const activation = getActivation();
|
|
176
|
+
if (activation === undefined)
|
|
177
|
+
throw new Error("通进司未激活");
|
|
178
|
+
try {
|
|
179
|
+
activation.ledger.beginOperational(COLLECTOR_BIND_TARGET_TOOL, toolCallId);
|
|
180
|
+
const prNumber = parsePositiveTicket(params.prNumber, "prNumber");
|
|
181
|
+
const issueNumber = parsePositiveTicket(params.issueNumber, "issueNumber");
|
|
182
|
+
if (prNumber === undefined && issueNumber === undefined) {
|
|
183
|
+
throw new CollectorTargetBindError("ak_collector_bind_target requires role-decided prNumber and/or issueNumber");
|
|
184
|
+
}
|
|
185
|
+
let bound = prNumber;
|
|
186
|
+
if (issueNumber !== undefined) {
|
|
187
|
+
const associated = await listPullRequestNumbersByTicket(createGhApiRunner(), {
|
|
188
|
+
owner: activation.repository.owner,
|
|
189
|
+
repo: activation.repository.repo,
|
|
190
|
+
ticketNumber: issueNumber,
|
|
191
|
+
});
|
|
192
|
+
if (associated.length === 0) {
|
|
193
|
+
throw new CollectorTargetBindError(`no PR associated with issue #${issueNumber} in ${activation.repository.canonical}; pass an explicit --pr or a different issueNumber`);
|
|
194
|
+
}
|
|
195
|
+
if (associated.length > 1) {
|
|
196
|
+
throw new CollectorTargetBindError(`multiple PRs associated with issue #${issueNumber}: ${associated.join(", ")}; pass an explicit prNumber or --pr`);
|
|
197
|
+
}
|
|
198
|
+
const fromIssue = associated[0];
|
|
199
|
+
if (prNumber !== undefined && prNumber !== fromIssue) {
|
|
200
|
+
throw new CollectorTargetBindError(`prNumber ${prNumber} conflicts with issue #${issueNumber} association PR ${fromIssue}`);
|
|
201
|
+
}
|
|
202
|
+
bound = fromIssue;
|
|
203
|
+
}
|
|
204
|
+
activation.ledger.bindTarget(bound);
|
|
205
|
+
activation.ledger.completeOperational(toolCallId);
|
|
206
|
+
return {
|
|
207
|
+
content: [{
|
|
208
|
+
type: "text",
|
|
209
|
+
text: `目标已绑定:${activation.repository.canonical}#${bound}`,
|
|
210
|
+
}],
|
|
211
|
+
details: {
|
|
212
|
+
repository: activation.repository.canonical,
|
|
213
|
+
prNumber: bound,
|
|
214
|
+
...(issueNumber === undefined ? {} : { issueNumber }),
|
|
215
|
+
},
|
|
216
|
+
};
|
|
185
217
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
text: JSON.stringify(contextView),
|
|
191
|
-
}],
|
|
192
|
-
// #641 the bounded projection is the only provider-visible face on every host
|
|
193
|
-
// (Grok/ACP relays tool details as MCP structuredContent); full bodies stay
|
|
194
|
-
// in the ledger volume and enter context only by explicit ak_collector_read.
|
|
195
|
-
details: contextView,
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
catch (error) {
|
|
199
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
|
-
});
|
|
203
|
-
pi.registerTool({
|
|
204
|
-
name: COLLECTOR_READ_TOOL,
|
|
205
|
-
label: "通进司开卷",
|
|
206
|
-
description: "按 evidenceId 开卷读取一条已观测材料的全量正文与指针;只在观察头部摘录不足以判读时调用。",
|
|
207
|
-
promptSnippet: "按指针开卷读材料",
|
|
208
|
-
parameters: readSchema,
|
|
209
|
-
async execute(toolCallId, params, _signal, _onUpdate, ctx) {
|
|
210
|
-
if (activation === undefined) {
|
|
211
|
-
throw new Error("通进司未激活");
|
|
212
|
-
}
|
|
213
|
-
try {
|
|
214
|
-
activation.ledger.beginOperational(COLLECTOR_READ_TOOL, toolCallId);
|
|
215
|
-
const record = activation.ledger.getEvidence(params.evidenceId);
|
|
216
|
-
if (record === undefined ||
|
|
217
|
-
(record.kind !== "review" && record.kind !== "issue_comment" && record.kind !== "review_comment" && record.kind !== "reaction") ||
|
|
218
|
-
typeof record.body !== "string") {
|
|
219
|
-
throw new CollectorUnknownEvidenceError(params.evidenceId);
|
|
218
|
+
catch (error) {
|
|
219
|
+
if (isCorrectableExecuteError(error))
|
|
220
|
+
throw error;
|
|
221
|
+
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
220
222
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
content: [{
|
|
257
|
-
type: "text",
|
|
258
|
-
text: `请求尝试已记录:request ${params.requestId}`,
|
|
259
|
-
}],
|
|
260
|
-
details,
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
catch (error) {
|
|
264
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
265
|
-
}
|
|
266
|
-
},
|
|
267
|
-
});
|
|
268
|
-
pi.registerTool({
|
|
269
|
-
name: COLLECTOR_WAIT_TOOL,
|
|
270
|
-
label: "通进司等待",
|
|
271
|
-
description: "再观察前等待;单次上限五分钟且不超剩余资格。",
|
|
272
|
-
promptSnippet: "资格截止前等待",
|
|
273
|
-
parameters: waitSchema,
|
|
274
|
-
async execute(toolCallId, params, signal, _onUpdate, ctx) {
|
|
275
|
-
if (activation === undefined) {
|
|
276
|
-
throw new Error("通进司未激活");
|
|
277
|
-
}
|
|
278
|
-
try {
|
|
279
|
-
activation.ledger.beginOperational(COLLECTOR_WAIT_TOOL, toolCallId);
|
|
280
|
-
const details = await activation.ledger.wait(params, activation.clock, signal);
|
|
281
|
-
activation.ledger.completeOperational(toolCallId);
|
|
282
|
-
return {
|
|
283
|
-
content: [{
|
|
284
|
-
type: "text",
|
|
285
|
-
text: `已等待 ${String(details.effectiveMs)}ms`,
|
|
286
|
-
}],
|
|
287
|
-
details,
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
catch (error) {
|
|
291
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
292
|
-
}
|
|
293
|
-
},
|
|
294
|
-
});
|
|
295
|
-
pi.registerTool({
|
|
296
|
-
name: COLLECTOR_OUTPUT_TOOL,
|
|
297
|
-
label: "通进司输出",
|
|
298
|
-
description: "观察完成后提交;回执由 runtime 组装。正常完工提交空对象 {}(如需报 finding,填 findings 指针数组);仅在基础设施真实失败时才可填 infrastructureFailure,无失败时必须省略该字段。",
|
|
299
|
-
promptSnippet: "提交通进司回执",
|
|
300
|
-
// #641 chain②: the seat owns the normal-completion decision. The probe
|
|
301
|
-
// runs the exact receipt assembly (validation only, no accept side
|
|
302
|
-
// effects); success ⇒ machine-verified normal completion ⇒ bounce.
|
|
303
|
-
bounceInfrastructureDeclaration(params) {
|
|
304
|
-
if (activation === undefined)
|
|
305
|
-
return undefined;
|
|
306
|
-
try {
|
|
307
|
-
buildCollectorReceipt(activation.ledger, params, activation.clock);
|
|
308
|
-
}
|
|
309
|
-
catch {
|
|
310
|
-
return undefined;
|
|
311
|
-
}
|
|
312
|
-
return new CollectorNormalCompletionDeclarationError();
|
|
313
|
-
},
|
|
314
|
-
parameters: outputSchema,
|
|
315
|
-
async execute(toolCallId, params, _signal, _onUpdate, ctx) {
|
|
316
|
-
if (activation === undefined) {
|
|
317
|
-
throw new Error("通进司未激活");
|
|
318
|
-
}
|
|
319
|
-
try {
|
|
320
|
-
activation.ledger.beginOperational(COLLECTOR_OUTPUT_TOOL, toolCallId);
|
|
321
|
-
const receipt = buildCollectorReceipt(activation.ledger, params, activation.clock);
|
|
322
|
-
activation.ledger.recordOutputCandidate();
|
|
323
|
-
const acceptedDetails = receipt;
|
|
324
|
-
return {
|
|
325
|
-
content: [{
|
|
326
|
-
type: "text",
|
|
327
|
-
text: COLLECTOR_ACCEPTED_TEXT,
|
|
328
|
-
}],
|
|
329
|
-
details: acceptedDetails,
|
|
330
|
-
terminate: true,
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
catch (error) {
|
|
334
|
-
// Output validation errors are model-visible rejections when non-fatal.
|
|
335
|
-
if (error instanceof Error &&
|
|
336
|
-
error.collectorFatal === true) {
|
|
223
|
+
finally {
|
|
224
|
+
try {
|
|
225
|
+
activation.ledger.completeOperational(toolCallId);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// already completed or not begun
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
pi.registerTool({
|
|
234
|
+
name: COLLECTOR_OBSERVE_TOOL,
|
|
235
|
+
label: "通进司观察",
|
|
236
|
+
description: "抓取配置目标的完整 GitHub PR 证据,存不可变快照入卷。正文在上下文中只给头部摘录加指针;需要头部之外的正文时,用 ak_collector_read 按 evidenceId 开卷;findings 的拆分与归类由你在交件时完成。目标未绑定前须先 ak_collector_bind_target。",
|
|
237
|
+
promptSnippet: "抓取配置目标 PR 证据",
|
|
238
|
+
parameters: observeSchema,
|
|
239
|
+
async execute(toolCallId, _params, signal, _onUpdate, ctx) {
|
|
240
|
+
const activation = getActivation();
|
|
241
|
+
if (activation === undefined)
|
|
242
|
+
throw new Error("通进司未激活");
|
|
243
|
+
try {
|
|
244
|
+
activation.ledger.beginOperational(COLLECTOR_OBSERVE_TOOL, toolCallId);
|
|
245
|
+
const { snapshot, contextView } = await activation.ledger.observe(activation.transport, activation.clock, signal);
|
|
246
|
+
activation.ledger.completeOperational(toolCallId);
|
|
247
|
+
return {
|
|
248
|
+
content: [{
|
|
249
|
+
type: "text",
|
|
250
|
+
text: JSON.stringify(contextView),
|
|
251
|
+
}],
|
|
252
|
+
details: contextView,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
if (isCorrectableExecuteError(error))
|
|
257
|
+
throw error;
|
|
337
258
|
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
338
259
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
pi.registerTool({
|
|
263
|
+
name: COLLECTOR_READ_TOOL,
|
|
264
|
+
label: "通进司开卷",
|
|
265
|
+
description: "按 evidenceId 开卷读取一条已观测材料的全量正文与指针;只在观察头部摘录不足以判读时调用。",
|
|
266
|
+
promptSnippet: "按指针开卷读材料",
|
|
267
|
+
parameters: readSchema,
|
|
268
|
+
async execute(toolCallId, params, _signal, _onUpdate, ctx) {
|
|
269
|
+
const activation = getActivation();
|
|
270
|
+
if (activation === undefined)
|
|
271
|
+
throw new Error("通进司未激活");
|
|
272
|
+
try {
|
|
273
|
+
activation.ledger.beginOperational(COLLECTOR_READ_TOOL, toolCallId);
|
|
274
|
+
const record = activation.ledger.getEvidence(params.evidenceId);
|
|
275
|
+
if (record === undefined ||
|
|
276
|
+
(record.kind !== "review" && record.kind !== "issue_comment" && record.kind !== "review_comment" && record.kind !== "reaction") ||
|
|
277
|
+
typeof record.body !== "string") {
|
|
278
|
+
throw new CollectorUnknownEvidenceError(params.evidenceId);
|
|
279
|
+
}
|
|
280
|
+
const material = projectEvidenceEntryView(record);
|
|
281
|
+
activation.ledger.completeOperational(toolCallId);
|
|
282
|
+
return {
|
|
283
|
+
content: [{
|
|
284
|
+
type: "text",
|
|
285
|
+
text: JSON.stringify(material),
|
|
286
|
+
}],
|
|
287
|
+
details: material,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
if (isCorrectableExecuteError(error))
|
|
292
|
+
throw error;
|
|
293
|
+
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
pi.registerTool({
|
|
298
|
+
name: COLLECTOR_REQUEST_TOOL,
|
|
299
|
+
label: "通进司请求",
|
|
300
|
+
description: "按配置请求体与关联标记,在所引最新快照 HEAD 发一次请求。",
|
|
301
|
+
promptSnippet: "按配置发一次请求",
|
|
302
|
+
parameters: requestSchema,
|
|
303
|
+
async execute(toolCallId, params, signal, _onUpdate, ctx) {
|
|
304
|
+
const activation = getActivation();
|
|
305
|
+
if (activation === undefined)
|
|
306
|
+
throw new Error("通进司未激活");
|
|
307
|
+
try {
|
|
308
|
+
activation.ledger.beginOperational(COLLECTOR_REQUEST_TOOL, toolCallId);
|
|
309
|
+
const details = await activation.ledger.request(params, activation.transport, activation.clock, signal);
|
|
310
|
+
activation.ledger.completeOperational(toolCallId);
|
|
311
|
+
return {
|
|
312
|
+
content: [{
|
|
313
|
+
type: "text",
|
|
314
|
+
text: `请求尝试已记录:request ${params.requestId}`,
|
|
315
|
+
}],
|
|
316
|
+
details,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
if (isCorrectableExecuteError(error))
|
|
321
|
+
throw error;
|
|
322
|
+
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
pi.registerTool({
|
|
327
|
+
name: COLLECTOR_WAIT_TOOL,
|
|
328
|
+
label: "通进司等待",
|
|
329
|
+
description: "再观察前等待;单次上限五分钟且不超剩余资格。",
|
|
330
|
+
promptSnippet: "资格截止前等待",
|
|
331
|
+
parameters: waitSchema,
|
|
332
|
+
async execute(toolCallId, params, signal, _onUpdate, ctx) {
|
|
333
|
+
const activation = getActivation();
|
|
334
|
+
if (activation === undefined)
|
|
335
|
+
throw new Error("通进司未激活");
|
|
336
|
+
try {
|
|
337
|
+
activation.ledger.beginOperational(COLLECTOR_WAIT_TOOL, toolCallId);
|
|
338
|
+
const details = await activation.ledger.wait(params, activation.clock, signal);
|
|
339
|
+
activation.ledger.completeOperational(toolCallId);
|
|
340
|
+
return {
|
|
341
|
+
content: [{
|
|
342
|
+
type: "text",
|
|
343
|
+
text: `已等待 ${String(details.effectiveMs)}ms`,
|
|
344
|
+
}],
|
|
345
|
+
details,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
});
|
|
353
|
+
pi.registerTool({
|
|
354
|
+
name: COLLECTOR_OUTPUT_TOOL,
|
|
355
|
+
label: "通进司输出",
|
|
356
|
+
description: "观察完成后提交;回执由 runtime 组装。正常完工提交空对象 {}(如需报 finding,填 findings 指针数组);仅在基础设施真实失败时才可填 infrastructureFailure,无失败时必须省略该字段。",
|
|
357
|
+
promptSnippet: "提交通进司回执",
|
|
358
|
+
bounceInfrastructureDeclaration(params) {
|
|
359
|
+
const activation = getActivation();
|
|
360
|
+
if (activation === undefined)
|
|
361
|
+
return undefined;
|
|
362
|
+
try {
|
|
363
|
+
buildCollectorReceipt(activation.ledger, params, activation.clock);
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
return undefined;
|
|
367
|
+
}
|
|
368
|
+
return new CollectorNormalCompletionDeclarationError();
|
|
369
|
+
},
|
|
370
|
+
parameters: outputSchema,
|
|
371
|
+
async execute(toolCallId, params, _signal, _onUpdate, ctx) {
|
|
372
|
+
const activation = getActivation();
|
|
373
|
+
if (activation === undefined)
|
|
374
|
+
throw new Error("通进司未激活");
|
|
375
|
+
try {
|
|
376
|
+
activation.ledger.beginOperational(COLLECTOR_OUTPUT_TOOL, toolCallId);
|
|
377
|
+
const receipt = buildCollectorReceipt(activation.ledger, params, activation.clock);
|
|
378
|
+
activation.ledger.recordOutputCandidate();
|
|
379
|
+
return {
|
|
380
|
+
content: [{
|
|
381
|
+
type: "text",
|
|
382
|
+
text: COLLECTOR_ACCEPTED_TEXT,
|
|
383
|
+
}],
|
|
384
|
+
details: receipt,
|
|
385
|
+
terminate: true,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
if (error instanceof Error &&
|
|
390
|
+
error.collectorFatal === true) {
|
|
391
|
+
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
392
|
+
}
|
|
393
|
+
throw error;
|
|
394
|
+
}
|
|
395
|
+
finally {
|
|
396
|
+
activation.ledger.completeOperational(toolCallId);
|
|
397
|
+
}
|
|
398
|
+
},
|
|
382
399
|
});
|
|
383
|
-
if (ambientCommands.length > 0) {
|
|
384
|
-
throw new Error(`Collector detected ambient instruction commands: ${ambientCommands.map((c) => c.name).join(", ")}`);
|
|
385
|
-
}
|
|
386
|
-
// Fail closed if a required name is already occupied before Collector registers.
|
|
387
|
-
const preExisting = pi.getAllTools();
|
|
388
|
-
for (const required of COLLECTOR_REQUIRED_TOOLS) {
|
|
389
|
-
const prior = preExisting.filter((tool) => tool.name === required);
|
|
390
|
-
if (prior.length > 0) {
|
|
391
|
-
throw new Error(`Collector required tool name collision: ${required}`);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
registerTools();
|
|
395
|
-
const allTools = pi.getAllTools();
|
|
396
|
-
for (const required of COLLECTOR_REQUIRED_TOOLS) {
|
|
397
|
-
const matches = allTools.filter((tool) => tool.name === required);
|
|
398
|
-
if (matches.length === 0) {
|
|
399
|
-
throw new Error(`Collector required tool missing: ${required}`);
|
|
400
|
-
}
|
|
401
|
-
if (matches.length > 1) {
|
|
402
|
-
throw new Error(`Collector required tool name collision: ${required}`);
|
|
403
|
-
}
|
|
404
|
-
const tool = matches[0];
|
|
405
|
-
if (dependencies.packageExtensionPath !== undefined &&
|
|
406
|
-
tool.sourceInfo?.path !== undefined &&
|
|
407
|
-
tool.sourceInfo.path !== dependencies.packageExtensionPath &&
|
|
408
|
-
!tool.sourceInfo.path.includes("role-runtime")) {
|
|
409
|
-
throw new Error(`Collector required tool ${required} is overridden by ${tool.sourceInfo.path}`);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
pi.setActiveTools([...COLLECTOR_REQUIRED_TOOLS]);
|
|
413
|
-
const active = new Set(pi.getActiveTools());
|
|
414
|
-
for (const required of COLLECTOR_REQUIRED_TOOLS) {
|
|
415
|
-
if (!active.has(required)) {
|
|
416
|
-
throw new Error(`Collector failed to activate required tool ${required}`);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
for (const name of active) {
|
|
420
|
-
if (!COLLECTOR_REQUIRED_TOOLS.includes(name)) {
|
|
421
|
-
throw new Error(`Collector active tool surface includes unexpected ${name}`);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
const clock = dependencies.createClock?.() ?? createSystemCollectorClock();
|
|
425
|
-
const transport = dependencies.createTransport();
|
|
426
|
-
const ledger = dependencies.createLedger({ repository, prNumber, manifest }, clock, ctx);
|
|
427
|
-
if (ledger.activationRecorded) {
|
|
428
|
-
firstDispatchDone = true;
|
|
429
|
-
}
|
|
430
|
-
activation = {
|
|
431
|
-
soul,
|
|
432
|
-
repository,
|
|
433
|
-
prNumber,
|
|
434
|
-
manifest,
|
|
435
|
-
ledger,
|
|
436
|
-
transport,
|
|
437
|
-
clock,
|
|
438
|
-
};
|
|
439
400
|
},
|
|
440
401
|
};
|
|
441
402
|
}
|