@akagilnc/pi-workflow-roles 0.1.3565 → 0.1.3621
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 +4 -1
- package/README.zh-CN.md +1 -1
- package/dist/acp-host/production-host.js +18477 -8099
- package/dist/analyst-gate-cycles-read.js +387 -0
- package/dist/archivist-record-entry.js +22 -1
- package/dist/audit-escalation.js +6 -0
- package/dist/auditor-soul.js +74 -0
- package/dist/collector-config.js +86 -0
- package/dist/collector-evidence.js +316 -0
- package/dist/collector-github.js +527 -0
- package/dist/collector-ledger.js +853 -0
- package/dist/collector-tool-schemas.js +33 -0
- package/dist/compliance-transport.js +104 -52
- package/dist/diarist-contracts.js +72 -0
- package/dist/diarist-mechanical.js +411 -0
- package/dist/diarist-ticket-resolution.js +177 -0
- package/dist/diarist.js +311 -0
- package/dist/doctor-auditor.js +25 -0
- package/dist/doctor-contracts.js +2 -0
- package/dist/doctor-evidence.js +142 -0
- package/dist/gatekeeper-role.js +104 -81
- package/dist/host-transition-prior-native.js +78 -0
- package/dist/institutional-resolution.js +1 -95
- package/dist/judge-auditor.js +26 -0
- package/dist/ledger-session-read.js +227 -0
- package/dist/merger-git-state.js +78 -0
- package/dist/navigator-attendance.js +15 -7
- package/dist/navigator-public-session.js +170 -0
- package/dist/navigator-session-contracts.js +36 -40
- package/dist/notary-source-run.js +122 -0
- package/dist/package-contracts/auditor-output.js +66 -0
- package/dist/package-contracts/evidence-child-output.js +34 -0
- package/dist/package-contracts/judge-output.js +2 -0
- package/dist/package-contracts/terminating-tools.js +44 -3
- package/dist/package-resources/method-skill.js +254 -0
- package/dist/packaged-role-registry.js +56 -0
- package/dist/pi/durable-principal.js +61 -0
- package/dist/pi/in-process-session.js +25 -6
- package/dist/pi/known-failure.js +52 -0
- package/dist/pi/role-turn-host.js +430 -0
- package/dist/public-cli/auto-resume.js +414 -0
- package/dist/public-cli/cli-errors.js +8 -0
- package/dist/public-cli/cli-io.js +1 -0
- package/dist/public-cli/command-renderer.js +5 -0
- package/dist/public-cli/doctor-run.js +84 -0
- package/dist/public-cli/inspector-run.js +136 -0
- package/dist/public-cli/instruction-seat-run.js +256 -0
- package/dist/public-cli/invocation.js +2225 -0
- package/dist/public-cli/judge-run.js +118 -0
- package/dist/public-cli/load-production-acp-host.js +40 -0
- package/dist/public-cli/main.js +1511 -1124
- package/dist/public-cli/notary-run.js +161 -0
- package/dist/public-cli/option-definitions.js +1192 -0
- package/dist/public-cli/post-admission.js +667 -0
- package/dist/public-cli/public-run-credentials.js +49 -0
- package/dist/public-cli/registry.js +8 -2
- package/dist/public-cli/reviewer-dispatch-rejection.js +77 -0
- package/dist/public-cli/run-lifecycle.js +1344 -0
- package/dist/public-cli/seat-ticket-binding.js +113 -0
- package/dist/public-cli/settlement.js +3441 -0
- package/dist/public-cli/terminal.js +135 -0
- package/dist/public-cli/turn-request.js +25 -0
- package/dist/public-role-summons.js +301 -0
- package/dist/receipt-delivery-policy.js +10 -0
- package/dist/reviewer-child-executor.js +80 -8
- package/dist/reviewer-execution-ledger.js +2 -1
- package/dist/run-terminal-artifacts.js +195 -0
- package/dist/run-ticket-number.js +40 -0
- package/dist/session-assistant-usage.js +107 -0
- package/dist/shape-unreadable-failure.js +34 -0
- package/dist/submission-errors.js +3 -0
- package/dist/submission-ledger.js +388 -0
- package/dist/ticket-provenance-contracts.js +115 -0
- package/dist/ticket-provenance.js +340 -0
- package/extensions/role-runtime.ts +8 -0
- package/package.json +1 -1
- package/resources/diarist-collect.md +6 -10
- package/scripts/build-package.mjs +2 -1
- package/souls/diarist.md +9 -0
- package/souls/doctor-auditor.md +1 -0
- package/souls/evidence-child.md +1 -0
- package/src/acp-host/production-host.ts +4 -0
- package/src/acp-host/role-envelope.ts +4 -3
- package/src/acp-host/role-turn-host.ts +9 -1
- package/src/analyst-gate-cycles-read.ts +37 -2
- package/src/archivist-record-entry.ts +45 -1
- package/src/audit-escalation.ts +17 -0
- package/src/auditor-role.ts +22 -0
- package/src/auditor-soul.ts +42 -0
- package/src/compliance-transport.ts +177 -66
- package/src/diarist-contracts.ts +88 -0
- package/src/diarist-role.ts +60 -0
- package/src/diarist.ts +253 -179
- package/src/doctor-auditor.ts +10 -14
- package/src/doctor-contracts.ts +1 -0
- package/src/doctor-role.ts +2 -2
- package/src/evidence-child-role.ts +22 -0
- package/src/gatekeeper-pass-envelope.ts +109 -0
- package/src/gatekeeper-role.ts +145 -104
- package/src/host-contracts.ts +15 -2
- package/src/institutional-resolution.ts +8 -163
- package/src/judge-auditor.ts +10 -15
- package/src/judge-role.ts +8 -0
- package/src/navigator-attendance.ts +25 -9
- package/src/navigator-public-session.ts +217 -0
- package/src/navigator-session-contracts.ts +61 -54
- package/src/notary-source-run.ts +3 -1
- package/src/package-contracts/auditor-output.ts +82 -0
- package/src/package-contracts/evidence-child-output.ts +51 -0
- package/src/package-contracts/judge-output.ts +1 -0
- package/src/package-contracts/reviewer-output.ts +2 -2
- package/src/package-contracts/terminating-tools.ts +53 -3
- package/src/packaged-role-registry.ts +63 -0
- package/src/pi/adapter.ts +2 -1
- package/src/pi/in-process-session.ts +38 -12
- package/src/pi/role-turn-host.ts +42 -0
- package/src/public-cli/cli.ts +43 -6
- package/src/public-cli/countersign-run.ts +9 -129
- package/src/public-cli/diarist-run.ts +312 -0
- package/src/public-cli/instruction-seat-run.ts +289 -50
- package/src/public-cli/invocation.ts +87 -25
- package/src/public-cli/option-definitions.ts +79 -0
- package/src/public-cli/post-admission.ts +9 -3
- package/src/public-cli/registry.ts +7 -1
- package/src/public-cli/run-lifecycle.ts +53 -6
- package/src/public-cli/settlement.ts +170 -7
- package/src/public-cli/terminal.ts +4 -1
- package/src/public-role-summons.ts +431 -0
- package/src/receipt-delivery-policy.ts +10 -0
- package/src/reviewer-agent.ts +1 -1
- package/src/reviewer-child-executor.ts +114 -12
- package/src/reviewer-execution-ledger.ts +3 -2
- package/src/role-runtime.ts +275 -23
- package/src/session-assistant-usage.ts +119 -0
- package/src/session-opening-materials.ts +1 -1
- package/src/shape-unreadable-failure.ts +49 -0
- package/src/submission-errors.ts +3 -0
- package/src/ticket-provenance-contracts.ts +1 -0
- package/src/ticket-provenance.ts +0 -23
- package/dist/evidence-child-executor.js +0 -829
- package/src/diarist-llm-collector.ts +0 -324
- package/src/evidence-child-executor.ts +0 -1140
package/src/pi/role-turn-host.ts
CHANGED
|
@@ -146,6 +146,18 @@ function buildActivationFlagArgs(activation: RoleTurnActivation): string[] {
|
|
|
146
146
|
return ["--ak-role", "gatekeeper"];
|
|
147
147
|
case "navigator":
|
|
148
148
|
return ["--ak-role", "navigator"];
|
|
149
|
+
case "auditor":
|
|
150
|
+
return ["--ak-role", "auditor"];
|
|
151
|
+
case "evidence-child":
|
|
152
|
+
return ["--ak-role", "evidence-child"];
|
|
153
|
+
case "diarist":
|
|
154
|
+
return [
|
|
155
|
+
"--ak-role",
|
|
156
|
+
"diarist",
|
|
157
|
+
...(activation.sourcesPath === undefined
|
|
158
|
+
? []
|
|
159
|
+
: ["--ak-diarist-sources", activation.sourcesPath]),
|
|
160
|
+
];
|
|
149
161
|
default: {
|
|
150
162
|
const _exhaustive: never = activation;
|
|
151
163
|
return _exhaustive;
|
|
@@ -205,6 +217,8 @@ export type PiSpawnRunner = (
|
|
|
205
217
|
cwd: string;
|
|
206
218
|
env: NodeJS.ProcessEnv;
|
|
207
219
|
timeoutMs?: number;
|
|
220
|
+
/** Parent cancellation; the child gets the same graceful SIGTERM as a budget. */
|
|
221
|
+
signal?: AbortSignal;
|
|
208
222
|
},
|
|
209
223
|
) => Promise<{
|
|
210
224
|
code: number | null;
|
|
@@ -338,9 +352,17 @@ export function createDefaultPiSpawnRunner(options: {
|
|
|
338
352
|
child.kill("SIGTERM");
|
|
339
353
|
}, spawnOptions.timeoutMs);
|
|
340
354
|
};
|
|
355
|
+
// Parent cancellation reaches the nested activation: same graceful SIGTERM,
|
|
356
|
+
// same single close settlement. SIGKILL stays forbidden (#675 / ADR 0010).
|
|
357
|
+
const parentSignal = spawnOptions.signal;
|
|
358
|
+
const terminateForParentAbort = (): void => {
|
|
359
|
+
child.kill("SIGTERM");
|
|
360
|
+
};
|
|
361
|
+
parentSignal?.addEventListener("abort", terminateForParentAbort, { once: true });
|
|
341
362
|
let identityRecorded: Promise<void> = Promise.resolve();
|
|
342
363
|
child.once("spawn", () => {
|
|
343
364
|
hasSpawned = true;
|
|
365
|
+
if (parentSignal?.aborted === true) terminateForParentAbort();
|
|
344
366
|
armTimeoutAfterChildReady();
|
|
345
367
|
const runDirectory = spawnOptions.env.AK_ROLE_RUN_DIR;
|
|
346
368
|
if (
|
|
@@ -363,11 +385,13 @@ export function createDefaultPiSpawnRunner(options: {
|
|
|
363
385
|
return;
|
|
364
386
|
}
|
|
365
387
|
if (timer !== undefined) clearTimeout(timer);
|
|
388
|
+
parentSignal?.removeEventListener("abort", terminateForParentAbort);
|
|
366
389
|
settled = true;
|
|
367
390
|
reject(error);
|
|
368
391
|
});
|
|
369
392
|
child.on("close", (code) => {
|
|
370
393
|
if (timer !== undefined) clearTimeout(timer);
|
|
394
|
+
parentSignal?.removeEventListener("abort", terminateForParentAbort);
|
|
371
395
|
void identityRecorded.then(
|
|
372
396
|
() => {
|
|
373
397
|
if (settled) return;
|
|
@@ -439,10 +463,27 @@ export function createPiRoleTurnHost(config: PiRoleTurnHostConfig): RoleTurnHost
|
|
|
439
463
|
HOME: request.home,
|
|
440
464
|
PI_CODING_AGENT_DIR: request.agentDir,
|
|
441
465
|
AK_ROLE_RUN_DIR: request.runDirectory,
|
|
466
|
+
// Nested public summons resolve package root without import.meta under jiti (#675).
|
|
467
|
+
// Child-process scoped only — not written back onto the parent process.env.
|
|
468
|
+
AK_ROLE_PACKAGE_ROOT: config.packageRoot,
|
|
442
469
|
};
|
|
443
470
|
if (request.courtAttemptId === undefined) delete env.AK_ROLE_COURT_ATTEMPT;
|
|
444
471
|
else env.AK_ROLE_COURT_ATTEMPT = request.courtAttemptId;
|
|
445
472
|
applyEngineChildEnv(env, request.engine);
|
|
473
|
+
// Nested auditor dossier tool binds the parent run pointer when published.
|
|
474
|
+
if (
|
|
475
|
+
typeof process.env.AK_ROLE_AUDITOR_SOURCE_RUN === "string"
|
|
476
|
+
&& process.env.AK_ROLE_AUDITOR_SOURCE_RUN.trim() !== ""
|
|
477
|
+
) {
|
|
478
|
+
env.AK_ROLE_AUDITOR_SOURCE_RUN = process.env.AK_ROLE_AUDITOR_SOURCE_RUN;
|
|
479
|
+
}
|
|
480
|
+
// Audited-subject input selects soul materials (same for nested and direct).
|
|
481
|
+
if (
|
|
482
|
+
typeof process.env.AK_ROLE_AUDITOR_SUBJECT === "string"
|
|
483
|
+
&& process.env.AK_ROLE_AUDITOR_SUBJECT.trim() !== ""
|
|
484
|
+
) {
|
|
485
|
+
env.AK_ROLE_AUDITOR_SUBJECT = process.env.AK_ROLE_AUDITOR_SUBJECT;
|
|
486
|
+
}
|
|
446
487
|
if (request.correlationId !== undefined && request.correlationId.trim() !== "") {
|
|
447
488
|
env.AK_CORRELATION_ID = request.correlationId;
|
|
448
489
|
}
|
|
@@ -460,6 +501,7 @@ export function createPiRoleTurnHost(config: PiRoleTurnHostConfig): RoleTurnHost
|
|
|
460
501
|
cwd: request.cwd,
|
|
461
502
|
env,
|
|
462
503
|
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
504
|
+
...(request.signal === undefined ? {} : { signal: request.signal }),
|
|
463
505
|
});
|
|
464
506
|
},
|
|
465
507
|
};
|
package/src/public-cli/cli.ts
CHANGED
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
parseCoderArgv,
|
|
52
52
|
parseCollectorArgv,
|
|
53
53
|
parseCountersignArgv,
|
|
54
|
+
parseDiaristArgv,
|
|
54
55
|
parseGleanerLeftArgv,
|
|
55
56
|
parseDoctorArgv,
|
|
56
57
|
parseFixerArgv,
|
|
@@ -59,6 +60,8 @@ import {
|
|
|
59
60
|
parseInspectorArgv,
|
|
60
61
|
parseMergerArgv,
|
|
61
62
|
parseNavigatorArgv,
|
|
63
|
+
parseAuditorArgv,
|
|
64
|
+
parseEvidenceChildArgv,
|
|
62
65
|
parseNotaryArgv,
|
|
63
66
|
parseReviewerArgv,
|
|
64
67
|
recordLaunchedPiIdentity,
|
|
@@ -78,6 +81,7 @@ import { runPublicCoder, runPublicCoderResume } from "./coder-run.ts";
|
|
|
78
81
|
import { runPublicInstructionSeat, runPublicInstructionSeatResume } from "./instruction-seat-run.ts";
|
|
79
82
|
import { runPublicCollector, runPublicCollectorResume } from "./collector-run.ts";
|
|
80
83
|
import { runPublicCountersign, runPublicCountersignResume } from "./countersign-run.ts";
|
|
84
|
+
import { runPublicDiarist, runPublicDiaristResume } from "./diarist-run.ts";
|
|
81
85
|
import { runPublicGleanerLeft, runPublicGleanerLeftResume } from "./gleaner-left-run.ts";
|
|
82
86
|
import { runPublicDoctor, runPublicDoctorResume } from "./doctor-run.ts";
|
|
83
87
|
import { runPublicFixer, runPublicFixerResume } from "./fixer-run.ts";
|
|
@@ -126,6 +130,9 @@ const RESUME_SEAT_DISPATCH: Record<
|
|
|
126
130
|
inspector: { seat: "inspector", run: runPublicInspectorResume },
|
|
127
131
|
gatekeeper: { seat: "gatekeeper", run: runPublicInstructionSeatResume },
|
|
128
132
|
navigator: { seat: "navigator", run: runPublicInstructionSeatResume },
|
|
133
|
+
auditor: { seat: "auditor", run: runPublicInstructionSeatResume },
|
|
134
|
+
"evidence-child": { seat: "evidence-child", run: runPublicInstructionSeatResume },
|
|
135
|
+
diarist: { seat: "diarist", run: runPublicDiaristResume },
|
|
129
136
|
};
|
|
130
137
|
import {
|
|
131
138
|
INTERNAL_ROLE_ENTRYPOINT_RELATIVE,
|
|
@@ -168,6 +175,9 @@ export const PUBLIC_ROLE_ARGV = {
|
|
|
168
175
|
reviewer: { parse: parseReviewerArgv, options: optionsForOwner("reviewer") },
|
|
169
176
|
gatekeeper: { parse: parseGatekeeperArgv, options: optionsForOwner("gatekeeper") },
|
|
170
177
|
navigator: { parse: parseNavigatorArgv, options: optionsForOwner("navigator") },
|
|
178
|
+
auditor: { parse: parseAuditorArgv, options: optionsForOwner("auditor") },
|
|
179
|
+
"evidence-child": { parse: parseEvidenceChildArgv, options: optionsForOwner("evidence-child") },
|
|
180
|
+
diarist: { parse: parseDiaristArgv, options: optionsForOwner("diarist") },
|
|
171
181
|
/** Deterministic analysis seat (#336) — argv parse only; no LLM admission. */
|
|
172
182
|
analyst: { parse: parseAnalystArgv, options: optionsForOwner("analyst") },
|
|
173
183
|
} as const;
|
|
@@ -1311,6 +1321,31 @@ export async function runAkRole(
|
|
|
1311
1321
|
};
|
|
1312
1322
|
}
|
|
1313
1323
|
|
|
1324
|
+
// Diarist public run path (#708): 起居郎 is summoned like any other seat.
|
|
1325
|
+
if (parsed.command === "diarist") {
|
|
1326
|
+
const agentDir = resolveAgentDir(env, home);
|
|
1327
|
+
const cwd = env.cwd ?? process.cwd();
|
|
1328
|
+
const config = await loadAndValidateConfig(home, env.packageRoot);
|
|
1329
|
+
const credentials =
|
|
1330
|
+
env.credentials ?? (await loadCredentialProviders(agentDir));
|
|
1331
|
+
const seat = resolveEffectiveSeat(
|
|
1332
|
+
config,
|
|
1333
|
+
"diarist",
|
|
1334
|
+
credentials,
|
|
1335
|
+
invocationFromParsed(parsed),
|
|
1336
|
+
);
|
|
1337
|
+
const result = await runPublicDiarist(
|
|
1338
|
+
parsed.args,
|
|
1339
|
+
createRoleEnvironment(env, { role: "diarist", home, agentDir, cwd, credentials, seat, config }),
|
|
1340
|
+
io,
|
|
1341
|
+
PUBLIC_ROLE_ARGV.diarist.parse,
|
|
1342
|
+
);
|
|
1343
|
+
return {
|
|
1344
|
+
exitCode: result.exitCode,
|
|
1345
|
+
...(result.terminal === undefined ? {} : { terminal: result.terminal }),
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1314
1349
|
// Coder public run path with package-owned TDD method (#109).
|
|
1315
1350
|
if (parsed.command === "coder") {
|
|
1316
1351
|
const agentDir = resolveAgentDir(env, home);
|
|
@@ -1510,9 +1545,13 @@ export async function runAkRole(
|
|
|
1510
1545
|
};
|
|
1511
1546
|
}
|
|
1512
1547
|
|
|
1513
|
-
//
|
|
1514
|
-
|
|
1515
|
-
|
|
1548
|
+
// Instruction seats (#639 / #675): gatekeeper / navigator / auditor / evidence-child.
|
|
1549
|
+
if (
|
|
1550
|
+
parsed.command === "gatekeeper"
|
|
1551
|
+
|| parsed.command === "navigator"
|
|
1552
|
+
|| parsed.command === "auditor"
|
|
1553
|
+
|| parsed.command === "evidence-child"
|
|
1554
|
+
) {
|
|
1516
1555
|
const agentDir = resolveAgentDir(env, home);
|
|
1517
1556
|
const cwd = env.cwd ?? process.cwd();
|
|
1518
1557
|
const config = await loadAndValidateConfig(home, env.packageRoot);
|
|
@@ -1529,9 +1568,7 @@ export async function runAkRole(
|
|
|
1529
1568
|
createRoleEnvironment(env, { role: parsed.command, home, agentDir, cwd, credentials, seat, config }),
|
|
1530
1569
|
io,
|
|
1531
1570
|
parsed.command,
|
|
1532
|
-
parsed.command
|
|
1533
|
-
? PUBLIC_ROLE_ARGV.gatekeeper.parse
|
|
1534
|
-
: PUBLIC_ROLE_ARGV.navigator.parse,
|
|
1571
|
+
PUBLIC_ROLE_ARGV[parsed.command].parse,
|
|
1535
1572
|
);
|
|
1536
1573
|
return {
|
|
1537
1574
|
exitCode: result.exitCode,
|
|
@@ -1,20 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Public Countersign Role run: admit ticket materials →
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* Public Countersign Role run: admit ticket materials → shared post-admission
|
|
3
|
+
* coordinator → settle Terminal result (#572 / ADR 0074). #599: manual resume
|
|
4
|
+
* continues the exact session. Unbound admission resolves its ticket via the
|
|
5
|
+
* shared seat LLM bind (#635). Who runs 起居郎, and when, is the caller's
|
|
6
|
+
* composition (ADR 0010 / ADR 0075 `no-call-rule`) — not this seat's business.
|
|
7
7
|
*/
|
|
8
8
|
import type { DurablePrincipalAuthority, RoleTurnRequest } from "../host-contracts.ts";
|
|
9
|
-
import {
|
|
10
|
-
createDiaristIssueFaceFetcher,
|
|
11
|
-
DiaristIssueSourceError,
|
|
12
|
-
resolveDiaristGithubOrigin,
|
|
13
|
-
runDiarist,
|
|
14
|
-
type DiaristIssueFace,
|
|
15
|
-
type DiaristRunResult,
|
|
16
|
-
} from "../diarist.ts";
|
|
17
|
-
import { appendIssueSourceFailureDiagnostic } from "../ticket-provenance.ts";
|
|
18
9
|
import { engineSessionMaterialFromOptions } from "../package-resources/engine-material.ts";
|
|
19
10
|
import { CliUsageError } from "./cli-errors.ts";
|
|
20
11
|
import {
|
|
@@ -29,7 +20,6 @@ import {
|
|
|
29
20
|
ticketNumberFromProbe,
|
|
30
21
|
tryResumeSameTicketSeatRun,
|
|
31
22
|
} from "./seat-ticket-binding.ts";
|
|
32
|
-
import { tryHomeFromAkRolesPath } from "../activation-ledger-topology.ts";
|
|
33
23
|
import {
|
|
34
24
|
prepareSummonsResumeMaterials,
|
|
35
25
|
runPostAdmissionOneShot,
|
|
@@ -194,7 +184,6 @@ export async function runPublicCountersign(
|
|
|
194
184
|
turnRequest,
|
|
195
185
|
buildCountersignTurnRequest(admitted, turnProjection),
|
|
196
186
|
);
|
|
197
|
-
await runCountersignDiaristStation(admitted, env);
|
|
198
187
|
},
|
|
199
188
|
}),
|
|
200
189
|
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
@@ -222,10 +211,9 @@ function countersignAdapters(options?: {
|
|
|
222
211
|
|
|
223
212
|
/**
|
|
224
213
|
* Resume a previously admitted Countersign run (#599 / DK-3 / #637).
|
|
225
|
-
* Restores role/ticket/session identity.
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
* keeps package-envelope / caller-message semantics and birth attachments.
|
|
214
|
+
* Restores role/ticket/session identity. Same-ticket summons deliver this turn's
|
|
215
|
+
* instruction + frozen attachments on the resume prompt; manual resume keeps
|
|
216
|
+
* package-envelope / caller-message semantics and birth attachments.
|
|
229
217
|
*/
|
|
230
218
|
export async function runPublicCountersignResume(
|
|
231
219
|
request: PublicResumeRequest,
|
|
@@ -261,115 +249,7 @@ export async function runPublicCountersignResume(
|
|
|
261
249
|
),
|
|
262
250
|
);
|
|
263
251
|
},
|
|
264
|
-
adapters: countersignAdapters(
|
|
265
|
-
beforeDispatch: async (admitted) => {
|
|
266
|
-
await runCountersignDiaristStation(admitted, env);
|
|
267
|
-
},
|
|
268
|
-
}),
|
|
252
|
+
adapters: countersignAdapters(),
|
|
269
253
|
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
270
254
|
});
|
|
271
255
|
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Court-pipeline prior station: refresh ticket-provenance before countersign turn.
|
|
275
|
-
* Caller-invisible — collector failures append durable volume diagnostics and the
|
|
276
|
-
* station continues; issue-source / ADR source-read / watermark honesty failures
|
|
277
|
-
* leave typed durable diagnostics and propagate (失败诚实).
|
|
278
|
-
* Missing ticketNumber (true-unbound after pre-court resolution) skips the station
|
|
279
|
-
* — no diary is minted for a true-unbound run.
|
|
280
|
-
*
|
|
281
|
-
* Issue body/comments come from the shared GitHub seam only. Attachments stay
|
|
282
|
-
* attachments — never merged and mislabeled as issue-body-comment.
|
|
283
|
-
* Bound ticket never silently degrades to “no issue face”.
|
|
284
|
-
*/
|
|
285
|
-
export async function runCountersignDiaristStation(
|
|
286
|
-
admitted: AdmittedCountersignInvocation,
|
|
287
|
-
env: Pick<CountersignRunEnv, "cwd" | "packageRoot">,
|
|
288
|
-
): Promise<DiaristRunResult | undefined> {
|
|
289
|
-
if (admitted.ticketNumber === undefined) return undefined;
|
|
290
|
-
|
|
291
|
-
const issueFace = await loadBoundIssueFace(admitted);
|
|
292
|
-
const home = tryHomeFromAkRolesPath(admitted.runDirectory);
|
|
293
|
-
|
|
294
|
-
const result = await runDiarist({
|
|
295
|
-
ticketNumber: admitted.ticketNumber,
|
|
296
|
-
cwd: admitted.projectRoot,
|
|
297
|
-
...(home === undefined ? {} : { home }),
|
|
298
|
-
issueFace,
|
|
299
|
-
sessionCwds: [admitted.projectRoot, env.cwd],
|
|
300
|
-
...(env.packageRoot === undefined ? {} : { packageRoot: env.packageRoot }),
|
|
301
|
-
});
|
|
302
|
-
return result;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Acquire issue face for a bound ticket. Failures are typed + durable on the
|
|
307
|
-
* ticket-provenance volume, then propagated — never washed into empty face.
|
|
308
|
-
*/
|
|
309
|
-
function persistIssueSourceFailure(
|
|
310
|
-
admitted: AdmittedCountersignInvocation,
|
|
311
|
-
ticketNumber: number,
|
|
312
|
-
error: DiaristIssueSourceError,
|
|
313
|
-
): never {
|
|
314
|
-
const home = tryHomeFromAkRolesPath(admitted.runDirectory);
|
|
315
|
-
appendIssueSourceFailureDiagnostic({
|
|
316
|
-
ticketNumber,
|
|
317
|
-
cwd: admitted.projectRoot,
|
|
318
|
-
...(home === undefined ? {} : { home }),
|
|
319
|
-
cause: error.message,
|
|
320
|
-
reason: error.reason,
|
|
321
|
-
});
|
|
322
|
-
throw error;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
async function loadBoundIssueFace(
|
|
326
|
-
admitted: AdmittedCountersignInvocation,
|
|
327
|
-
): Promise<DiaristIssueFace> {
|
|
328
|
-
const ticketNumber = admitted.ticketNumber;
|
|
329
|
-
if (ticketNumber === undefined) {
|
|
330
|
-
throw new Error("loadBoundIssueFace requires a bound ticketNumber");
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
const origin = resolveDiaristGithubOrigin(admitted.projectRoot);
|
|
334
|
-
if (origin === undefined) {
|
|
335
|
-
persistIssueSourceFailure(
|
|
336
|
-
admitted,
|
|
337
|
-
ticketNumber,
|
|
338
|
-
new DiaristIssueSourceError(
|
|
339
|
-
"origin-unresolved",
|
|
340
|
-
`bound ticket #${ticketNumber} issue face requires a resolvable github.com origin remote`,
|
|
341
|
-
),
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
const fetcher = createDiaristIssueFaceFetcher();
|
|
346
|
-
let face: DiaristIssueFace | undefined;
|
|
347
|
-
try {
|
|
348
|
-
face = await fetcher({
|
|
349
|
-
owner: origin.owner,
|
|
350
|
-
repo: origin.repo,
|
|
351
|
-
ticketNumber,
|
|
352
|
-
});
|
|
353
|
-
} catch (error) {
|
|
354
|
-
const typed =
|
|
355
|
-
error instanceof DiaristIssueSourceError
|
|
356
|
-
? error
|
|
357
|
-
: new DiaristIssueSourceError(
|
|
358
|
-
"issue-unavailable",
|
|
359
|
-
`issue face fetch failed for ${origin.owner}/${origin.repo}#${ticketNumber}`,
|
|
360
|
-
{ cause: error },
|
|
361
|
-
);
|
|
362
|
-
persistIssueSourceFailure(admitted, ticketNumber, typed);
|
|
363
|
-
}
|
|
364
|
-
if (face === undefined) {
|
|
365
|
-
persistIssueSourceFailure(
|
|
366
|
-
admitted,
|
|
367
|
-
ticketNumber,
|
|
368
|
-
new DiaristIssueSourceError(
|
|
369
|
-
"issue-unavailable",
|
|
370
|
-
`issue face unavailable for ${origin.owner}/${origin.repo}#${ticketNumber}`,
|
|
371
|
-
),
|
|
372
|
-
);
|
|
373
|
-
}
|
|
374
|
-
return face;
|
|
375
|
-
}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public Diarist (起居郎) Role run — #708 / ADR 0075 `diarist-is-role`.
|
|
3
|
+
* Same admit → post-admission → settle shape as the other instruction seats.
|
|
4
|
+
* Semantic collection is the role's own turn; this seat only freezes the
|
|
5
|
+
* mechanical source catalog the turn selects from (`diarist-collector-is-own-turn`).
|
|
6
|
+
* Who calls it and in what order is the caller's business (ADR 0010 `no-call-rule`).
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
|
+
import { writeFile } from "node:fs/promises";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
|
|
12
|
+
import type { DurablePrincipalAuthority, RoleTurnRequest } from "../host-contracts.ts";
|
|
13
|
+
import {
|
|
14
|
+
loadDiaristIssueFace,
|
|
15
|
+
prepareDiaristSourceCatalog,
|
|
16
|
+
serializeDiaristSourceCatalog,
|
|
17
|
+
} from "../diarist.ts";
|
|
18
|
+
import { engineSessionMaterialFromOptions } from "../package-resources/engine-material.ts";
|
|
19
|
+
import { CliUsageError } from "./cli-errors.ts";
|
|
20
|
+
import {
|
|
21
|
+
admitDiaristInvocation,
|
|
22
|
+
buildInstructionTransportPrompt,
|
|
23
|
+
type AdmittedDiaristInvocation,
|
|
24
|
+
type ParseDiaristArgvResult,
|
|
25
|
+
} from "./invocation.ts";
|
|
26
|
+
import {
|
|
27
|
+
applyInstructionTicketProbe,
|
|
28
|
+
probeInstructionTicket,
|
|
29
|
+
ticketNumberFromProbe,
|
|
30
|
+
tryResumeSameTicketSeatRun,
|
|
31
|
+
} from "./seat-ticket-binding.ts";
|
|
32
|
+
import {
|
|
33
|
+
prepareSummonsResumeMaterials,
|
|
34
|
+
runPostAdmissionOneShot,
|
|
35
|
+
runPostAdmissionSeatResume,
|
|
36
|
+
resumeTurnRequestProjectionOptions,
|
|
37
|
+
type PostAdmissionAdapters,
|
|
38
|
+
type PostAdmissionEnv,
|
|
39
|
+
} from "./post-admission.ts";
|
|
40
|
+
import {
|
|
41
|
+
loadResumableDiaristRun,
|
|
42
|
+
markRunAdmitted,
|
|
43
|
+
readRoleRunIdentity,
|
|
44
|
+
type PublicResumeRequest,
|
|
45
|
+
type SameTicketSummonsMaterials,
|
|
46
|
+
} from "./run-lifecycle.ts";
|
|
47
|
+
import {
|
|
48
|
+
presentStructuralRejection,
|
|
49
|
+
trySettleDiaristTerminalResult,
|
|
50
|
+
} from "./settlement.ts";
|
|
51
|
+
import type { CliIo } from "./cli-io.ts";
|
|
52
|
+
import type { TerminalResult } from "./terminal.ts";
|
|
53
|
+
import {
|
|
54
|
+
projectRoleTurnRequest,
|
|
55
|
+
type RoleTurnRequestProjectionOptions,
|
|
56
|
+
} from "./turn-request.ts";
|
|
57
|
+
|
|
58
|
+
export type DiaristRunEnv = PostAdmissionEnv & {
|
|
59
|
+
principalAuthority: DurablePrincipalAuthority;
|
|
60
|
+
createRunId?: () => string;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** Frozen catalog filename inside the run dossier (durable material, not argv). */
|
|
64
|
+
const DIARIST_SOURCE_CATALOG_FILE = "diarist-sources.json" as const;
|
|
65
|
+
|
|
66
|
+
/** Neutral path identifier only (ADR 0073) — catalog bytes stay on disk. */
|
|
67
|
+
function withDiaristCatalogPath(
|
|
68
|
+
prompt: string,
|
|
69
|
+
sourcesPath: string | undefined,
|
|
70
|
+
): string {
|
|
71
|
+
if (sourcesPath === undefined || sourcesPath.trim() === "") return prompt;
|
|
72
|
+
return `${prompt}\n\n已冻结来源文件(路径):\n- ${sourcesPath}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Project admitted invocation onto the host-neutral turn request. */
|
|
76
|
+
export function buildDiaristTurnRequest(
|
|
77
|
+
admitted: AdmittedDiaristInvocation,
|
|
78
|
+
options: RoleTurnRequestProjectionOptions,
|
|
79
|
+
sourcesPath?: string,
|
|
80
|
+
): RoleTurnRequest {
|
|
81
|
+
const continuation =
|
|
82
|
+
sourcesPath === undefined
|
|
83
|
+
? options.continuation
|
|
84
|
+
: {
|
|
85
|
+
...options.continuation,
|
|
86
|
+
prompt: withDiaristCatalogPath(options.continuation.prompt, sourcesPath),
|
|
87
|
+
};
|
|
88
|
+
return projectRoleTurnRequest(
|
|
89
|
+
admitted,
|
|
90
|
+
{
|
|
91
|
+
activation: {
|
|
92
|
+
role: "diarist" as const,
|
|
93
|
+
...(sourcesPath === undefined ? {} : { sourcesPath }),
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
{ ...options, continuation },
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Mechanical source enumeration for a bound summons: establish the per-ticket
|
|
102
|
+
* volume, freeze this turn's candidate catalog into the run dossier.
|
|
103
|
+
* A true-unbound summons has no ticket, so no diary is minted — undefined.
|
|
104
|
+
*
|
|
105
|
+
* A run that has not settled yet (crash-resume, open-court continuation) keeps
|
|
106
|
+
* the catalog its candidateIndexes were minted against — re-enumerating there
|
|
107
|
+
* would rebind the role's indexes to bytes it never saw. Only a settled run
|
|
108
|
+
* re-enumerates on the next same-ticket summons (ADR 0075 增量).
|
|
109
|
+
*/
|
|
110
|
+
async function freezeDiaristSourceCatalog(
|
|
111
|
+
admitted: AdmittedDiaristInvocation,
|
|
112
|
+
env: Pick<DiaristRunEnv, "cwd" | "home">,
|
|
113
|
+
): Promise<string | undefined> {
|
|
114
|
+
if (admitted.ticketNumber === undefined) return undefined;
|
|
115
|
+
const path = join(admitted.runDirectory, DIARIST_SOURCE_CATALOG_FILE);
|
|
116
|
+
if (existsSync(path)) {
|
|
117
|
+
const identity = await readRoleRunIdentity(admitted.runDirectory);
|
|
118
|
+
if (identity !== undefined && identity.state !== "terminal") return path;
|
|
119
|
+
}
|
|
120
|
+
const issueFace = await loadDiaristIssueFace({
|
|
121
|
+
ticketNumber: admitted.ticketNumber,
|
|
122
|
+
projectRoot: admitted.projectRoot,
|
|
123
|
+
home: env.home,
|
|
124
|
+
});
|
|
125
|
+
const catalog = await prepareDiaristSourceCatalog({
|
|
126
|
+
ticketNumber: admitted.ticketNumber,
|
|
127
|
+
cwd: admitted.projectRoot,
|
|
128
|
+
home: env.home,
|
|
129
|
+
issueFace,
|
|
130
|
+
sessionCwds: [admitted.projectRoot, env.cwd],
|
|
131
|
+
});
|
|
132
|
+
await writeFile(path, serializeDiaristSourceCatalog(catalog), "utf8");
|
|
133
|
+
return path;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function diaristAdapters(options?: {
|
|
137
|
+
beforeDispatch?: (
|
|
138
|
+
admitted: AdmittedDiaristInvocation,
|
|
139
|
+
) => void | Promise<void>;
|
|
140
|
+
}): PostAdmissionAdapters<AdmittedDiaristInvocation> {
|
|
141
|
+
return {
|
|
142
|
+
trySettle: (admitted, authority, scope) =>
|
|
143
|
+
trySettleDiaristTerminalResult(admitted, authority, scope),
|
|
144
|
+
shouldPresentSettled: () => true,
|
|
145
|
+
...(options?.beforeDispatch === undefined
|
|
146
|
+
? {}
|
|
147
|
+
: { beforeDispatch: options.beforeDispatch }),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export async function runPublicDiarist(
|
|
152
|
+
argv: readonly string[],
|
|
153
|
+
env: DiaristRunEnv,
|
|
154
|
+
io: CliIo,
|
|
155
|
+
parseDiaristArgv: (args: readonly string[]) => ParseDiaristArgvResult,
|
|
156
|
+
): Promise<{
|
|
157
|
+
exitCode: number;
|
|
158
|
+
admitted?: AdmittedDiaristInvocation;
|
|
159
|
+
terminal?: TerminalResult;
|
|
160
|
+
}> {
|
|
161
|
+
let parsed: ParseDiaristArgvResult;
|
|
162
|
+
try {
|
|
163
|
+
parsed = parseDiaristArgv(argv);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
if (error instanceof CliUsageError) {
|
|
166
|
+
presentStructuralRejection(error, io);
|
|
167
|
+
return { exitCode: 2 };
|
|
168
|
+
}
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// #637: same ticket → resume this seat's prior run with this summons' materials.
|
|
173
|
+
// Probe captures DiaristTicketResolutionError so admit+beforeDispatch can settle
|
|
174
|
+
// controlled failure (bare pre-admit throw skips terminal settlement).
|
|
175
|
+
const projectRoot = parsed.project ?? env.cwd;
|
|
176
|
+
const ticketProbe = await probeInstructionTicket(
|
|
177
|
+
parsed.instruction,
|
|
178
|
+
projectRoot,
|
|
179
|
+
env,
|
|
180
|
+
);
|
|
181
|
+
const probedTicketNumber = ticketNumberFromProbe(ticketProbe);
|
|
182
|
+
if (probedTicketNumber !== undefined) {
|
|
183
|
+
const summons: SameTicketSummonsMaterials = {
|
|
184
|
+
instruction: parsed.instruction,
|
|
185
|
+
instructionEmpty: parsed.instruction.trim() === "",
|
|
186
|
+
attachmentPaths: parsed.attachmentPaths,
|
|
187
|
+
};
|
|
188
|
+
const resumed = await tryResumeSameTicketSeatRun({
|
|
189
|
+
home: env.home,
|
|
190
|
+
projectRoot,
|
|
191
|
+
role: "diarist",
|
|
192
|
+
ticketNumber: probedTicketNumber,
|
|
193
|
+
freshSummons: env.freshSummons,
|
|
194
|
+
summons,
|
|
195
|
+
resume: (runId, materials) =>
|
|
196
|
+
runPublicDiaristResume(
|
|
197
|
+
{ runId, ...(materials === undefined ? {} : { summons: materials }) },
|
|
198
|
+
env,
|
|
199
|
+
io,
|
|
200
|
+
),
|
|
201
|
+
});
|
|
202
|
+
if (resumed !== undefined) return resumed;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let admitted: AdmittedDiaristInvocation;
|
|
206
|
+
try {
|
|
207
|
+
admitted = await admitDiaristInvocation({
|
|
208
|
+
home: env.home,
|
|
209
|
+
principalAuthority: env.principalAuthority,
|
|
210
|
+
cwd: env.cwd,
|
|
211
|
+
instruction: parsed.instruction,
|
|
212
|
+
attachmentPaths: parsed.attachmentPaths,
|
|
213
|
+
...(parsed.project === undefined ? {} : { project: parsed.project }),
|
|
214
|
+
...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
|
|
215
|
+
...(env.model === undefined ? {} : { model: env.model }),
|
|
216
|
+
...(env.correlationId === undefined ? {} : { correlationId: env.correlationId }),
|
|
217
|
+
});
|
|
218
|
+
} catch (error) {
|
|
219
|
+
if (error instanceof CliUsageError) {
|
|
220
|
+
presentStructuralRejection(error, io);
|
|
221
|
+
return { exitCode: 2 };
|
|
222
|
+
}
|
|
223
|
+
throw error;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
await markRunAdmitted(admitted, env.principalAuthority);
|
|
227
|
+
|
|
228
|
+
const turnProjection: RoleTurnRequestProjectionOptions = {
|
|
229
|
+
packageRoot: env.packageRoot,
|
|
230
|
+
home: env.home,
|
|
231
|
+
agentDir: env.agentDir,
|
|
232
|
+
...(env.model === undefined ? {} : { model: env.model }),
|
|
233
|
+
...(env.engine === undefined ? {} : { engine: env.engine }),
|
|
234
|
+
...(env.timeoutMs === undefined ? {} : { timeoutMs: env.timeoutMs }),
|
|
235
|
+
...(env.correlationId === undefined || env.correlationId.trim() === ""
|
|
236
|
+
? {}
|
|
237
|
+
: { correlationId: env.correlationId }),
|
|
238
|
+
continuation: {
|
|
239
|
+
kind: "initial",
|
|
240
|
+
prompt: buildInstructionTransportPrompt(
|
|
241
|
+
admitted,
|
|
242
|
+
engineSessionMaterialFromOptions({
|
|
243
|
+
...(env.engine === undefined ? {} : { engine: env.engine }),
|
|
244
|
+
packageRoot: env.packageRoot,
|
|
245
|
+
}),
|
|
246
|
+
),
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
// Mutable shell: ticket bind + catalog freeze re-project activation before executeTurn.
|
|
250
|
+
const turnRequest = buildDiaristTurnRequest(admitted, turnProjection);
|
|
251
|
+
|
|
252
|
+
return await runPostAdmissionOneShot({
|
|
253
|
+
admitted,
|
|
254
|
+
env,
|
|
255
|
+
io,
|
|
256
|
+
request: turnRequest,
|
|
257
|
+
adapters: diaristAdapters({
|
|
258
|
+
beforeDispatch: async (admittedSeat) => {
|
|
259
|
+
await applyInstructionTicketProbe(admittedSeat, ticketProbe);
|
|
260
|
+
const sourcesPath = await freezeDiaristSourceCatalog(admittedSeat, env);
|
|
261
|
+
Object.assign(
|
|
262
|
+
turnRequest,
|
|
263
|
+
buildDiaristTurnRequest(admittedSeat, turnProjection, sourcesPath),
|
|
264
|
+
);
|
|
265
|
+
},
|
|
266
|
+
}),
|
|
267
|
+
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Resume a previously admitted Diarist run (#708 / ADR 0079 同票传召 = resume).
|
|
273
|
+
* A re-entry into a settled run re-enumerates fresh sources; the offered
|
|
274
|
+
* watermark keeps the pass incremental so already-seen blocks are not
|
|
275
|
+
* re-offered. An unfinished run keeps its frozen catalog (see freeze above).
|
|
276
|
+
*/
|
|
277
|
+
export async function runPublicDiaristResume(
|
|
278
|
+
request: PublicResumeRequest,
|
|
279
|
+
env: DiaristRunEnv,
|
|
280
|
+
io: CliIo,
|
|
281
|
+
): Promise<{
|
|
282
|
+
exitCode: number;
|
|
283
|
+
admitted?: AdmittedDiaristInvocation;
|
|
284
|
+
terminal?: TerminalResult;
|
|
285
|
+
}> {
|
|
286
|
+
return await runPostAdmissionSeatResume({
|
|
287
|
+
request,
|
|
288
|
+
env,
|
|
289
|
+
io,
|
|
290
|
+
load: (effective) =>
|
|
291
|
+
loadResumableDiaristRun(env.home, effective.runId, env.principalAuthority),
|
|
292
|
+
buildTurnRequest: async (admitted, effective) => {
|
|
293
|
+
const summonsPrepared = await prepareSummonsResumeMaterials(
|
|
294
|
+
admitted.runDirectory,
|
|
295
|
+
effective.summons,
|
|
296
|
+
);
|
|
297
|
+
const sourcesPath = await freezeDiaristSourceCatalog(admitted, env);
|
|
298
|
+
return buildDiaristTurnRequest(
|
|
299
|
+
admitted,
|
|
300
|
+
resumeTurnRequestProjectionOptions(
|
|
301
|
+
admitted,
|
|
302
|
+
effective,
|
|
303
|
+
env,
|
|
304
|
+
summonsPrepared,
|
|
305
|
+
),
|
|
306
|
+
sourcesPath,
|
|
307
|
+
);
|
|
308
|
+
},
|
|
309
|
+
adapters: diaristAdapters(),
|
|
310
|
+
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
311
|
+
});
|
|
312
|
+
}
|