@akagilnc/pi-workflow-roles 0.1.3565 → 0.1.3572
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 +755 -60
- package/dist/diarist-contracts.js +72 -0
- package/dist/package-contracts/terminating-tools.js +20 -2
- package/dist/packaged-role-registry.js +20 -0
- package/dist/public-cli/main.js +533 -634
- package/dist/public-cli/registry.js +4 -1
- package/extensions/role-runtime.ts +1 -0
- package/package.json +1 -1
- package/resources/diarist-collect.md +6 -10
- package/souls/diarist.md +9 -0
- package/src/acp-host/production-host.ts +1 -0
- package/src/acp-host/role-envelope.ts +2 -1
- package/src/diarist-contracts.ts +88 -0
- package/src/diarist-role.ts +60 -0
- package/src/diarist.ts +253 -179
- package/src/host-contracts.ts +6 -1
- package/src/package-contracts/terminating-tools.ts +22 -2
- package/src/packaged-role-registry.ts +20 -0
- package/src/pi/role-turn-host.ts +8 -0
- package/src/public-cli/cli.ts +29 -0
- package/src/public-cli/countersign-run.ts +9 -129
- package/src/public-cli/diarist-run.ts +312 -0
- package/src/public-cli/invocation.ts +23 -2
- package/src/public-cli/option-definitions.ts +18 -0
- package/src/public-cli/post-admission.ts +2 -2
- package/src/public-cli/registry.ts +3 -0
- package/src/public-cli/run-lifecycle.ts +31 -3
- package/src/public-cli/settlement.ts +50 -2
- package/src/public-cli/terminal.ts +2 -1
- package/src/role-runtime.ts +117 -6
- package/src/ticket-provenance-contracts.ts +1 -0
- package/src/ticket-provenance.ts +0 -23
- package/src/diarist-llm-collector.ts +0 -324
package/src/pi/role-turn-host.ts
CHANGED
|
@@ -146,6 +146,14 @@ function buildActivationFlagArgs(activation: RoleTurnActivation): string[] {
|
|
|
146
146
|
return ["--ak-role", "gatekeeper"];
|
|
147
147
|
case "navigator":
|
|
148
148
|
return ["--ak-role", "navigator"];
|
|
149
|
+
case "diarist":
|
|
150
|
+
return [
|
|
151
|
+
"--ak-role",
|
|
152
|
+
"diarist",
|
|
153
|
+
...(activation.sourcesPath === undefined
|
|
154
|
+
? []
|
|
155
|
+
: ["--ak-diarist-sources", activation.sourcesPath]),
|
|
156
|
+
];
|
|
149
157
|
default: {
|
|
150
158
|
const _exhaustive: never = activation;
|
|
151
159
|
return _exhaustive;
|
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,
|
|
@@ -78,6 +79,7 @@ import { runPublicCoder, runPublicCoderResume } from "./coder-run.ts";
|
|
|
78
79
|
import { runPublicInstructionSeat, runPublicInstructionSeatResume } from "./instruction-seat-run.ts";
|
|
79
80
|
import { runPublicCollector, runPublicCollectorResume } from "./collector-run.ts";
|
|
80
81
|
import { runPublicCountersign, runPublicCountersignResume } from "./countersign-run.ts";
|
|
82
|
+
import { runPublicDiarist, runPublicDiaristResume } from "./diarist-run.ts";
|
|
81
83
|
import { runPublicGleanerLeft, runPublicGleanerLeftResume } from "./gleaner-left-run.ts";
|
|
82
84
|
import { runPublicDoctor, runPublicDoctorResume } from "./doctor-run.ts";
|
|
83
85
|
import { runPublicFixer, runPublicFixerResume } from "./fixer-run.ts";
|
|
@@ -126,6 +128,7 @@ const RESUME_SEAT_DISPATCH: Record<
|
|
|
126
128
|
inspector: { seat: "inspector", run: runPublicInspectorResume },
|
|
127
129
|
gatekeeper: { seat: "gatekeeper", run: runPublicInstructionSeatResume },
|
|
128
130
|
navigator: { seat: "navigator", run: runPublicInstructionSeatResume },
|
|
131
|
+
diarist: { seat: "diarist", run: runPublicDiaristResume },
|
|
129
132
|
};
|
|
130
133
|
import {
|
|
131
134
|
INTERNAL_ROLE_ENTRYPOINT_RELATIVE,
|
|
@@ -168,6 +171,7 @@ export const PUBLIC_ROLE_ARGV = {
|
|
|
168
171
|
reviewer: { parse: parseReviewerArgv, options: optionsForOwner("reviewer") },
|
|
169
172
|
gatekeeper: { parse: parseGatekeeperArgv, options: optionsForOwner("gatekeeper") },
|
|
170
173
|
navigator: { parse: parseNavigatorArgv, options: optionsForOwner("navigator") },
|
|
174
|
+
diarist: { parse: parseDiaristArgv, options: optionsForOwner("diarist") },
|
|
171
175
|
/** Deterministic analysis seat (#336) — argv parse only; no LLM admission. */
|
|
172
176
|
analyst: { parse: parseAnalystArgv, options: optionsForOwner("analyst") },
|
|
173
177
|
} as const;
|
|
@@ -1311,6 +1315,31 @@ export async function runAkRole(
|
|
|
1311
1315
|
};
|
|
1312
1316
|
}
|
|
1313
1317
|
|
|
1318
|
+
// Diarist public run path (#708): 起居郎 is summoned like any other seat.
|
|
1319
|
+
if (parsed.command === "diarist") {
|
|
1320
|
+
const agentDir = resolveAgentDir(env, home);
|
|
1321
|
+
const cwd = env.cwd ?? process.cwd();
|
|
1322
|
+
const config = await loadAndValidateConfig(home, env.packageRoot);
|
|
1323
|
+
const credentials =
|
|
1324
|
+
env.credentials ?? (await loadCredentialProviders(agentDir));
|
|
1325
|
+
const seat = resolveEffectiveSeat(
|
|
1326
|
+
config,
|
|
1327
|
+
"diarist",
|
|
1328
|
+
credentials,
|
|
1329
|
+
invocationFromParsed(parsed),
|
|
1330
|
+
);
|
|
1331
|
+
const result = await runPublicDiarist(
|
|
1332
|
+
parsed.args,
|
|
1333
|
+
createRoleEnvironment(env, { role: "diarist", home, agentDir, cwd, credentials, seat, config }),
|
|
1334
|
+
io,
|
|
1335
|
+
PUBLIC_ROLE_ARGV.diarist.parse,
|
|
1336
|
+
);
|
|
1337
|
+
return {
|
|
1338
|
+
exitCode: result.exitCode,
|
|
1339
|
+
...(result.terminal === undefined ? {} : { terminal: result.terminal }),
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1314
1343
|
// Coder public run path with package-owned TDD method (#109).
|
|
1315
1344
|
if (parsed.command === "coder") {
|
|
1316
1345
|
const agentDir = resolveAgentDir(env, home);
|
|
@@ -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
|
+
}
|
|
@@ -151,6 +151,10 @@ export type AdmittedNavigatorInvocation = AdmittedRoleInvocationBase & {
|
|
|
151
151
|
readonly role: "navigator";
|
|
152
152
|
};
|
|
153
153
|
|
|
154
|
+
export type AdmittedDiaristInvocation = AdmittedRoleInvocationBase & {
|
|
155
|
+
readonly role: "diarist";
|
|
156
|
+
};
|
|
157
|
+
|
|
154
158
|
export type CoderPhase = "plan" | "apply";
|
|
155
159
|
|
|
156
160
|
export type AdmittedCoderInvocation = AdmittedRoleInvocationBase & {
|
|
@@ -234,6 +238,7 @@ export type AdmittedRoleInvocation =
|
|
|
234
238
|
| AdmittedInspectorInvocation
|
|
235
239
|
| AdmittedGatekeeperInvocation
|
|
236
240
|
| AdmittedNavigatorInvocation
|
|
241
|
+
| AdmittedDiaristInvocation
|
|
237
242
|
| AdmittedCoderInvocation
|
|
238
243
|
| AdmittedFixerInvocation
|
|
239
244
|
| AdmittedCollectorInvocation
|
|
@@ -586,6 +591,7 @@ export type ParseCountersignArgvResult = ParseInstructionArgvResult;
|
|
|
586
591
|
export type ParseInspectorArgvResult = ParseInstructionArgvResult;
|
|
587
592
|
export type ParseGatekeeperArgvResult = ParseInstructionArgvResult;
|
|
588
593
|
export type ParseNavigatorArgvResult = ParseInstructionArgvResult;
|
|
594
|
+
export type ParseDiaristArgvResult = ParseInstructionArgvResult;
|
|
589
595
|
|
|
590
596
|
/** Positive ticket number for analyst query-scope face (and shared integer parse). */
|
|
591
597
|
export function parsePositiveTicketNumber(
|
|
@@ -606,7 +612,7 @@ export function parsePositiveTicketNumber(
|
|
|
606
612
|
/** 共享解析体:同形 owner 的 argv → instruction/attachments/project。 */
|
|
607
613
|
function parseInstructionArgv(
|
|
608
614
|
args: readonly string[],
|
|
609
|
-
owner: "judge" | "countersign" | "inspector" | "gatekeeper" | "navigator",
|
|
615
|
+
owner: "judge" | "countersign" | "inspector" | "gatekeeper" | "navigator" | "diarist",
|
|
610
616
|
): ParseInstructionArgvResult {
|
|
611
617
|
const attachmentPaths: string[] = [];
|
|
612
618
|
let project: string | undefined;
|
|
@@ -846,6 +852,10 @@ export function parseNavigatorArgv(args: readonly string[]): ParseNavigatorArgvR
|
|
|
846
852
|
return parseInstructionArgv(args, "navigator");
|
|
847
853
|
}
|
|
848
854
|
|
|
855
|
+
export function parseDiaristArgv(args: readonly string[]): ParseDiaristArgvResult {
|
|
856
|
+
return parseInstructionArgv(args, "diarist");
|
|
857
|
+
}
|
|
858
|
+
|
|
849
859
|
/**
|
|
850
860
|
* Parse Coder-specific argv after the `coder` token.
|
|
851
861
|
* Phase defaults to apply; spellings from PUBLIC_OPTION_TABLE.coder (#342).
|
|
@@ -1045,6 +1055,7 @@ export type AdmitInspectorInvocationOptions = AdmitJudgeInvocationOptions & {
|
|
|
1045
1055
|
|
|
1046
1056
|
export type AdmitGatekeeperInvocationOptions = AdmitInspectorInvocationOptions;
|
|
1047
1057
|
export type AdmitNavigatorInvocationOptions = AdmitInspectorInvocationOptions;
|
|
1058
|
+
export type AdmitDiaristInvocationOptions = AdmitInspectorInvocationOptions;
|
|
1048
1059
|
|
|
1049
1060
|
/**
|
|
1050
1061
|
* Shared instruction-seat admission for Judge and Inspector: project check,
|
|
@@ -1053,7 +1064,7 @@ export type AdmitNavigatorInvocationOptions = AdmitInspectorInvocationOptions;
|
|
|
1053
1064
|
* Ticket binding is post-admission via shared seat LLM path (#635).
|
|
1054
1065
|
*/
|
|
1055
1066
|
async function admitStandardMaterialInvocation<
|
|
1056
|
-
R extends "judge" | "inspector" | "gatekeeper" | "navigator",
|
|
1067
|
+
R extends "judge" | "inspector" | "gatekeeper" | "navigator" | "diarist",
|
|
1057
1068
|
>(
|
|
1058
1069
|
role: R,
|
|
1059
1070
|
options: AdmitJudgeInvocationOptions & { correlationId?: string },
|
|
@@ -1173,6 +1184,16 @@ export async function admitNavigatorInvocation(
|
|
|
1173
1184
|
return admitStandardMaterialInvocation("navigator", options);
|
|
1174
1185
|
}
|
|
1175
1186
|
|
|
1187
|
+
/**
|
|
1188
|
+
* Admit a direct Diarist (起居郎) run (#708): freeze attachments, persist the
|
|
1189
|
+
* request, reserve session placement — same instruction-seat face.
|
|
1190
|
+
*/
|
|
1191
|
+
export async function admitDiaristInvocation(
|
|
1192
|
+
options: AdmitDiaristInvocationOptions,
|
|
1193
|
+
): Promise<AdmittedDiaristInvocation> {
|
|
1194
|
+
return admitStandardMaterialInvocation("diarist", options);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1176
1197
|
/** Shared prompt transport for instruction-seat roles (judge/countersign/inspector). */
|
|
1177
1198
|
export function buildInstructionTransportPrompt(
|
|
1178
1199
|
admitted: { instruction: string; instructionEmpty: boolean; attachments: readonly { frozenPath: string }[] },
|
|
@@ -34,6 +34,7 @@ export type OptionOwner =
|
|
|
34
34
|
| "inspector"
|
|
35
35
|
| "gatekeeper"
|
|
36
36
|
| "navigator"
|
|
37
|
+
| "diarist"
|
|
37
38
|
| "analyst";
|
|
38
39
|
|
|
39
40
|
/**
|
|
@@ -427,6 +428,11 @@ const COUNTERSIGN_OPTIONS = [
|
|
|
427
428
|
bindOwner("countersign", SHARED_ATTACH_SEMANTICS),
|
|
428
429
|
] as const satisfies readonly PublicOptionDefinition[];
|
|
429
430
|
|
|
431
|
+
const DIARIST_OPTIONS = [
|
|
432
|
+
bindOwner("diarist", SHARED_PROJECT_SEMANTICS),
|
|
433
|
+
bindOwner("diarist", SHARED_ATTACH_SEMANTICS),
|
|
434
|
+
] as const satisfies readonly PublicOptionDefinition[];
|
|
435
|
+
|
|
430
436
|
const CODER_OPTIONS = [
|
|
431
437
|
{
|
|
432
438
|
id: "phase",
|
|
@@ -784,6 +790,7 @@ export const PUBLIC_OPTION_TABLE = {
|
|
|
784
790
|
inspector: INSPECTOR_OPTIONS,
|
|
785
791
|
gatekeeper: GATEKEEPER_OPTIONS,
|
|
786
792
|
navigator: NAVIGATOR_OPTIONS,
|
|
793
|
+
diarist: DIARIST_OPTIONS,
|
|
787
794
|
analyst: ANALYST_OPTIONS,
|
|
788
795
|
} as const satisfies Record<OptionOwner, readonly PublicOptionDefinition[]>;
|
|
789
796
|
|
|
@@ -804,6 +811,7 @@ export const PUBLIC_ROLE_OPTION_OWNERS = [
|
|
|
804
811
|
"inspector",
|
|
805
812
|
"gatekeeper",
|
|
806
813
|
"navigator",
|
|
814
|
+
"diarist",
|
|
807
815
|
"analyst",
|
|
808
816
|
] as const satisfies readonly PublicRoleOptionOwner[];
|
|
809
817
|
|
|
@@ -1201,6 +1209,16 @@ const ROLE_COMMAND_HELP = {
|
|
|
1201
1209
|
'ak-role navigator "刚完成 coder apply 收敛,下一步?"',
|
|
1202
1210
|
],
|
|
1203
1211
|
},
|
|
1212
|
+
diarist: {
|
|
1213
|
+
command: "diarist",
|
|
1214
|
+
summary:
|
|
1215
|
+
"Direct Diarist (起居郎) pass: gather and organize this case's decision basis into its 起居录.",
|
|
1216
|
+
usage: ["ak-role diarist [options] [instruction]"],
|
|
1217
|
+
examples: [
|
|
1218
|
+
'ak-role diarist "整理 #708 的本案依据。"',
|
|
1219
|
+
'ak-role diarist --attach ./design.md "补录本轮设计修订。"',
|
|
1220
|
+
],
|
|
1221
|
+
},
|
|
1204
1222
|
notary: {
|
|
1205
1223
|
command: "notary",
|
|
1206
1224
|
summary: "Direct Notary document check (quote fidelity + ticket alignment); zero prompt/attachment.",
|