@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
|
@@ -450,12 +450,13 @@ const COLLECTOR_OPTIONS = [
|
|
|
450
450
|
canonical: "--pr",
|
|
451
451
|
aliases: [],
|
|
452
452
|
valueMetavar: "number",
|
|
453
|
-
|
|
453
|
+
// #676 D1: optional when context uniquely determines the PR; ambiguous → require explicit.
|
|
454
|
+
required: false,
|
|
454
455
|
repeatable: false,
|
|
455
456
|
form: "option",
|
|
456
457
|
description: {
|
|
457
|
-
en: "
|
|
458
|
-
zh: "
|
|
458
|
+
en: "Positive GitHub pull request number. Optional when unique branch/HEAD association binds the PR, or when the Collector role can decide the target from task materials via bind-target; multi-candidate git context or a role that cannot decide requires an explicit value.",
|
|
459
|
+
zh: "正整数 GitHub PR 号。分支/HEAD 唯一关联可绑定时可省略;亦可由通进司从任务材料经 bind-target 判定。git 多候选或角色无法判定时必须显式提供。",
|
|
459
460
|
},
|
|
460
461
|
},
|
|
461
462
|
{
|
|
@@ -971,9 +972,10 @@ const ROLE_COMMAND_HELP = {
|
|
|
971
972
|
collector: {
|
|
972
973
|
command: "collector",
|
|
973
974
|
summary: "Collect GitHub PR review evidence.",
|
|
974
|
-
usage: ["ak-role collector --pr <number> [options] [instruction]"],
|
|
975
|
+
usage: ["ak-role collector [--pr <number>] [options] [instruction]"],
|
|
975
976
|
examples: [
|
|
976
977
|
"ak-role collector --pr 42 --repo owner/repository",
|
|
978
|
+
"ak-role collector --repo owner/repository \"Collect findings for #42\"",
|
|
977
979
|
"ak-role collector --pr 42 --request-manifest ./requests.json",
|
|
978
980
|
],
|
|
979
981
|
},
|
|
@@ -1313,8 +1313,8 @@ export async function loadResumableCollectorRun(home, runId, authority) {
|
|
|
1313
1313
|
throw new CliUsageError(`role run ${runId} belongs to ${loaded.run.role}, not collector`);
|
|
1314
1314
|
}
|
|
1315
1315
|
const { prNumber, repository, repositoryDisplay, manifestDigest } = loaded.admittedFields;
|
|
1316
|
-
|
|
1317
|
-
|
|
1316
|
+
// #676 A: prNumber may be unbound at admission (role bind-target). Repository is required.
|
|
1317
|
+
if (repository === undefined ||
|
|
1318
1318
|
repositoryDisplay === undefined ||
|
|
1319
1319
|
manifestDigest === undefined) {
|
|
1320
1320
|
throw new CliUsageError(`role run admitted collector repository identity is missing: ${runId}`);
|
|
@@ -1333,7 +1333,7 @@ export async function loadResumableCollectorRun(home, runId, authority) {
|
|
|
1333
1333
|
const admitted = {
|
|
1334
1334
|
role: "collector",
|
|
1335
1335
|
...resumedBaseAdmitted(loaded),
|
|
1336
|
-
prNumber,
|
|
1336
|
+
...(prNumber === undefined ? {} : { prNumber }),
|
|
1337
1337
|
repository: parsedRepository,
|
|
1338
1338
|
...(loaded.admittedFields.requestManifestPath === undefined
|
|
1339
1339
|
? {}
|
|
@@ -19,7 +19,7 @@ import { RESUME_TRANSPORT_ENVELOPE, isV1ResumableProvider, readLatestTypedProvid
|
|
|
19
19
|
import { AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE, AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE, COMPLIANCE_RESPONSE_ENTRY_TYPE, readComplianceCandidate, } from "../compliance-transport.js";
|
|
20
20
|
// COMPLIANCE_RESPONSE_ENTRY_TYPE remains for boundRetainedAuditResponse (call/result
|
|
21
21
|
// interval binding on historical session bytes). Provider-stop retain authority is Sitian.
|
|
22
|
-
import { COLLECTOR_OBSERVE_TOOL, COLLECTOR_READ_TOOL, COLLECTOR_REQUEST_TOOL, COLLECTOR_WAIT_TOOL, } from "../collector-ledger.js";
|
|
22
|
+
import { COLLECTOR_BIND_TARGET_TOOL, COLLECTOR_OBSERVE_TOOL, COLLECTOR_READ_TOOL, COLLECTOR_REQUEST_TOOL, COLLECTOR_WAIT_TOOL, } from "../collector-ledger.js";
|
|
23
23
|
import { ENGINE_DETOUR_TOOL_NAME } from "../engine-detour.js";
|
|
24
24
|
import { JUDGE_OUTPUT_TOOL_NAME, } from "../package-contracts/judge-output.js";
|
|
25
25
|
import { COLLECTOR_OUTPUT_TOOL, } from "../package-contracts/collector-output.js";
|
|
@@ -183,9 +183,34 @@ export function formatFailureStderrDiagnostic(failure) {
|
|
|
183
183
|
.find((line) => line.length > 0) ?? "failure";
|
|
184
184
|
return formatCliDiagnostic(boundConciseDiagnostic(oneLine));
|
|
185
185
|
}
|
|
186
|
-
/**
|
|
186
|
+
/**
|
|
187
|
+
* #676 B: one cause face for public stderr — Error.message, else object JSON with
|
|
188
|
+
* status/body/headers when present, else String. Never wash objects to [object Object].
|
|
189
|
+
*/
|
|
190
|
+
export function formatErrorCauseDetail(cause) {
|
|
191
|
+
if (cause instanceof Error)
|
|
192
|
+
return cause.message;
|
|
193
|
+
if (typeof cause === "object" && cause !== null) {
|
|
194
|
+
try {
|
|
195
|
+
return JSON.stringify(cause);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return String(cause);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return String(cause);
|
|
202
|
+
}
|
|
203
|
+
/** Pre-admission structural rejection: stderr only, no run, no Terminal. Cause retained when present. */
|
|
187
204
|
export function presentStructuralRejection(error, io) {
|
|
188
|
-
|
|
205
|
+
let message = error.message;
|
|
206
|
+
const cause = error.cause;
|
|
207
|
+
if (cause !== undefined) {
|
|
208
|
+
const detail = formatErrorCauseDetail(cause);
|
|
209
|
+
if (detail.trim().length > 0) {
|
|
210
|
+
message = `${message}; cause: ${detail}`;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
io.stderr(formatCliDiagnostic(message));
|
|
189
214
|
}
|
|
190
215
|
/** ControlledFailure face without admitted-run Terminal (stdout body + stderr line). */
|
|
191
216
|
export function presentControlledFailure(failure, io) {
|
|
@@ -1093,6 +1118,35 @@ function toolResultText(message) {
|
|
|
1093
1118
|
.join("")
|
|
1094
1119
|
.trim();
|
|
1095
1120
|
}
|
|
1121
|
+
/**
|
|
1122
|
+
* #676 D1/J3: durable bind-target rejection on the current attempt → public no_receipt facts.
|
|
1123
|
+
* Callers must learn they need explicit --pr without opening the session 正本.
|
|
1124
|
+
* Authority is the latest bind-target toolResult only — a later success clears an earlier failure.
|
|
1125
|
+
*/
|
|
1126
|
+
function extractCollectorTargetBindRejection(entries) {
|
|
1127
|
+
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
|
1128
|
+
const entry = entries[i];
|
|
1129
|
+
if (entry?.type !== "message")
|
|
1130
|
+
continue;
|
|
1131
|
+
const message = entry.message;
|
|
1132
|
+
if (message?.role !== "toolResult")
|
|
1133
|
+
continue;
|
|
1134
|
+
if (message.toolName !== COLLECTOR_BIND_TARGET_TOOL)
|
|
1135
|
+
continue;
|
|
1136
|
+
// Latest bind result wins: success means the attempt is no longer rejected.
|
|
1137
|
+
if (message.isError !== true)
|
|
1138
|
+
return undefined;
|
|
1139
|
+
const diagnostic = toolResultText(message);
|
|
1140
|
+
if (diagnostic.length === 0)
|
|
1141
|
+
return undefined;
|
|
1142
|
+
const details = message.details;
|
|
1143
|
+
const code = isRecord(details) && typeof details.code === "string" && details.code.trim() !== ""
|
|
1144
|
+
? details.code
|
|
1145
|
+
: undefined;
|
|
1146
|
+
return code === undefined ? { diagnostic } : { diagnostic, code };
|
|
1147
|
+
}
|
|
1148
|
+
return undefined;
|
|
1149
|
+
}
|
|
1096
1150
|
function boundErroredToolCandidate(entries, resultIndex, message, toolName) {
|
|
1097
1151
|
if (message.toolName !== toolName || message.isError !== true)
|
|
1098
1152
|
return undefined;
|
|
@@ -1208,7 +1262,9 @@ export function assertCollectorReceiptMatchesAdmitted(receipt, admitted) {
|
|
|
1208
1262
|
if (receipt.repository !== admitted.repository.canonical) {
|
|
1209
1263
|
throw collectorReceiptBindingFailure(`Collector receipt repository "${receipt.repository}" does not match admitted repository "${admitted.repository.canonical}"`);
|
|
1210
1264
|
}
|
|
1211
|
-
|
|
1265
|
+
// #676 A: when admission left the target unbound, receipt.prNumber is the role bind.
|
|
1266
|
+
// When admission bound explicitly/head-commit, receipt must match.
|
|
1267
|
+
if (admitted.prNumber !== undefined && receipt.prNumber !== admitted.prNumber) {
|
|
1212
1268
|
throw collectorReceiptBindingFailure(`Collector receipt prNumber ${receipt.prNumber} does not match admitted prNumber ${admitted.prNumber}`);
|
|
1213
1269
|
}
|
|
1214
1270
|
if (receipt.manifestDigest !== admitted.manifestDigest) {
|
|
@@ -1994,7 +2050,7 @@ export async function publishCollectorArtifacts(admitted, roleOutcome, coordinat
|
|
|
1994
2050
|
await writeFile(evidencePath, `${JSON.stringify({
|
|
1995
2051
|
runId: admitted.runId,
|
|
1996
2052
|
role: "collector",
|
|
1997
|
-
prNumber: admitted.prNumber,
|
|
2053
|
+
...(admitted.prNumber === undefined ? {} : { prNumber: admitted.prNumber }),
|
|
1998
2054
|
repository: admitted.repository.canonical,
|
|
1999
2055
|
manifestDigest: admitted.manifestDigest,
|
|
2000
2056
|
sessionDirectory: coordinates.sessionDirectory,
|
|
@@ -2841,7 +2897,19 @@ export async function settleFailureTerminalResult(admitted, failure, authority,
|
|
|
2841
2897
|
try {
|
|
2842
2898
|
const facts = parseNoReceiptLifecycleFacts(raw);
|
|
2843
2899
|
if (facts.runPointer === admitted.runDirectory && facts.attemptPointer === `current:${admitted.runDirectory}`) {
|
|
2844
|
-
|
|
2900
|
+
let decisiveFacts = facts;
|
|
2901
|
+
// #676 D1/J3: project durable bind-target rejection onto public no_receipt facts.
|
|
2902
|
+
if (admitted.role === "collector") {
|
|
2903
|
+
const bindRejection = extractCollectorTargetBindRejection(entries.slice(attemptStart));
|
|
2904
|
+
if (bindRejection !== undefined) {
|
|
2905
|
+
decisiveFacts = {
|
|
2906
|
+
...facts,
|
|
2907
|
+
targetBindRejected: true,
|
|
2908
|
+
targetBindDiagnostic: bindRejection.diagnostic,
|
|
2909
|
+
...(bindRejection.code === undefined ? {} : { targetBindCode: bindRejection.code }),
|
|
2910
|
+
};
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2845
2913
|
// #478: no_receipt is still a public Terminal — project accepted gate facts.
|
|
2846
2914
|
return withOptionalGateProjection({
|
|
2847
2915
|
roleOutcome: { kind: "no_receipt", role: admitted.role, status: "no-accepted-receipt", ...facts, decisiveFacts },
|
|
@@ -2927,6 +2995,15 @@ export function presentFailureTerminal(terminal, io) {
|
|
|
2927
2995
|
cause: terminal.roleOutcome.cause,
|
|
2928
2996
|
diagnostic: terminal.roleOutcome.diagnostic,
|
|
2929
2997
|
}));
|
|
2998
|
+
return;
|
|
2999
|
+
}
|
|
3000
|
+
// #676 D1/J3: no_receipt stays lawful exit 0; surface durable target-bind clarification on stderr.
|
|
3001
|
+
const bindDiagnostic = terminal.roleOutcome.decisiveFacts.targetBindDiagnostic;
|
|
3002
|
+
if (typeof bindDiagnostic === "string" && bindDiagnostic.trim() !== "") {
|
|
3003
|
+
io.stderr(formatFailureStderrDiagnostic({
|
|
3004
|
+
cause: "output",
|
|
3005
|
+
diagnostic: bindDiagnostic,
|
|
3006
|
+
}));
|
|
2930
3007
|
}
|
|
2931
3008
|
}
|
|
2932
3009
|
function defaultNavigatorGraceSleep() {
|
package/dist/role-runtime.js
CHANGED
|
@@ -14,7 +14,7 @@ import { createToolExecutionObservationFace, systemToolExecutionObservationMonoN
|
|
|
14
14
|
import { ENGINE_DETOUR_TOOL_NAME } from "./engine-detour.js";
|
|
15
15
|
import { registerEngineDetourTool } from "./engine-detour-tool.js";
|
|
16
16
|
import { createReceiptDeliveryPolicy, NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, RECEIPT_DELIVERY_PROMPT } from "./receipt-delivery-policy.js";
|
|
17
|
-
import {
|
|
17
|
+
import { COLLECTOR_REQUIRED_TOOLS, COLLECTOR_TRANSPORT_FLAGS, createCollectorRoleRuntime, } from "./collector-role.js";
|
|
18
18
|
import { createDoctorRoleRuntime } from "./doctor-role.js";
|
|
19
19
|
import { createNotaryRoleRuntime, NOTARY_SESSION_BOUND_ENTRY, projectNotaryBoundFromFlags, readNotaryTicketFlag, } from "./notary-role.js";
|
|
20
20
|
import { NOTARY_TICKET_FLAG } from "./notary-contracts.js";
|
|
@@ -670,6 +670,10 @@ export function createRoleRuntimeExtension(dependencies) {
|
|
|
670
670
|
for (const flag of GLEANER_LEFT_TRANSPORT_FLAGS) {
|
|
671
671
|
roleHost.registerFlag(flag.name, flag.definition);
|
|
672
672
|
}
|
|
673
|
+
// Collector transport flags: shared envelope owns registration (ADR 0018 / #676 E).
|
|
674
|
+
for (const flag of COLLECTOR_TRANSPORT_FLAGS) {
|
|
675
|
+
roleHost.registerFlag(flag.name, flag.definition);
|
|
676
|
+
}
|
|
673
677
|
let admitted = false;
|
|
674
678
|
let selectedRole;
|
|
675
679
|
/** Live Reviewer parent activation for envelope agent_start prompt assembly. */
|
|
@@ -818,11 +822,36 @@ export function createRoleRuntimeExtension(dependencies) {
|
|
|
818
822
|
}),
|
|
819
823
|
};
|
|
820
824
|
}
|
|
825
|
+
// #676 E / J1: collector materials + drift gates share this envelope hook (no parallel register).
|
|
826
|
+
if (role === "collector" && activeCollector !== undefined) {
|
|
827
|
+
const options = event.systemPromptOptions;
|
|
828
|
+
if (options.skills && options.skills.length > 0) {
|
|
829
|
+
failInfrastructure(activeCollector.ledger.latchFatal("通进司检测到系统提示中的环境 skills"), ctx);
|
|
830
|
+
}
|
|
831
|
+
if (options.contextFiles && options.contextFiles.length > 0) {
|
|
832
|
+
failInfrastructure(activeCollector.ledger.latchFatal("通进司检测到系统提示中的环境 context files"), ctx);
|
|
833
|
+
}
|
|
834
|
+
if (typeof options.appendSystemPrompt === "string"
|
|
835
|
+
&& options.appendSystemPrompt.trim().length > 0) {
|
|
836
|
+
failInfrastructure(activeCollector.ledger.latchFatal("通进司检测到 appendSystemPrompt 漂移"), ctx);
|
|
837
|
+
}
|
|
838
|
+
if (!collectorFirstDispatchDone) {
|
|
839
|
+
collectorFirstDispatchDone = true;
|
|
840
|
+
activeCollector.ledger.recordActivation(activeCollector.clock);
|
|
841
|
+
}
|
|
842
|
+
return {
|
|
843
|
+
systemPrompt: collectorBusiness.assembleMaterials(activeCollector, event.systemPrompt),
|
|
844
|
+
};
|
|
845
|
+
}
|
|
821
846
|
});
|
|
822
847
|
roleHost.on("tool_result", async (event) => {
|
|
823
848
|
const role = selectedRole;
|
|
824
849
|
if (role === undefined)
|
|
825
850
|
return;
|
|
851
|
+
// #676 E / J1: collector operational bookkeeping on the shared tool_result seam.
|
|
852
|
+
if (role === "collector" && activeCollector !== undefined) {
|
|
853
|
+
collectorBusiness.onToolResult(activeCollector, event);
|
|
854
|
+
}
|
|
826
855
|
const pendingInfra = pendingInfrastructureFailures.get(event.toolCallId);
|
|
827
856
|
const isRoleInfrastructureFailure = pendingInfra !== undefined;
|
|
828
857
|
if (pendingInfra !== undefined)
|
|
@@ -961,6 +990,14 @@ export function createRoleRuntimeExtension(dependencies) {
|
|
|
961
990
|
priorFetch = undefined;
|
|
962
991
|
fetchWrapped = false;
|
|
963
992
|
}
|
|
993
|
+
// #676 J4: collector fatal latch must surface nonzero exit on shutdown (envelope-owned).
|
|
994
|
+
if (selectedRole === "collector"
|
|
995
|
+
&& activeCollector !== undefined
|
|
996
|
+
&& activeCollector.ledger.fatal) {
|
|
997
|
+
if (process.exitCode === undefined || process.exitCode === 0) {
|
|
998
|
+
process.exitCode = 1;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
964
1001
|
// Flush any still-pending affirmative attendance before teardown. Accepted
|
|
965
1002
|
// grace-timeout paths normally emit on agent_settled; abort can skip that hook.
|
|
966
1003
|
const presentation = pendingNavigatorPresentation;
|
|
@@ -1150,7 +1187,11 @@ export function createRoleRuntimeExtension(dependencies) {
|
|
|
1150
1187
|
throw new Error("Merger runtime dependencies are not configured"); return sessionMergerGitState.completedMerge(mergeCommitId, automaticMergeTreeId); },
|
|
1151
1188
|
},
|
|
1152
1189
|
}, hostActions);
|
|
1153
|
-
|
|
1190
|
+
// #676 E: shared envelope owns collector lifecycle (mode/fork, tool surface,
|
|
1191
|
+
// event gates). Role module supplies business activate/tools/materials only.
|
|
1192
|
+
let activeCollector;
|
|
1193
|
+
let collectorFirstDispatchDone = false;
|
|
1194
|
+
const collectorBusiness = createCollectorRoleRuntime(roleHost, {
|
|
1154
1195
|
async loadSoul() {
|
|
1155
1196
|
if (dependencies.loadCollectorSoul === undefined) {
|
|
1156
1197
|
throw new Error("collector soul loader is not configured");
|
|
@@ -1182,12 +1223,101 @@ export function createRoleRuntimeExtension(dependencies) {
|
|
|
1182
1223
|
...(dependencies.createCollectorClock === undefined
|
|
1183
1224
|
? {}
|
|
1184
1225
|
: { createClock: dependencies.createCollectorClock }),
|
|
1185
|
-
...(dependencies.collectorPackageExtensionPath === undefined
|
|
1186
|
-
? {}
|
|
1187
|
-
: {
|
|
1188
|
-
packageExtensionPath: dependencies.collectorPackageExtensionPath,
|
|
1189
|
-
}),
|
|
1190
1226
|
}, hostActions);
|
|
1227
|
+
// #676 J1: business tools + tool_call gate register only behind admission/activation.
|
|
1228
|
+
// before_agent_start / tool_result collector branches live on the shared envelope hooks above.
|
|
1229
|
+
//
|
|
1230
|
+
// #676 J4 dispositions for guards removed from the role-private collector module:
|
|
1231
|
+
// - skills/contextFiles/appendSystemPrompt fail-closed → shared before_agent_start (above).
|
|
1232
|
+
// - ambient skill/prompt/template commands → activate (below).
|
|
1233
|
+
// - session_shutdown fatal exitCode → shared session_shutdown (above).
|
|
1234
|
+
// - subsequent-input latchFatal + fixed-kickoff rewrite → intentionally not migrated:
|
|
1235
|
+
// #676 materials-first multi-turn abolished the single-shot fixed kickoff; multi-turn
|
|
1236
|
+
// observe/request/wait requires later inputs. Judge r1: fixed-kickoff equality delete is authorized.
|
|
1237
|
+
// - tool sourceInfo path override check → intentionally not migrated: depended on
|
|
1238
|
+
// packageExtensionPath deleted under ADR 0018 / #676 E envelope ownership; uniqueness
|
|
1239
|
+
// + setActiveTools inventory checks remain on activate.
|
|
1240
|
+
let collectorToolCallRegistered = false;
|
|
1241
|
+
const collector = {
|
|
1242
|
+
async activate(context, event) {
|
|
1243
|
+
activeCollector = undefined;
|
|
1244
|
+
collectorFirstDispatchDone = false;
|
|
1245
|
+
// Envelope-owned mode / fork-reload gates (not role-private lifecycle).
|
|
1246
|
+
if (context.mode !== "print" && context.mode !== "json") {
|
|
1247
|
+
throw new Error(`Collector supports only print or json mode (got ${context.mode})`);
|
|
1248
|
+
}
|
|
1249
|
+
if (event.reason === "fork" || event.reason === "reload") {
|
|
1250
|
+
throw new Error(`Collector does not support session_start reason ${event.reason}`);
|
|
1251
|
+
}
|
|
1252
|
+
// #676 J4: ambient skill/prompt/template commands fail closed at activation.
|
|
1253
|
+
const commands = roleHost.getCommands?.() ?? [];
|
|
1254
|
+
const ambientCommands = commands.filter((command) => {
|
|
1255
|
+
const name = command.name.toLowerCase();
|
|
1256
|
+
return (name.includes("skill")
|
|
1257
|
+
|| name.includes("prompt")
|
|
1258
|
+
|| name.startsWith("template"));
|
|
1259
|
+
});
|
|
1260
|
+
if (ambientCommands.length > 0) {
|
|
1261
|
+
throw new Error(`Collector detected ambient instruction commands: ${ambientCommands.map((c) => c.name).join(", ")}`);
|
|
1262
|
+
}
|
|
1263
|
+
// Business tools behind admission barrier (inert-without-role invariant).
|
|
1264
|
+
// First activation: fail closed if a required name is already occupied.
|
|
1265
|
+
// Later activations reuse the once-registered tools (registerBusinessTools is idempotent).
|
|
1266
|
+
const preExisting = roleHost.getAllTools();
|
|
1267
|
+
const alreadyRegistered = COLLECTOR_REQUIRED_TOOLS.every((required) => preExisting.some((tool) => tool.name === required));
|
|
1268
|
+
if (!alreadyRegistered) {
|
|
1269
|
+
for (const required of COLLECTOR_REQUIRED_TOOLS) {
|
|
1270
|
+
const prior = preExisting.filter((tool) => tool.name === required);
|
|
1271
|
+
if (prior.length > 0) {
|
|
1272
|
+
throw new Error(`Collector required tool name collision: ${required}`);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
collectorBusiness.registerBusinessTools(() => activeCollector);
|
|
1277
|
+
const allTools = roleHost.getAllTools();
|
|
1278
|
+
for (const required of COLLECTOR_REQUIRED_TOOLS) {
|
|
1279
|
+
const matches = allTools.filter((tool) => tool.name === required);
|
|
1280
|
+
if (matches.length === 0) {
|
|
1281
|
+
throw new Error(`Collector required tool missing: ${required}`);
|
|
1282
|
+
}
|
|
1283
|
+
if (matches.length > 1) {
|
|
1284
|
+
throw new Error(`Collector required tool name collision: ${required}`);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
roleHost.setActiveTools([...COLLECTOR_REQUIRED_TOOLS]);
|
|
1288
|
+
const active = new Set(roleHost.getActiveTools());
|
|
1289
|
+
for (const required of COLLECTOR_REQUIRED_TOOLS) {
|
|
1290
|
+
if (!active.has(required)) {
|
|
1291
|
+
throw new Error(`Collector failed to activate required tool ${required}`);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
for (const name of active) {
|
|
1295
|
+
if (!COLLECTOR_REQUIRED_TOOLS.includes(name)) {
|
|
1296
|
+
throw new Error(`Collector active tool surface includes unexpected ${name}`);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
// Seat-scoped tool_call gate — registered only after collector admission (no install-time tool_call).
|
|
1300
|
+
if (!collectorToolCallRegistered) {
|
|
1301
|
+
collectorToolCallRegistered = true;
|
|
1302
|
+
roleHost.on("tool_call", (toolEvent) => {
|
|
1303
|
+
if (activeCollector === undefined || selectedRole !== "collector")
|
|
1304
|
+
return;
|
|
1305
|
+
if (!COLLECTOR_REQUIRED_TOOLS.includes(toolEvent.toolName)) {
|
|
1306
|
+
return {
|
|
1307
|
+
block: true,
|
|
1308
|
+
reason: `通进司禁用工具 ${toolEvent.toolName}`,
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
return collectorBusiness.onToolCall(activeCollector, toolEvent);
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
const activation = await collectorBusiness.activate(context);
|
|
1315
|
+
if (activation.ledger.activationRecorded) {
|
|
1316
|
+
collectorFirstDispatchDone = true;
|
|
1317
|
+
}
|
|
1318
|
+
activeCollector = activation;
|
|
1319
|
+
},
|
|
1320
|
+
};
|
|
1191
1321
|
const clock = dependencies.activationClock ?? (() => new Date().toISOString());
|
|
1192
1322
|
const writeTrace = dependencies.activationTraceWriter ?? writeActivationTraceRecord;
|
|
1193
1323
|
const observationFace = createToolExecutionObservationFace({
|
|
@@ -20,3 +20,27 @@ export function isCorrectableExecuteError(error) {
|
|
|
20
20
|
|| error instanceof WorkerPrefixReminderError
|
|
21
21
|
|| error instanceof WorkerUnfinishedReasonReminderError);
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* One authority for correctable execute → diagnostic text + structured details.
|
|
25
|
+
* Consumed by the ACP envelope tool catch; host adapters wrap transport shape only.
|
|
26
|
+
*/
|
|
27
|
+
export function projectCorrectableExecuteRejection(error) {
|
|
28
|
+
const diagnostic = error instanceof Error ? error.message : String(error);
|
|
29
|
+
if (error instanceof GatekeeperDecisionError) {
|
|
30
|
+
return { diagnostic, details: { ...error.result } };
|
|
31
|
+
}
|
|
32
|
+
if (error instanceof WorkerCommitReminderError
|
|
33
|
+
|| error instanceof WorkerPrefixReminderError
|
|
34
|
+
|| error instanceof WorkerUnfinishedReasonReminderError) {
|
|
35
|
+
return { diagnostic, details: { code: error.code } };
|
|
36
|
+
}
|
|
37
|
+
if (typeof error.code === "string") {
|
|
38
|
+
return { diagnostic, details: { code: error.code } };
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
diagnostic,
|
|
42
|
+
details: {
|
|
43
|
+
code: error instanceof Error && error.name ? error.name : "correctable-submission-error",
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -123,7 +123,6 @@ export default function roleRuntime(pi: ExtensionAPI): void {
|
|
|
123
123
|
createReviewerIssueFetcher: () => createGhIssueSoftFetcher(),
|
|
124
124
|
loadCollectorSoul: () => loadMainRoleSessionMaterials("collector"),
|
|
125
125
|
createCollectorTransport: () => createGhCollectorGitHubTransport(),
|
|
126
|
-
collectorPackageExtensionPath: extensionPath,
|
|
127
126
|
loadDoctorSoul: () => loadMainRoleSessionMaterials("doctor"),
|
|
128
127
|
loadDoctorCase,
|
|
129
128
|
// #590: auditors / navigator / reviewer consume HostContext directly (no Pi WeakMap recover).
|
package/package.json
CHANGED
package/souls/coder.md
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
# Coder Soul(将作监)
|
|
2
2
|
|
|
3
3
|
你负责把已授权任务实现为可验证的第一版,不负责独立评审、发布或最终裁决。
|
|
4
|
+
第一步:立法
|
|
5
|
+
读 全局宪法,仓内宪法,票面
|
|
6
|
+
第二步:施工
|
|
7
|
+
先核对派单文,输入prompt,你要做的任务有没有违法,如有,直接拒绝
|
|
8
|
+
事实不成立或需要未授权设计时,据理拒绝并给出可核验证据;不猜测施工。
|
|
4
9
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
核对当前代码和历史;主张必须有当前事实支撑。
|
|
11
|
+
做最小垂直切片,优先复用已有 seam;不造平行机制,不借机扩大 scope。
|
|
12
|
+
计划与施工都要说明真实刀口、行为边界和验证方法,不用空泛完成声明代替证据。
|
|
13
|
+
切片验证只跑本片聚焦测试与 typecheck。
|
|
14
|
+
不 amend、rewrite 或 push。Git 变化是证据之一,不取代角色报告。
|
|
15
|
+
做完自查是否违法,质量和复杂度,如有问题马上修正,没问题就commit交卷
|
package/souls/fixer.md
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
# 修内司
|
|
2
|
-
|
|
3
|
-
你是修内司。修类不修点,点名位置只是样本。
|
|
1
|
+
# 修内司 Soul
|
|
4
2
|
|
|
3
|
+
你是修内司。修类不修点。
|
|
5
4
|
apply 按顺序执行:
|
|
6
5
|
|
|
7
|
-
1.
|
|
8
|
-
2.
|
|
9
|
-
3.
|
|
10
|
-
4.
|
|
11
|
-
5.
|
|
12
|
-
6.
|
|
6
|
+
1. 立法:读 全局宪法,仓内宪法,票面。
|
|
7
|
+
2. 核对:先核对派单文,输入prompt,你要做的任务。有没有违法,如有,直接拒绝。 事实不成立或需要未授权设计时,据理拒绝并给出可核验证据;不猜测施工。
|
|
8
|
+
3. 归类:读完全部 finding,沿用已有裁决类别,明确各类错误与正确行为。
|
|
9
|
+
4. 扫类:修改前查全授权范围内的同类实例、已有实现和失败修法。根因不明先取证。
|
|
10
|
+
5. 整类修复:在共同原因处一次修净,优先删除、简化、复用,随同删除失效旧物。
|
|
11
|
+
6. 复扫:按原类别重新扫描;有遗漏继续修,保留项说明依据。
|
|
12
|
+
7. 自验:切片验证只跑本片聚焦测试与 typecheck。做完自查是否违法,质量和复杂度,如有问题马上修正。
|
|
13
|
+
8. 交卷:按类报告根因、扫描范围、修复与 质量和合法性的验证结果、未结项。保护已有改动,只建新提交。不 amend、rewrite 或 push。Git 变化是证据之一,不取代角色报告。
|
|
13
14
|
|
|
14
15
|
plan 只调查与提案,不施工。
|
|
@@ -27,12 +27,9 @@ import {
|
|
|
27
27
|
type AcpRoleTurnHostConfig,
|
|
28
28
|
} from "./role-turn-host.ts";
|
|
29
29
|
import {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
WorkerUnfinishedReasonReminderError,
|
|
34
|
-
} from "../submission-errors.ts";
|
|
35
|
-
import { isCorrectableExecuteError } from "../submission-correctable-error.ts";
|
|
30
|
+
isCorrectableExecuteError,
|
|
31
|
+
projectCorrectableExecuteRejection,
|
|
32
|
+
} from "../submission-correctable-error.ts";
|
|
36
33
|
import {
|
|
37
34
|
buildNavigatorInfrastructureFailureFact,
|
|
38
35
|
extractInfrastructureFailureEvidence,
|
|
@@ -98,7 +95,8 @@ export function projectAcpActivationFlags(request: RoleTurnRequest): Map<string,
|
|
|
98
95
|
}
|
|
99
96
|
if (activation.role === "collector") {
|
|
100
97
|
flags.set("ak-collector-repo", activation.repo);
|
|
101
|
-
|
|
98
|
+
// #676 D1: pr optional at admission; omit flag when role binds from materials.
|
|
99
|
+
if (activation.pr !== undefined) flags.set("ak-collector-pr", activation.pr);
|
|
102
100
|
if (activation.requestManifestPath !== undefined) flags.set("ak-collector-request-manifest", activation.requestManifestPath);
|
|
103
101
|
}
|
|
104
102
|
return flags;
|
|
@@ -500,21 +498,9 @@ export async function prepareAcpRoleEnvelope(options: {
|
|
|
500
498
|
let content: ContentPart[];
|
|
501
499
|
let details: Record<string, unknown>;
|
|
502
500
|
if (isCorrectableExecuteError(error)) {
|
|
503
|
-
const
|
|
504
|
-
content = [{ type: "text", text: diagnostic }];
|
|
505
|
-
|
|
506
|
-
details = { ...error.result };
|
|
507
|
-
} else if (
|
|
508
|
-
error instanceof WorkerCommitReminderError
|
|
509
|
-
|| error instanceof WorkerPrefixReminderError
|
|
510
|
-
|| error instanceof WorkerUnfinishedReasonReminderError
|
|
511
|
-
) {
|
|
512
|
-
details = { code: error.code };
|
|
513
|
-
} else if (typeof (error as unknown as { code?: unknown }).code === "string") {
|
|
514
|
-
details = { code: (error as unknown as { code: string }).code };
|
|
515
|
-
} else {
|
|
516
|
-
details = { code: error instanceof Error && error.name ? error.name : "correctable-submission-error" };
|
|
517
|
-
}
|
|
501
|
+
const projected = projectCorrectableExecuteRejection(error);
|
|
502
|
+
content = [{ type: "text", text: projected.diagnostic }];
|
|
503
|
+
details = projected.details;
|
|
518
504
|
} else {
|
|
519
505
|
// Slot-before-abort; projectToolResult may still project durable details.
|
|
520
506
|
({ content, details } = declareRoundInfrastructureFailure(error));
|
package/src/collector-config.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { readFile } from "node:fs/promises";
|
|
|
4
4
|
export const COLLECTOR_HOST = "github.com" as const;
|
|
5
5
|
export const COLLECTOR_OWNER_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/;
|
|
6
6
|
export const COLLECTOR_REPO_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,98}[A-Za-z0-9])?$/;
|
|
7
|
-
export const COLLECTOR_FIXED_KICKOFF = "采集目标已受理,本局开始。";
|
|
8
7
|
|
|
9
8
|
export type CollectorRepository = {
|
|
10
9
|
display: string;
|