@akagilnc/pi-workflow-roles 0.1.5577 → 0.1.5593
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/README.zh-CN.md +2 -0
- package/dist/acp-host/production-host.js +338 -617
- package/dist/collector-config.js +0 -44
- package/dist/headless-host/production-host.js +241 -520
- package/dist/migrate-book-topology.js +16 -32
- package/dist/package-resources/engine-material.js +22 -25
- package/dist/public-cli/instruction-seat-run.js +11 -21
- package/dist/public-cli/invocation.js +83 -193
- package/dist/public-cli/main.js +225 -528
- package/dist/public-cli/option-definitions.js +5 -5
- package/dist/public-cli/post-admission.js +12 -48
- package/dist/public-cli/run-lifecycle.js +13 -21
- package/dist/public-role-summons.js +1 -1
- package/dist/role-run-relocation.js +12 -20
- package/package.json +1 -1
- package/src/collector-config.ts +0 -42
- package/src/collector-role.ts +10 -20
- package/src/package-resources/engine-material.ts +42 -35
- package/src/public-cli/instruction-seat-run.ts +10 -27
- package/src/public-cli/invocation.ts +97 -292
- package/src/public-cli/option-definitions.ts +5 -5
- package/src/public-cli/post-admission.ts +13 -64
- package/src/public-cli/run-lifecycle.ts +13 -34
- package/src/public-role-summons.ts +1 -1
- package/src/role-run-placement.ts +0 -1
- package/src/role-run-relocation.ts +12 -22
- package/src/role-runtime.ts +18 -16
|
@@ -261,7 +261,7 @@ const SHARED_PROJECT_SEMANTICS = {
|
|
|
261
261
|
},
|
|
262
262
|
};
|
|
263
263
|
/**
|
|
264
|
-
* Immutable shared semantics for the common
|
|
264
|
+
* Immutable shared semantics for the common `--attach` face (#1165).
|
|
265
265
|
* Role rows bind owner only. Reviewer has no attach face; analyst sweep attach
|
|
266
266
|
* keeps its own modes/selectsMode/description and must not use this binding.
|
|
267
267
|
*/
|
|
@@ -274,8 +274,8 @@ const SHARED_ATTACH_SEMANTICS = {
|
|
|
274
274
|
repeatable: true,
|
|
275
275
|
form: "option",
|
|
276
276
|
description: {
|
|
277
|
-
en: "
|
|
278
|
-
zh: "
|
|
277
|
+
en: "Caller file path passed as-is to the role (repeatable). Package does not read, copy, or validate; use an absolute path when cwd differs from --project.",
|
|
278
|
+
zh: "调用方文件路径原样递给衙门(可重复)。包不读、不复制、不校验;调用方当前目录与 --project 不同时请给绝对路径。",
|
|
279
279
|
},
|
|
280
280
|
};
|
|
281
281
|
/** Minimal owner-binding: one immutable semantic row → one role table entry. */
|
|
@@ -461,8 +461,8 @@ const COLLECTOR_OPTIONS = [
|
|
|
461
461
|
repeatable: false,
|
|
462
462
|
form: "option",
|
|
463
463
|
description: {
|
|
464
|
-
en: "
|
|
465
|
-
zh: "
|
|
464
|
+
en: "Caller path to a request manifest JSON file, passed as-is. Package does not read, copy, or validate; use an absolute path when cwd differs from --project.",
|
|
465
|
+
zh: "调用方 request manifest JSON 路径,原样传递。包不读、不复制、不校验;调用方当前目录与 --project 不同时请给绝对路径。",
|
|
466
466
|
},
|
|
467
467
|
},
|
|
468
468
|
];
|
|
@@ -7,15 +7,12 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
9
|
import { writeFile } from "node:fs/promises";
|
|
10
|
-
import { isAbsolute, join, resolve } from "node:path";
|
|
11
10
|
import { buildAutoResumeContinuationPrompt, findRunDirectoryById, readRoleRunState, RESUME_TRANSPORT_ENVELOPE, } from "./run-lifecycle.js";
|
|
12
11
|
import { CliUsageError } from "./cli-errors.js";
|
|
13
12
|
import { projectPublicTurnAxes } from "./turn-request.js";
|
|
14
|
-
import { bindAdmittedTicketNumber, buildInstructionTransportPrompt,
|
|
13
|
+
import { bindAdmittedTicketNumber, buildInstructionTransportPrompt, relocateAdmittedRunToTicket, } from "./invocation.js";
|
|
15
14
|
import { readRecordedSubmissionRows } from "../submission-ledger.js";
|
|
16
15
|
import { parseTicketNumber, readDeclaredTicketNumber } from "../run-ticket-number.js";
|
|
17
|
-
import { pathContainedIn } from "../activation-ledger-topology.js";
|
|
18
|
-
import { pickEngineAxis } from "../package-resources/engine-material.js";
|
|
19
16
|
import { readStoredHostSessionId } from "../session-identity.js";
|
|
20
17
|
import { reportRunRecord } from "../sitian-facade.js";
|
|
21
18
|
import { createReceiptDeliveryPolicy, deliveryLimitFromConfig, } from "../receipt-delivery-policy.js";
|
|
@@ -52,11 +49,12 @@ function projectRelocatedTurnIdentity(request, result, admitted, relocation) {
|
|
|
52
49
|
mutableRequest.principal = admitted.principal;
|
|
53
50
|
const activation = mutableRequest.activation;
|
|
54
51
|
if (isRecord(activation)) {
|
|
55
|
-
for (const field of ["taskPath", "packetPath", "prerequisitesPath", "inputPath"
|
|
52
|
+
for (const field of ["taskPath", "packetPath", "prerequisitesPath", "inputPath"]) {
|
|
56
53
|
const record = activation;
|
|
57
54
|
if (field in record)
|
|
58
55
|
record[field] = rewrite(record[field]);
|
|
59
56
|
}
|
|
57
|
+
// #1165: activation.requestManifestPath is a caller path — do not rewrite.
|
|
60
58
|
}
|
|
61
59
|
if (result.terminal !== undefined) {
|
|
62
60
|
for (const artifact of result.terminal.artifacts) {
|
|
@@ -926,7 +924,7 @@ export function resumeTurnRequestProjectionOptions(admitted, request, env, summo
|
|
|
926
924
|
if (request.message !== undefined) {
|
|
927
925
|
if (summonsPrepared !== undefined) {
|
|
928
926
|
// #755: same-ticket review / open-court — caller words.
|
|
929
|
-
// #879 station-child officer: words only (
|
|
927
|
+
// #879 station-child officer: words only (paths remain on summons materials).
|
|
930
928
|
prompt = officerDialogue
|
|
931
929
|
? request.message
|
|
932
930
|
: buildInstructionTransportPrompt({
|
|
@@ -946,8 +944,10 @@ export function resumeTurnRequestProjectionOptions(admitted, request, env, summo
|
|
|
946
944
|
else if (summonsPrepared !== undefined) {
|
|
947
945
|
// #879 station-child officer: instruction bytes === peer body/reask (no wrap).
|
|
948
946
|
// Other seats keep #755 instruction + optional attachment path listing.
|
|
947
|
+
// #1165 J3: officer dialogue is raw instruction bytes; do not gate on
|
|
948
|
+
// instructionEmpty metadata (resume of older admitted pages).
|
|
949
949
|
prompt = officerDialogue
|
|
950
|
-
?
|
|
950
|
+
? summonsPrepared.instruction
|
|
951
951
|
: buildInstructionTransportPrompt(summonsPrepared);
|
|
952
952
|
}
|
|
953
953
|
else if (request.summons !== undefined) {
|
|
@@ -1047,34 +1047,18 @@ async function runSettledAutoResumeLoop(input) {
|
|
|
1047
1047
|
}),
|
|
1048
1048
|
});
|
|
1049
1049
|
}
|
|
1050
|
-
function isAlreadyFrozenSummonsAttachment(runDirectory, attachmentPath) {
|
|
1051
|
-
const absolute = isAbsolute(attachmentPath)
|
|
1052
|
-
? attachmentPath
|
|
1053
|
-
: resolve(attachmentPath);
|
|
1054
|
-
return pathContainedIn(join(runDirectory, "attachments"), absolute);
|
|
1055
|
-
}
|
|
1056
1050
|
/**
|
|
1057
|
-
*
|
|
1058
|
-
* No-op materials (no paths / instruction-only) skip the freeze.
|
|
1059
|
-
* Paths already under this run's attachments/ are the accepted freeze identity —
|
|
1060
|
-
* reuse them for the same internal re-summons flow.
|
|
1061
|
-
* Manual resume never calls this — old attachment semantics stay intact.
|
|
1051
|
+
* Same-ticket summons materials: caller paths as-is (#1165). No copy or freeze.
|
|
1062
1052
|
*/
|
|
1063
|
-
export async function prepareSummonsResumeMaterials(
|
|
1053
|
+
export async function prepareSummonsResumeMaterials(_runDirectory, summons) {
|
|
1064
1054
|
if (summons === undefined)
|
|
1065
1055
|
return undefined;
|
|
1066
1056
|
if (summons.instruction === undefined && (summons.attachmentPaths?.length ?? 0) === 0) {
|
|
1067
1057
|
return undefined;
|
|
1068
1058
|
}
|
|
1069
1059
|
const instruction = summons.instruction ?? "";
|
|
1070
|
-
const instructionEmpty = summons.instructionEmpty ?? instruction.
|
|
1071
|
-
|
|
1072
|
-
if (summons.attachmentPaths !== undefined && summons.attachmentPaths.length > 0) {
|
|
1073
|
-
const alreadyFrozen = summons.attachmentPaths.every((path) => isAlreadyFrozenSummonsAttachment(runDirectory, path));
|
|
1074
|
-
attachments = alreadyFrozen
|
|
1075
|
-
? summons.attachmentPaths.map((frozenPath) => ({ frozenPath }))
|
|
1076
|
-
: await freezeAttachmentsIntoRun(summons.attachmentPaths, runDirectory);
|
|
1077
|
-
}
|
|
1060
|
+
const instructionEmpty = summons.instructionEmpty ?? instruction.length === 0;
|
|
1061
|
+
const attachments = (summons.attachmentPaths ?? []).map((path) => ({ path }));
|
|
1078
1062
|
return { instruction, instructionEmpty, attachments };
|
|
1079
1063
|
}
|
|
1080
1064
|
/**
|
|
@@ -1138,20 +1122,6 @@ export async function runPostAdmissionSeatResume(input) {
|
|
|
1138
1122
|
openCourtAttemptId = openCourt.courtAttemptId;
|
|
1139
1123
|
}
|
|
1140
1124
|
}
|
|
1141
|
-
// Internal re-summons freezes external paths once and records that identity.
|
|
1142
|
-
if (request.summons !== undefined) {
|
|
1143
|
-
const prepared = await prepareSummonsResumeMaterials(admittedForBuild.runDirectory, request.summons);
|
|
1144
|
-
if (prepared !== undefined &&
|
|
1145
|
-
(request.summons.attachmentPaths?.length ?? 0) > 0) {
|
|
1146
|
-
request = {
|
|
1147
|
-
...request,
|
|
1148
|
-
summons: {
|
|
1149
|
-
...request.summons,
|
|
1150
|
-
attachmentPaths: prepared.attachments.map((attachment) => attachment.frozenPath),
|
|
1151
|
-
},
|
|
1152
|
-
};
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
1125
|
// Public explicit resume: attach the stored native id when the caller
|
|
1156
1126
|
// left it unset (399c3c8c). In-call auto-resume / 催交 / gate retry omit
|
|
1157
1127
|
// the field; those load inside the host adapter (host-contracts).
|
|
@@ -1345,13 +1315,7 @@ export async function runPostAdmissionOneShot(input) {
|
|
|
1345
1315
|
...input.request,
|
|
1346
1316
|
continuation: {
|
|
1347
1317
|
kind: "resume",
|
|
1348
|
-
prompt: buildAutoResumeContinuationPrompt(
|
|
1349
|
-
packageRoot: input.env.packageRoot,
|
|
1350
|
-
...pickEngineAxis({
|
|
1351
|
-
engine: input.effectiveEngine ?? input.env.engine,
|
|
1352
|
-
engineModel: input.env.engineModel,
|
|
1353
|
-
}),
|
|
1354
|
-
}),
|
|
1318
|
+
prompt: buildAutoResumeContinuationPrompt(),
|
|
1355
1319
|
},
|
|
1356
1320
|
}),
|
|
1357
1321
|
adapters: input.adapters,
|
|
@@ -13,9 +13,8 @@ import { isSafePositiveTicketNumber } from "../run-ticket-number.js";
|
|
|
13
13
|
import { CliUsageError } from "./cli-errors.js";
|
|
14
14
|
import { parseCollectorRepository } from "../collector-config.js";
|
|
15
15
|
import { retainedRunPathsMatch, rewriteAdmittedRoleRunPage, rewriteRunDirectoryPathValue, } from "../role-run-relocation.js";
|
|
16
|
-
import { appendEngineSessionMaterial, engineSessionMaterialFromOptions, pickEngineAxis, } from "../package-resources/engine-material.js";
|
|
17
16
|
import { packagedResumeSourcePath, packagedRoleMetadata, } from "../packaged-role-registry.js";
|
|
18
|
-
import { admittedReviewerBaseFault, recordEffectiveInvocationModel, requireAuthorityRef, isReviewerLens, } from "./invocation.js";
|
|
17
|
+
import { admittedReviewerBaseFault, recordEffectiveInvocationModel, requireAuthorityRef, isReviewerLens, normalizeAdmittedAttachment, } from "./invocation.js";
|
|
19
18
|
import { readPageSync, writeSectionSync } from "../run-dossier.js";
|
|
20
19
|
import { isRecord, errorText, isEnoent } from "../unknown-value.js";
|
|
21
20
|
/** Default for the #422 configurable single-call auto-resume ceiling
|
|
@@ -33,14 +32,12 @@ export const AUTO_RESUME_LIMIT = 2;
|
|
|
33
32
|
export const RESUME_TRANSPORT_ENVELOPE = "继续。";
|
|
34
33
|
/**
|
|
35
34
|
* Auto-resume continuation only (#959 / ADR 0080).
|
|
36
|
-
* Always non-empty
|
|
35
|
+
* Always non-empty Chinese neutral envelope. Engine / outsourcing material
|
|
36
|
+
* rides startup readingMaterial (#1167), never this continuation.
|
|
37
37
|
* Never call this from manual `ak-role resume`.
|
|
38
38
|
*/
|
|
39
|
-
export function buildAutoResumeContinuationPrompt(
|
|
40
|
-
return
|
|
41
|
-
packageRoot: options.packageRoot,
|
|
42
|
-
...pickEngineAxis(options),
|
|
43
|
-
})).join("\n");
|
|
39
|
+
export function buildAutoResumeContinuationPrompt() {
|
|
40
|
+
return RESUME_TRANSPORT_ENVELOPE;
|
|
44
41
|
}
|
|
45
42
|
const WRITER_LOCK_FILE = "writer.lock";
|
|
46
43
|
export async function writeRoleRunState(runDirectory, record) {
|
|
@@ -54,7 +51,7 @@ function parseSameTicketSummonsMaterials(raw) {
|
|
|
54
51
|
const instruction = typeof record.instruction === "string" ? record.instruction : undefined;
|
|
55
52
|
const instructionEmpty = typeof record.instructionEmpty === "boolean" ? record.instructionEmpty : undefined;
|
|
56
53
|
const attachmentPaths = Array.isArray(record.attachmentPaths)
|
|
57
|
-
? record.attachmentPaths.filter((p) => typeof p === "string"
|
|
54
|
+
? record.attachmentPaths.filter((p) => typeof p === "string")
|
|
58
55
|
: undefined;
|
|
59
56
|
const sourceRunPath = typeof record.sourceRunPath === "string" && record.sourceRunPath.trim() !== ""
|
|
60
57
|
? record.sourceRunPath
|
|
@@ -784,7 +781,6 @@ async function loadResumableRunRecord(home, runId, authority) {
|
|
|
784
781
|
let repository;
|
|
785
782
|
let repositoryDisplay;
|
|
786
783
|
let requestManifestPath;
|
|
787
|
-
let manifestDigest;
|
|
788
784
|
let waitWindowMs;
|
|
789
785
|
let issueNumber;
|
|
790
786
|
let caseRunsPath;
|
|
@@ -810,7 +806,9 @@ async function loadResumableRunRecord(home, runId, authority) {
|
|
|
810
806
|
instructionEmpty = record.instructionEmpty;
|
|
811
807
|
}
|
|
812
808
|
if (Array.isArray(record.attachments)) {
|
|
813
|
-
attachments = record.attachments
|
|
809
|
+
attachments = record.attachments
|
|
810
|
+
.map((item) => normalizeAdmittedAttachment(item))
|
|
811
|
+
.filter((item) => item !== undefined);
|
|
814
812
|
}
|
|
815
813
|
if (record.phase === "plan" || record.phase === "apply") {
|
|
816
814
|
phase = record.phase;
|
|
@@ -845,12 +843,10 @@ async function loadResumableRunRecord(home, runId, authority) {
|
|
|
845
843
|
if (typeof record.repositoryDisplay === "string" && record.repositoryDisplay.trim() !== "") {
|
|
846
844
|
repositoryDisplay = record.repositoryDisplay;
|
|
847
845
|
}
|
|
848
|
-
|
|
846
|
+
// #1165: opaque caller path on resume — do not trim-filter provided values.
|
|
847
|
+
if (typeof record.requestManifestPath === "string") {
|
|
849
848
|
requestManifestPath = record.requestManifestPath;
|
|
850
849
|
}
|
|
851
|
-
if (typeof record.manifestDigest === "string" && record.manifestDigest.trim() !== "") {
|
|
852
|
-
manifestDigest = record.manifestDigest;
|
|
853
|
-
}
|
|
854
850
|
if (typeof record.waitWindowMs === "number" && Number.isSafeInteger(record.waitWindowMs) && record.waitWindowMs >= 1) {
|
|
855
851
|
waitWindowMs = record.waitWindowMs;
|
|
856
852
|
}
|
|
@@ -993,7 +989,6 @@ async function loadResumableRunRecord(home, runId, authority) {
|
|
|
993
989
|
...(repository === undefined ? {} : { repository }),
|
|
994
990
|
...(repositoryDisplay === undefined ? {} : { repositoryDisplay }),
|
|
995
991
|
...(requestManifestPath === undefined ? {} : { requestManifestPath }),
|
|
996
|
-
...(manifestDigest === undefined ? {} : { manifestDigest }),
|
|
997
992
|
...(waitWindowMs === undefined ? {} : { waitWindowMs }),
|
|
998
993
|
...(issueNumber === undefined ? {} : { issueNumber }),
|
|
999
994
|
...(caseRunsPath === undefined ? {} : { caseRunsPath }),
|
|
@@ -1182,10 +1177,8 @@ function admitResumedRole(loaded) {
|
|
|
1182
1177
|
return admitted;
|
|
1183
1178
|
}
|
|
1184
1179
|
case "collect-target": {
|
|
1185
|
-
const { prNumber, repository, repositoryDisplay
|
|
1186
|
-
if (repository === undefined ||
|
|
1187
|
-
repositoryDisplay === undefined ||
|
|
1188
|
-
manifestDigest === undefined) {
|
|
1180
|
+
const { prNumber, repository, repositoryDisplay } = fields;
|
|
1181
|
+
if (repository === undefined || repositoryDisplay === undefined) {
|
|
1189
1182
|
throw new CliUsageError(`role run admitted collector repository identity is missing: ${runId}`);
|
|
1190
1183
|
}
|
|
1191
1184
|
let parsedRepository;
|
|
@@ -1208,7 +1201,6 @@ function admitResumedRole(loaded) {
|
|
|
1208
1201
|
? {}
|
|
1209
1202
|
: { requestManifestPath: fields.requestManifestPath }),
|
|
1210
1203
|
...(fields.waitWindowMs === undefined ? {} : { waitWindowMs: fields.waitWindowMs }),
|
|
1211
|
-
manifestDigest,
|
|
1212
1204
|
};
|
|
1213
1205
|
return admitted;
|
|
1214
1206
|
}
|
|
@@ -22,7 +22,7 @@ const ADMITTED_PAGE_FIELDS = [
|
|
|
22
22
|
"taskPath",
|
|
23
23
|
"packetPath",
|
|
24
24
|
"prerequisitesPath",
|
|
25
|
-
|
|
25
|
+
// #1165: requestManifestPath is a caller path — never rewrite.
|
|
26
26
|
"mergerInputPath",
|
|
27
27
|
"sourceRunPath",
|
|
28
28
|
];
|
|
@@ -150,28 +150,19 @@ function rewriteSourceRunLocator(value, rewrites) {
|
|
|
150
150
|
return;
|
|
151
151
|
rewriteRunDirectoryPathFieldsAgainstRewrites(value, SOURCE_RUN_LOCATOR_FIELDS, rewrites);
|
|
152
152
|
}
|
|
153
|
-
/** Same-ticket summons materials: typed source-run
|
|
153
|
+
/** Same-ticket summons materials: typed source-run; caller attach paths stay as-is (#1165). */
|
|
154
154
|
function rewriteSummonsMaterials(value, rewrites) {
|
|
155
155
|
if (!isRecord(value))
|
|
156
156
|
return;
|
|
157
157
|
rewriteRunDirectoryPathFieldsAgainstRewrites(value, SUMMONS_PATH_FIELDS, rewrites);
|
|
158
158
|
rewriteSourceRunLocator(value.sourceRun, rewrites);
|
|
159
|
-
//
|
|
160
|
-
if (Array.isArray(value.attachmentPaths)) {
|
|
161
|
-
value.attachmentPaths = value.attachmentPaths.map((path) => rewriteRunDirectoryPathValueAgainstRewrites(path, rewrites));
|
|
162
|
-
}
|
|
159
|
+
// #1165: summons.attachmentPaths are caller paths — do not rewrite.
|
|
163
160
|
}
|
|
164
161
|
/** Project one admitted page in memory after its containing run moved. */
|
|
165
162
|
export function rewriteAdmittedRoleRunPage(page, rewrites) {
|
|
166
163
|
rewriteRunDirectoryPathFieldsAgainstRewrites(page, ADMITTED_PAGE_FIELDS, rewrites);
|
|
167
164
|
rewriteSourceRunLocator(page.sourceRun, rewrites);
|
|
168
|
-
|
|
169
|
-
for (const attachment of page.attachments) {
|
|
170
|
-
if (isRecord(attachment)) {
|
|
171
|
-
rewriteRunDirectoryPathFieldsAgainstRewrites(attachment, ["frozenPath"], rewrites);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
165
|
+
// #1165: admitted attachments are caller paths outside the run directory — do not rewrite.
|
|
175
166
|
if (isRecord(page.principal)) {
|
|
176
167
|
rewriteRunDirectoryPathFieldsAgainstRewrites(page.principal, ["sessionDirectory", "sessionFile"], rewrites);
|
|
177
168
|
}
|
|
@@ -347,12 +338,13 @@ async function rewriteNestedMachinePathPages(pagesDirectory, rewrites) {
|
|
|
347
338
|
await walk(sessionRoot);
|
|
348
339
|
}
|
|
349
340
|
/**
|
|
350
|
-
* Rewrite the admitted / invocation / runState fact rows (
|
|
351
|
-
*
|
|
352
|
-
* session seams. Callers may rewrite before or after
|
|
353
|
-
* `pagesDirectory` is where the pages currently live
|
|
354
|
-
* still name `oldRunDirectory` become
|
|
355
|
-
* covers officer/parent pointers that
|
|
341
|
+
* Rewrite the admitted / invocation / runState fact rows (typed run placement
|
|
342
|
+
* paths only; caller --attach / --request-manifest paths stay as-is, #1165),
|
|
343
|
+
* then nested package-owned session seams. Callers may rewrite before or after
|
|
344
|
+
* the filesystem move/copy: `pagesDirectory` is where the pages currently live
|
|
345
|
+
* on disk; path strings that still name `oldRunDirectory` become
|
|
346
|
+
* `newRunDirectory`. `crossRunRewrites` covers officer/parent pointers that
|
|
347
|
+
* landed under a different final placement.
|
|
356
348
|
*/
|
|
357
349
|
export async function rewriteRoleRunDurablePages(input) {
|
|
358
350
|
const { pagesDirectory } = input;
|
|
@@ -366,7 +358,7 @@ export async function rewriteRoleRunDurablePages(input) {
|
|
|
366
358
|
if (isRecord(page.principal)) {
|
|
367
359
|
rewriteRunDirectoryPathFieldsAgainstRewrites(page.principal, ["sessionDirectory", "sessionFile"], rewrites);
|
|
368
360
|
}
|
|
369
|
-
// Open court summons: source-run locators
|
|
361
|
+
// Open court summons: source-run locators only; caller attach paths stay (#1165).
|
|
370
362
|
if (isRecord(page.currentCourt)) {
|
|
371
363
|
rewriteSummonsMaterials(page.currentCourt.summons, rewrites);
|
|
372
364
|
}
|
package/package.json
CHANGED
package/src/collector-config.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
|
|
3
|
-
import { sha256Hex } from "./sha256.ts";
|
|
4
|
-
import { isRecord } from "./unknown-value.ts";
|
|
5
1
|
import { isTicketNumberString, parseTicketNumber } from "./run-ticket-number.ts";
|
|
6
2
|
|
|
7
3
|
export const COLLECTOR_OWNER_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/;
|
|
@@ -14,14 +10,6 @@ export type CollectorRepository = {
|
|
|
14
10
|
repo: string;
|
|
15
11
|
};
|
|
16
12
|
|
|
17
|
-
export type CollectorRequestConfig = { id: string; requestBody: string };
|
|
18
|
-
export type CollectorManifest = {
|
|
19
|
-
requests: readonly CollectorRequestConfig[];
|
|
20
|
-
canonicalJson: string;
|
|
21
|
-
digest: string;
|
|
22
|
-
sourcePath?: string;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
13
|
function fail(message: string, cause?: unknown): never {
|
|
26
14
|
throw new Error(message, cause === undefined ? undefined : { cause });
|
|
27
15
|
}
|
|
@@ -54,33 +42,3 @@ export function parseCollectorPrNumber(raw: unknown): number {
|
|
|
54
42
|
: "Collector pull request number must be a positive safe integer");
|
|
55
43
|
return value;
|
|
56
44
|
}
|
|
57
|
-
|
|
58
|
-
function canonicalManifest(requests: readonly CollectorRequestConfig[]): string {
|
|
59
|
-
return `${JSON.stringify({ requests: requests.map((request) => ({ id: request.id, body: request.requestBody })) })}\n`;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function emptyCollectorManifest(): CollectorManifest {
|
|
63
|
-
const canonicalJson = canonicalManifest([]);
|
|
64
|
-
return { requests: [], canonicalJson, digest: sha256Hex(canonicalJson) };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** Optional request configuration. It names requests, never expected observers. */
|
|
68
|
-
export async function loadCollectorManifest(path: string): Promise<CollectorManifest> {
|
|
69
|
-
let bytes: Buffer;
|
|
70
|
-
try { bytes = await readFile(path); } catch (error) { fail(`Collector request manifest is unreadable at ${path}`, error); }
|
|
71
|
-
let parsed: unknown;
|
|
72
|
-
try { parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes)); } catch (error) { fail("Collector request manifest must be UTF-8 JSON", error); }
|
|
73
|
-
if (!isRecord(parsed)) fail("Collector request manifest must be an object");
|
|
74
|
-
const rawRequests = parsed.requests ?? [];
|
|
75
|
-
if (!Array.isArray(rawRequests)) fail("Collector request manifest requests must be an array");
|
|
76
|
-
const requests: CollectorRequestConfig[] = [];
|
|
77
|
-
const ids = new Set<string>();
|
|
78
|
-
for (const [index, item] of rawRequests.entries()) {
|
|
79
|
-
if (!isRecord(item) || typeof item.id !== "string" || item.id.length === 0 || typeof item.body !== "string" || item.body.trim() === "") fail(`Collector request manifest requests[${index}] is invalid`);
|
|
80
|
-
if (ids.has(item.id)) fail(`Collector request manifest has duplicate request id "${item.id}"`);
|
|
81
|
-
ids.add(item.id);
|
|
82
|
-
requests.push({ id: item.id, requestBody: item.body });
|
|
83
|
-
}
|
|
84
|
-
const canonicalJson = canonicalManifest(requests);
|
|
85
|
-
return { requests, canonicalJson, digest: sha256Hex(canonicalJson), sourcePath: path };
|
|
86
|
-
}
|
package/src/collector-role.ts
CHANGED
|
@@ -2,17 +2,15 @@
|
|
|
2
2
|
* Collector business: soul/material assembly and sole submission tool.
|
|
3
3
|
* #1088: code no longer observes/requests/merges GitHub findings — the LLM
|
|
4
4
|
* uses host CLI tools. Envelope owns host-surface + construction seatbelt.
|
|
5
|
+
* #1165: --request-manifest is a caller path only; package does not load it.
|
|
5
6
|
*/
|
|
6
7
|
import type { RoleHost, HostContext } from "./host-contracts.ts";
|
|
7
8
|
import { registerFiledSubmissionTool } from "./filed-submission.ts";
|
|
8
9
|
import { roleSubmissionDeclaration } from "./role-submission-declarations.ts";
|
|
9
10
|
|
|
10
11
|
import {
|
|
11
|
-
emptyCollectorManifest,
|
|
12
|
-
loadCollectorManifest,
|
|
13
12
|
parseCollectorPrNumber,
|
|
14
13
|
parseCollectorRepository,
|
|
15
|
-
type CollectorManifest,
|
|
16
14
|
type CollectorRepository,
|
|
17
15
|
} from "./collector-config.ts";
|
|
18
16
|
import {
|
|
@@ -47,7 +45,7 @@ export const COLLECTOR_TRANSPORT_FLAGS = Object.freeze([
|
|
|
47
45
|
name: "ak-collector-request-manifest",
|
|
48
46
|
definition: Object.freeze({
|
|
49
47
|
description:
|
|
50
|
-
"
|
|
48
|
+
"Caller path to a request manifest JSON file (passed as-is; package does not read or validate).",
|
|
51
49
|
type: "string" as const,
|
|
52
50
|
}),
|
|
53
51
|
}),
|
|
@@ -62,7 +60,7 @@ export type CollectorActivation = {
|
|
|
62
60
|
soul: string;
|
|
63
61
|
repository: CollectorRepository;
|
|
64
62
|
prNumber: number | undefined;
|
|
65
|
-
|
|
63
|
+
requestManifestPath?: string;
|
|
66
64
|
};
|
|
67
65
|
|
|
68
66
|
function buildMethodContext(activation: CollectorActivation): string {
|
|
@@ -71,19 +69,11 @@ function buildMethodContext(activation: CollectorActivation): string {
|
|
|
71
69
|
`host: github.com`,
|
|
72
70
|
`repository: ${activation.repository.canonical}`,
|
|
73
71
|
`prNumber: ${activation.prNumber === undefined ? "未绑定" : String(activation.prNumber)}`,
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
...(activation.requestManifestPath === undefined
|
|
73
|
+
? []
|
|
74
|
+
: [`requestManifestPath: ${activation.requestManifestPath}`]),
|
|
76
75
|
"</collector_method>",
|
|
77
76
|
];
|
|
78
|
-
if (activation.manifest.requests.length > 0) {
|
|
79
|
-
const payload = JSON.stringify({
|
|
80
|
-
requests: activation.manifest.requests.map((request) => ({
|
|
81
|
-
id: request.id,
|
|
82
|
-
body: request.requestBody,
|
|
83
|
-
})),
|
|
84
|
-
}).replaceAll("<", "\\u003c");
|
|
85
|
-
lines.push("", "<collector_request_manifest>", payload, "</collector_request_manifest>");
|
|
86
|
-
}
|
|
87
77
|
return lines.join("\n");
|
|
88
78
|
}
|
|
89
79
|
|
|
@@ -123,15 +113,15 @@ export function createCollectorRoleRuntime(
|
|
|
123
113
|
} else if (typeof prFlag === "number") {
|
|
124
114
|
prNumber = parseCollectorPrNumber(prFlag);
|
|
125
115
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
:
|
|
116
|
+
// #1165: opaque caller path — keep spaces/"" when the flag was provided.
|
|
117
|
+
const requestManifestPath =
|
|
118
|
+
typeof requestManifestFlag === "string" ? requestManifestFlag : undefined;
|
|
129
119
|
|
|
130
120
|
return {
|
|
131
121
|
soul,
|
|
132
122
|
repository,
|
|
133
123
|
prNumber,
|
|
134
|
-
|
|
124
|
+
...(requestManifestPath === undefined ? {} : { requestManifestPath }),
|
|
135
125
|
};
|
|
136
126
|
},
|
|
137
127
|
|
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Packaged engine method-material seam (#356 T1 / ADR 0069 / #376).
|
|
2
|
+
* Packaged engine method-material seam (#356 T1 / ADR 0069 / #376 / #1167).
|
|
3
3
|
* Engine names are owner pool-directive labels — not a closed material catalog.
|
|
4
4
|
* Material body is optional data for the LLM, not a code contract.
|
|
5
5
|
* Only path-safety syntax is checked at real I/O seams.
|
|
6
|
+
* Delivery is startup readingMaterial (handbook bodies), never the transport prompt.
|
|
6
7
|
*/
|
|
7
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
8
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
8
9
|
import { join } from "node:path";
|
|
9
10
|
|
|
10
11
|
const ENGINE_MATERIAL_RELATIVE_ROOT = "resources/engines" as const;
|
|
12
|
+
const ENGINE_DISPATCH_RELATIVE_PATH = "resources/engine-dispatch.md" as const;
|
|
11
13
|
|
|
12
14
|
export type EngineSessionMaterial = Readonly<{
|
|
13
15
|
name: string;
|
|
14
|
-
/** Present only when a packaged notes file exists for this name. */
|
|
15
|
-
materialPath?: string;
|
|
16
16
|
/**
|
|
17
17
|
* Optional owner pool-directive model id for multi-model engines (#883).
|
|
18
18
|
* Opaque pass-through coordinate for the seat — not validated against a catalog.
|
|
19
19
|
*/
|
|
20
20
|
model?: string;
|
|
21
|
+
/** Packaged per-engine handbook body when notes exist. */
|
|
22
|
+
handbook?: string;
|
|
23
|
+
/** Shared engine-dispatch.md body when the per-engine handbook exists. */
|
|
24
|
+
dispatchHandbook?: string;
|
|
21
25
|
}>;
|
|
22
26
|
|
|
23
27
|
/** Project engine name + optional model for env / request / material spreads. */
|
|
@@ -57,6 +61,10 @@ export function resolveEngineMaterialDirectory(packageRoot: string): string {
|
|
|
57
61
|
return join(packageRoot, ENGINE_MATERIAL_RELATIVE_ROOT);
|
|
58
62
|
}
|
|
59
63
|
|
|
64
|
+
export function resolveEngineDispatchMaterialPath(packageRoot: string): string {
|
|
65
|
+
return join(packageRoot, ENGINE_DISPATCH_RELATIVE_PATH);
|
|
66
|
+
}
|
|
67
|
+
|
|
60
68
|
/**
|
|
61
69
|
* Enumerate packaged engine notes stems (discovery only — not a legal-name gate).
|
|
62
70
|
* Only `*.md` stems that pass name syntax are listed.
|
|
@@ -99,7 +107,7 @@ export function assertLegalEngineName(name: string): string {
|
|
|
99
107
|
/**
|
|
100
108
|
* Resolve optional engine options into session material coordinates.
|
|
101
109
|
* No engine → undefined (caller keeps default prompt bytes).
|
|
102
|
-
* Engine with packaged notes → name +
|
|
110
|
+
* Engine with packaged notes → name + handbook body + dispatch handbook body.
|
|
103
111
|
* Engine without notes → name only (pass-through; no warning).
|
|
104
112
|
*/
|
|
105
113
|
export function engineSessionMaterialFromOptions(options: {
|
|
@@ -119,41 +127,40 @@ export function engineSessionMaterialFromOptions(options: {
|
|
|
119
127
|
: assertLegalEngineModel(options.engineModel);
|
|
120
128
|
const materialPath = resolveEngineMaterialPath(options.packageRoot, name);
|
|
121
129
|
const modelField = model === undefined ? {} : { model };
|
|
122
|
-
if (existsSync(materialPath)) {
|
|
123
|
-
return Object.freeze({ name,
|
|
130
|
+
if (!existsSync(materialPath)) {
|
|
131
|
+
return Object.freeze({ name, ...modelField });
|
|
124
132
|
}
|
|
125
|
-
|
|
133
|
+
const handbook = readFileSync(materialPath, "utf8");
|
|
134
|
+
const dispatchPath = resolveEngineDispatchMaterialPath(options.packageRoot);
|
|
135
|
+
const dispatchField = existsSync(dispatchPath)
|
|
136
|
+
? { dispatchHandbook: readFileSync(dispatchPath, "utf8") }
|
|
137
|
+
: {};
|
|
138
|
+
return Object.freeze({ name, handbook, ...dispatchField, ...modelField });
|
|
126
139
|
}
|
|
127
140
|
|
|
128
141
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* With notes → Chinese neutral handbook header + engine name + absolute material path.
|
|
132
|
-
* Name only → engine name coordinate only (no handbook header, no path, no warning).
|
|
133
|
-
* Never delivers material body.
|
|
142
|
+
* Typed startup readingMaterial for a resolved engine session material.
|
|
143
|
+
* Undefined engine → undefined (no outsourcing segment).
|
|
134
144
|
*/
|
|
135
|
-
export function
|
|
136
|
-
lines: readonly string[],
|
|
145
|
+
export function engineSessionReadingMaterial(
|
|
137
146
|
engineMaterial?: EngineSessionMaterial,
|
|
138
|
-
):
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
out.push("本次配置的劳务引擎及其手册:");
|
|
146
|
-
out.push(`- engine: ${engineMaterial.name}`);
|
|
147
|
-
if (engineMaterial.model !== undefined) {
|
|
148
|
-
out.push(`- engineModel: ${engineMaterial.model}`);
|
|
149
|
-
}
|
|
150
|
-
out.push(`- ${engineMaterial.materialPath}`);
|
|
151
|
-
} else {
|
|
152
|
-
// Name-only pass-through: no packaged bytes to claim as handbook.
|
|
153
|
-
out.push(`- engine: ${engineMaterial.name}`);
|
|
154
|
-
if (engineMaterial.model !== undefined) {
|
|
155
|
-
out.push(`- engineModel: ${engineMaterial.model}`);
|
|
147
|
+
):
|
|
148
|
+
| {
|
|
149
|
+
readonly kind: "engine-session-material";
|
|
150
|
+
readonly name: string;
|
|
151
|
+
readonly model?: string;
|
|
152
|
+
readonly handbook?: string;
|
|
153
|
+
readonly dispatchHandbook?: string;
|
|
156
154
|
}
|
|
157
|
-
|
|
158
|
-
return
|
|
155
|
+
| undefined {
|
|
156
|
+
if (engineMaterial === undefined) return undefined;
|
|
157
|
+
return {
|
|
158
|
+
kind: "engine-session-material" as const,
|
|
159
|
+
name: engineMaterial.name,
|
|
160
|
+
...(engineMaterial.model === undefined ? {} : { model: engineMaterial.model }),
|
|
161
|
+
...(engineMaterial.handbook === undefined ? {} : { handbook: engineMaterial.handbook }),
|
|
162
|
+
...(engineMaterial.dispatchHandbook === undefined
|
|
163
|
+
? {}
|
|
164
|
+
: { dispatchHandbook: engineMaterial.dispatchHandbook }),
|
|
165
|
+
};
|
|
159
166
|
}
|