@akagilnc/pi-workflow-roles 0.1.3673 → 0.1.3685
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 +297 -166
- package/dist/audit-escalation.js +4 -3
- package/dist/diarist-contracts.js +4 -4
- package/dist/diarist-role.js +10 -4
- package/dist/diarist.js +121 -17
- package/dist/doctor-role.js +2 -1
- package/dist/gatekeeper-role.js +3 -2
- package/dist/judge-role.js +2 -1
- 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 +714 -728
- package/dist/public-cli/run-lifecycle.js +0 -28
- package/dist/public-cli/seat-ticket-binding.js +15 -104
- package/dist/readable-gate-item.js +16 -0
- package/dist/role-runtime.js +89 -10
- package/dist/submission-errors.js +4 -1
- package/package.json +1 -1
- package/resources/diarist-collect.md +10 -0
- package/src/audit-escalation.ts +4 -3
- package/src/diarist-contracts.ts +21 -9
- package/src/diarist-role.ts +15 -4
- package/src/diarist.ts +192 -22
- package/src/doctor-role.ts +2 -1
- package/src/gatekeeper-role.ts +3 -2
- package/src/judge-role.ts +2 -1
- 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 +14 -138
- package/src/readable-gate-item.ts +20 -0
- package/src/role-runtime.ts +107 -9
- package/src/submission-errors.ts +4 -1
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
|
|
package/src/doctor-role.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RoleHost, HostContext, HostToolResult } from "./host-contracts.ts";
|
|
2
2
|
import { disposeComplianceDecision } from "./audit-escalation.ts";
|
|
3
3
|
import { ComplianceResponseRetentionError, type ComplianceDecision } from "./compliance-transport.ts";
|
|
4
|
+
import { joinReadableGateItems } from "./readable-gate-item.ts";
|
|
4
5
|
import { DOCTOR_CANDIDATE_ENTRY_TYPE } from "./dossier-resolution.ts";
|
|
5
6
|
import { DOCTOR_ACCEPTED_AUDIT_NO_RECEIPT_TEXT, DOCTOR_ACCEPTED_TEXT, DOCTOR_EVIDENCE_TOOL_NAME, DOCTOR_OUTPUT_TOOL_DESCRIPTION, DOCTOR_OUTPUT_TOOL_NAME, DoctorEvidenceStore, doctorEvidenceReadSchema, doctorSubmissionSchema, validateDoctorOutput, type DoctorCase } from "./doctor-contracts.ts";
|
|
6
7
|
|
|
@@ -35,7 +36,7 @@ export function createDoctorRoleRuntime(pi: RoleHost, dependencies: DoctorRoleDe
|
|
|
35
36
|
let activation: { soul: string; patient: DoctorCase; store: DoctorEvidenceStore } | undefined; let registered = false; pi.registerFlag(DOCTOR_CASE_FLAG.name, DOCTOR_CASE_FLAG.definition);
|
|
36
37
|
return { async activate() { const path = pi.getFlag(DOCTOR_CASE_FLAG.name); if (typeof path !== "string" || !path.trim()) throw new Error("Doctor requires --ak-doctor-case"); const soul = (await dependencies.loadSoul()).trim(); if (!soul) throw new Error("Doctor soul is empty"); const patient = await dependencies.loadCase(path); activation = { soul, patient, store: new DoctorEvidenceStore(patient) };
|
|
37
38
|
if (!registered) { registered = true; pi.registerTool({ name: DOCTOR_EVIDENCE_TOOL_NAME, label: "太医署证据", description: "分页读取留存的 Pi session 字节。", parameters: doctorEvidenceReadSchema, async execute(_id: string, params: { evidenceId: string; offset?: number; limit?: number }) { if (!activation) throw new Error("太医署未激活"); const details = activation.store.read(params.evidenceId, params.offset, params.limit); return { content: [{ type: "text" as const, text: JSON.stringify(details) }], details }; } });
|
|
38
|
-
pi.registerTool({ name: DOCTOR_OUTPUT_TOOL_NAME, label: "太医署输出", description: DOCTOR_OUTPUT_TOOL_DESCRIPTION, parameters: doctorSubmissionSchema, async execute(id: string, params: unknown, signal: AbortSignal | undefined, _update: unknown, ctx: HostContext): Promise<HostToolResult<unknown>> { if (!activation) throw new Error("太医署未激活"); const testimony = validateDoctorOutput(params, activation.patient, activation.store); try { appendCandidate(ctx, { version: 1, testimony, readRecord: activation.store.readRecord(), patientIdentity: activation.patient.identity }); } catch (error) { host.failInfrastructure(error, ctx, id); } let audit: ComplianceDecision; try { audit = await dependencies.auditCompliance(signal === undefined ? { context: ctx } : { context: ctx, signal }); } catch (error) { host.failInfrastructure(error, ctx, id); } const details = testimony.status === "completed" ? { ...testimony, cost: activation.patient.cost } : testimony; const acceptedDetails = details; return disposeComplianceDecision<HostToolResult<unknown>>(audit, { pass: (usage) => ({ content: [{ type: "text" as const, text: DOCTOR_ACCEPTED_TEXT }], details: acceptedDetails, terminate: true as const, ...(usage === undefined ? {} : { usage }) }), noReceipt: (auditNoReceipt, usageProjection) => ({ content: [{ type: "text" as const, text: DOCTOR_ACCEPTED_AUDIT_NO_RECEIPT_TEXT }], details: { ...acceptedDetails, auditNoReceipt }, terminate: true as const, ...usageProjection }), received: (auditReceived, usageProjection) => ({ content: [{ type: "text" as const, text: DOCTOR_ACCEPTED_TEXT }], details: { ...acceptedDetails, audit: auditReceived.reply }, terminate: true as const, ...usageProjection }), bounce: (violations) => { throw new Error(`太医署回执违 soul:${violations
|
|
39
|
+
pi.registerTool({ name: DOCTOR_OUTPUT_TOOL_NAME, label: "太医署输出", description: DOCTOR_OUTPUT_TOOL_DESCRIPTION, parameters: doctorSubmissionSchema, async execute(id: string, params: unknown, signal: AbortSignal | undefined, _update: unknown, ctx: HostContext): Promise<HostToolResult<unknown>> { if (!activation) throw new Error("太医署未激活"); const testimony = validateDoctorOutput(params, activation.patient, activation.store); try { appendCandidate(ctx, { version: 1, testimony, readRecord: activation.store.readRecord(), patientIdentity: activation.patient.identity }); } catch (error) { host.failInfrastructure(error, ctx, id); } let audit: ComplianceDecision; try { audit = await dependencies.auditCompliance(signal === undefined ? { context: ctx } : { context: ctx, signal }); } catch (error) { host.failInfrastructure(error, ctx, id); } const details = testimony.status === "completed" ? { ...testimony, cost: activation.patient.cost } : testimony; const acceptedDetails = details; return disposeComplianceDecision<HostToolResult<unknown>>(audit, { pass: (usage) => ({ content: [{ type: "text" as const, text: DOCTOR_ACCEPTED_TEXT }], details: acceptedDetails, terminate: true as const, ...(usage === undefined ? {} : { usage }) }), noReceipt: (auditNoReceipt, usageProjection) => ({ content: [{ type: "text" as const, text: DOCTOR_ACCEPTED_AUDIT_NO_RECEIPT_TEXT }], details: { ...acceptedDetails, auditNoReceipt }, terminate: true as const, ...usageProjection }), received: (auditReceived, usageProjection) => ({ content: [{ type: "text" as const, text: DOCTOR_ACCEPTED_TEXT }], details: { ...acceptedDetails, audit: auditReceived.reply }, terminate: true as const, ...usageProjection }), bounce: (violations) => { throw new Error(`太医署回执违 soul:${joinReadableGateItems(violations)}`); }, escalate: (result) => result }, acceptedDetails); } });
|
|
39
40
|
pi.on("before_agent_start", (event) => { if (!activation) throw new Error("太医署未激活"); const catalog = { version: activation.patient.version, identity: activation.patient.identity, admittedMetrics: { provenance: "由留存 session 字节推导,封入受理回执。", cost: activation.patient.cost }, lawfulTargetKeys: ["case", ...activation.patient.cost.invocations.sources], evidence: activation.patient.evidence.map(({ id, kind, sha256, byteLength, contentLength }) => ({ id, kind, sha256, byteLength, contentLength })) }; return { systemPrompt: `${event.systemPrompt}\n\n<doctor_soul>\n${activation.soul}\n</doctor_soul>\n\n<doctor_case>\n${JSON.stringify(catalog)}\n</doctor_case>` }; }); }
|
|
40
41
|
const required = [DOCTOR_EVIDENCE_TOOL_NAME, DOCTOR_OUTPUT_TOOL_NAME]; const names = pi.getAllTools().map((tool) => tool.name); for (const name of required) if (names.filter((item) => item === name).length !== 1) throw new Error(`Doctor required tool collision or missing: ${name}`); pi.setActiveTools(required); const active = pi.getActiveTools?.() ?? required; if (active.length !== 2 || !required.every((name) => active.includes(name))) throw new Error("Doctor active tool narrowing failed"); } };
|
|
41
42
|
}
|
package/src/gatekeeper-role.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "./auditor-dossier-tool.ts";
|
|
8
8
|
import type { NoReceiptLifecycleFacts } from "./receipt-delivery-policy.ts";
|
|
9
9
|
import { GatekeeperDecisionError } from "./submission-errors.ts";
|
|
10
|
+
import { readableGateItem } from "./readable-gate-item.ts";
|
|
10
11
|
import { INSPECTOR_OUTPUT_TOOL_NAME } from "./inspector-contracts.ts";
|
|
11
12
|
import {
|
|
12
13
|
GATEKEEPER_OUTPUT_TOOL_NAME,
|
|
@@ -147,8 +148,8 @@ function failureReason(error: unknown): string {
|
|
|
147
148
|
function asStringArray(value: unknown): readonly string[] {
|
|
148
149
|
if (!Array.isArray(value)) return [];
|
|
149
150
|
// Officer findings may be structured objects (category/law/evidence); the
|
|
150
|
-
// parent role must see them verbatim, not an empty list (#750
|
|
151
|
-
return value.map(
|
|
151
|
+
// parent role must see them verbatim, not an empty list (#750 / #775).
|
|
152
|
+
return value.map(readableGateItem);
|
|
152
153
|
}
|
|
153
154
|
|
|
154
155
|
/** Serializable stand-in when the child tool call had no arguments object. */
|
package/src/judge-role.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Type, type Static } from "typebox";
|
|
|
4
4
|
|
|
5
5
|
import { disposeComplianceDecision } from "./audit-escalation.ts";
|
|
6
6
|
import type { ComplianceDecision } from "./compliance-transport.ts";
|
|
7
|
+
import { joinReadableGateItems } from "./readable-gate-item.ts";
|
|
7
8
|
import { withInfrastructureFailureDeclaration } from "./package-contracts/terminating-infrastructure.ts";
|
|
8
9
|
|
|
9
10
|
import {
|
|
@@ -136,7 +137,7 @@ export function createJudgeRoleRuntime(
|
|
|
136
137
|
}),
|
|
137
138
|
bounce: (violations) => {
|
|
138
139
|
throw new Error(
|
|
139
|
-
`大理寺回执违 soul:${violations
|
|
140
|
+
`大理寺回执违 soul:${joinReadableGateItems(violations)}`,
|
|
140
141
|
);
|
|
141
142
|
},
|
|
142
143
|
escalate: (result) => result,
|
|
@@ -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
|
}
|