@akagilnc/pi-workflow-roles 0.1.2107 → 0.1.2114
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/README.md +5 -5
- package/README.zh-CN.md +5 -5
- package/dist/engine-detour-tool.js +1 -1
- package/dist/engine-detour.js +14 -1
- package/dist/engine-labor-fallback.js +1 -1
- package/dist/public-cli/main.js +127 -82
- package/package.json +16 -2
- package/src/collector-role.ts +9 -1
- package/src/doctor-role.ts +5 -1
- package/src/engine-detour-tool.ts +2 -2
- package/src/engine-detour.ts +17 -1
- package/src/engine-labor-fallback.ts +1 -1
- package/src/merger-role.ts +9 -1
- package/src/public-cli/cli.ts +44 -25
- package/src/public-cli/coder-run.ts +16 -3
- package/src/public-cli/collector-run.ts +17 -3
- package/src/public-cli/config.ts +18 -13
- package/src/public-cli/doctor-run.ts +17 -3
- package/src/public-cli/fixer-run.ts +17 -3
- package/src/public-cli/invocation.ts +10 -5
- package/src/public-cli/judge-run.ts +2 -9
- package/src/public-cli/merger-run.ts +15 -3
- package/src/public-cli/option-definitions.ts +2 -2
- package/src/public-cli/reviewer-run.ts +2 -9
- package/src/public-cli/run-lifecycle.ts +1 -1
- package/src/role-runtime.ts +3 -6
- package/src/worker-role.ts +16 -2
package/src/collector-role.ts
CHANGED
|
@@ -4,6 +4,10 @@ import type {
|
|
|
4
4
|
} from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import type { Static } from "typebox";
|
|
6
6
|
|
|
7
|
+
import {
|
|
8
|
+
readActivationEngineLaborFallbackField,
|
|
9
|
+
withEngineLaborFallbackField,
|
|
10
|
+
} from "./engine-labor-fallback.ts";
|
|
7
11
|
import {
|
|
8
12
|
COLLECTOR_FIXED_KICKOFF,
|
|
9
13
|
emptyCollectorManifest,
|
|
@@ -446,12 +450,16 @@ export function createCollectorRoleRuntime(
|
|
|
446
450
|
);
|
|
447
451
|
activation.ledger.markOutputAccepted();
|
|
448
452
|
activation.ledger.completeOperational(toolCallId);
|
|
453
|
+
const acceptedDetails = withEngineLaborFallbackField(
|
|
454
|
+
receipt,
|
|
455
|
+
readActivationEngineLaborFallbackField(),
|
|
456
|
+
);
|
|
449
457
|
return {
|
|
450
458
|
content: [{
|
|
451
459
|
type: "text" as const,
|
|
452
460
|
text: "Collector receipt accepted",
|
|
453
461
|
}],
|
|
454
|
-
details:
|
|
462
|
+
details: acceptedDetails,
|
|
455
463
|
terminate: true as const,
|
|
456
464
|
};
|
|
457
465
|
} catch (error) {
|
package/src/doctor-role.ts
CHANGED
|
@@ -2,6 +2,10 @@ import type { AgentToolResult, ExtensionAPI, ExtensionContext } from "@earendil-
|
|
|
2
2
|
import { disposeComplianceDecision } from "./audit-escalation.ts";
|
|
3
3
|
import { appendActiveSessionCustomEntry, type ComplianceDecision } from "./compliance-transport.ts";
|
|
4
4
|
import { DOCTOR_CANDIDATE_ENTRY_TYPE } from "./dossier-resolution.ts";
|
|
5
|
+
import {
|
|
6
|
+
readActivationEngineLaborFallbackField,
|
|
7
|
+
withEngineLaborFallbackField,
|
|
8
|
+
} from "./engine-labor-fallback.ts";
|
|
5
9
|
import { DOCTOR_EVIDENCE_TOOL_NAME, DOCTOR_OUTPUT_TOOL_DESCRIPTION, DOCTOR_OUTPUT_TOOL_NAME, DoctorEvidenceStore, doctorEvidenceReadSchema, doctorSubmissionSchema, validateDoctorOutput, type DoctorCase } from "./doctor-contracts.ts";
|
|
6
10
|
|
|
7
11
|
export { DOCTOR_EVIDENCE_TOOL_NAME, DOCTOR_OUTPUT_TOOL_NAME };
|
|
@@ -12,7 +16,7 @@ export function createDoctorRoleRuntime(pi: ExtensionAPI, dependencies: DoctorRo
|
|
|
12
16
|
let activation: { soul: string; patient: DoctorCase; store: DoctorEvidenceStore } | undefined; let registered = false; pi.registerFlag(DOCTOR_CASE_FLAG.name, DOCTOR_CASE_FLAG.definition);
|
|
13
17
|
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) };
|
|
14
18
|
if (!registered) { registered = true; pi.registerTool({ name: DOCTOR_EVIDENCE_TOOL_NAME, label: "Doctor Evidence", description: "Read retained Pi session bytes with bounded pagination.", parameters: doctorEvidenceReadSchema, async execute(_id, params: { evidenceId: string; offset?: number; limit?: number }) { if (!activation) throw new Error("Doctor is not activated"); const details = activation.store.read(params.evidenceId, params.offset, params.limit); return { content: [{ type: "text" as const, text: JSON.stringify(details) }], details }; } });
|
|
15
|
-
pi.registerTool({ name: DOCTOR_OUTPUT_TOOL_NAME, label: "Doctor Output", description: DOCTOR_OUTPUT_TOOL_DESCRIPTION, parameters: doctorSubmissionSchema, async execute(id, params, signal, _update, ctx): Promise<AgentToolResult<unknown>> { if (!activation) throw new Error("Doctor is not activated"); singleton(id, ctx); const testimony = validateDoctorOutput(params, activation.patient, activation.store); try { appendActiveSessionCustomEntry(ctx, DOCTOR_CANDIDATE_ENTRY_TYPE, { version: 1, testimony, readRecord: activation.store.readRecord(), patientIdentity: activation.patient.identity }, { unavailable: "doctor candidate retention is unavailable", failed: "doctor candidate retention failed" }); } 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; return disposeComplianceDecision<AgentToolResult<unknown>>(audit, { pass: (usage) => ({ content: [{ type: "text" as const, text: "Doctor output accepted" }], details, terminate: true as const, ...(usage === undefined ? {} : { usage }) }), noReceipt: (auditNoReceipt, usageProjection) => ({ content: [{ type: "text" as const, text: "Doctor output accepted; compliance audit produced no receipt" }], details: { ...
|
|
19
|
+
pi.registerTool({ name: DOCTOR_OUTPUT_TOOL_NAME, label: "Doctor Output", description: DOCTOR_OUTPUT_TOOL_DESCRIPTION, parameters: doctorSubmissionSchema, async execute(id, params, signal, _update, ctx): Promise<AgentToolResult<unknown>> { if (!activation) throw new Error("Doctor is not activated"); singleton(id, ctx); const testimony = validateDoctorOutput(params, activation.patient, activation.store); try { appendActiveSessionCustomEntry(ctx, DOCTOR_CANDIDATE_ENTRY_TYPE, { version: 1, testimony, readRecord: activation.store.readRecord(), patientIdentity: activation.patient.identity }, { unavailable: "doctor candidate retention is unavailable", failed: "doctor candidate retention failed" }); } 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 = withEngineLaborFallbackField(details, readActivationEngineLaborFallbackField()); return disposeComplianceDecision<AgentToolResult<unknown>>(audit, { pass: (usage) => ({ content: [{ type: "text" as const, text: "Doctor output accepted" }], details: acceptedDetails, terminate: true as const, ...(usage === undefined ? {} : { usage }) }), noReceipt: (auditNoReceipt, usageProjection) => ({ content: [{ type: "text" as const, text: "Doctor output accepted; compliance audit produced no receipt" }], details: { ...acceptedDetails, auditNoReceipt }, terminate: true as const, ...usageProjection }), revise: (violations) => { throw new Error(`Doctor output violates its soul: ${violations.join("; ")}`); }, escalate: (result) => result, auditIncomplete: (result) => result }, acceptedDetails); } });
|
|
16
20
|
pi.on("before_agent_start", (event) => { if (!activation) throw new Error("Doctor is not activated"); const catalog = { version: activation.patient.version, identity: activation.patient.identity, admittedMetrics: { provenance: "runtime-derived from retained session bytes and sealed into the accepted receipt", 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>` }; }); }
|
|
17
21
|
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"); } };
|
|
18
22
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Package-owned engine detour tool (#357 T2 / #378 / #380).
|
|
3
|
-
* Registered by shared role-runtime when
|
|
3
|
+
* Registered by shared role-runtime when any role + engine activation signal is present.
|
|
4
4
|
* Evidence-child legs install the same definition via customTools (no spawn in role modules).
|
|
5
5
|
* #380: engine process failure soft-returns so the seat rejoins the main road; declaration
|
|
6
6
|
* is recorded once via engine-labor-fallback (no fail-closed reject-leg).
|
|
@@ -204,7 +204,7 @@ export function createEngineDetourToolDefinition(input: {
|
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
/**
|
|
207
|
-
* Register the engine-generic detour tool once for this process when
|
|
207
|
+
* Register the engine-generic detour tool once for this process when any role has
|
|
208
208
|
* an engine activation signal. Returns whether registration occurred.
|
|
209
209
|
* Once-latch is activation-scoped via the returned reset handle.
|
|
210
210
|
*/
|
package/src/engine-detour.ts
CHANGED
|
@@ -8,9 +8,25 @@ import { spawn } from "node:child_process";
|
|
|
8
8
|
/** Package-owned detour tool name (settlement whitelist + session principal). */
|
|
9
9
|
export const ENGINE_DETOUR_TOOL_NAME = "ak_engine_detour" as const;
|
|
10
10
|
|
|
11
|
-
/** Env presence/name signal injected by public
|
|
11
|
+
/** Env presence/name signal injected by public role runs (registration gate only). */
|
|
12
12
|
export const AK_ROLE_ENGINE_ENV = "AK_ROLE_ENGINE" as const;
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Sole AK_ROLE_ENGINE write seam for public role child env (#391 E2).
|
|
16
|
+
* Delete ambient inheritance first; own-key undefined mask survives process.env re-merge.
|
|
17
|
+
*/
|
|
18
|
+
export function applyEngineChildEnv(
|
|
19
|
+
childEnv: NodeJS.ProcessEnv,
|
|
20
|
+
engine?: string,
|
|
21
|
+
): void {
|
|
22
|
+
delete childEnv[AK_ROLE_ENGINE_ENV];
|
|
23
|
+
if (engine !== undefined && engine.trim() !== "") {
|
|
24
|
+
childEnv[AK_ROLE_ENGINE_ENV] = engine.trim();
|
|
25
|
+
} else {
|
|
26
|
+
childEnv[AK_ROLE_ENGINE_ENV] = undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
14
30
|
export const ENGINE_DETOUR_EMPTY_STDOUT_DIAGNOSTIC =
|
|
15
31
|
"engine detour produced empty stdout" as const;
|
|
16
32
|
|
|
@@ -82,7 +82,7 @@ export function withEngineLaborFallbackField<T extends object>(
|
|
|
82
82
|
return rest as T;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
/** Install the activation-scoped latch (
|
|
85
|
+
/** Install the activation-scoped latch (any role session_start with engine). */
|
|
86
86
|
export function installActivationEngineLaborFallbackLatch(
|
|
87
87
|
latch: EngineLaborFallbackLatch,
|
|
88
88
|
): void {
|
package/src/merger-role.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import {
|
|
3
|
+
readActivationEngineLaborFallbackField,
|
|
4
|
+
withEngineLaborFallbackField,
|
|
5
|
+
} from "./engine-labor-fallback.ts";
|
|
2
6
|
import { MERGER_ACCEPTED_TEXT, MERGER_OUTPUT_TOOL_NAME, mergerOutputSchema, validateMergerInput, validateMergerOutput, type MergerInput } from "./merger-contracts.ts";
|
|
3
7
|
import type { MergerGitState } from "./merger-git-state.ts";
|
|
4
8
|
import { exactUtf8 } from "./exact-utf8.ts";
|
|
@@ -47,7 +51,11 @@ export function createMergerRoleRuntime(pi: ExtensionAPI, dependencies: MergerRo
|
|
|
47
51
|
if (state.mergeCommitId !== output.mergeCommitId || !same(state.parentObjectIds, [activation.input.targetObjectId, activation.input.sourceObjectId]) || state.unmergedPaths.length !== 0 || !state.worktreeClean || state.resolutionChangedPaths.some(path => !scope.has(path))) host.failInfrastructure(new Error("Merger completed-state verification failed"), ctx, id);
|
|
48
52
|
}
|
|
49
53
|
accepted = true;
|
|
50
|
-
|
|
54
|
+
const acceptedDetails = withEngineLaborFallbackField(
|
|
55
|
+
output,
|
|
56
|
+
readActivationEngineLaborFallbackField(),
|
|
57
|
+
);
|
|
58
|
+
return { content: [{ type: "text" as const, text: MERGER_ACCEPTED_TEXT }], details: acceptedDetails, terminate: true as const };
|
|
51
59
|
} });
|
|
52
60
|
pi.on("before_agent_start", event => { if (!activation) throw new Error("Merger is not activated"); const admitted = { attemptId: activation.input.attemptId, targetObjectId: activation.input.targetObjectId, sourceObjectId: activation.input.sourceObjectId, task: materialText(activation.input, "task"), authority: materialText(activation.input, "authority"), targetIntent: materialText(activation.input, "targetIntent"), sourceIntent: materialText(activation.input, "sourceIntent"), expectedConflictPaths: activation.input.expectedConflictPaths, resolutionScope: activation.input.resolutionScope, authorizedChecks: activation.input.authorizedChecks }; return { systemPrompt: `${event.systemPrompt}\n\n<merger_soul>\n${activation.soul}\n</merger_soul>\n\n<merger_assignment>\n${JSON.stringify(admitted)}\n</merger_assignment>` }; });
|
|
53
61
|
}
|
package/src/public-cli/cli.ts
CHANGED
|
@@ -11,12 +11,12 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
effectiveSeatConfigurations,
|
|
13
13
|
formatModelSpec,
|
|
14
|
+
isEngineAxisSeat,
|
|
14
15
|
loadCredentialProviders,
|
|
15
16
|
loadPublicCliConfig,
|
|
16
17
|
parseModelSpec,
|
|
17
18
|
resolveEffectiveSeat,
|
|
18
19
|
savePublicCliConfig,
|
|
19
|
-
isEngineAxisSeat,
|
|
20
20
|
setPersistentSeatConfig,
|
|
21
21
|
setPersistentSeatEngine,
|
|
22
22
|
validatePublicCliConfigEngines,
|
|
@@ -58,10 +58,13 @@ import { runPublicReviewer, runPublicReviewerResume } from "./reviewer-run.ts";
|
|
|
58
58
|
import { runPublicTaishi } from "./taishi-run.ts";
|
|
59
59
|
import { peekRoleRunRole } from "./run-lifecycle.ts";
|
|
60
60
|
import {
|
|
61
|
+
AUTOMATIC_NAVIGATOR_SEAT,
|
|
61
62
|
INTERNAL_ROLE_ENTRYPOINT_RELATIVE,
|
|
63
|
+
isPublicCallableRole,
|
|
62
64
|
isPublicCliSupportCommand,
|
|
63
65
|
isPublicConfigurableSeat,
|
|
64
66
|
listHelpCapabilities,
|
|
67
|
+
type PublicCallableRole,
|
|
65
68
|
type PublicThinkingLevel,
|
|
66
69
|
} from "./registry.ts";
|
|
67
70
|
import {
|
|
@@ -336,6 +339,31 @@ function requireLegalEngineName(name: string): string {
|
|
|
336
339
|
}
|
|
337
340
|
}
|
|
338
341
|
|
|
342
|
+
/**
|
|
343
|
+
* Persistent engine axis gate (#391 E1): PUBLIC_CALLABLE_ROLES only.
|
|
344
|
+
* Navigator is a configurable model seat but has no independent activation path.
|
|
345
|
+
*/
|
|
346
|
+
function requireEngineAxisSeat(
|
|
347
|
+
seat: string,
|
|
348
|
+
verb: "set-engine" | "unset-engine",
|
|
349
|
+
): asserts seat is PublicCallableRole {
|
|
350
|
+
if (seat === AUTOMATIC_NAVIGATOR_SEAT) {
|
|
351
|
+
throw new CliUsageError(
|
|
352
|
+
`config ${verb} refuses navigator: no independent activation path; storing would be silently ineffective`,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
if (!isEngineAxisSeat(seat)) {
|
|
356
|
+
throw new CliUsageError(`unknown engine-axis seat: ${seat}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** Single seat.engine → run-options projection (#391 E2). */
|
|
361
|
+
function projectSeatEngine(
|
|
362
|
+
seat: Readonly<{ engine?: string }>,
|
|
363
|
+
): { engine: string } | Record<PropertyKey, never> {
|
|
364
|
+
return seat.engine === undefined ? {} : { engine: seat.engine };
|
|
365
|
+
}
|
|
366
|
+
|
|
339
367
|
function loadAndValidateConfig(
|
|
340
368
|
home: string,
|
|
341
369
|
packageRoot: string,
|
|
@@ -419,7 +447,7 @@ function renderHelp(): string {
|
|
|
419
447
|
"",
|
|
420
448
|
"Role options: ak-role help <command>",
|
|
421
449
|
"Persistent config: ak-role config set <seat> <provider/model[:thinking]>",
|
|
422
|
-
"Persistent engine (
|
|
450
|
+
"Persistent engine (callable roles): ak-role config set-engine <seat> <name> | unset-engine <seat>",
|
|
423
451
|
"Effective seats: ak-role roles",
|
|
424
452
|
);
|
|
425
453
|
return `${lines.join("\n")}\n`;
|
|
@@ -536,19 +564,12 @@ async function runConfigCommand(
|
|
|
536
564
|
if (args[0] === "set-engine") {
|
|
537
565
|
if (args.length !== 3) {
|
|
538
566
|
throw new CliUsageError(
|
|
539
|
-
"usage: ak-role config set-engine <
|
|
567
|
+
"usage: ak-role config set-engine <seat> <name>",
|
|
540
568
|
);
|
|
541
569
|
}
|
|
542
570
|
const seat = args[1]!;
|
|
543
571
|
const name = args[2]!;
|
|
544
|
-
|
|
545
|
-
throw new CliUsageError(`unknown configurable seat: ${seat}`);
|
|
546
|
-
}
|
|
547
|
-
if (!isEngineAxisSeat(seat)) {
|
|
548
|
-
throw new CliUsageError(
|
|
549
|
-
`engine axis is judge+reviewer only; refused seat ${seat}`,
|
|
550
|
-
);
|
|
551
|
-
}
|
|
572
|
+
requireEngineAxisSeat(seat, "set-engine");
|
|
552
573
|
requireLegalEngineName(name);
|
|
553
574
|
let config = await loadAndValidateConfig(home, packageRoot);
|
|
554
575
|
try {
|
|
@@ -567,18 +588,11 @@ async function runConfigCommand(
|
|
|
567
588
|
if (args[0] === "unset-engine") {
|
|
568
589
|
if (args.length !== 2) {
|
|
569
590
|
throw new CliUsageError(
|
|
570
|
-
"usage: ak-role config unset-engine <
|
|
591
|
+
"usage: ak-role config unset-engine <seat>",
|
|
571
592
|
);
|
|
572
593
|
}
|
|
573
594
|
const seat = args[1]!;
|
|
574
|
-
|
|
575
|
-
throw new CliUsageError(`unknown configurable seat: ${seat}`);
|
|
576
|
-
}
|
|
577
|
-
if (!isEngineAxisSeat(seat)) {
|
|
578
|
-
throw new CliUsageError(
|
|
579
|
-
`engine axis is judge+reviewer only; refused seat ${seat}`,
|
|
580
|
-
);
|
|
581
|
-
}
|
|
595
|
+
requireEngineAxisSeat(seat, "unset-engine");
|
|
582
596
|
let config = await loadAndValidateConfig(home, packageRoot);
|
|
583
597
|
try {
|
|
584
598
|
config = setPersistentSeatEngine(config, seat, undefined);
|
|
@@ -609,17 +623,17 @@ export async function runAkRole(
|
|
|
609
623
|
env = { ...env, packageRoot: await realpath(env.packageRoot) };
|
|
610
624
|
const parsed = parseArgv(argv);
|
|
611
625
|
// Invocation --engine rejects at the call-request seam (not role submission).
|
|
612
|
-
// #356 / #378: engine axis is
|
|
626
|
+
// #356 / #378 / #391: engine axis is every callable role (not resume / support).
|
|
613
627
|
if (parsed.engine !== undefined) {
|
|
614
628
|
requireLegalEngineName(parsed.engine);
|
|
615
629
|
if (
|
|
616
630
|
!parsed.help &&
|
|
617
631
|
parsed.command !== undefined &&
|
|
618
632
|
parsed.command !== "help" &&
|
|
619
|
-
!
|
|
633
|
+
!isPublicCallableRole(parsed.command)
|
|
620
634
|
) {
|
|
621
635
|
throw new CliUsageError(
|
|
622
|
-
`engine axis is
|
|
636
|
+
`engine axis is role commands only; refused command ${parsed.command}`,
|
|
623
637
|
);
|
|
624
638
|
}
|
|
625
639
|
}
|
|
@@ -876,7 +890,7 @@ export async function runAkRole(
|
|
|
876
890
|
: { correlationId: env.correlationId }),
|
|
877
891
|
...(env.piRunner === undefined ? {} : { piRunner: env.piRunner }),
|
|
878
892
|
...(seat.selection === undefined ? {} : { model: seat.selection }),
|
|
879
|
-
...(seat
|
|
893
|
+
...projectSeatEngine(seat),
|
|
880
894
|
...(env.judgeExtraPiArgs === undefined
|
|
881
895
|
? {}
|
|
882
896
|
: { extraPiArgs: env.judgeExtraPiArgs }),
|
|
@@ -920,6 +934,7 @@ export async function runAkRole(
|
|
|
920
934
|
: { correlationId: env.correlationId }),
|
|
921
935
|
...(env.piRunner === undefined ? {} : { piRunner: env.piRunner }),
|
|
922
936
|
...(seat.selection === undefined ? {} : { model: seat.selection }),
|
|
937
|
+
...projectSeatEngine(seat),
|
|
923
938
|
...(env.coderExtraPiArgs === undefined
|
|
924
939
|
? {}
|
|
925
940
|
: { extraPiArgs: env.coderExtraPiArgs }),
|
|
@@ -963,6 +978,7 @@ export async function runAkRole(
|
|
|
963
978
|
: { correlationId: env.correlationId }),
|
|
964
979
|
...(env.piRunner === undefined ? {} : { piRunner: env.piRunner }),
|
|
965
980
|
...(seat.selection === undefined ? {} : { model: seat.selection }),
|
|
981
|
+
...projectSeatEngine(seat),
|
|
966
982
|
...(env.fixerExtraPiArgs === undefined
|
|
967
983
|
? {}
|
|
968
984
|
: { extraPiArgs: env.fixerExtraPiArgs }),
|
|
@@ -1006,6 +1022,7 @@ export async function runAkRole(
|
|
|
1006
1022
|
: { correlationId: env.correlationId }),
|
|
1007
1023
|
...(env.piRunner === undefined ? {} : { piRunner: env.piRunner }),
|
|
1008
1024
|
...(seat.selection === undefined ? {} : { model: seat.selection }),
|
|
1025
|
+
...projectSeatEngine(seat),
|
|
1009
1026
|
...(env.collectorExtraPiArgs === undefined
|
|
1010
1027
|
? {}
|
|
1011
1028
|
: { extraPiArgs: env.collectorExtraPiArgs }),
|
|
@@ -1049,7 +1066,7 @@ export async function runAkRole(
|
|
|
1049
1066
|
: { correlationId: env.correlationId }),
|
|
1050
1067
|
...(env.piRunner === undefined ? {} : { piRunner: env.piRunner }),
|
|
1051
1068
|
...(seat.selection === undefined ? {} : { model: seat.selection }),
|
|
1052
|
-
...(seat
|
|
1069
|
+
...projectSeatEngine(seat),
|
|
1053
1070
|
...(env.reviewerExtraPiArgs === undefined
|
|
1054
1071
|
? {}
|
|
1055
1072
|
: { extraPiArgs: env.reviewerExtraPiArgs }),
|
|
@@ -1093,6 +1110,7 @@ export async function runAkRole(
|
|
|
1093
1110
|
: { correlationId: env.correlationId }),
|
|
1094
1111
|
...(env.piRunner === undefined ? {} : { piRunner: env.piRunner }),
|
|
1095
1112
|
...(seat.selection === undefined ? {} : { model: seat.selection }),
|
|
1113
|
+
...projectSeatEngine(seat),
|
|
1096
1114
|
...(env.doctorExtraPiArgs === undefined
|
|
1097
1115
|
? {}
|
|
1098
1116
|
: { extraPiArgs: env.doctorExtraPiArgs }),
|
|
@@ -1136,6 +1154,7 @@ export async function runAkRole(
|
|
|
1136
1154
|
: { correlationId: env.correlationId }),
|
|
1137
1155
|
...(env.piRunner === undefined ? {} : { piRunner: env.piRunner }),
|
|
1138
1156
|
...(seat.selection === undefined ? {} : { model: seat.selection }),
|
|
1157
|
+
...projectSeatEngine(seat),
|
|
1139
1158
|
...(env.mergerExtraPiArgs === undefined
|
|
1140
1159
|
? {}
|
|
1141
1160
|
: { extraPiArgs: env.mergerExtraPiArgs }),
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import { writeFile } from "node:fs/promises";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
|
|
9
|
+
import { applyEngineChildEnv } from "../engine-detour.ts";
|
|
10
|
+
import { engineSessionMaterialFromOptions } from "../package-resources/engine-material.ts";
|
|
9
11
|
import {
|
|
10
12
|
loadPackagedMethodSkillMaterial,
|
|
11
13
|
resolvePackagedMethodSkillPath,
|
|
@@ -79,6 +81,8 @@ export type CoderRunEnv = {
|
|
|
79
81
|
correlationId?: string;
|
|
80
82
|
piRunner?: ExplicitInternalPiRunner;
|
|
81
83
|
model?: SeatModelConfig;
|
|
84
|
+
/** Optional labor engine name (config→activation; session material + env signal). */
|
|
85
|
+
engine?: string;
|
|
82
86
|
credentials?: CredentialProviders;
|
|
83
87
|
createRunId?: () => string;
|
|
84
88
|
extraPiArgs?: readonly string[];
|
|
@@ -96,10 +100,14 @@ export function buildCoderActivationExtraArgs(
|
|
|
96
100
|
options: {
|
|
97
101
|
packageRoot: string;
|
|
98
102
|
model?: SeatModelConfig;
|
|
103
|
+
engine?: string;
|
|
99
104
|
extraPiArgs?: readonly string[];
|
|
100
105
|
},
|
|
101
106
|
): string[] {
|
|
102
|
-
const prompt = buildCoderTransportPrompt(
|
|
107
|
+
const prompt = buildCoderTransportPrompt(
|
|
108
|
+
admitted,
|
|
109
|
+
engineSessionMaterialFromOptions(options),
|
|
110
|
+
);
|
|
103
111
|
const skillArgs =
|
|
104
112
|
admitted.phase === "apply"
|
|
105
113
|
? [
|
|
@@ -276,12 +284,14 @@ async function dispatchAdmittedCoder(input: {
|
|
|
276
284
|
extraArgs: string[];
|
|
277
285
|
lease: RunWriterLease;
|
|
278
286
|
methodProvenance?: PackagedMethodSkillProvenance;
|
|
287
|
+
/** Mechanical engine provenance for initial Coder dispatch only. */
|
|
288
|
+
effectiveEngine?: string;
|
|
279
289
|
}): Promise<{
|
|
280
290
|
exitCode: number;
|
|
281
291
|
admitted: AdmittedCoderInvocation;
|
|
282
292
|
terminal?: TerminalResult;
|
|
283
293
|
}> {
|
|
284
|
-
const { admitted, env, io, extraArgs, lease, methodProvenance } = input;
|
|
294
|
+
const { admitted, env, io, extraArgs, lease, methodProvenance, effectiveEngine } = input;
|
|
285
295
|
try {
|
|
286
296
|
const missingCredential = missingCredentialPreDispatchFailure(
|
|
287
297
|
env.model,
|
|
@@ -294,7 +304,7 @@ async function dispatchAdmittedCoder(input: {
|
|
|
294
304
|
io,
|
|
295
305
|
);
|
|
296
306
|
}
|
|
297
|
-
await markRunRunning(admitted.runDirectory, env.model);
|
|
307
|
+
await markRunRunning(admitted.runDirectory, env.model, effectiveEngine);
|
|
298
308
|
await clearTypedProviderHttpObservation(admitted.runDirectory);
|
|
299
309
|
|
|
300
310
|
const childEnv: NodeJS.ProcessEnv = {
|
|
@@ -303,6 +313,7 @@ async function dispatchAdmittedCoder(input: {
|
|
|
303
313
|
PI_CODING_AGENT_DIR: env.agentDir,
|
|
304
314
|
AK_ROLE_RUN_DIR: admitted.runDirectory,
|
|
305
315
|
};
|
|
316
|
+
applyEngineChildEnv(childEnv, env.engine);
|
|
306
317
|
const correlationId = admitted.correlationId ?? env.correlationId;
|
|
307
318
|
if (correlationId !== undefined && correlationId.trim() !== "") {
|
|
308
319
|
childEnv.AK_CORRELATION_ID = correlationId;
|
|
@@ -474,6 +485,7 @@ export async function runPublicCoder(
|
|
|
474
485
|
const extraArgs = buildCoderActivationExtraArgs(admitted, {
|
|
475
486
|
packageRoot: env.packageRoot,
|
|
476
487
|
...(env.model === undefined ? {} : { model: env.model }),
|
|
488
|
+
...(env.engine === undefined ? {} : { engine: env.engine }),
|
|
477
489
|
...(env.extraPiArgs === undefined ? {} : { extraPiArgs: env.extraPiArgs }),
|
|
478
490
|
});
|
|
479
491
|
|
|
@@ -487,6 +499,7 @@ export async function runPublicCoder(
|
|
|
487
499
|
extraArgs,
|
|
488
500
|
lease,
|
|
489
501
|
...(methodProvenance === undefined ? {} : { methodProvenance }),
|
|
502
|
+
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
490
503
|
});
|
|
491
504
|
}
|
|
492
505
|
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import { writeFile } from "node:fs/promises";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
|
|
9
|
+
import { applyEngineChildEnv } from "../engine-detour.ts";
|
|
10
|
+
import { engineSessionMaterialFromOptions } from "../package-resources/engine-material.ts";
|
|
9
11
|
import {
|
|
10
12
|
runExplicitInternalActivation,
|
|
11
13
|
type ExplicitInternalKnownFailure,
|
|
@@ -64,6 +66,8 @@ export type CollectorRunEnv = {
|
|
|
64
66
|
correlationId?: string;
|
|
65
67
|
piRunner?: ExplicitInternalPiRunner;
|
|
66
68
|
model?: SeatModelConfig;
|
|
69
|
+
/** Optional labor engine name (config→activation; session material + env signal). */
|
|
70
|
+
engine?: string;
|
|
67
71
|
credentials?: CredentialProviders;
|
|
68
72
|
createRunId?: () => string;
|
|
69
73
|
extraPiArgs?: readonly string[];
|
|
@@ -79,10 +83,15 @@ export function buildCollectorActivationExtraArgs(
|
|
|
79
83
|
admitted: AdmittedCollectorInvocation,
|
|
80
84
|
options: {
|
|
81
85
|
model?: SeatModelConfig;
|
|
86
|
+
engine?: string;
|
|
87
|
+
packageRoot?: string;
|
|
82
88
|
extraPiArgs?: readonly string[];
|
|
83
89
|
} = {},
|
|
84
90
|
): string[] {
|
|
85
|
-
const prompt = buildCollectorTransportPrompt(
|
|
91
|
+
const prompt = buildCollectorTransportPrompt(
|
|
92
|
+
admitted,
|
|
93
|
+
engineSessionMaterialFromOptions(options),
|
|
94
|
+
);
|
|
86
95
|
return [
|
|
87
96
|
"--no-skills",
|
|
88
97
|
"--no-prompt-templates",
|
|
@@ -174,12 +183,13 @@ async function dispatchAdmittedCollector(input: {
|
|
|
174
183
|
io: CliIo;
|
|
175
184
|
extraArgs: string[];
|
|
176
185
|
lease: RunWriterLease;
|
|
186
|
+
effectiveEngine?: string;
|
|
177
187
|
}): Promise<{
|
|
178
188
|
exitCode: number;
|
|
179
189
|
admitted: AdmittedCollectorInvocation;
|
|
180
190
|
terminal?: TerminalResult;
|
|
181
191
|
}> {
|
|
182
|
-
const { admitted, env, io, extraArgs, lease } = input;
|
|
192
|
+
const { admitted, env, io, extraArgs, lease, effectiveEngine } = input;
|
|
183
193
|
try {
|
|
184
194
|
const missingCredential = missingCredentialPreDispatchFailure(
|
|
185
195
|
env.model,
|
|
@@ -192,7 +202,7 @@ async function dispatchAdmittedCollector(input: {
|
|
|
192
202
|
io,
|
|
193
203
|
);
|
|
194
204
|
}
|
|
195
|
-
await markRunRunning(admitted.runDirectory, env.model);
|
|
205
|
+
await markRunRunning(admitted.runDirectory, env.model, effectiveEngine);
|
|
196
206
|
await clearTypedProviderHttpObservation(admitted.runDirectory);
|
|
197
207
|
|
|
198
208
|
const childEnv: NodeJS.ProcessEnv = {
|
|
@@ -201,6 +211,7 @@ async function dispatchAdmittedCollector(input: {
|
|
|
201
211
|
PI_CODING_AGENT_DIR: env.agentDir,
|
|
202
212
|
AK_ROLE_RUN_DIR: admitted.runDirectory,
|
|
203
213
|
};
|
|
214
|
+
applyEngineChildEnv(childEnv, env.engine);
|
|
204
215
|
const correlationId = admitted.correlationId ?? env.correlationId;
|
|
205
216
|
if (correlationId !== undefined && correlationId.trim() !== "") {
|
|
206
217
|
childEnv.AK_CORRELATION_ID = correlationId;
|
|
@@ -354,7 +365,9 @@ export async function runPublicCollector(
|
|
|
354
365
|
}
|
|
355
366
|
|
|
356
367
|
const extraArgs = buildCollectorActivationExtraArgs(admitted, {
|
|
368
|
+
packageRoot: env.packageRoot,
|
|
357
369
|
...(env.model === undefined ? {} : { model: env.model }),
|
|
370
|
+
...(env.engine === undefined ? {} : { engine: env.engine }),
|
|
358
371
|
...(env.extraPiArgs === undefined ? {} : { extraPiArgs: env.extraPiArgs }),
|
|
359
372
|
});
|
|
360
373
|
|
|
@@ -367,5 +380,6 @@ export async function runPublicCollector(
|
|
|
367
380
|
io,
|
|
368
381
|
extraArgs,
|
|
369
382
|
lease,
|
|
383
|
+
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
370
384
|
});
|
|
371
385
|
}
|
package/src/public-cli/config.ts
CHANGED
|
@@ -10,7 +10,10 @@ import {
|
|
|
10
10
|
AUTOMATIC_NAVIGATOR_SEAT,
|
|
11
11
|
PUBLIC_CALLABLE_ROLES,
|
|
12
12
|
PUBLIC_CONFIGURABLE_SEATS,
|
|
13
|
+
isPublicCallableRole,
|
|
14
|
+
isPublicConfigurableSeat,
|
|
13
15
|
type ModelRef,
|
|
16
|
+
type PublicCallableRole,
|
|
14
17
|
type PublicConfigurableSeat,
|
|
15
18
|
type PublicThinkingLevel,
|
|
16
19
|
publicStartupCandidates,
|
|
@@ -116,23 +119,24 @@ export function setPersistentSeatConfig(
|
|
|
116
119
|
};
|
|
117
120
|
}
|
|
118
121
|
|
|
119
|
-
/**
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Seats that own the labor-engine axis (#391: PUBLIC_CALLABLE_ROLES only).
|
|
124
|
+
* Navigator is configurable for model but has no independent activation path.
|
|
125
|
+
*/
|
|
126
|
+
export function isEngineAxisSeat(seat: string): seat is PublicCallableRole {
|
|
127
|
+
return isPublicCallableRole(seat);
|
|
122
128
|
}
|
|
123
129
|
|
|
124
130
|
/**
|
|
125
|
-
* Set or clear persistent engine on
|
|
131
|
+
* Set or clear persistent engine on a callable role seat (#356 / #378 / #391).
|
|
126
132
|
* Engine-only seats are rejected — provider/model[:thinking] remains required first.
|
|
133
|
+
* Seat type is PublicCallableRole (navigator excluded at the type boundary).
|
|
127
134
|
*/
|
|
128
135
|
export function setPersistentSeatEngine(
|
|
129
136
|
config: PublicCliConfig,
|
|
130
|
-
seat:
|
|
137
|
+
seat: PublicCallableRole,
|
|
131
138
|
engine: string | undefined,
|
|
132
139
|
): PublicCliConfig {
|
|
133
|
-
if (!isEngineAxisSeat(seat)) {
|
|
134
|
-
throw new Error(`engine axis is judge+reviewer only; refused seat ${seat}`);
|
|
135
|
-
}
|
|
136
140
|
const previous = config.seats[seat];
|
|
137
141
|
if (previous === undefined) {
|
|
138
142
|
throw new Error(
|
|
@@ -166,9 +170,10 @@ export function seatModelOnly(seat: PersistentSeatConfig): SeatModelConfig {
|
|
|
166
170
|
}
|
|
167
171
|
|
|
168
172
|
/**
|
|
169
|
-
* Config-parse seam: engine axis is
|
|
170
|
-
* path-safety syntax (no closed material catalog; #376 / #378 / ADR 0069).
|
|
171
|
-
*
|
|
173
|
+
* Config-parse seam: engine axis is PUBLIC_CALLABLE_ROLES; engine names need only
|
|
174
|
+
* path-safety syntax (no closed material catalog; #376 / #378 / #391 / ADR 0069).
|
|
175
|
+
* Disk-handwritten navigator.engine is rejected (no independent activation → silent
|
|
176
|
+
* ineffective would violate failure honesty). Call after load / before dispatch (#356).
|
|
172
177
|
* Syntax authority = assertLegalEngineName (no injected duplicate).
|
|
173
178
|
*/
|
|
174
179
|
export function validatePublicCliConfigEngines(
|
|
@@ -180,7 +185,7 @@ export function validatePublicCliConfigEngines(
|
|
|
180
185
|
if (row?.engine === undefined) continue;
|
|
181
186
|
if (!isEngineAxisSeat(seat)) {
|
|
182
187
|
throw new Error(
|
|
183
|
-
`config seat ${seat} engine
|
|
188
|
+
`config seat ${seat} cannot persist engine: no independent activation path; storing would be silently ineffective`,
|
|
184
189
|
);
|
|
185
190
|
}
|
|
186
191
|
try {
|
|
@@ -356,7 +361,7 @@ function attachEngineAxis(
|
|
|
356
361
|
config: PublicCliConfig,
|
|
357
362
|
invocation?: InvocationModelOverride,
|
|
358
363
|
): EffectiveSeat {
|
|
359
|
-
// #
|
|
364
|
+
// #391: engine axis is PUBLIC_CALLABLE_ROLES only (single isEngineAxisSeat predicate).
|
|
360
365
|
if (!isEngineAxisSeat(seat.seat)) {
|
|
361
366
|
return {
|
|
362
367
|
...seat,
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import { writeFile } from "node:fs/promises";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
|
|
9
|
+
import { applyEngineChildEnv } from "../engine-detour.ts";
|
|
10
|
+
import { engineSessionMaterialFromOptions } from "../package-resources/engine-material.ts";
|
|
9
11
|
import {
|
|
10
12
|
runExplicitInternalActivation,
|
|
11
13
|
type ExplicitInternalKnownFailure,
|
|
@@ -65,6 +67,8 @@ export type DoctorRunEnv = {
|
|
|
65
67
|
correlationId?: string;
|
|
66
68
|
piRunner?: ExplicitInternalPiRunner;
|
|
67
69
|
model?: SeatModelConfig;
|
|
70
|
+
/** Optional labor engine name (config→activation; session material + env signal). */
|
|
71
|
+
engine?: string;
|
|
68
72
|
credentials?: CredentialProviders;
|
|
69
73
|
createRunId?: () => string;
|
|
70
74
|
extraPiArgs?: readonly string[];
|
|
@@ -81,10 +85,15 @@ export function buildDoctorActivationExtraArgs(
|
|
|
81
85
|
admitted: AdmittedDoctorInvocation,
|
|
82
86
|
options: {
|
|
83
87
|
model?: SeatModelConfig;
|
|
88
|
+
engine?: string;
|
|
89
|
+
packageRoot?: string;
|
|
84
90
|
extraPiArgs?: readonly string[];
|
|
85
91
|
} = {},
|
|
86
92
|
): string[] {
|
|
87
|
-
const prompt = buildDoctorTransportPrompt(
|
|
93
|
+
const prompt = buildDoctorTransportPrompt(
|
|
94
|
+
admitted,
|
|
95
|
+
engineSessionMaterialFromOptions(options),
|
|
96
|
+
);
|
|
88
97
|
return [
|
|
89
98
|
"--no-skills",
|
|
90
99
|
"--no-prompt-templates",
|
|
@@ -155,12 +164,13 @@ async function dispatchAdmittedDoctor(input: {
|
|
|
155
164
|
io: CliIo;
|
|
156
165
|
extraArgs: string[];
|
|
157
166
|
lease: RunWriterLease;
|
|
167
|
+
effectiveEngine?: string;
|
|
158
168
|
}): Promise<{
|
|
159
169
|
exitCode: number;
|
|
160
170
|
admitted: AdmittedDoctorInvocation;
|
|
161
171
|
terminal?: TerminalResult;
|
|
162
172
|
}> {
|
|
163
|
-
const { admitted, env, io, extraArgs, lease } = input;
|
|
173
|
+
const { admitted, env, io, extraArgs, lease, effectiveEngine } = input;
|
|
164
174
|
try {
|
|
165
175
|
const missingCredential = missingCredentialPreDispatchFailure(
|
|
166
176
|
env.model,
|
|
@@ -173,7 +183,7 @@ async function dispatchAdmittedDoctor(input: {
|
|
|
173
183
|
io,
|
|
174
184
|
);
|
|
175
185
|
}
|
|
176
|
-
await markRunRunning(admitted.runDirectory, env.model);
|
|
186
|
+
await markRunRunning(admitted.runDirectory, env.model, effectiveEngine);
|
|
177
187
|
await clearTypedProviderHttpObservation(admitted.runDirectory);
|
|
178
188
|
|
|
179
189
|
const childEnv: NodeJS.ProcessEnv = {
|
|
@@ -182,6 +192,7 @@ async function dispatchAdmittedDoctor(input: {
|
|
|
182
192
|
PI_CODING_AGENT_DIR: env.agentDir,
|
|
183
193
|
AK_ROLE_RUN_DIR: admitted.runDirectory,
|
|
184
194
|
};
|
|
195
|
+
applyEngineChildEnv(childEnv, env.engine);
|
|
185
196
|
if (env.correlationId !== undefined && env.correlationId.trim() !== "") {
|
|
186
197
|
childEnv.AK_CORRELATION_ID = env.correlationId;
|
|
187
198
|
}
|
|
@@ -333,7 +344,9 @@ export async function runPublicDoctor(
|
|
|
333
344
|
}
|
|
334
345
|
|
|
335
346
|
const extraArgs = buildDoctorActivationExtraArgs(admitted, {
|
|
347
|
+
packageRoot: env.packageRoot,
|
|
336
348
|
...(env.model === undefined ? {} : { model: env.model }),
|
|
349
|
+
...(env.engine === undefined ? {} : { engine: env.engine }),
|
|
337
350
|
...(env.extraPiArgs === undefined ? {} : { extraPiArgs: env.extraPiArgs }),
|
|
338
351
|
});
|
|
339
352
|
|
|
@@ -343,5 +356,6 @@ export async function runPublicDoctor(
|
|
|
343
356
|
io,
|
|
344
357
|
extraArgs,
|
|
345
358
|
lease,
|
|
359
|
+
...(env.engine === undefined ? {} : { effectiveEngine: env.engine }),
|
|
346
360
|
});
|
|
347
361
|
}
|