@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/src/collector-role.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
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.
|
|
7
|
+
*/
|
|
1
8
|
import type { RoleHost, HostContext, HostToolResult } from "./host-contracts.ts";
|
|
2
9
|
import type { Static } from "typebox";
|
|
3
10
|
|
|
4
11
|
import {
|
|
5
|
-
COLLECTOR_FIXED_KICKOFF,
|
|
6
12
|
emptyCollectorManifest,
|
|
7
13
|
loadCollectorManifest,
|
|
8
14
|
parseCollectorPrNumber,
|
|
@@ -14,8 +20,13 @@ import {
|
|
|
14
20
|
createSystemCollectorClock,
|
|
15
21
|
type CollectorClock,
|
|
16
22
|
} from "./collector-evidence.ts";
|
|
17
|
-
import type { CollectorGitHubTransport } from "./collector-github.ts";
|
|
18
23
|
import {
|
|
24
|
+
createGhApiRunner,
|
|
25
|
+
listPullRequestNumbersByTicket,
|
|
26
|
+
type CollectorGitHubTransport,
|
|
27
|
+
} from "./collector-github.ts";
|
|
28
|
+
import {
|
|
29
|
+
COLLECTOR_BIND_TARGET_TOOL,
|
|
19
30
|
COLLECTOR_OBSERVE_TOOL,
|
|
20
31
|
COLLECTOR_OUTPUT_TOOL,
|
|
21
32
|
COLLECTOR_READ_TOOL,
|
|
@@ -30,6 +41,7 @@ import {
|
|
|
30
41
|
type CollectorReceipt,
|
|
31
42
|
} from "./collector-receipt.ts";
|
|
32
43
|
import {
|
|
44
|
+
collectorBindTargetArgsSchema,
|
|
33
45
|
collectorObserveArgsSchema,
|
|
34
46
|
collectorOutputArgsSchema,
|
|
35
47
|
collectorReadArgsSchema,
|
|
@@ -54,10 +66,16 @@ export class CollectorNormalCompletionDeclarationError extends CorrectableSubmis
|
|
|
54
66
|
}
|
|
55
67
|
}
|
|
56
68
|
|
|
69
|
+
/** #676 A: role-chosen target could not bind uniquely — correctable, not host failure. */
|
|
70
|
+
export class CollectorTargetBindError extends CorrectableSubmissionError {
|
|
71
|
+
constructor(message: string) {
|
|
72
|
+
super(message);
|
|
73
|
+
this.name = "CollectorTargetBindError";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
57
77
|
export {
|
|
58
|
-
|
|
59
|
-
} from "./collector-config.ts";
|
|
60
|
-
export {
|
|
78
|
+
COLLECTOR_BIND_TARGET_TOOL,
|
|
61
79
|
COLLECTOR_OBSERVE_TOOL,
|
|
62
80
|
COLLECTOR_OUTPUT_TOOL,
|
|
63
81
|
COLLECTOR_READ_TOOL,
|
|
@@ -66,6 +84,7 @@ export {
|
|
|
66
84
|
};
|
|
67
85
|
|
|
68
86
|
export const COLLECTOR_REQUIRED_TOOLS = [
|
|
87
|
+
COLLECTOR_BIND_TARGET_TOOL,
|
|
69
88
|
COLLECTOR_OBSERVE_TOOL,
|
|
70
89
|
COLLECTOR_READ_TOOL,
|
|
71
90
|
COLLECTOR_REQUEST_TOOL,
|
|
@@ -73,15 +92,45 @@ export const COLLECTOR_REQUIRED_TOOLS = [
|
|
|
73
92
|
COLLECTOR_OUTPUT_TOOL,
|
|
74
93
|
] as const;
|
|
75
94
|
|
|
95
|
+
/** Envelope-owned transport flags (ADR 0018 / #676 E). */
|
|
96
|
+
export const COLLECTOR_TRANSPORT_FLAGS = Object.freeze([
|
|
97
|
+
Object.freeze({
|
|
98
|
+
name: "ak-collector-repo",
|
|
99
|
+
definition: Object.freeze({
|
|
100
|
+
description:
|
|
101
|
+
"GitHub owner/repo target for Collector (github.com only; conservative ASCII grammar).",
|
|
102
|
+
type: "string" as const,
|
|
103
|
+
}),
|
|
104
|
+
}),
|
|
105
|
+
Object.freeze({
|
|
106
|
+
name: "ak-collector-pr",
|
|
107
|
+
definition: Object.freeze({
|
|
108
|
+
description:
|
|
109
|
+
"Optional positive safe-integer pull request number for Collector. Omit when the role will bind from task materials.",
|
|
110
|
+
type: "string" as const,
|
|
111
|
+
}),
|
|
112
|
+
}),
|
|
113
|
+
Object.freeze({
|
|
114
|
+
name: "ak-collector-request-manifest",
|
|
115
|
+
definition: Object.freeze({
|
|
116
|
+
description:
|
|
117
|
+
"Path to the Collector v1 request manifest JSON file.",
|
|
118
|
+
type: "string" as const,
|
|
119
|
+
}),
|
|
120
|
+
}),
|
|
121
|
+
] as const);
|
|
122
|
+
|
|
76
123
|
const observeSchema = collectorObserveArgsSchema;
|
|
77
124
|
const readSchema = collectorReadArgsSchema;
|
|
78
125
|
const requestSchema = collectorRequestArgsSchema;
|
|
79
126
|
const waitSchema = collectorWaitArgsSchema;
|
|
127
|
+
const bindSchema = collectorBindTargetArgsSchema;
|
|
80
128
|
const outputSchema = collectorOutputArgsSchema;
|
|
81
129
|
|
|
82
130
|
type RequestParams = Static<typeof requestSchema>;
|
|
83
131
|
type ReadParams = Static<typeof readSchema>;
|
|
84
132
|
type WaitParams = Static<typeof waitSchema>;
|
|
133
|
+
type BindParams = Static<typeof bindSchema>;
|
|
85
134
|
type OutputParams = Static<typeof outputSchema>;
|
|
86
135
|
|
|
87
136
|
export type CollectorRoleDependencies = {
|
|
@@ -89,167 +138,126 @@ export type CollectorRoleDependencies = {
|
|
|
89
138
|
createTransport(): CollectorGitHubTransport;
|
|
90
139
|
createClock?(): CollectorClock;
|
|
91
140
|
createLedger(config: CollectorConfigState, clock: CollectorClock, ctx: HostContext): CollectorLedger;
|
|
92
|
-
packageExtensionPath?: string;
|
|
93
141
|
};
|
|
94
142
|
|
|
95
143
|
export type CollectorRoleHostActions = {
|
|
96
144
|
failInfrastructure(error: unknown, ctx: HostContext, toolCallId?: string): never;
|
|
97
145
|
};
|
|
98
146
|
|
|
99
|
-
type CollectorActivation = {
|
|
147
|
+
export type CollectorActivation = {
|
|
100
148
|
soul: string;
|
|
101
149
|
repository: CollectorRepository;
|
|
102
|
-
prNumber: number;
|
|
103
150
|
manifest: CollectorManifest;
|
|
104
151
|
ledger: CollectorLedger;
|
|
105
152
|
transport: CollectorGitHubTransport;
|
|
106
153
|
clock: CollectorClock;
|
|
107
154
|
};
|
|
108
155
|
|
|
109
|
-
/** Sole-final at the output execution seam (same shape as other terminating roles). */
|
|
110
|
-
|
|
111
156
|
function buildMethodContext(activation: CollectorActivation): string {
|
|
157
|
+
const pr = activation.ledger.config.prNumber;
|
|
112
158
|
return [
|
|
113
159
|
"<collector_method>",
|
|
114
160
|
`host: github.com`,
|
|
115
161
|
`repository: ${activation.repository.canonical}`,
|
|
116
|
-
`prNumber: ${
|
|
162
|
+
`prNumber: ${pr === undefined ? "unbound — call ak_collector_bind_target with the role-decided issue/PR before observe" : String(pr)}`,
|
|
117
163
|
`requests: ${JSON.stringify(activation.manifest.requests.map((request) => ({ id: request.id })))}`,
|
|
118
164
|
"</collector_method>",
|
|
119
165
|
].join("\n");
|
|
120
166
|
}
|
|
121
167
|
|
|
168
|
+
function parsePositiveTicket(raw: unknown, label: string): number | undefined {
|
|
169
|
+
if (raw === undefined || raw === null) return undefined;
|
|
170
|
+
if (typeof raw === "number" && Number.isSafeInteger(raw) && raw >= 1) return raw;
|
|
171
|
+
if (typeof raw === "string" && /^[1-9]\d*$/.test(raw.trim())) return Number(raw.trim());
|
|
172
|
+
throw new CollectorTargetBindError(`ak_collector_bind_target ${label} must be a positive safe integer`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Shared envelope installs business tools and material callback after lifecycle gates.
|
|
177
|
+
* Role module does not self-hook events, setActiveTools, or mode/fork checks (ADR 0018 / #676 E).
|
|
178
|
+
*/
|
|
122
179
|
export function createCollectorRoleRuntime(
|
|
123
180
|
pi: RoleHost,
|
|
124
181
|
dependencies: CollectorRoleDependencies,
|
|
125
182
|
hostActions: CollectorRoleHostActions,
|
|
126
183
|
): {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
):
|
|
184
|
+
/** Business activation only: soul, flags→config, ledger. Envelope owns lifecycle gates. */
|
|
185
|
+
activate(ctx: HostContext): Promise<CollectorActivation>;
|
|
186
|
+
/** Business material assembly — envelope hangs this on before_agent_start. */
|
|
187
|
+
assembleMaterials(activation: CollectorActivation, baseSystemPrompt: string): string;
|
|
188
|
+
/** Business operational bookkeeping callbacks for shared tool_call / tool_result seams. */
|
|
189
|
+
onToolCall(
|
|
190
|
+
activation: CollectorActivation,
|
|
191
|
+
event: { toolName: string; toolCallId: string },
|
|
192
|
+
): { block: true; reason: string } | undefined;
|
|
193
|
+
onToolResult(activation: CollectorActivation, event: { toolCallId: string }): void;
|
|
194
|
+
/** Register business tools once. Envelope owns uniqueness + setActiveTools. */
|
|
195
|
+
registerBusinessTools(getActivation: () => CollectorActivation | undefined): void;
|
|
131
196
|
} {
|
|
132
|
-
let activation: CollectorActivation | undefined;
|
|
133
|
-
let inputCount = 0;
|
|
134
|
-
let lifecycleRegistered = false;
|
|
135
197
|
let toolsRegistered = false;
|
|
136
|
-
let firstDispatchDone = false;
|
|
137
|
-
|
|
138
|
-
pi.registerFlag("ak-collector-repo", {
|
|
139
|
-
description:
|
|
140
|
-
"GitHub owner/repo target for Collector (github.com only; conservative ASCII grammar). Collector forbids every Skill, including command-only Skills.",
|
|
141
|
-
type: "string",
|
|
142
|
-
});
|
|
143
|
-
pi.registerFlag("ak-collector-pr", {
|
|
144
|
-
description:
|
|
145
|
-
"Positive safe-integer pull request number for Collector. Supported profile: --no-skills, --no-extensions with only the explicit Collector package extension, no prompt templates/context files, one print/JSON prompt",
|
|
146
|
-
type: "string",
|
|
147
|
-
});
|
|
148
|
-
pi.registerFlag("ak-collector-request-manifest", {
|
|
149
|
-
description:
|
|
150
|
-
"Path to the Collector v1 request manifest JSON file. In Pi latest, late hostile sibling-extension Skill injection is unsupported and fail-closed when detected; drift prevention only, not a security boundary or provider-zero guarantee",
|
|
151
|
-
type: "string",
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
const ensureLifecycle = (): void => {
|
|
155
|
-
if (lifecycleRegistered) return;
|
|
156
|
-
lifecycleRegistered = true;
|
|
157
|
-
|
|
158
|
-
pi.on("input", (event, ctx) => {
|
|
159
|
-
if (activation === undefined) {
|
|
160
|
-
// role not active
|
|
161
|
-
return { action: "continue" as const };
|
|
162
|
-
}
|
|
163
|
-
if (inputCount >= 1) {
|
|
164
|
-
activation.ledger.latchFatal("通进司已拒绝后续输入");
|
|
165
|
-
if (process.exitCode === undefined || process.exitCode === 0) {
|
|
166
|
-
process.exitCode = 1;
|
|
167
|
-
}
|
|
168
|
-
console.error("Collector rejected later input");
|
|
169
|
-
return { action: "handled" as const };
|
|
170
|
-
}
|
|
171
|
-
inputCount += 1;
|
|
172
|
-
return {
|
|
173
|
-
action: "transform" as const,
|
|
174
|
-
text: COLLECTOR_FIXED_KICKOFF,
|
|
175
|
-
images: [],
|
|
176
|
-
};
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
pi.on("before_agent_start", (event, ctx) => {
|
|
180
|
-
if (activation === undefined) return;
|
|
181
198
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
hostActions.failInfrastructure(
|
|
194
|
-
activation.ledger.latchFatal(
|
|
195
|
-
"通进司检测到系统提示中的环境 context files",
|
|
196
|
-
),
|
|
197
|
-
ctx,
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
if (
|
|
201
|
-
typeof options.appendSystemPrompt === "string" &&
|
|
202
|
-
options.appendSystemPrompt.trim().length > 0
|
|
203
|
-
) {
|
|
204
|
-
hostActions.failInfrastructure(
|
|
205
|
-
activation.ledger.latchFatal(
|
|
206
|
-
"通进司检测到 appendSystemPrompt 漂移",
|
|
207
|
-
),
|
|
208
|
-
ctx,
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (event.prompt !== COLLECTOR_FIXED_KICKOFF) {
|
|
213
|
-
hostActions.failInfrastructure(
|
|
214
|
-
activation.ledger.latchFatal(
|
|
215
|
-
"通进司首条提示不是固定开场令",
|
|
216
|
-
),
|
|
217
|
-
ctx,
|
|
218
|
-
);
|
|
199
|
+
return {
|
|
200
|
+
async activate(ctx) {
|
|
201
|
+
const soul = (await dependencies.loadSoul()).trim();
|
|
202
|
+
if (soul.length === 0) throw new Error("Collector soul is empty");
|
|
203
|
+
|
|
204
|
+
// Flags registered by the shared envelope (COLLECTOR_TRANSPORT_FLAGS).
|
|
205
|
+
const repoFlag = pi.getFlag("ak-collector-repo");
|
|
206
|
+
const prFlag = pi.getFlag("ak-collector-pr");
|
|
207
|
+
const requestManifestFlag = pi.getFlag("ak-collector-request-manifest");
|
|
208
|
+
if (typeof repoFlag !== "string" || repoFlag.trim().length === 0) {
|
|
209
|
+
throw new Error("Collector requires --ak-collector-repo");
|
|
219
210
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
211
|
+
const repository = parseCollectorRepository(repoFlag);
|
|
212
|
+
// #676 A: PR may be unbound — role binds via ak_collector_bind_target from materials.
|
|
213
|
+
let prNumber: number | undefined;
|
|
214
|
+
if (typeof prFlag === "string" && prFlag.trim().length > 0) {
|
|
215
|
+
prNumber = parseCollectorPrNumber(prFlag);
|
|
216
|
+
} else if (typeof prFlag === "number") {
|
|
217
|
+
prNumber = parseCollectorPrNumber(prFlag);
|
|
224
218
|
}
|
|
219
|
+
const manifest = typeof requestManifestFlag === "string" && requestManifestFlag.trim().length > 0
|
|
220
|
+
? await loadCollectorManifest(requestManifestFlag)
|
|
221
|
+
: emptyCollectorManifest();
|
|
222
|
+
|
|
223
|
+
const clock = dependencies.createClock?.() ?? createSystemCollectorClock();
|
|
224
|
+
const transport = dependencies.createTransport();
|
|
225
|
+
const ledger = dependencies.createLedger(
|
|
226
|
+
{ repository, prNumber, manifest },
|
|
227
|
+
clock,
|
|
228
|
+
ctx,
|
|
229
|
+
);
|
|
225
230
|
|
|
226
231
|
return {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
"",
|
|
234
|
-
buildMethodContext(activation),
|
|
235
|
-
].join("\n"),
|
|
232
|
+
soul,
|
|
233
|
+
repository,
|
|
234
|
+
manifest,
|
|
235
|
+
ledger,
|
|
236
|
+
transport,
|
|
237
|
+
clock,
|
|
236
238
|
};
|
|
237
|
-
}
|
|
239
|
+
},
|
|
238
240
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
+
assembleMaterials(activation, baseSystemPrompt) {
|
|
242
|
+
return [
|
|
243
|
+
baseSystemPrompt,
|
|
244
|
+
"",
|
|
245
|
+
"<collector_soul>",
|
|
246
|
+
activation.soul,
|
|
247
|
+
"</collector_soul>",
|
|
248
|
+
"",
|
|
249
|
+
buildMethodContext(activation),
|
|
250
|
+
].join("\n");
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
onToolCall(activation, event) {
|
|
254
|
+
// Business ledger facts only — allowed-tool / mode gates live on the envelope.
|
|
241
255
|
if (activation.ledger.fatal) {
|
|
242
256
|
return {
|
|
243
257
|
block: true,
|
|
244
258
|
reason: activation.ledger.fatalReason ?? "通进司致命状态",
|
|
245
259
|
};
|
|
246
260
|
}
|
|
247
|
-
if (!(COLLECTOR_REQUIRED_TOOLS as readonly string[]).includes(event.toolName)) {
|
|
248
|
-
return {
|
|
249
|
-
block: true,
|
|
250
|
-
reason: `通进司禁用工具 ${event.toolName}`,
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
261
|
if (event.toolName === COLLECTOR_OUTPUT_TOOL) {
|
|
254
262
|
activation.ledger.beginOperational(COLLECTOR_OUTPUT_TOOL, event.toolCallId);
|
|
255
263
|
}
|
|
@@ -263,346 +271,264 @@ export function createCollectorRoleRuntime(
|
|
|
263
271
|
};
|
|
264
272
|
}
|
|
265
273
|
return undefined;
|
|
266
|
-
}
|
|
274
|
+
},
|
|
267
275
|
|
|
268
|
-
|
|
269
|
-
if (activation === undefined) return;
|
|
276
|
+
onToolResult(activation, event) {
|
|
270
277
|
activation.ledger.completeOperational(event.toolCallId);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
pi.on("session_shutdown", () => {
|
|
274
|
-
if (activation === undefined) return;
|
|
275
|
-
if (activation.ledger.fatal) {
|
|
276
|
-
if (process.exitCode === undefined || process.exitCode === 0) {
|
|
277
|
-
process.exitCode = 1;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
const registerTools = (): void => {
|
|
284
|
-
if (toolsRegistered) return;
|
|
285
|
-
toolsRegistered = true;
|
|
286
|
-
|
|
287
|
-
pi.registerTool({
|
|
288
|
-
name: COLLECTOR_OBSERVE_TOOL,
|
|
289
|
-
label: "通进司观察",
|
|
290
|
-
description: "抓取配置目标的完整 GitHub PR 证据,存不可变快照入卷。正文在上下文中只给头部摘录加指针;需要头部之外的正文时,用 ak_collector_read 按 evidenceId 开卷;findings 的拆分与归类由你在交件时完成。",
|
|
291
|
-
promptSnippet: "抓取配置目标 PR 证据",
|
|
292
|
-
parameters: observeSchema,
|
|
293
|
-
async execute(toolCallId: string, _params: unknown, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
294
|
-
if (activation === undefined) {
|
|
295
|
-
throw new Error("通进司未激活");
|
|
296
|
-
}
|
|
297
|
-
try {
|
|
298
|
-
activation.ledger.beginOperational(COLLECTOR_OBSERVE_TOOL, toolCallId);
|
|
299
|
-
const { snapshot, contextView } = await activation.ledger.observe(
|
|
300
|
-
activation.transport,
|
|
301
|
-
activation.clock,
|
|
302
|
-
signal,
|
|
303
|
-
);
|
|
304
|
-
activation.ledger.completeOperational(toolCallId);
|
|
305
|
-
if (snapshot.prState !== "OPEN") {
|
|
306
|
-
// Non-OPEN observed as latest complete snapshot is target-state failure at output,
|
|
307
|
-
// but observe itself may return the fact. If this is a final observation, still return.
|
|
308
|
-
}
|
|
309
|
-
return {
|
|
310
|
-
content: [{
|
|
311
|
-
type: "text" as const,
|
|
312
|
-
// #641 chain①: model context carries bounded body heads + pointers only.
|
|
313
|
-
text: JSON.stringify(contextView),
|
|
314
|
-
}],
|
|
315
|
-
// #641 the bounded projection is the only provider-visible face on every host
|
|
316
|
-
// (Grok/ACP relays tool details as MCP structuredContent); full bodies stay
|
|
317
|
-
// in the ledger volume and enter context only by explicit ak_collector_read.
|
|
318
|
-
details: contextView,
|
|
319
|
-
};
|
|
320
|
-
} catch (error) {
|
|
321
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
322
|
-
}
|
|
323
|
-
},
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
pi.registerTool({
|
|
327
|
-
name: COLLECTOR_READ_TOOL,
|
|
328
|
-
label: "通进司开卷",
|
|
329
|
-
description: "按 evidenceId 开卷读取一条已观测材料的全量正文与指针;只在观察头部摘录不足以判读时调用。",
|
|
330
|
-
promptSnippet: "按指针开卷读材料",
|
|
331
|
-
parameters: readSchema,
|
|
332
|
-
async execute(toolCallId: string, params: ReadParams, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
333
|
-
if (activation === undefined) {
|
|
334
|
-
throw new Error("通进司未激活");
|
|
335
|
-
}
|
|
336
|
-
try {
|
|
337
|
-
activation.ledger.beginOperational(COLLECTOR_READ_TOOL, toolCallId);
|
|
338
|
-
const record = activation.ledger.getEvidence(params.evidenceId);
|
|
339
|
-
if (
|
|
340
|
-
record === undefined ||
|
|
341
|
-
(record.kind !== "review" && record.kind !== "issue_comment" && record.kind !== "review_comment" && record.kind !== "reaction") ||
|
|
342
|
-
typeof record.body !== "string"
|
|
343
|
-
) {
|
|
344
|
-
throw new CollectorUnknownEvidenceError(params.evidenceId);
|
|
345
|
-
}
|
|
346
|
-
const material = projectEvidenceEntryView(record);
|
|
347
|
-
activation.ledger.completeOperational(toolCallId);
|
|
348
|
-
return {
|
|
349
|
-
content: [{
|
|
350
|
-
type: "text" as const,
|
|
351
|
-
// #641 chain①: full bodies enter provider context only by explicit pointer.
|
|
352
|
-
text: JSON.stringify(material),
|
|
353
|
-
}],
|
|
354
|
-
details: material,
|
|
355
|
-
};
|
|
356
|
-
} catch (error) {
|
|
357
|
-
if (isCorrectableExecuteError(error)) throw error;
|
|
358
|
-
// typed toolCallId books the shared infrastructure fact so settlement can
|
|
359
|
-
// tell a read tool's real host failure from a correctable pointer bounce.
|
|
360
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
361
|
-
}
|
|
362
|
-
},
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
pi.registerTool({
|
|
366
|
-
name: COLLECTOR_REQUEST_TOOL,
|
|
367
|
-
label: "通进司请求",
|
|
368
|
-
description: "按配置请求体与关联标记,在所引最新快照 HEAD 发一次请求。",
|
|
369
|
-
promptSnippet: "按配置发一次请求",
|
|
370
|
-
parameters: requestSchema,
|
|
371
|
-
async execute(toolCallId: string, params: RequestParams, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
372
|
-
if (activation === undefined) {
|
|
373
|
-
throw new Error("通进司未激活");
|
|
374
|
-
}
|
|
375
|
-
try {
|
|
376
|
-
activation.ledger.beginOperational(COLLECTOR_REQUEST_TOOL, toolCallId);
|
|
377
|
-
const details = await activation.ledger.request(
|
|
378
|
-
params,
|
|
379
|
-
activation.transport,
|
|
380
|
-
activation.clock,
|
|
381
|
-
signal,
|
|
382
|
-
);
|
|
383
|
-
activation.ledger.completeOperational(toolCallId);
|
|
384
|
-
return {
|
|
385
|
-
content: [{
|
|
386
|
-
type: "text" as const,
|
|
387
|
-
text: `请求尝试已记录:request ${params.requestId}`,
|
|
388
|
-
}],
|
|
389
|
-
details,
|
|
390
|
-
};
|
|
391
|
-
} catch (error) {
|
|
392
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
393
|
-
}
|
|
394
|
-
},
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
pi.registerTool({
|
|
398
|
-
name: COLLECTOR_WAIT_TOOL,
|
|
399
|
-
label: "通进司等待",
|
|
400
|
-
description: "再观察前等待;单次上限五分钟且不超剩余资格。",
|
|
401
|
-
promptSnippet: "资格截止前等待",
|
|
402
|
-
parameters: waitSchema,
|
|
403
|
-
async execute(toolCallId: string, params: WaitParams, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
404
|
-
if (activation === undefined) {
|
|
405
|
-
throw new Error("通进司未激活");
|
|
406
|
-
}
|
|
407
|
-
try {
|
|
408
|
-
activation.ledger.beginOperational(COLLECTOR_WAIT_TOOL, toolCallId);
|
|
409
|
-
const details = await activation.ledger.wait(
|
|
410
|
-
params,
|
|
411
|
-
activation.clock,
|
|
412
|
-
signal,
|
|
413
|
-
);
|
|
414
|
-
activation.ledger.completeOperational(toolCallId);
|
|
415
|
-
return {
|
|
416
|
-
content: [{
|
|
417
|
-
type: "text" as const,
|
|
418
|
-
text: `已等待 ${String((details as { effectiveMs: number }).effectiveMs)}ms`,
|
|
419
|
-
}],
|
|
420
|
-
details,
|
|
421
|
-
};
|
|
422
|
-
} catch (error) {
|
|
423
|
-
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
424
|
-
}
|
|
425
|
-
},
|
|
426
|
-
});
|
|
278
|
+
},
|
|
427
279
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
280
|
+
registerBusinessTools(getActivation) {
|
|
281
|
+
if (toolsRegistered) return;
|
|
282
|
+
toolsRegistered = true;
|
|
283
|
+
|
|
284
|
+
pi.registerTool({
|
|
285
|
+
name: COLLECTOR_BIND_TARGET_TOOL,
|
|
286
|
+
label: "通进司认票绑定",
|
|
287
|
+
description:
|
|
288
|
+
"角色判定任务材料后绑定本仓唯一 PR 目标。可提交 prNumber 或 issueNumber(线上关联唯一 PR);显式 --pr 已绑定时无需再调。多义或无法确定时会正确驳回,要求调用方明确 --pr。",
|
|
289
|
+
promptSnippet: "绑定角色判定的 issue/PR 目标",
|
|
290
|
+
parameters: bindSchema,
|
|
291
|
+
async execute(toolCallId: string, params: BindParams, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
292
|
+
const activation = getActivation();
|
|
293
|
+
if (activation === undefined) throw new Error("通进司未激活");
|
|
294
|
+
try {
|
|
295
|
+
activation.ledger.beginOperational(COLLECTOR_BIND_TARGET_TOOL, toolCallId);
|
|
296
|
+
const prNumber = parsePositiveTicket(params.prNumber, "prNumber");
|
|
297
|
+
const issueNumber = parsePositiveTicket(params.issueNumber, "issueNumber");
|
|
298
|
+
if (prNumber === undefined && issueNumber === undefined) {
|
|
299
|
+
throw new CollectorTargetBindError(
|
|
300
|
+
"ak_collector_bind_target requires role-decided prNumber and/or issueNumber",
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
let bound = prNumber;
|
|
305
|
+
if (issueNumber !== undefined) {
|
|
306
|
+
const associated = await listPullRequestNumbersByTicket(createGhApiRunner(), {
|
|
307
|
+
owner: activation.repository.owner,
|
|
308
|
+
repo: activation.repository.repo,
|
|
309
|
+
ticketNumber: issueNumber,
|
|
310
|
+
});
|
|
311
|
+
if (associated.length === 0) {
|
|
312
|
+
throw new CollectorTargetBindError(
|
|
313
|
+
`no PR associated with issue #${issueNumber} in ${activation.repository.canonical}; pass an explicit --pr or a different issueNumber`,
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
if (associated.length > 1) {
|
|
317
|
+
throw new CollectorTargetBindError(
|
|
318
|
+
`multiple PRs associated with issue #${issueNumber}: ${associated.join(", ")}; pass an explicit prNumber or --pr`,
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
const fromIssue = associated[0]!;
|
|
322
|
+
if (prNumber !== undefined && prNumber !== fromIssue) {
|
|
323
|
+
throw new CollectorTargetBindError(
|
|
324
|
+
`prNumber ${prNumber} conflicts with issue #${issueNumber} association PR ${fromIssue}`,
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
bound = fromIssue;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
activation.ledger.bindTarget(bound!);
|
|
331
|
+
activation.ledger.completeOperational(toolCallId);
|
|
332
|
+
return {
|
|
333
|
+
content: [{
|
|
334
|
+
type: "text" as const,
|
|
335
|
+
text: `目标已绑定:${activation.repository.canonical}#${bound}`,
|
|
336
|
+
}],
|
|
337
|
+
details: {
|
|
338
|
+
repository: activation.repository.canonical,
|
|
339
|
+
prNumber: bound,
|
|
340
|
+
...(issueNumber === undefined ? {} : { issueNumber }),
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
} catch (error) {
|
|
344
|
+
if (isCorrectableExecuteError(error)) throw error;
|
|
473
345
|
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
346
|
+
} finally {
|
|
347
|
+
try {
|
|
348
|
+
activation.ledger.completeOperational(toolCallId);
|
|
349
|
+
} catch {
|
|
350
|
+
// already completed or not begun
|
|
351
|
+
}
|
|
474
352
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
const repoFlag = pi.getFlag("ak-collector-repo");
|
|
506
|
-
const prFlag = pi.getFlag("ak-collector-pr");
|
|
507
|
-
const requestManifestFlag = pi.getFlag("ak-collector-request-manifest");
|
|
508
|
-
if (typeof repoFlag !== "string" || repoFlag.trim().length === 0) {
|
|
509
|
-
throw new Error("Collector requires --ak-collector-repo");
|
|
510
|
-
}
|
|
511
|
-
if (typeof prFlag !== "string" && typeof prFlag !== "number") {
|
|
512
|
-
throw new Error("Collector requires --ak-collector-pr");
|
|
513
|
-
}
|
|
514
|
-
const repository = parseCollectorRepository(repoFlag);
|
|
515
|
-
const prNumber = parseCollectorPrNumber(prFlag);
|
|
516
|
-
const manifest = typeof requestManifestFlag === "string" && requestManifestFlag.trim().length > 0
|
|
517
|
-
? await loadCollectorManifest(requestManifestFlag)
|
|
518
|
-
: emptyCollectorManifest();
|
|
519
|
-
|
|
520
|
-
// Detectable ambient command surface (skills/templates) when exposed.
|
|
521
|
-
const commands = pi.getCommands?.() ?? [];
|
|
522
|
-
const ambientCommands = commands.filter((command) => {
|
|
523
|
-
const name = command.name.toLowerCase();
|
|
524
|
-
return (
|
|
525
|
-
name.includes("skill") ||
|
|
526
|
-
name.includes("prompt") ||
|
|
527
|
-
name.startsWith("template")
|
|
528
|
-
);
|
|
529
|
-
});
|
|
530
|
-
if (ambientCommands.length > 0) {
|
|
531
|
-
throw new Error(
|
|
532
|
-
`Collector detected ambient instruction commands: ${
|
|
533
|
-
ambientCommands.map((c) => c.name).join(", ")
|
|
534
|
-
}`,
|
|
535
|
-
);
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// Fail closed if a required name is already occupied before Collector registers.
|
|
539
|
-
const preExisting = pi.getAllTools();
|
|
540
|
-
for (const required of COLLECTOR_REQUIRED_TOOLS) {
|
|
541
|
-
const prior = preExisting.filter((tool) => tool.name === required);
|
|
542
|
-
if (prior.length > 0) {
|
|
543
|
-
throw new Error(`Collector required tool name collision: ${required}`);
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
pi.registerTool({
|
|
357
|
+
name: COLLECTOR_OBSERVE_TOOL,
|
|
358
|
+
label: "通进司观察",
|
|
359
|
+
description: "抓取配置目标的完整 GitHub PR 证据,存不可变快照入卷。正文在上下文中只给头部摘录加指针;需要头部之外的正文时,用 ak_collector_read 按 evidenceId 开卷;findings 的拆分与归类由你在交件时完成。目标未绑定前须先 ak_collector_bind_target。",
|
|
360
|
+
promptSnippet: "抓取配置目标 PR 证据",
|
|
361
|
+
parameters: observeSchema,
|
|
362
|
+
async execute(toolCallId: string, _params: unknown, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
363
|
+
const activation = getActivation();
|
|
364
|
+
if (activation === undefined) throw new Error("通进司未激活");
|
|
365
|
+
try {
|
|
366
|
+
activation.ledger.beginOperational(COLLECTOR_OBSERVE_TOOL, toolCallId);
|
|
367
|
+
const { snapshot, contextView } = await activation.ledger.observe(
|
|
368
|
+
activation.transport,
|
|
369
|
+
activation.clock,
|
|
370
|
+
signal,
|
|
371
|
+
);
|
|
372
|
+
activation.ledger.completeOperational(toolCallId);
|
|
373
|
+
return {
|
|
374
|
+
content: [{
|
|
375
|
+
type: "text" as const,
|
|
376
|
+
text: JSON.stringify(contextView),
|
|
377
|
+
}],
|
|
378
|
+
details: contextView,
|
|
379
|
+
};
|
|
380
|
+
} catch (error) {
|
|
381
|
+
if (isCorrectableExecuteError(error)) throw error;
|
|
382
|
+
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
544
383
|
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
pi.registerTool({
|
|
388
|
+
name: COLLECTOR_READ_TOOL,
|
|
389
|
+
label: "通进司开卷",
|
|
390
|
+
description: "按 evidenceId 开卷读取一条已观测材料的全量正文与指针;只在观察头部摘录不足以判读时调用。",
|
|
391
|
+
promptSnippet: "按指针开卷读材料",
|
|
392
|
+
parameters: readSchema,
|
|
393
|
+
async execute(toolCallId: string, params: ReadParams, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
394
|
+
const activation = getActivation();
|
|
395
|
+
if (activation === undefined) throw new Error("通进司未激活");
|
|
396
|
+
try {
|
|
397
|
+
activation.ledger.beginOperational(COLLECTOR_READ_TOOL, toolCallId);
|
|
398
|
+
const record = activation.ledger.getEvidence(params.evidenceId);
|
|
399
|
+
if (
|
|
400
|
+
record === undefined ||
|
|
401
|
+
(record.kind !== "review" && record.kind !== "issue_comment" && record.kind !== "review_comment" && record.kind !== "reaction") ||
|
|
402
|
+
typeof record.body !== "string"
|
|
403
|
+
) {
|
|
404
|
+
throw new CollectorUnknownEvidenceError(params.evidenceId);
|
|
405
|
+
}
|
|
406
|
+
const material = projectEvidenceEntryView(record);
|
|
407
|
+
activation.ledger.completeOperational(toolCallId);
|
|
408
|
+
return {
|
|
409
|
+
content: [{
|
|
410
|
+
type: "text" as const,
|
|
411
|
+
text: JSON.stringify(material),
|
|
412
|
+
}],
|
|
413
|
+
details: material,
|
|
414
|
+
};
|
|
415
|
+
} catch (error) {
|
|
416
|
+
if (isCorrectableExecuteError(error)) throw error;
|
|
417
|
+
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
554
418
|
}
|
|
555
|
-
|
|
556
|
-
|
|
419
|
+
},
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
pi.registerTool({
|
|
423
|
+
name: COLLECTOR_REQUEST_TOOL,
|
|
424
|
+
label: "通进司请求",
|
|
425
|
+
description: "按配置请求体与关联标记,在所引最新快照 HEAD 发一次请求。",
|
|
426
|
+
promptSnippet: "按配置发一次请求",
|
|
427
|
+
parameters: requestSchema,
|
|
428
|
+
async execute(toolCallId: string, params: RequestParams, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
429
|
+
const activation = getActivation();
|
|
430
|
+
if (activation === undefined) throw new Error("通进司未激活");
|
|
431
|
+
try {
|
|
432
|
+
activation.ledger.beginOperational(COLLECTOR_REQUEST_TOOL, toolCallId);
|
|
433
|
+
const details = await activation.ledger.request(
|
|
434
|
+
params,
|
|
435
|
+
activation.transport,
|
|
436
|
+
activation.clock,
|
|
437
|
+
signal,
|
|
438
|
+
);
|
|
439
|
+
activation.ledger.completeOperational(toolCallId);
|
|
440
|
+
return {
|
|
441
|
+
content: [{
|
|
442
|
+
type: "text" as const,
|
|
443
|
+
text: `请求尝试已记录:request ${params.requestId}`,
|
|
444
|
+
}],
|
|
445
|
+
details,
|
|
446
|
+
};
|
|
447
|
+
} catch (error) {
|
|
448
|
+
if (isCorrectableExecuteError(error)) throw error;
|
|
449
|
+
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
557
450
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
451
|
+
},
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
pi.registerTool({
|
|
455
|
+
name: COLLECTOR_WAIT_TOOL,
|
|
456
|
+
label: "通进司等待",
|
|
457
|
+
description: "再观察前等待;单次上限五分钟且不超剩余资格。",
|
|
458
|
+
promptSnippet: "资格截止前等待",
|
|
459
|
+
parameters: waitSchema,
|
|
460
|
+
async execute(toolCallId: string, params: WaitParams, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
461
|
+
const activation = getActivation();
|
|
462
|
+
if (activation === undefined) throw new Error("通进司未激活");
|
|
463
|
+
try {
|
|
464
|
+
activation.ledger.beginOperational(COLLECTOR_WAIT_TOOL, toolCallId);
|
|
465
|
+
const details = await activation.ledger.wait(
|
|
466
|
+
params,
|
|
467
|
+
activation.clock,
|
|
468
|
+
signal,
|
|
567
469
|
);
|
|
470
|
+
activation.ledger.completeOperational(toolCallId);
|
|
471
|
+
return {
|
|
472
|
+
content: [{
|
|
473
|
+
type: "text" as const,
|
|
474
|
+
text: `已等待 ${String((details as { effectiveMs: number }).effectiveMs)}ms`,
|
|
475
|
+
}],
|
|
476
|
+
details,
|
|
477
|
+
};
|
|
478
|
+
} catch (error) {
|
|
479
|
+
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
568
480
|
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
pi.registerTool({
|
|
485
|
+
name: COLLECTOR_OUTPUT_TOOL,
|
|
486
|
+
label: "通进司输出",
|
|
487
|
+
description: "观察完成后提交;回执由 runtime 组装。正常完工提交空对象 {}(如需报 finding,填 findings 指针数组);仅在基础设施真实失败时才可填 infrastructureFailure,无失败时必须省略该字段。",
|
|
488
|
+
promptSnippet: "提交通进司回执",
|
|
489
|
+
bounceInfrastructureDeclaration(params) {
|
|
490
|
+
const activation = getActivation();
|
|
491
|
+
if (activation === undefined) return undefined;
|
|
492
|
+
try {
|
|
493
|
+
buildCollectorReceipt(activation.ledger, params, activation.clock);
|
|
494
|
+
} catch {
|
|
495
|
+
return undefined;
|
|
576
496
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
497
|
+
return new CollectorNormalCompletionDeclarationError();
|
|
498
|
+
},
|
|
499
|
+
parameters: outputSchema,
|
|
500
|
+
async execute(toolCallId: string, params: OutputParams, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext) {
|
|
501
|
+
const activation = getActivation();
|
|
502
|
+
if (activation === undefined) throw new Error("通进司未激活");
|
|
503
|
+
try {
|
|
504
|
+
activation.ledger.beginOperational(COLLECTOR_OUTPUT_TOOL, toolCallId);
|
|
505
|
+
const receipt: CollectorReceipt = buildCollectorReceipt(
|
|
506
|
+
activation.ledger,
|
|
507
|
+
params,
|
|
508
|
+
activation.clock,
|
|
509
|
+
);
|
|
510
|
+
activation.ledger.recordOutputCandidate();
|
|
511
|
+
return {
|
|
512
|
+
content: [{
|
|
513
|
+
type: "text" as const,
|
|
514
|
+
text: COLLECTOR_ACCEPTED_TEXT,
|
|
515
|
+
}],
|
|
516
|
+
details: receipt,
|
|
517
|
+
terminate: true as const,
|
|
518
|
+
};
|
|
519
|
+
} catch (error) {
|
|
520
|
+
if (
|
|
521
|
+
error instanceof Error &&
|
|
522
|
+
(error as { collectorFatal?: boolean }).collectorFatal === true
|
|
523
|
+
) {
|
|
524
|
+
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
525
|
+
}
|
|
526
|
+
throw error;
|
|
527
|
+
} finally {
|
|
528
|
+
activation.ledger.completeOperational(toolCallId);
|
|
581
529
|
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
const clock = dependencies.createClock?.() ?? createSystemCollectorClock();
|
|
585
|
-
const transport = dependencies.createTransport();
|
|
586
|
-
|
|
587
|
-
const ledger = dependencies.createLedger(
|
|
588
|
-
{ repository, prNumber, manifest },
|
|
589
|
-
clock,
|
|
590
|
-
ctx,
|
|
591
|
-
);
|
|
592
|
-
|
|
593
|
-
if (ledger.activationRecorded) {
|
|
594
|
-
firstDispatchDone = true;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
activation = {
|
|
598
|
-
soul,
|
|
599
|
-
repository,
|
|
600
|
-
prNumber,
|
|
601
|
-
manifest,
|
|
602
|
-
ledger,
|
|
603
|
-
transport,
|
|
604
|
-
clock,
|
|
605
|
-
};
|
|
530
|
+
},
|
|
531
|
+
});
|
|
606
532
|
},
|
|
607
533
|
};
|
|
608
534
|
}
|