@akagilnc/pi-workflow-roles 0.1.1794 → 0.1.1815
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/audit-escalation.js +7 -0
- package/dist/compliance-transport.js +7 -1
- package/dist/evidence-child-executor.js +92 -14
- package/dist/navigator-attendance.js +75 -16
- package/dist/public-cli/main.js +115 -46
- package/dist/receipt-delivery-policy.js +72 -0
- package/extensions/role-runtime.ts +5 -2
- package/package.json +1 -1
- package/src/audit-escalation.ts +11 -0
- package/src/compliance-transport.ts +8 -2
- package/src/doctor-role.ts +1 -1
- package/src/evidence-child-executor.ts +88 -12
- package/src/judge-role.ts +5 -0
- package/src/navigator-attendance.ts +72 -4
- package/src/public-cli/settlement.ts +47 -8
- package/src/public-cli/terminal.ts +10 -1
- package/src/receipt-delivery-policy.ts +89 -0
- package/src/reviewer-role.ts +4 -0
- package/src/role-runtime.ts +60 -5
package/dist/audit-escalation.js
CHANGED
|
@@ -95,6 +95,13 @@ export async function disposeComplianceDecision(decision, handlers, deliveredOut
|
|
|
95
95
|
switch (decision.status) {
|
|
96
96
|
case "pass":
|
|
97
97
|
return await handlers.pass(decision.usage);
|
|
98
|
+
case "no-receipt":
|
|
99
|
+
// The parent candidate remains accepted, but its parallel audit leg is a
|
|
100
|
+
// public typed fact and must not be collapsed into an ordinary pass.
|
|
101
|
+
if (handlers.noReceipt === undefined) {
|
|
102
|
+
throw new Error("Compliance no-receipt projection handler is unavailable");
|
|
103
|
+
}
|
|
104
|
+
return await handlers.noReceipt(decision);
|
|
98
105
|
case "revise":
|
|
99
106
|
return await handlers.revise(decision.violations);
|
|
100
107
|
case "escalate":
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { executeAuditorChild, } from "./evidence-child-executor.js";
|
|
3
3
|
import { createAuditorDossierTool } from "./auditor-dossier-tool.js";
|
|
4
|
+
import { parseNoReceiptLifecycleFacts } from "./receipt-delivery-policy.js";
|
|
4
5
|
/** Zero-projection kickoff — soul already carries dossier-fetch duty; no hand-delivered materials. */
|
|
5
6
|
export const AUDITOR_DOSSIER_PROMPT = "Audit the current run dossier.";
|
|
6
7
|
const nonblank = Type.String({ minLength: 1, pattern: "\\S" });
|
|
@@ -73,5 +74,10 @@ export async function runComplianceAudit(options) {
|
|
|
73
74
|
...(options.runCompletion === undefined ? {} : { runCompletion: options.runCompletion }),
|
|
74
75
|
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
75
76
|
});
|
|
76
|
-
|
|
77
|
+
try {
|
|
78
|
+
return { status: "no-receipt", ...parseNoReceiptLifecycleFacts(receipt.decision) };
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return readComplianceCandidate(receipt.decision, receipt.response.usage);
|
|
82
|
+
}
|
|
77
83
|
}
|
|
@@ -10,6 +10,7 @@ import { createAssistantMessageEventStream, InMemoryCredentialStore, } from "@ea
|
|
|
10
10
|
import { AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE, AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE, prepareComplianceDispatch, } from "./compliance-transport.js";
|
|
11
11
|
import { wrapPackageOwnedToolDefinition } from "./package-owned-tool-idle.js";
|
|
12
12
|
import { createStreamIdleGuard, isStreamIdleTimeoutError } from "./stream-idle-guard.js";
|
|
13
|
+
import { createReceiptDeliveryPolicy, NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, RECEIPT_DELIVERY_PROMPT } from "./receipt-delivery-policy.js";
|
|
13
14
|
// ── shared constants / types ──────────────────────────────────────────────
|
|
14
15
|
export const AUDITOR_TURN_LIMIT = 32;
|
|
15
16
|
export const DEFAULT_COMPLIANCE_IDLE_MAX_RETRIES = 2;
|
|
@@ -430,6 +431,7 @@ export async function executeAuditorChild(options) {
|
|
|
430
431
|
let decisionSubmitted = false;
|
|
431
432
|
let decisionCallId;
|
|
432
433
|
let decisionToolFailure;
|
|
434
|
+
const decisionToolFailures = new Map();
|
|
433
435
|
const tool = wrapPackageOwnedToolDefinition({
|
|
434
436
|
...options.tool,
|
|
435
437
|
label: options.roleLabel,
|
|
@@ -441,11 +443,14 @@ export async function executeAuditorChild(options) {
|
|
|
441
443
|
const result = await options.tool.execute(...args);
|
|
442
444
|
decision = args[1];
|
|
443
445
|
decisionCallId = args[0];
|
|
446
|
+
decisionToolFailure = undefined;
|
|
447
|
+
decisionToolFailures.delete(args[0]);
|
|
444
448
|
decisionSubmitted = true;
|
|
445
449
|
return result;
|
|
446
450
|
}
|
|
447
451
|
catch (error) {
|
|
448
452
|
decisionToolFailure = error;
|
|
453
|
+
decisionToolFailures.set(args[0], error);
|
|
449
454
|
throw error;
|
|
450
455
|
}
|
|
451
456
|
},
|
|
@@ -488,6 +493,8 @@ export async function executeAuditorChild(options) {
|
|
|
488
493
|
let boundaryResponse;
|
|
489
494
|
let retentionFailure;
|
|
490
495
|
let retainedResponse;
|
|
496
|
+
let rejectedDecisionResponse;
|
|
497
|
+
let promptNeighboringFailure;
|
|
491
498
|
const registeredToolNames = new Set(session.getAllTools().map((entry) => entry.name));
|
|
492
499
|
const evidenceToolFailures = new Map();
|
|
493
500
|
for (const name of registeredToolNames) {
|
|
@@ -526,24 +533,39 @@ export async function executeAuditorChild(options) {
|
|
|
526
533
|
catch (error) {
|
|
527
534
|
retentionFailure = error;
|
|
528
535
|
}
|
|
536
|
+
// A tool call in assistant output is only an observation. Preserve its
|
|
537
|
+
// candidate for typed malformed-decision settlement, but the wrapped
|
|
538
|
+
// execute path above is the sole owner of accepted-receipt state; a
|
|
539
|
+
// rejected execution must remain retryable in this same session.
|
|
529
540
|
for (const part of event.message.content) {
|
|
530
|
-
if (part.type
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
541
|
+
if (part.type === "toolCall" && part.name === tool.name) {
|
|
542
|
+
rejectedDecisionResponse = event.message;
|
|
543
|
+
if (decision === undefined) {
|
|
544
|
+
decision = part.arguments;
|
|
545
|
+
decisionCallId = part.id;
|
|
546
|
+
// Pi can reject malformed root arguments before invoking execute;
|
|
547
|
+
// that remains the existing typed audit-incomplete candidate path.
|
|
548
|
+
if (part.arguments === undefined)
|
|
549
|
+
decisionSubmitted = true;
|
|
550
|
+
}
|
|
539
551
|
}
|
|
540
552
|
}
|
|
541
553
|
if (turns >= AUDITOR_TURN_LIMIT)
|
|
542
554
|
boundaryResponse = event.message;
|
|
543
555
|
}
|
|
544
|
-
if (event.type === "turn_end"
|
|
545
|
-
(
|
|
546
|
-
|
|
556
|
+
if (event.type === "turn_end") {
|
|
557
|
+
if (rejectedDecisionResponse !== undefined) {
|
|
558
|
+
promptNeighboringFailure = findToolFailure(rejectedDecisionResponse);
|
|
559
|
+
const rejectedCall = rejectedDecisionResponse.content.find((part) => part.type === "toolCall" && part.name === tool.name && decisionToolFailures.has(part.id));
|
|
560
|
+
if (rejectedCall?.type === "toolCall") {
|
|
561
|
+
decisionToolFailure = decisionToolFailures.get(rejectedCall.id);
|
|
562
|
+
decisionToolFailures.delete(rejectedCall.id);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (decisionSubmitted || promptNeighboringFailure !== undefined
|
|
566
|
+
|| boundaryResponse !== undefined || retentionFailure !== undefined) {
|
|
567
|
+
void session.abort();
|
|
568
|
+
}
|
|
547
569
|
}
|
|
548
570
|
});
|
|
549
571
|
const abort = () => { void session.abort(); };
|
|
@@ -553,7 +575,63 @@ export async function executeAuditorChild(options) {
|
|
|
553
575
|
options.signal?.addEventListener("abort", abort, { once: true });
|
|
554
576
|
try {
|
|
555
577
|
try {
|
|
556
|
-
|
|
578
|
+
const delivery = createReceiptDeliveryPolicy();
|
|
579
|
+
const promptAllowingRejectedDecision = async (prompt) => {
|
|
580
|
+
rejectedDecisionResponse = undefined;
|
|
581
|
+
promptNeighboringFailure = undefined;
|
|
582
|
+
decisionToolFailure = undefined;
|
|
583
|
+
let promptFailure;
|
|
584
|
+
try {
|
|
585
|
+
await session.prompt(prompt);
|
|
586
|
+
}
|
|
587
|
+
catch (error) {
|
|
588
|
+
promptFailure = error;
|
|
589
|
+
}
|
|
590
|
+
// Prefer turn_end correlation, but Pi may reject prompt() before that
|
|
591
|
+
// event. In that case correlate against this prompt's captured decision
|
|
592
|
+
// response and call-id maps at the catch boundary.
|
|
593
|
+
const correlatedResponse = rejectedDecisionResponse;
|
|
594
|
+
if (correlatedResponse !== undefined) {
|
|
595
|
+
promptNeighboringFailure ??= findToolFailure(correlatedResponse);
|
|
596
|
+
const rejectedCall = correlatedResponse.content.find((part) => part.type === "toolCall" && part.name === tool.name && decisionToolFailures.has(part.id));
|
|
597
|
+
if (rejectedCall?.type === "toolCall") {
|
|
598
|
+
decisionToolFailure = decisionToolFailures.get(rejectedCall.id);
|
|
599
|
+
decisionToolFailures.delete(rejectedCall.id);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
// An adjacent failure outranks correctable decision feedback.
|
|
603
|
+
if (promptNeighboringFailure !== undefined)
|
|
604
|
+
throw promptNeighboringFailure;
|
|
605
|
+
if (decisionToolFailure !== undefined)
|
|
606
|
+
return;
|
|
607
|
+
if (promptFailure !== undefined)
|
|
608
|
+
throw promptFailure;
|
|
609
|
+
};
|
|
610
|
+
await promptAllowingRejectedDecision(options.prompt);
|
|
611
|
+
while (!decisionSubmitted && boundaryResponse === undefined && inherited.streamFailure === undefined
|
|
612
|
+
&& delivery.nextAction() === "request-delivery") {
|
|
613
|
+
if (decisionToolFailure !== undefined) {
|
|
614
|
+
delivery.recordRejected(decisionToolFailure instanceof Error ? decisionToolFailure.message : String(decisionToolFailure));
|
|
615
|
+
decisionToolFailure = undefined;
|
|
616
|
+
if (delivery.nextAction() === "request-delivery") {
|
|
617
|
+
await promptAllowingRejectedDecision(RECEIPT_DELIVERY_PROMPT);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
else {
|
|
621
|
+
delivery.recordDeliveryRequest();
|
|
622
|
+
await promptAllowingRejectedDecision(RECEIPT_DELIVERY_PROMPT);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (!decisionSubmitted && boundaryResponse === undefined && inherited.streamFailure === undefined
|
|
626
|
+
&& delivery.nextAction() === "no-receipt") {
|
|
627
|
+
const runPointer = options.context.sessionManager.getSessionFile() ?? options.context.cwd ?? process.cwd();
|
|
628
|
+
const attemptPointer = binding.parent.attemptEntryId ?? binding.parent.sessionId ?? `current:${runPointer}`;
|
|
629
|
+
decision = delivery.facts({ runPointer, attemptPointer });
|
|
630
|
+
// Late turn_end feedback cannot overturn a lifecycle that has already
|
|
631
|
+
// charged this prompt to the exhausted shared budget.
|
|
632
|
+
decisionToolFailure = undefined;
|
|
633
|
+
auditorSessionManager.appendCustomEntry(NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, decision);
|
|
634
|
+
}
|
|
557
635
|
}
|
|
558
636
|
catch (error) {
|
|
559
637
|
if (options.signal?.aborted)
|
|
@@ -647,7 +725,7 @@ export async function executeAuditorChild(options) {
|
|
|
647
725
|
if (response === undefined
|
|
648
726
|
|| response.stopReason === "error"
|
|
649
727
|
|| response.stopReason === "aborted"
|
|
650
|
-
|| !decisionSubmitted) {
|
|
728
|
+
|| (!decisionSubmitted && decision === undefined)) {
|
|
651
729
|
throw new Error(`${options.roleLabel} exited without a readable decision receipt`);
|
|
652
730
|
}
|
|
653
731
|
return { decision, response };
|
|
@@ -17,6 +17,7 @@ import { openInProcessAgentSession } from "./in-process-session.js";
|
|
|
17
17
|
import { renderPublicAkRoleCommand } from "./public-command-renderer.js";
|
|
18
18
|
import { issueRoot, subjectPath } from "./work-subject-identity.js";
|
|
19
19
|
import { wrapPackageOwnedToolDefinition } from "./package-owned-tool-idle.js";
|
|
20
|
+
import { createReceiptDeliveryPolicy, NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, RECEIPT_DELIVERY_PROMPT } from "./receipt-delivery-policy.js";
|
|
20
21
|
const NAVIGATOR_EVENT_TYPE = "ak-navigator-attendance";
|
|
21
22
|
const NAVIGATOR_PREPARE_TOOL_NAME = "ak_navigator_prepare";
|
|
22
23
|
const NAVIGATOR_DEFAULT_MODEL = "openai-codex/gpt-5.6-luna:max";
|
|
@@ -105,6 +106,28 @@ function unavailableKey(value) {
|
|
|
105
106
|
function exactRecord(value) {
|
|
106
107
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
107
108
|
}
|
|
109
|
+
function rejectedPrepareReason(entries, start) {
|
|
110
|
+
const recent = entries.slice(start);
|
|
111
|
+
const prepareCalls = /* @__PURE__ */ new Set();
|
|
112
|
+
for (const entry of recent) {
|
|
113
|
+
if (!exactRecord(entry) || entry.type !== "message" || !exactRecord(entry.message) || entry.message.role !== "assistant" || !Array.isArray(entry.message.content)) continue;
|
|
114
|
+
for (const part of entry.message.content) {
|
|
115
|
+
if (exactRecord(part) && part.type === "toolCall" && part.name === NAVIGATOR_PREPARE_TOOL_NAME && typeof part.id === "string") prepareCalls.add(part.id);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
let reason;
|
|
119
|
+
for (const entry of recent) {
|
|
120
|
+
if (!exactRecord(entry) || entry.type !== "message" || !exactRecord(entry.message) || entry.message.role !== "toolResult" || entry.message.isError !== true) continue;
|
|
121
|
+
const callId = entry.message.toolCallId;
|
|
122
|
+
if (entry.message.toolName !== NAVIGATOR_PREPARE_TOOL_NAME || typeof callId !== "string" || !prepareCalls.has(callId)) {
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
const content = entry.message.content;
|
|
126
|
+
const text = Array.isArray(content) ? content.flatMap((part) => exactRecord(part) && typeof part.text === "string" ? [part.text] : []).join("") : typeof content === "string" ? content : "";
|
|
127
|
+
if (text.trim() !== "") reason = text.trim();
|
|
128
|
+
}
|
|
129
|
+
return reason;
|
|
130
|
+
}
|
|
108
131
|
function targetIsValid(value) {
|
|
109
132
|
if (!exactRecord(value) || !targetRoles.has(String(value.role))) return false;
|
|
110
133
|
const metadata = packagedRoleMetadata(String(value.role));
|
|
@@ -327,6 +350,7 @@ function createNavigatorAttendance(options) {
|
|
|
327
350
|
let settlementTail = Promise.resolve();
|
|
328
351
|
let settlementFailure;
|
|
329
352
|
let preparationFailure;
|
|
353
|
+
let preparationNoReceipt = false;
|
|
330
354
|
let routePlaybookReadFailure;
|
|
331
355
|
let disposed = false;
|
|
332
356
|
let warmedHelp;
|
|
@@ -526,7 +550,38 @@ ${helpContext}
|
|
|
526
550
|
try {
|
|
527
551
|
try {
|
|
528
552
|
if (disposed) throw navigatorUnavailableError("session", new Error("Navigator attendance was disposed"));
|
|
529
|
-
|
|
553
|
+
const delivery = createReceiptDeliveryPolicy();
|
|
554
|
+
const promptAllowingRejectedPrepare = async (text, deliveryRequest) => {
|
|
555
|
+
const entryStart = activeSession.entries().length;
|
|
556
|
+
let promptFailure;
|
|
557
|
+
try {
|
|
558
|
+
await activeSession.prompt(text);
|
|
559
|
+
} catch (error) {
|
|
560
|
+
promptFailure = error;
|
|
561
|
+
}
|
|
562
|
+
const providerFailure = activeSession.providerFailure?.();
|
|
563
|
+
if (providerFailure !== void 0) {
|
|
564
|
+
throw navigatorUnavailableError(providerFailure.source, promptFailure ?? "Navigator provider failure", providerFailure.cause);
|
|
565
|
+
}
|
|
566
|
+
const rejectedReason = rejectedPrepareReason(activeSession.entries(), entryStart);
|
|
567
|
+
if (rejectedReason !== void 0) {
|
|
568
|
+
delivery.recordRejected(rejectedReason);
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
if (promptFailure !== void 0) throw promptFailure;
|
|
572
|
+
if (deliveryRequest && output === void 0) delivery.recordDeliveryRequest();
|
|
573
|
+
};
|
|
574
|
+
await promptAllowingRejectedPrepare(request, false);
|
|
575
|
+
while (output === void 0 && delivery.nextAction() === "request-delivery") {
|
|
576
|
+
await promptAllowingRejectedPrepare(RECEIPT_DELIVERY_PROMPT, true);
|
|
577
|
+
}
|
|
578
|
+
if (output === void 0 && delivery.nextAction() === "no-receipt" && activeSession.providerFailure?.() === void 0) {
|
|
579
|
+
const facts = delivery.facts({ runPointer: sessionDir, attemptPointer: invocationId });
|
|
580
|
+
activeSession.appendEntry(NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, facts);
|
|
581
|
+
preparationNoReceipt = true;
|
|
582
|
+
candidates = [];
|
|
583
|
+
return candidates;
|
|
584
|
+
}
|
|
530
585
|
} catch (error) {
|
|
531
586
|
throw error instanceof NavigatorUnavailableError ? error : navigatorUnavailableError("transport", error);
|
|
532
587
|
}
|
|
@@ -655,22 +710,25 @@ ${helpContext}
|
|
|
655
710
|
throw new Error("Navigator advice contradicts the accepted settlement");
|
|
656
711
|
}
|
|
657
712
|
}
|
|
658
|
-
if (selected?.next === void 0) {
|
|
713
|
+
if (selected?.next === void 0 && preparationNoReceipt) {
|
|
714
|
+
report = { disposition: "no-advice" };
|
|
715
|
+
} else if (selected?.next === void 0) {
|
|
659
716
|
throw new Error("Navigator prepared no machine-usable next direction");
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
717
|
+
} else {
|
|
718
|
+
const selectedRoute = selected.route;
|
|
719
|
+
const routeChanged = selectedRoute !== void 0 && !routeEqual(previousRoute, selectedRoute);
|
|
720
|
+
const command = renderPublicAkRoleCommand(selected.next);
|
|
721
|
+
report = {
|
|
722
|
+
disposition: "recommendation",
|
|
723
|
+
...routeChanged ? { route: selectedRoute } : {},
|
|
724
|
+
next: selected.next,
|
|
725
|
+
...selected.reason === void 0 ? {} : { reason: oneLine(selected.reason) },
|
|
726
|
+
...command === void 0 ? {} : { command }
|
|
727
|
+
};
|
|
728
|
+
if (selectedRoute !== void 0) {
|
|
729
|
+
previousRoute = selectedRoute;
|
|
730
|
+
session?.appendEntry(ROUTE_ENTRY, { invocationId, subjectKey, route: selectedRoute });
|
|
731
|
+
}
|
|
674
732
|
}
|
|
675
733
|
} catch (error) {
|
|
676
734
|
report = unavailable(invocationId, error);
|
|
@@ -704,6 +762,7 @@ ${helpContext}
|
|
|
704
762
|
sessionReady = void 0;
|
|
705
763
|
candidates = void 0;
|
|
706
764
|
preparationFailure = void 0;
|
|
765
|
+
preparationNoReceipt = false;
|
|
707
766
|
routePlaybookSettlement = void 0;
|
|
708
767
|
routePlaybookReadFailure = void 0;
|
|
709
768
|
}
|