@akagilnc/pi-workflow-roles 0.1.4035 → 0.1.4193
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/dist/acp-host/production-host.js +2368 -1155
- package/dist/archivist-record-entry.js +204 -20
- package/dist/archivist-record-topology.js +36 -16
- package/dist/auditor-dossier-tool.js +2 -1
- package/dist/diarist-contracts.js +96 -0
- package/dist/doctor-auditor.js +1 -1
- package/dist/doctor-evidence.js +3 -2
- package/dist/dossier-resolution.js +21 -8
- package/dist/gatekeeper-role.js +34 -27
- package/dist/headless-host/description.js +7 -8
- package/dist/headless-host/production-host.js +2375 -1149
- package/dist/host-contracts.js +16 -0
- package/dist/inspector-contracts.js +8 -0
- package/dist/migrate-book-topology.js +278917 -0
- package/dist/navigator-public-session.js +22 -25
- package/dist/notary-source-run.js +7 -4
- package/dist/packaged-role-registry.js +1 -1
- package/dist/pi/durable-principal.js +9 -6
- package/dist/pi/role-turn-host.js +43 -13
- package/dist/public-cli/auto-resume.js +2 -5
- package/dist/public-cli/case-dossier-delivery.js +60 -1
- package/dist/public-cli/diarist-run.js +27 -25
- package/dist/public-cli/inspector-run.js +17 -3
- package/dist/public-cli/instruction-seat-run.js +17 -17
- package/dist/public-cli/invocation.js +127 -63
- package/dist/public-cli/main.js +1685 -696
- package/dist/public-cli/notary-run.js +7 -4
- package/dist/public-cli/option-definitions.js +2 -2
- package/dist/public-cli/post-admission.js +113 -71
- package/dist/public-cli/run-lifecycle.js +209 -58
- package/dist/public-cli/seat-ticket-binding.js +3 -6
- package/dist/public-cli/settlement.js +29 -10
- package/dist/public-role-summons.js +8 -0
- package/dist/role-run-placement.js +84 -0
- package/dist/role-run-relocation.js +358 -0
- package/dist/run-terminal-artifacts.js +2 -1
- package/dist/run-ticket-number.js +51 -32
- package/dist/sitian-appender.js +11 -26
- package/dist/submission-ledger.js +106 -25
- package/dist/ticket-provenance.js +3 -1
- package/dist/user-dialogue-stdin.js +33 -0
- package/extensions/role-runtime.ts +1 -0
- package/package.json +3 -2
- package/resources/836-deleted-machine-instruction-inventory.md +1 -1
- package/scripts/build-package.mjs +34 -2
- package/souls/fixer.md +3 -2
- package/src/analyst-ledger.ts +14 -15
- package/src/archivist-record-entry.ts +285 -38
- package/src/archivist-record-topology.ts +43 -18
- package/src/auditor-dossier-tool.ts +2 -1
- package/src/book-topology-migration-placement.ts +493 -0
- package/src/book-topology-migration.ts +220 -0
- package/src/book-topology-migrators-t11.ts +856 -0
- package/src/book-topology-mixed-volume-migrators.ts +470 -0
- package/src/book-topology-partition-migrators.ts +25 -0
- package/src/book-topology-record-class-migrators.ts +779 -0
- package/src/book-topology-runs-migrator.ts +244 -0
- package/src/diarist-contracts.ts +116 -0
- package/src/diarist-role.ts +6 -0
- package/src/diarist.ts +2 -0
- package/src/doctor-auditor.ts +1 -1
- package/src/doctor-evidence.ts +4 -1
- package/src/doctor-role.ts +1 -1
- package/src/dossier-resolution.ts +26 -8
- package/src/external-host-turn-loop.ts +9 -0
- package/src/gatekeeper-pass-envelope.ts +6 -0
- package/src/gatekeeper-role.ts +47 -28
- package/src/headless-host/description.ts +8 -11
- package/src/headless-host/role-turn-host.ts +20 -5
- package/src/host-contracts.ts +22 -3
- package/src/inspector-contracts.ts +7 -0
- package/src/judge-role.ts +4 -0
- package/src/navigator-public-session.ts +33 -30
- package/src/navigator-work-context.ts +3 -4
- package/src/notary-role.ts +1 -0
- package/src/notary-source-run.ts +9 -4
- package/src/packaged-role-registry.ts +1 -1
- package/src/pi/adapter.ts +19 -4
- package/src/pi/durable-principal.ts +9 -13
- package/src/pi/role-turn-host.ts +48 -14
- package/src/public-cli/auto-resume.ts +2 -6
- package/src/public-cli/case-dossier-delivery.ts +86 -1
- package/src/public-cli/countersign-run.ts +182 -32
- package/src/public-cli/diarist-run.ts +34 -26
- package/src/public-cli/inspector-run.ts +26 -2
- package/src/public-cli/instruction-seat-run.ts +24 -20
- package/src/public-cli/invocation.ts +194 -58
- package/src/public-cli/notary-run.ts +13 -4
- package/src/public-cli/option-definitions.ts +2 -2
- package/src/public-cli/post-admission.ts +147 -78
- package/src/public-cli/run-lifecycle.ts +258 -65
- package/src/public-cli/seat-ticket-binding.ts +3 -6
- package/src/public-cli/settlement.ts +38 -10
- package/src/public-role-summons.ts +31 -7
- package/src/role-envelope.ts +9 -30
- package/src/role-run-placement.ts +128 -0
- package/src/role-run-relocation.ts +491 -0
- package/src/role-runtime-dependencies.ts +1 -0
- package/src/role-runtime.ts +106 -24
- package/src/run-terminal-artifacts.ts +3 -1
- package/src/run-ticket-number.ts +80 -20
- package/src/sitian-appender.ts +25 -28
- package/src/submission-ledger.ts +148 -30
- package/src/ticket-provenance.ts +4 -1
- package/src/ticket-trajectory.ts +22 -35
- package/src/user-dialogue-stdin.ts +37 -0
- package/src/worker-role.ts +8 -4
- package/src/worker-submission-gates.ts +6 -6
|
@@ -15,6 +15,7 @@ import { readSitianRecords, resolveSitianRecordPath, sitianReport } from "../sit
|
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
17
|
hasFreshAttemptSubmission,
|
|
18
|
+
readAttemptScopedSubmissionRows,
|
|
18
19
|
readRecordedSubmissionRows,
|
|
19
20
|
readRecordedSubmissions,
|
|
20
21
|
} from "../submission-ledger.ts";
|
|
@@ -125,6 +126,7 @@ import type {
|
|
|
125
126
|
DurablePrincipalAuthority,
|
|
126
127
|
DurablePrincipalCoordinates,
|
|
127
128
|
} from "../host-contracts.ts";
|
|
129
|
+
import { roleRunArtifactsDirectory } from "../role-run-placement.ts";
|
|
128
130
|
import {
|
|
129
131
|
ensureRunArtifactsDir,
|
|
130
132
|
homeFromRunDirectory,
|
|
@@ -161,9 +163,10 @@ export type SettlementCourtScope = {
|
|
|
161
163
|
function ledgerReadScope(
|
|
162
164
|
admitted: Pick<AdmittedRoleInvocation, "runDirectory">,
|
|
163
165
|
scope?: SettlementCourtScope,
|
|
164
|
-
): { home: string; attemptId?: string } {
|
|
166
|
+
): { home: string; sessionParent: string; attemptId?: string } {
|
|
165
167
|
return {
|
|
166
168
|
home: sealedLedgerHome(admitted),
|
|
169
|
+
sessionParent: join(admitted.runDirectory, "session", "session.jsonl"),
|
|
167
170
|
...(scope?.courtAttemptId === undefined || scope.courtAttemptId.length === 0
|
|
168
171
|
? {}
|
|
169
172
|
: { attemptId: scope.courtAttemptId }),
|
|
@@ -198,6 +201,22 @@ async function sealedLedgerOutcome(
|
|
|
198
201
|
role: TerminalRoleName,
|
|
199
202
|
scope?: SettlementCourtScope,
|
|
200
203
|
): Promise<Extract<TerminalRoleOutcome, { kind: "accepted" | "audit_escalation" }> | undefined> {
|
|
204
|
+
const home = sealedLedgerHome(admitted);
|
|
205
|
+
// #879: when court scope is present, roleOutcome is this-court original only —
|
|
206
|
+
// never last-wins over an undivided historical array, and never falls back to
|
|
207
|
+
// full-run history when this court sealed zero rows. #836 presentation of full
|
|
208
|
+
// history stays on attachRecordedSubmissions / terminal.submissions (run-scoped).
|
|
209
|
+
if (scope?.courtAttemptId !== undefined && scope.courtAttemptId.length > 0) {
|
|
210
|
+
const thisCourt = await readAttemptScopedSubmissionRows(
|
|
211
|
+
admitted.projectRoot,
|
|
212
|
+
admitted.runId,
|
|
213
|
+
scope.courtAttemptId,
|
|
214
|
+
home,
|
|
215
|
+
);
|
|
216
|
+
// Scope present + zero this-court rows → no this-court outcome (not run history).
|
|
217
|
+
return roleOutcomeFromRows(role, thisCourt);
|
|
218
|
+
}
|
|
219
|
+
// No court scope: keep run-scoped ledger face (#836).
|
|
201
220
|
const rows = await readRecordedSubmissionRows(
|
|
202
221
|
admitted.projectRoot,
|
|
203
222
|
admitted.runId,
|
|
@@ -225,7 +244,7 @@ export async function attemptProducedFreshSubmission(
|
|
|
225
244
|
admitted.projectRoot,
|
|
226
245
|
admitted.runId,
|
|
227
246
|
scope.courtAttemptId,
|
|
228
|
-
|
|
247
|
+
ledgerReadScope(admitted, scope),
|
|
229
248
|
);
|
|
230
249
|
}
|
|
231
250
|
|
|
@@ -247,12 +266,14 @@ export function withSubmissions<T extends TerminalResult>(
|
|
|
247
266
|
): T {
|
|
248
267
|
if (submissions.length === 0) return terminal;
|
|
249
268
|
const roleOutcome = terminal.roleOutcome;
|
|
250
|
-
//
|
|
251
|
-
//
|
|
269
|
+
// #879 keeps an already scoped this-court result; #881 full run history is
|
|
270
|
+
// presented independently on terminal.submissions. Only fill a missing result payload.
|
|
252
271
|
const withPayloads =
|
|
253
|
-
roleOutcome.kind === "accepted" || roleOutcome.kind === "audit_escalation"
|
|
254
|
-
? { ...roleOutcome, payloads: submissions }
|
|
255
|
-
: roleOutcome
|
|
272
|
+
roleOutcome.kind === "accepted" || roleOutcome.kind === "audit_escalation"
|
|
273
|
+
? { ...roleOutcome, payloads: (roleOutcome.payloads ?? []).length > 0 ? roleOutcome.payloads : submissions }
|
|
274
|
+
: roleOutcome.kind === "failure"
|
|
275
|
+
? { ...roleOutcome, payloads: roleOutcome.payloads ?? submissions }
|
|
276
|
+
: roleOutcome;
|
|
256
277
|
return { ...terminal, roleOutcome: withPayloads, submissions };
|
|
257
278
|
}
|
|
258
279
|
|
|
@@ -262,7 +283,14 @@ export async function attachRecordedSubmissions<T extends TerminalResult>(
|
|
|
262
283
|
terminal: T,
|
|
263
284
|
scope?: SettlementCourtScope,
|
|
264
285
|
): Promise<T> {
|
|
265
|
-
|
|
286
|
+
// #836 / #879: submissions presentation is always run-scoped history. Do not
|
|
287
|
+
// pass courtAttemptId here — roleOutcome already carries this-court payloads
|
|
288
|
+
// from sealedLedgerOutcome when scoped; withSubmissions keeps them.
|
|
289
|
+
void scope;
|
|
290
|
+
return withSubmissions(
|
|
291
|
+
terminal,
|
|
292
|
+
await recordedSubmissionPayloads(admitted, undefined),
|
|
293
|
+
);
|
|
266
294
|
}
|
|
267
295
|
|
|
268
296
|
/**
|
|
@@ -3818,7 +3846,7 @@ function uniqueFailureFallbackDirs(
|
|
|
3818
3846
|
async function resolveFailureArtifactsBase(
|
|
3819
3847
|
runDirectory: string,
|
|
3820
3848
|
): Promise<{ baseDir: string; attempt?: PublicationAttempt }> {
|
|
3821
|
-
const artifactsDir =
|
|
3849
|
+
const artifactsDir = roleRunArtifactsDirectory(runDirectory);
|
|
3822
3850
|
try {
|
|
3823
3851
|
await ensureRunArtifactsDir(runDirectory);
|
|
3824
3852
|
return { baseDir: artifactsDir };
|
|
@@ -3910,7 +3938,7 @@ export async function publishFailureArtifacts(
|
|
|
3910
3938
|
// Prefer conventional names; unique fallback dirs keep colliding fixed paths
|
|
3911
3939
|
// from stranding the original failure outside settlement. Include the ledger
|
|
3912
3940
|
// runs/ parent so a locked run directory (EACCES) cannot exhaust durability.
|
|
3913
|
-
const underArtifacts = baseDir ===
|
|
3941
|
+
const underArtifacts = baseDir === roleRunArtifactsDirectory(admitted.runDirectory);
|
|
3914
3942
|
const uniqueFallbackDirs = uniqueFailureFallbackDirs(
|
|
3915
3943
|
admitted.runDirectory,
|
|
3916
3944
|
baseDir,
|
|
@@ -62,6 +62,12 @@ export type PublicSummonRequest = {
|
|
|
62
62
|
* summons.instruction. Never folded into argv / parent-run lookup keys.
|
|
63
63
|
*/
|
|
64
64
|
readonly reviewReask?: string;
|
|
65
|
+
/**
|
|
66
|
+
* #879 same-parent (and first-mint) gate summons: verbatim parent-submission body.
|
|
67
|
+
* Rides summons.instruction / first-mint prompt when reviewReask is absent.
|
|
68
|
+
* Never folded into argv / parent-run lookup keys (binding pointer stays pure).
|
|
69
|
+
*/
|
|
70
|
+
readonly gateReviewInstruction?: string;
|
|
65
71
|
/**
|
|
66
72
|
* Pi-adapter inject (tests). Used only as the `pi` row when composing the
|
|
67
73
|
* adapter table — never as an override of the child seat's selected host.
|
|
@@ -302,6 +308,10 @@ export async function summonPublicRole(
|
|
|
302
308
|
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
303
309
|
// #753: reask rides the existing notary same-ticket resume summons.instruction.
|
|
304
310
|
...(options.reviewReask === undefined ? {} : { reviewReask: options.reviewReask }),
|
|
311
|
+
// #879: verbatim submission body on officer dialogue content channel.
|
|
312
|
+
...(options.gateReviewInstruction === undefined
|
|
313
|
+
? {}
|
|
314
|
+
: { gateReviewInstruction: options.gateReviewInstruction }),
|
|
305
315
|
...(options.boundTicketNumber === undefined
|
|
306
316
|
? {}
|
|
307
317
|
: { boundTicketNumber: options.boundTicketNumber }),
|
|
@@ -430,6 +440,12 @@ export async function summonGateOfficer(options: {
|
|
|
430
440
|
* Never concatenated into argv (inspector parentRunPath is the pure 卷宗指针).
|
|
431
441
|
*/
|
|
432
442
|
readonly reask?: string;
|
|
443
|
+
/**
|
|
444
|
+
* In-flight parent 交卷 body (tool-call arguments). Relayed verbatim as officer
|
|
445
|
+
* dialogue content when reask is absent (#879). Binding pointer stays on
|
|
446
|
+
* --source-run / 卷宗指针 argv — never a content substitute.
|
|
447
|
+
*/
|
|
448
|
+
readonly submission?: unknown;
|
|
433
449
|
/** Pi-adapter inject — forwarded to summonPublicRole (not a parent-host override). */
|
|
434
450
|
readonly roleTurnHost?: RoleTurnHost;
|
|
435
451
|
readonly hostAdapters?: readonly NamedRoleTurnHostAdapter[];
|
|
@@ -444,8 +460,13 @@ export async function summonGateOfficer(options: {
|
|
|
444
460
|
}
|
|
445
461
|
// Officer host is seat-owned only (#821). Parent invocation.json.host stays a
|
|
446
462
|
// hostTransition consumer in post-admission — not an officer override channel.
|
|
447
|
-
// #
|
|
448
|
-
//
|
|
463
|
+
// #879: binding pointer = parent run directory; dialogue content = submission body.
|
|
464
|
+
// Conclusion re-ask keeps sole ownership of reviewReask when present.
|
|
465
|
+
let gateReviewInstruction: string | undefined;
|
|
466
|
+
if (options.reask === undefined && options.submission !== undefined) {
|
|
467
|
+
const { readableGateItem } = await import("./readable-gate-item.ts");
|
|
468
|
+
gateReviewInstruction = readableGateItem(options.submission);
|
|
469
|
+
}
|
|
449
470
|
const common = {
|
|
450
471
|
cwd: options.cwd,
|
|
451
472
|
...(home === undefined ? {} : { home }),
|
|
@@ -453,13 +474,16 @@ export async function summonGateOfficer(options: {
|
|
|
453
474
|
...(options.io === undefined ? {} : { io: options.io }),
|
|
454
475
|
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
455
476
|
...(options.reask === undefined ? {} : { reviewReask: options.reask }),
|
|
477
|
+
...(gateReviewInstruction === undefined
|
|
478
|
+
? {}
|
|
479
|
+
: { gateReviewInstruction }),
|
|
456
480
|
...(options.roleTurnHost === undefined ? {} : { roleTurnHost: options.roleTurnHost }),
|
|
457
481
|
...(options.hostAdapters === undefined ? {} : { hostAdapters: options.hostAdapters }),
|
|
458
482
|
...(options.createRunId === undefined ? {} : { createRunId: options.createRunId }),
|
|
459
483
|
} as const;
|
|
460
484
|
if (options.officer === "notary") {
|
|
461
|
-
// #753: reask
|
|
462
|
-
//
|
|
485
|
+
// #753/#879: reask or verbatim body rides summons.instruction / first-mint prompt.
|
|
486
|
+
// Binding stays --source-run (never folded into content).
|
|
463
487
|
return summonPublicRole({
|
|
464
488
|
role: "notary",
|
|
465
489
|
argv: ["--source-run", options.sourceRunDirectory, "--project", options.cwd],
|
|
@@ -468,7 +492,7 @@ export async function summonGateOfficer(options: {
|
|
|
468
492
|
}
|
|
469
493
|
if (options.officer === "auditor") {
|
|
470
494
|
// #756: judge compliance path — same queue law as notary/inspector.
|
|
471
|
-
//
|
|
495
|
+
// Binding pointer on argv; dialogue content rides gateReviewInstruction.
|
|
472
496
|
return summonPublicRole({
|
|
473
497
|
role: "auditor",
|
|
474
498
|
argv: [
|
|
@@ -481,8 +505,8 @@ export async function summonGateOfficer(options: {
|
|
|
481
505
|
...common,
|
|
482
506
|
});
|
|
483
507
|
}
|
|
484
|
-
// Inspector: argv stays pure 卷宗指针 (#747 parentRunPath
|
|
485
|
-
//
|
|
508
|
+
// Inspector: argv stays pure 卷宗指针 (#747 parentRunPath lookup key).
|
|
509
|
+
// Content (body/reask) rides env → summons.instruction / first-mint prompt.
|
|
486
510
|
return summonPublicRole({
|
|
487
511
|
role: "inspector",
|
|
488
512
|
argv: [`卷宗指针:${options.sourceRunDirectory}`],
|
package/src/role-envelope.ts
CHANGED
|
@@ -172,6 +172,8 @@ export async function prepareRoleEnvelope(options: {
|
|
|
172
172
|
cwd: request.cwd,
|
|
173
173
|
mode: "print",
|
|
174
174
|
model: request.model === undefined ? undefined : { provider: request.model.provider },
|
|
175
|
+
runDirectory: request.runDirectory,
|
|
176
|
+
...(request.courtAttemptId === undefined ? {} : { courtAttemptId: request.courtAttemptId }),
|
|
175
177
|
sessionManager: {
|
|
176
178
|
getLeafEntry: () => sessionEntries.at(-1) as ReturnType<HostContext["sessionManager"]["getLeafEntry"]>,
|
|
177
179
|
getLeafId: () => runId,
|
|
@@ -239,6 +241,7 @@ export async function prepareRoleEnvelope(options: {
|
|
|
239
241
|
bindSubmissionNonPass: options.hostActions.bindSubmissionNonPass,
|
|
240
242
|
},
|
|
241
243
|
toolCallId: options.toolCallId,
|
|
244
|
+
...(options.submission === undefined ? {} : { submission: options.submission }),
|
|
242
245
|
});
|
|
243
246
|
},
|
|
244
247
|
on(...registration: HostEventRegistration) {
|
|
@@ -527,22 +530,8 @@ export async function prepareRoleEnvelope(options: {
|
|
|
527
530
|
const relay = fileURLToPath(new URL("./mcp-relay.mjs", import.meta.url));
|
|
528
531
|
await listen(server, options.socketPath);
|
|
529
532
|
let disposed = false;
|
|
530
|
-
//
|
|
531
|
-
//
|
|
532
|
-
// identity sees the same signals. Engine axis is request-scoped via RoleHost
|
|
533
|
-
// flag — never process.env (#818 P1). Run-dir may wait
|
|
534
|
-
// until prepare succeeds. Dispose restores including unset.
|
|
535
|
-
let priorAkRoleRunDir: string | undefined;
|
|
536
|
-
let priorAkRoleCourtAttempt: string | undefined;
|
|
537
|
-
let runDirInjected = false;
|
|
538
|
-
const restoreAkRoleRunEnv = (): void => {
|
|
539
|
-
if (!runDirInjected) return;
|
|
540
|
-
runDirInjected = false;
|
|
541
|
-
if (priorAkRoleRunDir === undefined) delete process.env.AK_ROLE_RUN_DIR;
|
|
542
|
-
else process.env.AK_ROLE_RUN_DIR = priorAkRoleRunDir;
|
|
543
|
-
if (priorAkRoleCourtAttempt === undefined) delete process.env.AK_ROLE_COURT_ATTEMPT;
|
|
544
|
-
else process.env.AK_ROLE_COURT_ATTEMPT = priorAkRoleCourtAttempt;
|
|
545
|
-
};
|
|
533
|
+
// Per-turn run/court identity lives on HostContext (request-scoped). Engine
|
|
534
|
+
// axis is already a RoleHost flag — never process.env (#818 P1 / #879).
|
|
546
535
|
const dispose = async (): Promise<void> => {
|
|
547
536
|
if (disposed) return;
|
|
548
537
|
disposed = true;
|
|
@@ -552,11 +541,6 @@ export async function prepareRoleEnvelope(options: {
|
|
|
552
541
|
} catch (error) {
|
|
553
542
|
cleanupFailures.push(error);
|
|
554
543
|
}
|
|
555
|
-
try {
|
|
556
|
-
restoreAkRoleRunEnv();
|
|
557
|
-
} catch (error) {
|
|
558
|
-
cleanupFailures.push(error);
|
|
559
|
-
}
|
|
560
544
|
try {
|
|
561
545
|
const closeAll = (server as unknown as { closeAllConnections?: () => void }).closeAllConnections;
|
|
562
546
|
if (typeof closeAll === "function") closeAll.call(server);
|
|
@@ -640,6 +624,8 @@ export async function prepareRoleEnvelope(options: {
|
|
|
640
624
|
}
|
|
641
625
|
// Method notes only here — role before_agent_start injects soul once.
|
|
642
626
|
// Preloading session materials duplicated soul under the role tag (#632).
|
|
627
|
+
// #879: case-dossier owner reads runDirectory from this turn's HostContext.
|
|
628
|
+
|
|
643
629
|
const methodPrompt = (await Promise.all(request.methods.map(({ path }) => readFile(path, "utf8")))).join("\n\n");
|
|
644
630
|
const promptResults = await emit("before_agent_start", {
|
|
645
631
|
prompt,
|
|
@@ -652,8 +638,8 @@ export async function prepareRoleEnvelope(options: {
|
|
|
652
638
|
return [value.systemPrompt];
|
|
653
639
|
});
|
|
654
640
|
const systemPromptBody = systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : methodPrompt;
|
|
655
|
-
// Typed reading materials from agent-start handlers (
|
|
656
|
-
// Folded into
|
|
641
|
+
// Typed reading materials from agent-start handlers (incl. single shared
|
|
642
|
+
// case-dossier owner). Folded into provider-visible systemPrompt at send.
|
|
657
643
|
const readingMaterials: unknown[] = [];
|
|
658
644
|
for (const value of promptResults) {
|
|
659
645
|
if (typeof value !== "object" || value === null) continue;
|
|
@@ -662,13 +648,6 @@ export async function prepareRoleEnvelope(options: {
|
|
|
662
648
|
if (material !== undefined) readingMaterials.push(material);
|
|
663
649
|
}
|
|
664
650
|
|
|
665
|
-
priorAkRoleRunDir = process.env.AK_ROLE_RUN_DIR;
|
|
666
|
-
priorAkRoleCourtAttempt = process.env.AK_ROLE_COURT_ATTEMPT;
|
|
667
|
-
process.env.AK_ROLE_RUN_DIR = request.runDirectory;
|
|
668
|
-
if (request.courtAttemptId === undefined) delete process.env.AK_ROLE_COURT_ATTEMPT;
|
|
669
|
-
else process.env.AK_ROLE_COURT_ATTEMPT = request.courtAttemptId;
|
|
670
|
-
runDirInjected = true;
|
|
671
|
-
|
|
672
651
|
const terminating = tools.get(terminatingToolName);
|
|
673
652
|
if (terminating === undefined) {
|
|
674
653
|
throw new Error(`terminating tool not registered after activation: ${terminatingToolName}`);
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
activationBookDirectory,
|
|
6
|
+
ensureRealDirectoryTree,
|
|
7
|
+
} from "./activation-ledger-topology.ts";
|
|
8
|
+
import { requireSafePositiveTicketNumber } from "./run-ticket-number.ts";
|
|
9
|
+
|
|
10
|
+
export type RoleRunSubject =
|
|
11
|
+
| { readonly ticketNumber: number }
|
|
12
|
+
| { readonly unbound: true };
|
|
13
|
+
|
|
14
|
+
export type RoleRunPlacement = {
|
|
15
|
+
readonly runDirectory: string;
|
|
16
|
+
readonly sessionDirectory: string;
|
|
17
|
+
readonly sessionFile: string;
|
|
18
|
+
readonly artifactsDirectory: string;
|
|
19
|
+
readonly attachmentsDirectory: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function isMissingPathError(error: unknown): boolean {
|
|
23
|
+
return (
|
|
24
|
+
error instanceof Error &&
|
|
25
|
+
"code" in error &&
|
|
26
|
+
(error as NodeJS.ErrnoException).code === "ENOENT"
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Sole book-level run directory walk: flat legacy `runs/` plus each
|
|
32
|
+
* `subject/runs/` child (ticket / unbound). One authority for read-side
|
|
33
|
+
* enumeration under a book directory — findRunDirectoryById, analyst scan,
|
|
34
|
+
* and ticket trajectory must not hardcode a second subject-tree walk.
|
|
35
|
+
*/
|
|
36
|
+
export async function listBookRunDirectories(bookDir: string): Promise<string[]> {
|
|
37
|
+
const out: string[] = [];
|
|
38
|
+
const seen = new Set<string>();
|
|
39
|
+
|
|
40
|
+
const collect = async (runsDir: string): Promise<void> => {
|
|
41
|
+
let entries;
|
|
42
|
+
try {
|
|
43
|
+
entries = await readdir(runsDir, { withFileTypes: true });
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (isMissingPathError(error)) return;
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
if (!entry.isDirectory()) continue;
|
|
50
|
+
const runDir = join(runsDir, entry.name);
|
|
51
|
+
if (seen.has(runDir)) continue;
|
|
52
|
+
seen.add(runDir);
|
|
53
|
+
out.push(runDir);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
await collect(join(bookDir, "runs"));
|
|
58
|
+
|
|
59
|
+
let subjects;
|
|
60
|
+
try {
|
|
61
|
+
subjects = await readdir(bookDir, { withFileTypes: true });
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (isMissingPathError(error)) return out.sort();
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
for (const subject of subjects) {
|
|
67
|
+
if (!subject.isDirectory() || subject.name === "runs") continue;
|
|
68
|
+
await collect(join(bookDir, subject.name, "runs"));
|
|
69
|
+
}
|
|
70
|
+
return out.sort();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The single authority for every path belonging to an admitted role run. */
|
|
74
|
+
export function roleRunPlacement(
|
|
75
|
+
ledgerHome: string,
|
|
76
|
+
input: {
|
|
77
|
+
readonly bookKey: string;
|
|
78
|
+
readonly subject: RoleRunSubject;
|
|
79
|
+
readonly runId: string;
|
|
80
|
+
readonly role: string;
|
|
81
|
+
},
|
|
82
|
+
): RoleRunPlacement {
|
|
83
|
+
const subjectDirectory = "ticketNumber" in input.subject
|
|
84
|
+
? String(requireSafePositiveTicketNumber(
|
|
85
|
+
input.subject.ticketNumber,
|
|
86
|
+
"roleRunPlacement subject.ticketNumber",
|
|
87
|
+
))
|
|
88
|
+
: "unbound";
|
|
89
|
+
const runDirectory = join(
|
|
90
|
+
activationBookDirectory(ledgerHome, input.bookKey),
|
|
91
|
+
subjectDirectory,
|
|
92
|
+
"runs",
|
|
93
|
+
`${input.runId}@${input.role}`,
|
|
94
|
+
);
|
|
95
|
+
const sessionDirectory = join(runDirectory, "session");
|
|
96
|
+
return {
|
|
97
|
+
runDirectory,
|
|
98
|
+
sessionDirectory,
|
|
99
|
+
sessionFile: join(sessionDirectory, "session.jsonl"),
|
|
100
|
+
artifactsDirectory: roleRunArtifactsDirectory(runDirectory),
|
|
101
|
+
attachmentsDirectory: join(runDirectory, "attachments"),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The single artifacts subpath definition for new and resumed role runs. */
|
|
106
|
+
export function roleRunArtifactsDirectory(runDirectory: string): string {
|
|
107
|
+
return join(runDirectory, "artifacts");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The placement seam owns creation for both new runs and resumed legacy runs. */
|
|
111
|
+
export function ensureRoleRunDirectory(
|
|
112
|
+
ledgerHome: string,
|
|
113
|
+
directory: string,
|
|
114
|
+
): string {
|
|
115
|
+
return ensureRealDirectoryTree(ledgerHome, directory);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function ensureRoleRunPlacement(
|
|
119
|
+
ledgerHome: string,
|
|
120
|
+
placement: RoleRunPlacement,
|
|
121
|
+
): void {
|
|
122
|
+
for (const directory of [
|
|
123
|
+
placement.sessionDirectory,
|
|
124
|
+
placement.attachmentsDirectory,
|
|
125
|
+
]) {
|
|
126
|
+
ensureRoleRunDirectory(ledgerHome, directory);
|
|
127
|
+
}
|
|
128
|
+
}
|