@akagilnc/pi-workflow-roles 0.1.3837 → 0.1.3873
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 +24606 -24532
- package/dist/engine-detour-tool.js +2 -2
- package/dist/engine-detour.js +23 -7
- package/dist/headless-host/production-host.js +4197 -4139
- package/dist/host-descriptions.js +5 -2
- package/dist/pi/role-turn-host.js +20 -92
- package/dist/public-cli/load-production-acp-host.js +1 -1
- package/dist/public-cli/load-production-external-host.js +10 -0
- package/dist/public-cli/load-production-headless-host.js +1 -1
- package/dist/public-cli/main.js +95 -102
- package/dist/public-role-summons.js +7 -49
- package/dist/reviewer-agent.js +1 -0
- package/dist/reviewer-child-executor.js +3 -2
- package/dist/role-activation-flags.js +50 -0
- package/dist/role-runtime.js +8 -3
- package/package.json +1 -1
- package/scripts/build-package.mjs +2 -2
- package/src/acp-host/production-host.ts +31 -94
- package/src/acp-host/role-turn-host.ts +178 -263
- package/src/diagnostic-tail.ts +18 -0
- package/src/engine-detour-tool.ts +5 -4
- package/src/engine-detour.ts +44 -9
- package/src/external-host-turn-loop.ts +172 -0
- package/src/headless-host/production-host.ts +10 -7
- package/src/headless-host/role-turn-host.ts +271 -263
- package/src/host-descriptions.ts +5 -2
- package/src/host-session-record.ts +41 -0
- package/src/pi/role-turn-host.ts +18 -93
- package/src/prepared-role-turn.ts +68 -0
- package/src/public-cli/cli.ts +11 -20
- package/src/public-cli/load-production-acp-host.ts +2 -1
- package/src/public-cli/load-production-external-host.ts +17 -0
- package/src/public-cli/load-production-headless-host.ts +2 -1
- package/src/public-role-summons.ts +12 -69
- package/src/reviewer-agent.ts +12 -1
- package/src/reviewer-child-executor.ts +10 -2
- package/src/role-activation-flags.ts +53 -0
- package/src/{acp-host/role-envelope.ts → role-envelope.ts} +28 -84
- package/src/role-runtime-dependencies.ts +91 -0
- package/src/role-runtime.ts +14 -4
- package/src/{acp-host/session-identity.ts → session-identity.ts} +6 -6
- /package/src/{acp-host/mcp-relay.mjs → mcp-relay.mjs} +0 -0
|
@@ -4,7 +4,8 @@ import { createServer, type Server, type Socket } from "node:net";
|
|
|
4
4
|
import { basename, dirname, join } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { ENGINE_FLAG_NAME, normalizeEngineName } from "./engine-detour.ts";
|
|
8
|
+
import { requireGatekeeperPass } from "./gatekeeper-pass-envelope.ts";
|
|
8
9
|
import type {
|
|
9
10
|
HostContext,
|
|
10
11
|
HostEventRegistration,
|
|
@@ -14,27 +15,26 @@ import type {
|
|
|
14
15
|
RoleHost,
|
|
15
16
|
RoleTurnKnownFailure,
|
|
16
17
|
RoleTurnRequest,
|
|
17
|
-
} from "
|
|
18
|
-
import {
|
|
19
|
-
import { stripSkillFrontmatter } from "
|
|
18
|
+
} from "./host-contracts.ts";
|
|
19
|
+
import { packagedRoleOutputTool } from "./packaged-role-registry.ts";
|
|
20
|
+
import { stripSkillFrontmatter } from "./package-resources/method-skill.ts";
|
|
20
21
|
import {
|
|
21
22
|
createRoleRuntimeExtension,
|
|
22
23
|
type RoleRuntimeDependencies,
|
|
23
|
-
} from "
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
type AcpPreparedTurn,
|
|
27
|
-
type AcpRoleTurnHostConfig,
|
|
28
|
-
} from "./role-turn-host.ts";
|
|
24
|
+
} from "./role-runtime.ts";
|
|
25
|
+
import type { PreparedRoleTurn } from "./prepared-role-turn.ts";
|
|
26
|
+
import { projectActivationFlags } from "./role-activation-flags.ts";
|
|
29
27
|
import {
|
|
30
28
|
isCorrectableExecuteError,
|
|
31
29
|
mechanicalSubmissionRejectionResumeMessage,
|
|
32
30
|
projectCorrectableExecuteRejection,
|
|
33
|
-
} from "
|
|
31
|
+
} from "./submission-correctable-error.ts";
|
|
34
32
|
import {
|
|
35
33
|
buildNavigatorInfrastructureFailureFact,
|
|
36
34
|
extractInfrastructureFailureEvidence,
|
|
37
|
-
} from "
|
|
35
|
+
} from "./navigator-invocation-identity.ts";
|
|
36
|
+
|
|
37
|
+
export { projectActivationFlags };
|
|
38
38
|
|
|
39
39
|
type Handler = HostEventRegistration[1];
|
|
40
40
|
type RpcRequest = { readonly id: number; readonly token: string; readonly method: string; readonly params?: Record<string, unknown> };
|
|
@@ -47,7 +47,7 @@ type ContentPart = { type: "text"; text: string } | { type: "image"; data: strin
|
|
|
47
47
|
* a single bound method treats the plain prompt as the preserved user message.
|
|
48
48
|
* Pi-native slash forms stay inside `src/pi/` only.
|
|
49
49
|
*/
|
|
50
|
-
export function
|
|
50
|
+
export function buildSkillExpansion(
|
|
51
51
|
methodSkills: ReadonlyMap<string, { readonly path: string; readonly body: string }>,
|
|
52
52
|
prompt: string,
|
|
53
53
|
): HostSkillExpansionEvidence | undefined {
|
|
@@ -64,45 +64,6 @@ export function buildAcpSkillExpansion(
|
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
export function projectAcpActivationFlags(request: RoleTurnRequest): Map<string, boolean | string> {
|
|
68
|
-
const activation = request.activation;
|
|
69
|
-
const flags = new Map<string, boolean | string>([["ak-role", activation.role]]);
|
|
70
|
-
const inputFlag = packagedRoleInputFlag(activation.role);
|
|
71
|
-
const phaseFlag = packagedRolePhaseFlag(activation.role);
|
|
72
|
-
if ("phase" in activation && phaseFlag !== undefined) flags.set(phaseFlag, activation.phase);
|
|
73
|
-
if (inputFlag !== undefined) {
|
|
74
|
-
const path = "taskPath" in activation ? activation.taskPath
|
|
75
|
-
: "packetPath" in activation ? activation.packetPath
|
|
76
|
-
: "casePath" in activation ? activation.casePath
|
|
77
|
-
: "inputPath" in activation ? activation.inputPath
|
|
78
|
-
: "sourceRun" in activation ? activation.sourceRun
|
|
79
|
-
: undefined;
|
|
80
|
-
if (path !== undefined) flags.set(inputFlag, path);
|
|
81
|
-
}
|
|
82
|
-
if (activation.role === "fixer" && activation.prerequisitesPath !== undefined) flags.set("ak-fixer-prerequisites", activation.prerequisitesPath);
|
|
83
|
-
if (activation.role === "reviewer") {
|
|
84
|
-
flags.set("ak-review-base", activation.baseRevision);
|
|
85
|
-
flags.set("ak-review-authority-refs", JSON.stringify(activation.authorityRefs));
|
|
86
|
-
if (activation.ticketNumber !== undefined) flags.set("ak-review-ticket-number", String(activation.ticketNumber));
|
|
87
|
-
}
|
|
88
|
-
// countersign ticketNumber stays on activation/admission/invocation only —
|
|
89
|
-
// no private transport flag (inner-gate material path deleted in #632).
|
|
90
|
-
if (activation.role === "notary" && activation.ticketNumber !== undefined) {
|
|
91
|
-
flags.set("ak-notary-ticket-number", String(activation.ticketNumber));
|
|
92
|
-
}
|
|
93
|
-
if (activation.role === "gleaner-left") {
|
|
94
|
-
flags.set("ak-gleaner-left-base", activation.baseRevision);
|
|
95
|
-
}
|
|
96
|
-
if (activation.role === "collector") {
|
|
97
|
-
flags.set("ak-collector-repo", activation.repo);
|
|
98
|
-
// #676 D1: pr optional at admission; omit flag when role binds from materials.
|
|
99
|
-
if (activation.pr !== undefined) flags.set("ak-collector-pr", activation.pr);
|
|
100
|
-
if (activation.requestManifestPath !== undefined) flags.set("ak-collector-request-manifest", activation.requestManifestPath);
|
|
101
|
-
if (activation.waitMs !== undefined) flags.set("ak-collector-wait-ms", activation.waitMs);
|
|
102
|
-
}
|
|
103
|
-
return flags;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
67
|
async function listen(server: Server, path: string): Promise<void> {
|
|
107
68
|
await new Promise<void>((resolve, reject) => {
|
|
108
69
|
server.once("error", reject);
|
|
@@ -110,28 +71,6 @@ async function listen(server: Server, path: string): Promise<void> {
|
|
|
110
71
|
});
|
|
111
72
|
}
|
|
112
73
|
|
|
113
|
-
/**
|
|
114
|
-
* Build one AK-owned MCP projection from the shared eight-seat envelope.
|
|
115
|
-
* The child process is a protocol relay only; all tools execute in this process.
|
|
116
|
-
*/
|
|
117
|
-
export function createComposedAcpRoleTurnHost(
|
|
118
|
-
config: Omit<AcpRoleTurnHostConfig, "prepare"> & {
|
|
119
|
-
readonly roleRuntimeDependencies: RoleRuntimeDependencies;
|
|
120
|
-
readonly socketPath?: (request: RoleTurnRequest) => string;
|
|
121
|
-
},
|
|
122
|
-
) {
|
|
123
|
-
return createAcpRoleTurnHost({
|
|
124
|
-
...config,
|
|
125
|
-
prepare: (request) => prepareAcpRoleEnvelope({
|
|
126
|
-
request,
|
|
127
|
-
dependencies: config.roleRuntimeDependencies,
|
|
128
|
-
// Same durable-principal path settlement uses for isAvailable (#617 DK-4 layout).
|
|
129
|
-
sessionFile: config.sessionIdentity.resolveSessionFile(request.principal),
|
|
130
|
-
socketPath: config.socketPath?.(request) ?? `/tmp/ak-acp-mcp-${randomUUID()}.sock`,
|
|
131
|
-
}),
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
74
|
/** JSON Schema draft-07 document for host-native `--json-schema` (headless). */
|
|
136
75
|
export function terminatingToolJsonSchema(parameters: unknown): Readonly<Record<string, unknown>> {
|
|
137
76
|
const cloned = JSON.parse(JSON.stringify(parameters)) as Record<string, unknown>;
|
|
@@ -142,7 +81,7 @@ export function terminatingToolJsonSchema(parameters: unknown): Readonly<Record<
|
|
|
142
81
|
});
|
|
143
82
|
}
|
|
144
83
|
|
|
145
|
-
export async function
|
|
84
|
+
export async function prepareRoleEnvelope(options: {
|
|
146
85
|
readonly request: RoleTurnRequest;
|
|
147
86
|
readonly dependencies: RoleRuntimeDependencies;
|
|
148
87
|
/**
|
|
@@ -165,17 +104,20 @@ export async function prepareAcpRoleEnvelope(options: {
|
|
|
165
104
|
* isAvailable and envelope mint the same file. Tests may omit → runDirectory default.
|
|
166
105
|
*/
|
|
167
106
|
readonly sessionFile?: string;
|
|
168
|
-
}): Promise<
|
|
107
|
+
}): Promise<PreparedRoleTurn> {
|
|
169
108
|
const { request } = options;
|
|
170
109
|
if (options.socketPath === "") {
|
|
171
|
-
throw new Error("
|
|
110
|
+
throw new Error("prepareRoleEnvelope requires socketPath");
|
|
172
111
|
}
|
|
173
112
|
const listTerminatingToolOnMcp = options.listTerminatingToolOnMcp !== false;
|
|
174
113
|
const earlyTerminatingTool = packagedRoleOutputTool(request.activation.role);
|
|
175
114
|
if (earlyTerminatingTool === undefined) {
|
|
176
115
|
throw new Error(`role has no terminating tool: ${request.activation.role}`);
|
|
177
116
|
}
|
|
178
|
-
const flags =
|
|
117
|
+
const flags = projectActivationFlags(request);
|
|
118
|
+
// #818 P1: engine axis is request-scoped on this RoleHost — never process.env.
|
|
119
|
+
// Always project ("" = no engine) so ambient AK_ROLE_ENGINE cannot arm detour.
|
|
120
|
+
flags.set(ENGINE_FLAG_NAME, normalizeEngineName(request.engine) ?? "");
|
|
179
121
|
const tools = new Map<string, HostToolDefinition>();
|
|
180
122
|
const handlers = new Map<string, Handler[]>();
|
|
181
123
|
const calls: Array<{ toolCallId: string; toolName: string }> = [];
|
|
@@ -275,7 +217,7 @@ export async function prepareAcpRoleEnvelope(options: {
|
|
|
275
217
|
},
|
|
276
218
|
capabilities: {
|
|
277
219
|
skillExpansion(prompt): HostSkillExpansionEvidence | undefined {
|
|
278
|
-
return
|
|
220
|
+
return buildSkillExpansion(methodSkills, prompt);
|
|
279
221
|
},
|
|
280
222
|
},
|
|
281
223
|
registerFlag(name, definition) { if (!flags.has(name) && definition.default !== undefined) flags.set(name, definition.default); },
|
|
@@ -589,8 +531,9 @@ export async function prepareAcpRoleEnvelope(options: {
|
|
|
589
531
|
let disposed = false;
|
|
590
532
|
// Tools execute in this process (relay is protocol-only). Mirror Pi's child-env
|
|
591
533
|
// AK_ROLE_RUN_DIR / AK_ROLE_COURT_ATTEMPT injection onto the parent so ledger
|
|
592
|
-
//
|
|
593
|
-
//
|
|
534
|
+
// identity sees the same signals. Engine axis is request-scoped via RoleHost
|
|
535
|
+
// flag — never process.env (#818 P1). Run-dir may wait
|
|
536
|
+
// until prepare succeeds. Dispose restores including unset.
|
|
594
537
|
let priorAkRoleRunDir: string | undefined;
|
|
595
538
|
let priorAkRoleCourtAttempt: string | undefined;
|
|
596
539
|
let runDirInjected = false;
|
|
@@ -641,7 +584,7 @@ export async function prepareAcpRoleEnvelope(options: {
|
|
|
641
584
|
await invokeAkTool(terminatingToolName, params ?? {});
|
|
642
585
|
}
|
|
643
586
|
|
|
644
|
-
const closeRound:
|
|
587
|
+
const closeRound: PreparedRoleTurn["closeRound"] = async () => {
|
|
645
588
|
// Typed round boundary: hand the complete call list to the shared ledger once.
|
|
646
589
|
if (calls.length > 0) {
|
|
647
590
|
const roundCalls = [...calls];
|
|
@@ -683,7 +626,8 @@ export async function prepareAcpRoleEnvelope(options: {
|
|
|
683
626
|
|
|
684
627
|
// Shared envelope activation. systemPrompt must be ready before session/new
|
|
685
628
|
// (delivered via _meta.systemPromptOverride where the host honors it), so
|
|
686
|
-
// activation runs during prepare.
|
|
629
|
+
// activation runs during prepare. Engine axis already on RoleHost flags
|
|
630
|
+
// (request-scoped); registerEngineDetourTool resolves via resolveEngineName.
|
|
687
631
|
try {
|
|
688
632
|
await emit("session_start", { reason: request.continuation.kind });
|
|
689
633
|
const inputResults = await emit("input", { text: request.continuation.prompt, source: "interactive" });
|
|
@@ -761,7 +705,7 @@ export async function prepareAcpRoleEnvelope(options: {
|
|
|
761
705
|
} catch (cleanupFailure) {
|
|
762
706
|
throw new AggregateError(
|
|
763
707
|
[error, cleanupFailure],
|
|
764
|
-
"
|
|
708
|
+
"prepareRoleEnvelope activation failed and its dispose cleanup also failed",
|
|
765
709
|
{ cause: error },
|
|
766
710
|
);
|
|
767
711
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
|
|
4
|
+
import { loadCanonicalSkillBinding as loadHomeCanonicalSkillBinding } from "./canonical-skill-binding.ts";
|
|
5
|
+
import { createGhCollectorGitHubTransport, createGhIssueSoftFetcher } from "./collector-github.ts";
|
|
6
|
+
import { createPiDoctorAuditor } from "./doctor-auditor.ts";
|
|
7
|
+
import { loadDoctorCase } from "./doctor-evidence.ts";
|
|
8
|
+
import { createProductionMergerGitState } from "./merger-git-state.ts";
|
|
9
|
+
import { createNativeNavigatorSessionFactory, createNavigatorAttendance } from "./navigator-attendance.ts";
|
|
10
|
+
import { loadNavigatorWorkContext } from "./navigator-work-context.ts";
|
|
11
|
+
import { loadNotarySourceRunLocator } from "./notary-source-run.ts";
|
|
12
|
+
import { loadPackagedCanonicalSkillBinding } from "./package-resources/method-skill-binding.ts";
|
|
13
|
+
import { createPerDispatchReviewerAgent } from "./reviewer-agent.ts";
|
|
14
|
+
import { formatNavigatorRoleHelp, type RoleRuntimeDependencies } from "./role-runtime.ts";
|
|
15
|
+
import { createReviewerPinnedGitReader } from "./reviewer-pinned-git.ts";
|
|
16
|
+
import { loadAuditorSoulFromSubjectInput } from "./auditor-soul.ts";
|
|
17
|
+
import { loadGatekeeperSessionMaterials, loadMainRoleSessionMaterials } from "./session-opening-materials.ts";
|
|
18
|
+
|
|
19
|
+
const navigatorRoutePlaybookPath = fileURLToPath(
|
|
20
|
+
new URL("../resources/navigator-route-playbook.md", import.meta.url),
|
|
21
|
+
);
|
|
22
|
+
const collectorHandbookSeedPath = fileURLToPath(
|
|
23
|
+
new URL("../resources/collector-bot-handbook.md", import.meta.url),
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
/** Host-neutral packaged role runtime deps for the parent-process envelope. */
|
|
27
|
+
export function createRoleRuntimeDependencies(packageRoot: string): RoleRuntimeDependencies {
|
|
28
|
+
const doctorAuditor = createPiDoctorAuditor();
|
|
29
|
+
const reviewerAgent = createPerDispatchReviewerAgent({ packageRoot });
|
|
30
|
+
const navigatorSessionFactory = createNativeNavigatorSessionFactory();
|
|
31
|
+
return {
|
|
32
|
+
loadJudgeSoul: () => loadMainRoleSessionMaterials("judge"),
|
|
33
|
+
loadFixerSoul: () => loadMainRoleSessionMaterials("fixer"),
|
|
34
|
+
loadFixPacket: (path) => readFile(path, "utf8"),
|
|
35
|
+
loadCoderSoul: () => loadMainRoleSessionMaterials("coder"),
|
|
36
|
+
loadCoderTask: (path) => readFile(path, "utf8"),
|
|
37
|
+
loadReviewerSoul: () => loadMainRoleSessionMaterials("reviewer"),
|
|
38
|
+
createReviewerPinnedGitReader: () => createReviewerPinnedGitReader(),
|
|
39
|
+
createReviewerIssueFetcher: () => createGhIssueSoftFetcher(),
|
|
40
|
+
loadCollectorSoul: () => loadMainRoleSessionMaterials("collector"),
|
|
41
|
+
loadCollectorHandbookSeed: () => readFile(collectorHandbookSeedPath, "utf8"),
|
|
42
|
+
createCollectorTransport: () => createGhCollectorGitHubTransport(),
|
|
43
|
+
loadDoctorSoul: () => loadMainRoleSessionMaterials("doctor"),
|
|
44
|
+
loadDoctorCase,
|
|
45
|
+
loadInspectorSoul: () => loadMainRoleSessionMaterials("inspector"),
|
|
46
|
+
loadGatekeeperSoul: () => loadGatekeeperSessionMaterials("gatekeeper"),
|
|
47
|
+
loadNavigatorSoul: () => loadMainRoleSessionMaterials("navigator"),
|
|
48
|
+
loadAuditorSoul: () => loadAuditorSoulFromSubjectInput(),
|
|
49
|
+
loadNotarySoul: () => loadMainRoleSessionMaterials("notary"),
|
|
50
|
+
loadCountersignSoul: () => loadMainRoleSessionMaterials("countersign"),
|
|
51
|
+
loadGleanerLeftSoul: () => loadMainRoleSessionMaterials("gleaner-left"),
|
|
52
|
+
loadDiaristSoul: () => loadMainRoleSessionMaterials("diarist"),
|
|
53
|
+
loadNotarySourceRun: loadNotarySourceRunLocator,
|
|
54
|
+
loadMergerSoul: () => loadMainRoleSessionMaterials("merger"),
|
|
55
|
+
loadMergerInput: async (path) => JSON.parse(await readFile(path, "utf8")),
|
|
56
|
+
createMergerGitState: (repositoryRoot) => createProductionMergerGitState(repositoryRoot),
|
|
57
|
+
async loadCanonicalSkillBinding(name) {
|
|
58
|
+
if (name === "tdd") {
|
|
59
|
+
return loadPackagedCanonicalSkillBinding(packageRoot, "tdd");
|
|
60
|
+
}
|
|
61
|
+
if (name === "code-review") {
|
|
62
|
+
return loadPackagedCanonicalSkillBinding(packageRoot, "code-review");
|
|
63
|
+
}
|
|
64
|
+
return loadHomeCanonicalSkillBinding(name);
|
|
65
|
+
},
|
|
66
|
+
// #590: doctor compliance still on disposeCompliance path; judge→auditor is gate queue (#756).
|
|
67
|
+
auditDoctorCompliance: (options) => doctorAuditor(options),
|
|
68
|
+
runReviewerDispatch: (dispatch, options) => reviewerAgent.run(dispatch, options),
|
|
69
|
+
shutdownReviewerAgent: () => reviewerAgent.shutdown(),
|
|
70
|
+
loadNavigatorWorkContext: (options) => loadNavigatorWorkContext({
|
|
71
|
+
context: options.context,
|
|
72
|
+
role: options.role,
|
|
73
|
+
...(options.getFlag === undefined ? {} : { getFlag: options.getFlag }),
|
|
74
|
+
}),
|
|
75
|
+
createNavigatorAttendance: (options) => createNavigatorAttendance({
|
|
76
|
+
context: options.context,
|
|
77
|
+
role: options.role,
|
|
78
|
+
phase: options.phase,
|
|
79
|
+
subjectKey: options.subjectKey,
|
|
80
|
+
subject: options.subject,
|
|
81
|
+
authority: options.authority,
|
|
82
|
+
invocationId: options.invocationId,
|
|
83
|
+
loadSoul: () => loadMainRoleSessionMaterials("navigator"),
|
|
84
|
+
loadRoutePlaybook: () => readFile(navigatorRoutePlaybookPath, "utf8"),
|
|
85
|
+
loadRoleHelp: async (role) => formatNavigatorRoleHelp(role),
|
|
86
|
+
createSession: navigatorSessionFactory,
|
|
87
|
+
...(options.contextError === undefined ? {} : { contextError: options.contextError }),
|
|
88
|
+
onEvent: options.onEvent,
|
|
89
|
+
}),
|
|
90
|
+
};
|
|
91
|
+
}
|
package/src/role-runtime.ts
CHANGED
|
@@ -638,7 +638,12 @@ export type RoleRuntimeDependencies = {
|
|
|
638
638
|
): Promise<AnyCanonicalSkillBinding>;
|
|
639
639
|
runReviewerDispatch?(
|
|
640
640
|
dispatch: AcceptedReviewerExecution,
|
|
641
|
-
options: {
|
|
641
|
+
options: {
|
|
642
|
+
context: HostContext;
|
|
643
|
+
signal?: AbortSignal;
|
|
644
|
+
/** Request-scoped RoleHost flag reader for engine axis (#818). */
|
|
645
|
+
getFlag?: (name: string) => boolean | string | undefined;
|
|
646
|
+
},
|
|
642
647
|
): Promise<ReviewerDispatchRunResult>;
|
|
643
648
|
shutdownReviewerAgent?(): Promise<void>;
|
|
644
649
|
activationClock?(): string;
|
|
@@ -1598,7 +1603,12 @@ export function createRoleRuntimeExtension(
|
|
|
1598
1603
|
: { fetchIssue: dependencies.createReviewerIssueFetcher() }),
|
|
1599
1604
|
async runDispatch(dispatch, options) {
|
|
1600
1605
|
if (dependencies.runReviewerDispatch === undefined) throw new Error("Reviewer runtime dependencies are not configured");
|
|
1601
|
-
|
|
1606
|
+
// #818: request-scoped engine flag reaches axis sub-session via same RoleHost
|
|
1607
|
+
// that parent registerEngineDetourTool reads — never ambient process.env.
|
|
1608
|
+
return dependencies.runReviewerDispatch(dispatch, {
|
|
1609
|
+
...options,
|
|
1610
|
+
getFlag: (name) => roleHost.getFlag(name),
|
|
1611
|
+
});
|
|
1602
1612
|
},
|
|
1603
1613
|
...(dependencies.shutdownReviewerAgent === undefined
|
|
1604
1614
|
? {}
|
|
@@ -2093,8 +2103,8 @@ export function createRoleRuntimeExtension(
|
|
|
2093
2103
|
}
|
|
2094
2104
|
|
|
2095
2105
|
await executeActivationStage(entry.role, activationStage(entry.role, runtime), { clock, writeTrace });
|
|
2096
|
-
// #357 T2 / #378 / #380 / #391: any role+engine activation registers the package detour tool once.
|
|
2097
|
-
// Gate is env
|
|
2106
|
+
// #357 T2 / #378 / #380 / #391 / #818: any role+engine activation registers the package detour tool once.
|
|
2107
|
+
// Gate is resolveEngineName (RoleHost flag → env fallback) — no per-engine execute branch; no role-module spawn.
|
|
2098
2108
|
if (!engineDetourRegistered) {
|
|
2099
2109
|
engineDetourRegistered = registerEngineDetourTool(roleHost, hostActions);
|
|
2100
2110
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
|
|
4
|
-
import type { DurablePrincipal, DurablePrincipalAuthority } from "
|
|
5
|
-
import type {
|
|
4
|
+
import type { DurablePrincipal, DurablePrincipalAuthority } from "./host-contracts.ts";
|
|
5
|
+
import type { SessionIdentityAuthority } from "./prepared-role-turn.ts";
|
|
6
6
|
|
|
7
|
-
/** Durable
|
|
8
|
-
export function
|
|
7
|
+
/** Durable session binding stored beside the host-owned session principal. */
|
|
8
|
+
export function createSessionIdentityAuthority(
|
|
9
9
|
authority: DurablePrincipalAuthority,
|
|
10
10
|
sessionBindingFile: string,
|
|
11
|
-
):
|
|
11
|
+
): SessionIdentityAuthority {
|
|
12
12
|
const bindingPath = (principal: DurablePrincipal): string =>
|
|
13
13
|
join(authority.decode(principal).sessionDirectory, sessionBindingFile);
|
|
14
14
|
return {
|
|
@@ -19,7 +19,7 @@ export function createAcpSessionIdentityAuthority(
|
|
|
19
19
|
try {
|
|
20
20
|
const value: unknown = JSON.parse(await readFile(bindingPath(principal), "utf8"));
|
|
21
21
|
if (typeof value !== "object" || value === null || typeof (value as { sessionId?: unknown }).sessionId !== "string") {
|
|
22
|
-
throw new Error("durable
|
|
22
|
+
throw new Error("durable session binding is invalid");
|
|
23
23
|
}
|
|
24
24
|
return (value as { sessionId: string }).sessionId;
|
|
25
25
|
} catch (error) {
|
|
File without changes
|