@akagilnc/pi-workflow-roles 0.1.3671 → 0.1.3682
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 +272 -166
- package/dist/diarist-contracts.js +4 -4
- package/dist/diarist-role.js +10 -4
- package/dist/diarist.js +121 -17
- package/dist/package-contracts/terminating-tools.js +1 -1
- package/dist/public-cli/inspector-run.js +3 -13
- package/dist/public-cli/instruction-seat-run.js +6 -31
- package/dist/public-cli/invocation.js +32 -0
- package/dist/public-cli/judge-run.js +1 -7
- package/dist/public-cli/main.js +721 -754
- package/dist/public-cli/run-lifecycle.js +0 -28
- package/dist/public-cli/seat-ticket-binding.js +16 -113
- package/dist/role-runtime.js +89 -10
- package/package.json +1 -1
- package/resources/diarist-collect.md +10 -0
- package/src/diarist-contracts.ts +21 -9
- package/src/diarist-role.ts +15 -4
- package/src/diarist.ts +192 -22
- package/src/package-contracts/terminating-tools.ts +1 -1
- package/src/public-cli/coder-run.ts +1 -7
- package/src/public-cli/countersign-run.ts +238 -86
- package/src/public-cli/diarist-run.ts +83 -28
- package/src/public-cli/fixer-run.ts +1 -7
- package/src/public-cli/inspector-run.ts +3 -17
- package/src/public-cli/instruction-seat-run.ts +6 -36
- package/src/public-cli/invocation.ts +52 -0
- package/src/public-cli/judge-run.ts +1 -7
- package/src/public-cli/run-lifecycle.ts +0 -31
- package/src/public-cli/seat-ticket-binding.ts +15 -146
- package/src/role-runtime.ts +107 -9
package/src/diarist.ts
CHANGED
|
@@ -45,6 +45,130 @@ import { execFileSync } from "node:child_process";
|
|
|
45
45
|
|
|
46
46
|
export type { DiaristIssueFace } from "./diarist-mechanical.ts";
|
|
47
47
|
|
|
48
|
+
/** Live existence check for an asserted ticket number. */
|
|
49
|
+
export type TicketExistenceChecker = (input: {
|
|
50
|
+
readonly owner: string;
|
|
51
|
+
readonly repo: string;
|
|
52
|
+
readonly ticketNumber: number;
|
|
53
|
+
readonly signal?: AbortSignal;
|
|
54
|
+
}) => Promise<boolean>;
|
|
55
|
+
|
|
56
|
+
export type DiaristTicketVerificationReason =
|
|
57
|
+
| "number-not-in-instruction"
|
|
58
|
+
| "ticket-missing"
|
|
59
|
+
| "origin-unresolved"
|
|
60
|
+
| "assertion-uninterpretable";
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Honest failure of mechanical ticket verification (ADR 0075).
|
|
64
|
+
* Must settle as failure — never wash into true-unbound / 无录.
|
|
65
|
+
*/
|
|
66
|
+
export class DiaristTicketVerificationError extends Error {
|
|
67
|
+
readonly code = "diarist-ticket-verification" as const;
|
|
68
|
+
readonly reason: DiaristTicketVerificationReason;
|
|
69
|
+
constructor(
|
|
70
|
+
reason: DiaristTicketVerificationReason,
|
|
71
|
+
message: string,
|
|
72
|
+
options?: ErrorOptions,
|
|
73
|
+
) {
|
|
74
|
+
super(message, options);
|
|
75
|
+
this.name = "DiaristTicketVerificationError";
|
|
76
|
+
this.reason = reason;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Decision-key exact identity: complete decimal number N appears in instruction.
|
|
82
|
+
* A longer number's digit substring (e.g. 82 inside 582) is not N.
|
|
83
|
+
* Verifies a typed claim — does not harvest candidates from prose.
|
|
84
|
+
*/
|
|
85
|
+
export function instructionContainsTicketNumber(
|
|
86
|
+
instruction: string,
|
|
87
|
+
ticketNumber: number,
|
|
88
|
+
): boolean {
|
|
89
|
+
if (!Number.isSafeInteger(ticketNumber) || ticketNumber < 1) return false;
|
|
90
|
+
// Complete decimal token: not preceded or followed by another digit.
|
|
91
|
+
// Plain string (not template) so the digit-class backslash survives emit.
|
|
92
|
+
return new RegExp("(?<!\\d)" + String(ticketNumber) + "(?!\\d)").test(
|
|
93
|
+
instruction,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Production live-ticket check over shared gh issue-body projection.
|
|
99
|
+
* Available issue face → exists; unavailable/invalid → missing.
|
|
100
|
+
*/
|
|
101
|
+
export function createGhTicketExistenceChecker(options?: {
|
|
102
|
+
readonly runner?: GhApiRunner;
|
|
103
|
+
}): TicketExistenceChecker {
|
|
104
|
+
const runner = options?.runner ?? createGhApiRunner();
|
|
105
|
+
return async (input) => {
|
|
106
|
+
const projected = await projectGhIssueBody(runner, {
|
|
107
|
+
owner: input.owner,
|
|
108
|
+
repo: input.repo,
|
|
109
|
+
ticketNumber: input.ticketNumber,
|
|
110
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
111
|
+
});
|
|
112
|
+
return projected.status === "available";
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Mechanical verify of an LLM typed ticket assertion (ADR 0075).
|
|
118
|
+
* Complete decimal of N must appear in the summons; ticket must exist live.
|
|
119
|
+
* Throws DiaristTicketVerificationError — caller must not wash into 无录.
|
|
120
|
+
*/
|
|
121
|
+
export async function verifyAssertedTicketNumber(input: {
|
|
122
|
+
readonly ticketNumber: number;
|
|
123
|
+
readonly instruction: string;
|
|
124
|
+
readonly projectRoot: string;
|
|
125
|
+
readonly checkExistence?: TicketExistenceChecker;
|
|
126
|
+
readonly signal?: AbortSignal;
|
|
127
|
+
}): Promise<void> {
|
|
128
|
+
const n = input.ticketNumber;
|
|
129
|
+
if (!Number.isSafeInteger(n) || n < 1) {
|
|
130
|
+
throw new DiaristTicketVerificationError(
|
|
131
|
+
"assertion-uninterpretable",
|
|
132
|
+
`diarist ticket assertion must be a safe integer >= 1, got ${String(n)}`,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
if (!instructionContainsTicketNumber(input.instruction, n)) {
|
|
136
|
+
throw new DiaristTicketVerificationError(
|
|
137
|
+
"number-not-in-instruction",
|
|
138
|
+
`diarist ticket assertion #${n} complete decimal number does not appear in accepted instruction`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
const origin = resolveDiaristGithubOrigin(input.projectRoot);
|
|
142
|
+
if (origin === undefined) {
|
|
143
|
+
throw new DiaristTicketVerificationError(
|
|
144
|
+
"origin-unresolved",
|
|
145
|
+
`diarist ticket assertion #${n} requires a resolvable github.com origin remote for live verification`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
const checkExistence = input.checkExistence ?? createGhTicketExistenceChecker();
|
|
149
|
+
let exists: boolean;
|
|
150
|
+
try {
|
|
151
|
+
exists = await checkExistence({
|
|
152
|
+
owner: origin.owner,
|
|
153
|
+
repo: origin.repo,
|
|
154
|
+
ticketNumber: n,
|
|
155
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
156
|
+
});
|
|
157
|
+
} catch (error) {
|
|
158
|
+
throw new DiaristTicketVerificationError(
|
|
159
|
+
"ticket-missing",
|
|
160
|
+
`diarist ticket assertion #${n} live verification failed`,
|
|
161
|
+
{ cause: error },
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
if (!exists) {
|
|
165
|
+
throw new DiaristTicketVerificationError(
|
|
166
|
+
"ticket-missing",
|
|
167
|
+
`diarist ticket assertion #${n} does not exist as a live issue on ${origin.owner}/${origin.repo}`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
48
172
|
/** Typed reasons when bound-ticket issue face cannot be acquired honestly. */
|
|
49
173
|
export type DiaristIssueSourceReason =
|
|
50
174
|
| "origin-unresolved"
|
|
@@ -232,12 +356,19 @@ export type DiaristSourceCandidate = DiaristSourceBlock & {
|
|
|
232
356
|
};
|
|
233
357
|
|
|
234
358
|
/**
|
|
235
|
-
* Frozen
|
|
236
|
-
*
|
|
237
|
-
* re-
|
|
359
|
+
* Frozen catalog handed to the diarist turn and re-read by the envelope at
|
|
360
|
+
* accept time. Carries summons text + run coordinates so accept can verify an
|
|
361
|
+
* LLM ticket assertion (ADR 0075) without re-deriving ambient state.
|
|
362
|
+
* ticketNumber absent = first summons still open for LLM assertion.
|
|
238
363
|
*/
|
|
239
364
|
export type DiaristSourceCatalog = {
|
|
240
|
-
|
|
365
|
+
/** Bound before turn when book already records the ticket; else LLM asserts. */
|
|
366
|
+
readonly ticketNumber?: number;
|
|
367
|
+
/** Caller summons — sole text mechanical verify reads for complete-decimal check. */
|
|
368
|
+
readonly instruction: string;
|
|
369
|
+
/** Run dossier pages accept binds after a verified first-summons assertion. */
|
|
370
|
+
readonly runDirectory: string;
|
|
371
|
+
readonly projectRoot: string;
|
|
241
372
|
readonly cwd: string;
|
|
242
373
|
readonly home?: string;
|
|
243
374
|
readonly candidates: readonly DiaristSourceCandidate[];
|
|
@@ -311,7 +442,11 @@ function loadSourceBlocks(input: {
|
|
|
311
442
|
}
|
|
312
443
|
|
|
313
444
|
export type PrepareDiaristSourceCatalogInput = {
|
|
314
|
-
|
|
445
|
+
/** Bound ticket when known before the turn; omit on first-summons identity round. */
|
|
446
|
+
readonly ticketNumber?: number;
|
|
447
|
+
readonly instruction: string;
|
|
448
|
+
readonly runDirectory: string;
|
|
449
|
+
readonly projectRoot: string;
|
|
315
450
|
readonly cwd: string;
|
|
316
451
|
/** Explicit package home (admitted run / tests); never process.env.HOME (#604). */
|
|
317
452
|
readonly home?: string;
|
|
@@ -323,32 +458,44 @@ export type PrepareDiaristSourceCatalogInput = {
|
|
|
323
458
|
|
|
324
459
|
/**
|
|
325
460
|
* Mechanical half A — source enumeration into a frozen catalog.
|
|
326
|
-
*
|
|
327
|
-
* `ticket-provenance-file` 每票一份起居录)
|
|
328
|
-
*
|
|
461
|
+
* When ticketNumber is known: establishes the per-ticket volume + human view
|
|
462
|
+
* (ADR 0075 `ticket-provenance-file` 每票一份起居录) and offers only blocks not
|
|
463
|
+
* yet on the volume / offered watermark (增量幂等).
|
|
464
|
+
* When ticketNumber is absent (first summons): offers session candidates without
|
|
465
|
+
* minting a volume — the LLM turn asserts identity; accept verifies and mints.
|
|
329
466
|
*/
|
|
330
467
|
export async function prepareDiaristSourceCatalog(
|
|
331
468
|
input: PrepareDiaristSourceCatalogInput,
|
|
332
469
|
): Promise<DiaristSourceCatalog> {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
470
|
+
const known = input.ticketNumber;
|
|
471
|
+
if (known !== undefined) {
|
|
472
|
+
ensureTicketProvenanceVolume(known, input.cwd, input.home);
|
|
473
|
+
const volume = await readTicketProvenance(known, input.cwd, input.home);
|
|
474
|
+
writeTicketProvenanceHumanView({
|
|
475
|
+
ticketNumber: known,
|
|
476
|
+
cwd: input.cwd,
|
|
477
|
+
...(input.home === undefined ? {} : { home: input.home }),
|
|
478
|
+
entries: volume.entries,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
341
481
|
|
|
342
482
|
const rawBlocks = loadSourceBlocks(input);
|
|
343
483
|
// Safeguard only (notify filter + dedupe) — never prose-based exclusion.
|
|
344
484
|
const safeguarded = mechanicalSafeguardPipeline(rawBlocks);
|
|
345
|
-
const seen =
|
|
485
|
+
const seen =
|
|
486
|
+
known === undefined
|
|
487
|
+
? new Set<string>()
|
|
488
|
+
: await loadSeenEntryIdentities(known, input.cwd, input.home);
|
|
346
489
|
const fresh = safeguarded.filter(
|
|
347
|
-
(block) =>
|
|
490
|
+
(block) =>
|
|
491
|
+
known === undefined || !seen.has(blockEntryIdentity(known, block)),
|
|
348
492
|
);
|
|
349
493
|
|
|
350
494
|
return {
|
|
351
|
-
ticketNumber:
|
|
495
|
+
...(known === undefined ? {} : { ticketNumber: known }),
|
|
496
|
+
instruction: input.instruction,
|
|
497
|
+
runDirectory: input.runDirectory,
|
|
498
|
+
projectRoot: input.projectRoot,
|
|
352
499
|
cwd: input.cwd,
|
|
353
500
|
...(input.home === undefined ? {} : { home: input.home }),
|
|
354
501
|
candidates: fresh.map((block, candidateIndex) => ({ ...block, candidateIndex })),
|
|
@@ -366,8 +513,25 @@ export function loadDiaristSourceCatalog(path: string): DiaristSourceCatalog {
|
|
|
366
513
|
throw new Error(`diarist source catalog is not an object (${path})`);
|
|
367
514
|
}
|
|
368
515
|
const record = parsed as Record<string, unknown>;
|
|
369
|
-
if (typeof record.
|
|
370
|
-
throw new Error(`diarist source catalog is missing
|
|
516
|
+
if (typeof record.cwd !== "string") {
|
|
517
|
+
throw new Error(`diarist source catalog is missing cwd (${path})`);
|
|
518
|
+
}
|
|
519
|
+
if (typeof record.instruction !== "string") {
|
|
520
|
+
throw new Error(`diarist source catalog is missing instruction (${path})`);
|
|
521
|
+
}
|
|
522
|
+
if (typeof record.runDirectory !== "string") {
|
|
523
|
+
throw new Error(`diarist source catalog is missing runDirectory (${path})`);
|
|
524
|
+
}
|
|
525
|
+
if (typeof record.projectRoot !== "string") {
|
|
526
|
+
throw new Error(`diarist source catalog is missing projectRoot (${path})`);
|
|
527
|
+
}
|
|
528
|
+
if (
|
|
529
|
+
record.ticketNumber !== undefined &&
|
|
530
|
+
(typeof record.ticketNumber !== "number" ||
|
|
531
|
+
!Number.isSafeInteger(record.ticketNumber) ||
|
|
532
|
+
record.ticketNumber < 1)
|
|
533
|
+
) {
|
|
534
|
+
throw new Error(`diarist source catalog ticketNumber is not a safe ticket (${path})`);
|
|
371
535
|
}
|
|
372
536
|
if (!Array.isArray(record.candidates)) {
|
|
373
537
|
throw new Error(`diarist source catalog is missing candidates (${path})`);
|
|
@@ -426,7 +590,13 @@ export async function commitDiaristSelections(input: {
|
|
|
426
590
|
readonly catalog: DiaristSourceCatalog;
|
|
427
591
|
readonly selections: readonly DiaristSelection[];
|
|
428
592
|
}): Promise<DiaristCommitFacts> {
|
|
429
|
-
const
|
|
593
|
+
const ticketNumber = input.catalog.ticketNumber;
|
|
594
|
+
if (ticketNumber === undefined) {
|
|
595
|
+
throw new Error(
|
|
596
|
+
"commitDiaristSelections requires a bound ticketNumber on the catalog",
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
const { cwd } = input.catalog;
|
|
430
600
|
const homeOpt = input.catalog.home === undefined ? {} : { home: input.catalog.home };
|
|
431
601
|
const volumePaths = ensureTicketProvenanceVolume(ticketNumber, cwd, input.catalog.home);
|
|
432
602
|
|
|
@@ -228,7 +228,7 @@ export function validateAcceptedDetails(
|
|
|
228
228
|
[GATEKEEPER_OUTPUT_TOOL_NAME]: ["dispatch", "pass"],
|
|
229
229
|
[NAVIGATOR_OUTPUT_TOOL_NAME]: ["advice"],
|
|
230
230
|
[AUDITOR_OUTPUT_TOOL_NAME]: ["pass", "bounce", "escalate"],
|
|
231
|
-
[DIARIST_OUTPUT_TOOL_NAME]: ["completed"],
|
|
231
|
+
[DIARIST_OUTPUT_TOOL_NAME]: ["completed", "escalate"],
|
|
232
232
|
};
|
|
233
233
|
const collectorDiscriminator = toolName === COLLECTOR_OUTPUT_TOOL && Array.isArray(candidate?.groups);
|
|
234
234
|
const baseDiscriminator = discriminator;
|
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
buildCoderTransportPrompt,
|
|
22
22
|
type AdmittedCoderInvocation,
|
|
23
23
|
} from "./invocation.ts";
|
|
24
|
-
import { resolveSeatTicketBinding } from "./seat-ticket-binding.ts";
|
|
25
24
|
import {
|
|
26
25
|
buildResumeContinuationPrompt,
|
|
27
26
|
loadResumableCoderRun,
|
|
@@ -199,12 +198,7 @@ export async function runPublicCoder(
|
|
|
199
198
|
}),
|
|
200
199
|
},
|
|
201
200
|
}),
|
|
202
|
-
adapters:
|
|
203
|
-
...coderAdapters(methodProvenance),
|
|
204
|
-
beforeDispatch: async (admittedSeat) => {
|
|
205
|
-
await resolveSeatTicketBinding(admittedSeat, env);
|
|
206
|
-
},
|
|
207
|
-
},
|
|
201
|
+
adapters: coderAdapters(methodProvenance),
|
|
208
202
|
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
209
203
|
});
|
|
210
204
|
}
|