@akagilnc/pi-workflow-roles 0.1.3558 → 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/description.js +21 -0
- package/dist/{grok → acp-host}/mcp-relay.mjs +5 -5
- package/dist/{grok → acp-host}/production-host.js +860 -162
- package/dist/diarist-contracts.js +72 -0
- package/dist/host-descriptions.js +34 -0
- package/dist/package-contracts/terminating-tools.js +20 -2
- package/dist/packaged-role-registry.js +20 -0
- package/dist/public-cli/config.js +23 -10
- package/dist/public-cli/main.js +622 -671
- 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/scripts/build-package.mjs +8 -8
- package/souls/diarist.md +9 -0
- package/src/acp-host/description.ts +43 -0
- package/src/{grok → acp-host}/mcp-relay.mjs +5 -5
- package/src/{grok → acp-host}/production-host.ts +31 -37
- package/src/{grok → acp-host}/role-envelope.ts +25 -24
- package/src/{grok → acp-host}/role-turn-host.ts +46 -81
- package/src/{grok → acp-host}/session-identity.ts +7 -7
- 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 +11 -4
- package/src/host-descriptions.ts +51 -0
- package/src/host-transition-prior-native.ts +12 -24
- package/src/package-contracts/terminating-tools.ts +22 -2
- package/src/packaged-role-registry.ts +20 -0
- package/src/pi/role-turn-host.ts +10 -1
- package/src/public-cli/cli.ts +39 -9
- package/src/public-cli/config.ts +24 -11
- 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/load-production-acp-host.ts +52 -0
- package/src/public-cli/option-definitions.ts +18 -0
- package/src/public-cli/post-admission.ts +3 -3
- 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/public-cli/load-production-grok-host.ts +0 -35
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Single authority for #617 DK-4 cross-host prior-native projection.
|
|
3
|
-
*
|
|
3
|
+
* Classifies the prior volume into the two record families that exist
|
|
4
|
+
* (Pi native session file / sitian run records), never by host name.
|
|
4
5
|
*/
|
|
5
6
|
import { access, readdir } from "node:fs/promises";
|
|
6
7
|
import { dirname, join } from "node:path";
|
|
7
8
|
|
|
8
9
|
import type { RoleTurnHostTransition } from "./host-contracts.ts";
|
|
9
10
|
|
|
10
|
-
const KNOWN_ROLE_TURN_HOSTS = ["pi", "grok-build"] as const;
|
|
11
|
-
type KnownRoleTurnHost = (typeof KNOWN_ROLE_TURN_HOSTS)[number];
|
|
12
|
-
|
|
13
|
-
function isKnownRoleTurnHost(value: string): value is KnownRoleTurnHost {
|
|
14
|
-
return (KNOWN_ROLE_TURN_HOSTS as readonly string[]).includes(value);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
11
|
function isEnoent(error: unknown): boolean {
|
|
18
12
|
return typeof error === "object" && error !== null && (error as NodeJS.ErrnoException).code === "ENOENT";
|
|
19
13
|
}
|
|
@@ -59,13 +53,12 @@ async function listSitianRecordPaths(sessionParent: string): Promise<string[]> {
|
|
|
59
53
|
}
|
|
60
54
|
|
|
61
55
|
/**
|
|
62
|
-
* Project one hostTransition only for a real switch
|
|
63
|
-
*
|
|
64
|
-
* yields a typed switch (empty path list).
|
|
56
|
+
* Project one hostTransition only for a real host switch. Empty native volume
|
|
57
|
+
* still yields a typed switch (empty path list).
|
|
65
58
|
*
|
|
66
|
-
* Pi
|
|
67
|
-
*
|
|
68
|
-
* stay in the operator
|
|
59
|
+
* Pi wrote its own session.jsonl; every other host's run volume is the sitian
|
|
60
|
+
* record set on the live run (ADR 0077 `record-scope-phase-two`, #717) — the CLI's
|
|
61
|
+
* own journals stay in the operator home and are never copied here.
|
|
69
62
|
*/
|
|
70
63
|
export async function projectHostTransitionPriorNative(input: {
|
|
71
64
|
readonly previousHost: string;
|
|
@@ -73,20 +66,15 @@ export async function projectHostTransitionPriorNative(input: {
|
|
|
73
66
|
readonly piSessionFile: string;
|
|
74
67
|
}): Promise<RoleTurnHostTransition | undefined> {
|
|
75
68
|
if (input.previousHost === input.liveHost) return undefined;
|
|
76
|
-
if (!isKnownRoleTurnHost(input.previousHost) || !isKnownRoleTurnHost(input.liveHost)) {
|
|
77
|
-
return undefined;
|
|
78
|
-
}
|
|
79
69
|
if (input.previousHost === "pi") {
|
|
80
|
-
const paths = await listPiNativeRecordPaths(input.piSessionFile);
|
|
81
70
|
return {
|
|
82
|
-
|
|
83
|
-
priorNativePaths:
|
|
71
|
+
priorNativeKind: "pi-native",
|
|
72
|
+
priorNativePaths: await listPiNativeRecordPaths(input.piSessionFile),
|
|
84
73
|
};
|
|
85
74
|
}
|
|
86
|
-
//
|
|
87
|
-
const paths = await listSitianRecordPaths(input.piSessionFile);
|
|
75
|
+
// Sitian path handoff only — do not read bytes.
|
|
88
76
|
return {
|
|
89
|
-
|
|
90
|
-
priorNativePaths:
|
|
77
|
+
priorNativeKind: "sitian",
|
|
78
|
+
priorNativePaths: await listSitianRecordPaths(input.piSessionFile),
|
|
91
79
|
};
|
|
92
80
|
}
|
|
@@ -34,6 +34,7 @@ import { NOTARY_ACCEPTED_TEXT, NOTARY_OUTPUT_TOOL_NAME, validateRecordedNotaryOu
|
|
|
34
34
|
import { COUNTERSIGN_ACCEPTED_TEXT, COUNTERSIGN_OUTPUT_TOOL_NAME, validateRecordedCountersignOutput, type CountersignVerdict } from "../countersign-contracts.ts";
|
|
35
35
|
import { GLEANER_LEFT_ACCEPTED_TEXT, GLEANER_LEFT_OUTPUT_TOOL_NAME, validateRecordedGleanerLeftOutput, type GleanerLeftOutput } from "../gleaner-left-contracts.ts";
|
|
36
36
|
import { INSPECTOR_ACCEPTED_TEXT, INSPECTOR_OUTPUT_TOOL_NAME, validateRecordedInspectorOutput, type InspectorOutput } from "../inspector-contracts.ts";
|
|
37
|
+
import { DIARIST_ACCEPTED_TEXT, DIARIST_OUTPUT_TOOL_NAME, validateRecordedDiaristOutput, type DiaristOutput } from "../diarist-contracts.ts";
|
|
37
38
|
import {
|
|
38
39
|
CODER_ACCEPTED_TEXT,
|
|
39
40
|
CODER_OUTPUT_TOOL_NAME,
|
|
@@ -71,6 +72,7 @@ export {
|
|
|
71
72
|
validateRecordedInspectorOutput,
|
|
72
73
|
validateRecordedGatekeeperOutput,
|
|
73
74
|
validateRecordedNavigatorOutput,
|
|
75
|
+
validateRecordedDiaristOutput,
|
|
74
76
|
};
|
|
75
77
|
export type {
|
|
76
78
|
CollectorReceipt,
|
|
@@ -87,6 +89,7 @@ export type {
|
|
|
87
89
|
InspectorOutput,
|
|
88
90
|
GatekeeperDirectOutput,
|
|
89
91
|
NavigatorAdvice,
|
|
92
|
+
DiaristOutput,
|
|
90
93
|
};
|
|
91
94
|
|
|
92
95
|
export const TERMINATING_TOOL_NAMES = [
|
|
@@ -103,6 +106,7 @@ export const TERMINATING_TOOL_NAMES = [
|
|
|
103
106
|
INSPECTOR_OUTPUT_TOOL_NAME,
|
|
104
107
|
GATEKEEPER_OUTPUT_TOOL_NAME,
|
|
105
108
|
NAVIGATOR_OUTPUT_TOOL_NAME,
|
|
109
|
+
DIARIST_OUTPUT_TOOL_NAME,
|
|
106
110
|
] as const;
|
|
107
111
|
|
|
108
112
|
export type TerminatingToolName = (typeof TERMINATING_TOOL_NAMES)[number];
|
|
@@ -119,7 +123,8 @@ export type AcceptedDetails =
|
|
|
119
123
|
| GleanerLeftOutput
|
|
120
124
|
| InspectorOutput
|
|
121
125
|
| GatekeeperDirectOutput
|
|
122
|
-
| NavigatorAdvice
|
|
126
|
+
| NavigatorAdvice
|
|
127
|
+
| DiaristOutput;
|
|
123
128
|
|
|
124
129
|
export function isTerminatingToolName(
|
|
125
130
|
name: string,
|
|
@@ -155,6 +160,8 @@ export function acceptedTextFor(toolName: TerminatingToolName): string {
|
|
|
155
160
|
return GATEKEEPER_ACCEPTED_TEXT;
|
|
156
161
|
case NAVIGATOR_OUTPUT_TOOL_NAME:
|
|
157
162
|
return NAVIGATOR_ACCEPTED_TEXT;
|
|
163
|
+
case DIARIST_OUTPUT_TOOL_NAME:
|
|
164
|
+
return DIARIST_ACCEPTED_TEXT;
|
|
158
165
|
}
|
|
159
166
|
}
|
|
160
167
|
|
|
@@ -213,6 +220,7 @@ export function validateAcceptedDetails(
|
|
|
213
220
|
[INSPECTOR_OUTPUT_TOOL_NAME]: ["pass", "bounce"],
|
|
214
221
|
[GATEKEEPER_OUTPUT_TOOL_NAME]: ["dispatch", "pass"],
|
|
215
222
|
[NAVIGATOR_OUTPUT_TOOL_NAME]: ["advice"],
|
|
223
|
+
[DIARIST_OUTPUT_TOOL_NAME]: ["completed"],
|
|
216
224
|
};
|
|
217
225
|
const collectorDiscriminator = toolName === COLLECTOR_OUTPUT_TOOL && Array.isArray(candidate?.groups);
|
|
218
226
|
const baseDiscriminator = discriminator;
|
|
@@ -253,6 +261,8 @@ export function validateAcceptedDetails(
|
|
|
253
261
|
return validateRecordedGatekeeperOutput(details);
|
|
254
262
|
case NAVIGATOR_OUTPUT_TOOL_NAME:
|
|
255
263
|
return validateRecordedNavigatorOutput(details);
|
|
264
|
+
case DIARIST_OUTPUT_TOOL_NAME:
|
|
265
|
+
return validateRecordedDiaristOutput(details);
|
|
256
266
|
}
|
|
257
267
|
} catch (error) {
|
|
258
268
|
if (error instanceof Error && error.constructor === Error) throw new AcceptedDetailsContractError(error.message, { cause: error });
|
|
@@ -280,6 +290,15 @@ export function validateAcceptedLifecycle(
|
|
|
280
290
|
if (!deepEqual(testimony, projected)) throw new Error("accepted tool lifecycle details mismatch");
|
|
281
291
|
return details;
|
|
282
292
|
}
|
|
293
|
+
if (toolName === DIARIST_OUTPUT_TOOL_NAME) {
|
|
294
|
+
// Envelope-owned mechanical sitian facts are runtime-bound on details only
|
|
295
|
+
// (same shape as the Doctor runtime cost); the submitted arguments carry the
|
|
296
|
+
// role's own selections. Machine facts never come from model self-report.
|
|
297
|
+
const { sitian: _mechanical, ...submitted } = details as DiaristOutput & { sitian?: unknown };
|
|
298
|
+
const testimony = validateAcceptedDetails(toolName, argumentsValue);
|
|
299
|
+
if (!deepEqual(testimony, submitted)) throw new Error("accepted tool lifecycle details mismatch");
|
|
300
|
+
return details;
|
|
301
|
+
}
|
|
283
302
|
const argumentsDetails = validateAcceptedDetails(toolName, argumentsValue);
|
|
284
303
|
if (!deepEqual(argumentsDetails, details)) throw new Error("accepted tool lifecycle details mismatch");
|
|
285
304
|
return details;
|
|
@@ -301,7 +320,8 @@ export function acceptedFacts(toolName: TerminatingToolName, details: AcceptedDe
|
|
|
301
320
|
case GLEANER_LEFT_OUTPUT_TOOL_NAME:
|
|
302
321
|
case INSPECTOR_OUTPUT_TOOL_NAME:
|
|
303
322
|
case GATEKEEPER_OUTPUT_TOOL_NAME:
|
|
304
|
-
case NAVIGATOR_OUTPUT_TOOL_NAME:
|
|
323
|
+
case NAVIGATOR_OUTPUT_TOOL_NAME:
|
|
324
|
+
case DIARIST_OUTPUT_TOOL_NAME: return { status: (details as { status: string }).status };
|
|
305
325
|
case JUDGE_OUTPUT_TOOL_NAME: return { status: (details as { judgeStatus: string }).judgeStatus };
|
|
306
326
|
case COUNTERSIGN_OUTPUT_TOOL_NAME: return { status: (details as { countersignStatus: string }).countersignStatus };
|
|
307
327
|
case MERGER_OUTPUT_TOOL_NAME: {
|
|
@@ -11,6 +11,10 @@ import { NOTARY_OUTPUT_TOOL_NAME } from "./notary-contracts.ts";
|
|
|
11
11
|
import { COUNTERSIGN_OUTPUT_TOOL_NAME } from "./countersign-contracts.ts";
|
|
12
12
|
import { GLEANER_LEFT_OUTPUT_TOOL_NAME } from "./gleaner-left-contracts.ts";
|
|
13
13
|
import { INSPECTOR_OUTPUT_TOOL_NAME } from "./inspector-contracts.ts";
|
|
14
|
+
import {
|
|
15
|
+
DIARIST_OUTPUT_TOOL_NAME,
|
|
16
|
+
DIARIST_SOURCES_FLAG,
|
|
17
|
+
} from "./diarist-contracts.ts";
|
|
14
18
|
|
|
15
19
|
/** Shared by public notary and gatekeeper-province notary. */
|
|
16
20
|
export const NOTARY_SESSION_MATERIALS = [
|
|
@@ -185,6 +189,22 @@ export const PUBLIC_ROLE_RECORDS = [
|
|
|
185
189
|
activationStage: "load-and-install",
|
|
186
190
|
sessionMaterials: ["CLAUDE.md", "souls/navigator.md"],
|
|
187
191
|
},
|
|
192
|
+
// #708 / ADR 0075 `diarist-is-role`: 起居郎 is a seat like any other. The
|
|
193
|
+
// frozen source catalog rides the shared input-flag seam; it is absent for a
|
|
194
|
+
// true-unbound summons (no ticket → no diary).
|
|
195
|
+
{
|
|
196
|
+
role: "diarist",
|
|
197
|
+
phases: [null],
|
|
198
|
+
outputTool: DIARIST_OUTPUT_TOOL_NAME,
|
|
199
|
+
inputFlag: DIARIST_SOURCES_FLAG.name,
|
|
200
|
+
phaseFlag: undefined,
|
|
201
|
+
activationStage: "load-and-install",
|
|
202
|
+
sessionMaterials: [
|
|
203
|
+
"CLAUDE.md",
|
|
204
|
+
"souls/diarist.md",
|
|
205
|
+
"resources/diarist-collect.md",
|
|
206
|
+
],
|
|
207
|
+
},
|
|
188
208
|
] as const;
|
|
189
209
|
|
|
190
210
|
export type PublicRoleRecord = (typeof PUBLIC_ROLE_RECORDS)[number];
|
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;
|
|
@@ -406,9 +414,10 @@ export function createPiRoleTurnHost(config: PiRoleTurnHostConfig): RoleTurnHost
|
|
|
406
414
|
return {
|
|
407
415
|
async executeTurn(request: RoleTurnRequest): Promise<RoleTurnResult> {
|
|
408
416
|
// #617 DK-7: Pi argv gets projected native paths once; never record bytes.
|
|
417
|
+
// Pi already owns its own session file, so only sitian prior volume rides in.
|
|
409
418
|
let turnRequest = request;
|
|
410
419
|
const paths =
|
|
411
|
-
request.hostTransition?.
|
|
420
|
+
request.hostTransition?.priorNativeKind === "sitian"
|
|
412
421
|
? request.hostTransition.priorNativePaths
|
|
413
422
|
: undefined;
|
|
414
423
|
if (
|
package/src/public-cli/cli.ts
CHANGED
|
@@ -38,8 +38,9 @@ import { seatModelOnly } from "./registry.ts";
|
|
|
38
38
|
import { CliUsageError } from "./cli-errors.ts";
|
|
39
39
|
import type { CliIo } from "./cli-io.ts";
|
|
40
40
|
import type { PostAdmissionEnv } from "./post-admission.ts";
|
|
41
|
-
import type { RoleTurnHost } from "../host-contracts.ts";
|
|
42
|
-
import {
|
|
41
|
+
import type { RoleTurnHost, RoleTurnRequest } from "../host-contracts.ts";
|
|
42
|
+
import { packagedExternalHostNames } from "../host-descriptions.ts";
|
|
43
|
+
import { loadProductionAcpHostFactory } from "./load-production-acp-host.ts";
|
|
43
44
|
import {
|
|
44
45
|
createPiRoleTurnHost,
|
|
45
46
|
appendPiSessionCustomEntry,
|
|
@@ -50,6 +51,7 @@ import {
|
|
|
50
51
|
parseCoderArgv,
|
|
51
52
|
parseCollectorArgv,
|
|
52
53
|
parseCountersignArgv,
|
|
54
|
+
parseDiaristArgv,
|
|
53
55
|
parseGleanerLeftArgv,
|
|
54
56
|
parseDoctorArgv,
|
|
55
57
|
parseFixerArgv,
|
|
@@ -77,6 +79,7 @@ import { runPublicCoder, runPublicCoderResume } from "./coder-run.ts";
|
|
|
77
79
|
import { runPublicInstructionSeat, runPublicInstructionSeatResume } from "./instruction-seat-run.ts";
|
|
78
80
|
import { runPublicCollector, runPublicCollectorResume } from "./collector-run.ts";
|
|
79
81
|
import { runPublicCountersign, runPublicCountersignResume } from "./countersign-run.ts";
|
|
82
|
+
import { runPublicDiarist, runPublicDiaristResume } from "./diarist-run.ts";
|
|
80
83
|
import { runPublicGleanerLeft, runPublicGleanerLeftResume } from "./gleaner-left-run.ts";
|
|
81
84
|
import { runPublicDoctor, runPublicDoctorResume } from "./doctor-run.ts";
|
|
82
85
|
import { runPublicFixer, runPublicFixerResume } from "./fixer-run.ts";
|
|
@@ -125,6 +128,7 @@ const RESUME_SEAT_DISPATCH: Record<
|
|
|
125
128
|
inspector: { seat: "inspector", run: runPublicInspectorResume },
|
|
126
129
|
gatekeeper: { seat: "gatekeeper", run: runPublicInstructionSeatResume },
|
|
127
130
|
navigator: { seat: "navigator", run: runPublicInstructionSeatResume },
|
|
131
|
+
diarist: { seat: "diarist", run: runPublicDiaristResume },
|
|
128
132
|
};
|
|
129
133
|
import {
|
|
130
134
|
INTERNAL_ROLE_ENTRYPOINT_RELATIVE,
|
|
@@ -167,6 +171,7 @@ export const PUBLIC_ROLE_ARGV = {
|
|
|
167
171
|
reviewer: { parse: parseReviewerArgv, options: optionsForOwner("reviewer") },
|
|
168
172
|
gatekeeper: { parse: parseGatekeeperArgv, options: optionsForOwner("gatekeeper") },
|
|
169
173
|
navigator: { parse: parseNavigatorArgv, options: optionsForOwner("navigator") },
|
|
174
|
+
diarist: { parse: parseDiaristArgv, options: optionsForOwner("diarist") },
|
|
170
175
|
/** Deterministic analysis seat (#336) — argv parse only; no LLM admission. */
|
|
171
176
|
analyst: { parse: parseAnalystArgv, options: optionsForOwner("analyst") },
|
|
172
177
|
} as const;
|
|
@@ -321,19 +326,19 @@ function resolveRoleTurnHost(
|
|
|
321
326
|
recordLaunchedRolePackageIdentity,
|
|
322
327
|
observeLaunchedRolePackageIdentity,
|
|
323
328
|
});
|
|
324
|
-
// Composition-root
|
|
325
|
-
const adapters = env.hostAdapters ?? [
|
|
329
|
+
// Composition-root adapter table: pi (in-process default) + one ACP adapter per description-table key.
|
|
330
|
+
const adapters: readonly NamedRoleTurnHostAdapter[] = env.hostAdapters ?? [
|
|
326
331
|
{ name: "pi", create: () => ({ ok: true as const, host: piHost }) },
|
|
327
|
-
{
|
|
328
|
-
name
|
|
332
|
+
...packagedExternalHostNames().map((name) => ({
|
|
333
|
+
name,
|
|
329
334
|
create: () => {
|
|
330
335
|
// Factory loads outside the public bin static graph (ADR 0052 peer-free discovery).
|
|
331
336
|
let hostPromise: Promise<RoleTurnHost> | undefined;
|
|
332
337
|
return {
|
|
333
338
|
ok: true as const,
|
|
334
339
|
host: {
|
|
335
|
-
executeTurn: async (request) => {
|
|
336
|
-
hostPromise ??=
|
|
340
|
+
executeTurn: async (request: RoleTurnRequest) => {
|
|
341
|
+
hostPromise ??= loadProductionAcpHostFactory(env.packageRoot, name).then((create) =>
|
|
337
342
|
create({
|
|
338
343
|
packageRoot: env.packageRoot,
|
|
339
344
|
principalAuthority: options.principalAuthority,
|
|
@@ -344,7 +349,7 @@ function resolveRoleTurnHost(
|
|
|
344
349
|
},
|
|
345
350
|
};
|
|
346
351
|
},
|
|
347
|
-
},
|
|
352
|
+
})),
|
|
348
353
|
];
|
|
349
354
|
const hostName = options.seat.host;
|
|
350
355
|
const adapter = adapters.find((candidate) => candidate.name === hostName);
|
|
@@ -1310,6 +1315,31 @@ export async function runAkRole(
|
|
|
1310
1315
|
};
|
|
1311
1316
|
}
|
|
1312
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
|
+
|
|
1313
1343
|
// Coder public run path with package-owned TDD method (#109).
|
|
1314
1344
|
if (parsed.command === "coder") {
|
|
1315
1345
|
const agentDir = resolveAgentDir(env, home);
|
package/src/public-cli/config.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
|
|
7
|
+
import { assertRegisteredHostName, DEFAULT_ROLE_TURN_HOST } from "../host-descriptions.ts";
|
|
7
8
|
import { assertLegalEngineName } from "../package-resources/engine-material.ts";
|
|
8
9
|
import { resolveConfiguredProvinceOfficer } from "../institutional-resolution.ts";
|
|
9
10
|
import {
|
|
@@ -218,7 +219,8 @@ export function setPersistentSeatHost(
|
|
|
218
219
|
}
|
|
219
220
|
return { ...config, seats: { ...config.seats, [seat]: rest } };
|
|
220
221
|
}
|
|
221
|
-
|
|
222
|
+
const registered = assertRegisteredHostName(host);
|
|
223
|
+
return { ...config, seats: { ...config.seats, [seat]: { ...previous, host: registered } } };
|
|
222
224
|
}
|
|
223
225
|
|
|
224
226
|
/**
|
|
@@ -295,7 +297,7 @@ export function setAutoResumeLimit(
|
|
|
295
297
|
* Config-parse seam: persistent call axes belong to PUBLIC_CALLABLE_ROLES;
|
|
296
298
|
* engine names need only path-safety syntax (no closed material catalog;
|
|
297
299
|
* #376 / #378 / #391 / ADR 0069). Syntax authority = assertLegalEngineName
|
|
298
|
-
* (no injected duplicate).
|
|
300
|
+
* (no injected duplicate). Persistent host must be pi or a description-table key (#510 / #731).
|
|
299
301
|
*/
|
|
300
302
|
export function validatePublicCliConfigAxes(
|
|
301
303
|
config: PublicCliConfig,
|
|
@@ -303,14 +305,25 @@ export function validatePublicCliConfigAxes(
|
|
|
303
305
|
): void {
|
|
304
306
|
for (const seat of Object.keys(config.seats) as PublicConfigurableSeat[]) {
|
|
305
307
|
const row = config.seats[seat];
|
|
306
|
-
if (row?.engine
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
308
|
+
if (row?.engine !== undefined) {
|
|
309
|
+
try {
|
|
310
|
+
assertLegalEngineName(row.engine);
|
|
311
|
+
} catch (error) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
`config seat ${seat} engine is illegal: ${row.engine}`,
|
|
314
|
+
{ cause: error },
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (row?.host !== undefined) {
|
|
319
|
+
try {
|
|
320
|
+
assertRegisteredHostName(row.host);
|
|
321
|
+
} catch (error) {
|
|
322
|
+
throw new Error(
|
|
323
|
+
`config seat ${seat} host is unregistered: ${row.host}`,
|
|
324
|
+
{ cause: error },
|
|
325
|
+
);
|
|
326
|
+
}
|
|
314
327
|
}
|
|
315
328
|
}
|
|
316
329
|
}
|
|
@@ -550,7 +563,7 @@ function attachHostAxis(
|
|
|
550
563
|
if (invocation?.host !== undefined) return { ...seat, host: invocation.host, hostSource: "invocation" };
|
|
551
564
|
const persistent = config.seats[seat.seat]?.host;
|
|
552
565
|
if (persistent !== undefined) return { ...seat, host: persistent, hostSource: "persistent" };
|
|
553
|
-
return { ...seat, host:
|
|
566
|
+
return { ...seat, host: DEFAULT_ROLE_TURN_HOST, hostSource: "default" };
|
|
554
567
|
}
|
|
555
568
|
|
|
556
569
|
function attachEngineAxis(
|
|
@@ -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
|
-
}
|