@akagilnc/pi-workflow-roles 0.1.2312 → 0.1.2322
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/evidence-child-executor.js +33 -3
- package/dist/public-cli/config.js +491 -0
- package/dist/public-cli/main.js +103 -21
- package/dist/public-cli/registry.js +121 -0
- package/package.json +1 -1
- package/src/evidence-child-executor.ts +58 -3
- package/src/gatekeeper-role.ts +5 -1
- package/src/public-cli/cli.ts +43 -7
- package/src/public-cli/config.ts +122 -27
- package/src/public-cli/option-definitions.ts +2 -0
- package/src/public-cli/registry.ts +25 -2
package/dist/public-cli/main.js
CHANGED
|
@@ -14473,6 +14473,9 @@ var init_packaged_role_registry = __esm({
|
|
|
14473
14473
|
});
|
|
14474
14474
|
|
|
14475
14475
|
// src/public-cli/registry.ts
|
|
14476
|
+
function isAutomaticConfigurableSeat(value) {
|
|
14477
|
+
return AUTOMATIC_CONFIGURABLE_SEATS.includes(value);
|
|
14478
|
+
}
|
|
14476
14479
|
function publicStartupCandidates(seat) {
|
|
14477
14480
|
return STARTUP_CANDIDATES[seat];
|
|
14478
14481
|
}
|
|
@@ -14508,7 +14511,7 @@ function isPublicConfigurableSeat(value) {
|
|
|
14508
14511
|
function isPublicCliSupportCommand(value) {
|
|
14509
14512
|
return PUBLIC_CLI_SUPPORT_COMMANDS.includes(value);
|
|
14510
14513
|
}
|
|
14511
|
-
var INTERNAL_ROLE_ENTRYPOINT_RELATIVE, PUBLIC_CALLABLE_ROLES, AUTOMATIC_NAVIGATOR_SEAT, PUBLIC_CONFIGURABLE_SEATS, PUBLIC_CLI_SUPPORT_COMMANDS, STARTUP_CANDIDATES, PUBLIC_DETERMINISTIC_COMMANDS;
|
|
14514
|
+
var INTERNAL_ROLE_ENTRYPOINT_RELATIVE, PUBLIC_CALLABLE_ROLES, AUTOMATIC_NAVIGATOR_SEAT, AUTOMATIC_GATEKEEPER_SEAT, AUTOMATIC_INSPECTOR_SEAT, AUTOMATIC_CONFIGURABLE_SEATS, PUBLIC_CONFIGURABLE_SEATS, PUBLIC_CLI_SUPPORT_COMMANDS, STARTUP_CANDIDATES, PUBLIC_DETERMINISTIC_COMMANDS;
|
|
14512
14515
|
var init_registry2 = __esm({
|
|
14513
14516
|
"src/public-cli/registry.ts"() {
|
|
14514
14517
|
"use strict";
|
|
@@ -14518,9 +14521,16 @@ var init_registry2 = __esm({
|
|
|
14518
14521
|
(entry) => entry.role
|
|
14519
14522
|
);
|
|
14520
14523
|
AUTOMATIC_NAVIGATOR_SEAT = "navigator";
|
|
14524
|
+
AUTOMATIC_GATEKEEPER_SEAT = "gatekeeper";
|
|
14525
|
+
AUTOMATIC_INSPECTOR_SEAT = "inspector";
|
|
14526
|
+
AUTOMATIC_CONFIGURABLE_SEATS = [
|
|
14527
|
+
AUTOMATIC_GATEKEEPER_SEAT,
|
|
14528
|
+
AUTOMATIC_INSPECTOR_SEAT,
|
|
14529
|
+
AUTOMATIC_NAVIGATOR_SEAT
|
|
14530
|
+
];
|
|
14521
14531
|
PUBLIC_CONFIGURABLE_SEATS = [
|
|
14522
14532
|
...PUBLIC_CALLABLE_ROLES,
|
|
14523
|
-
|
|
14533
|
+
...AUTOMATIC_CONFIGURABLE_SEATS
|
|
14524
14534
|
];
|
|
14525
14535
|
PUBLIC_CLI_SUPPORT_COMMANDS = [
|
|
14526
14536
|
"roles",
|
|
@@ -14561,6 +14571,9 @@ var init_registry2 = __esm({
|
|
|
14561
14571
|
{ provider: "openai-codex", model: "gpt-5.6-luna", thinking: "high" },
|
|
14562
14572
|
{ provider: "xai", model: "grok-4.5", thinking: "high" }
|
|
14563
14573
|
],
|
|
14574
|
+
// #453: automatic menxia seats have no startup default — unset means inherit parent.
|
|
14575
|
+
gatekeeper: [],
|
|
14576
|
+
inspector: [],
|
|
14564
14577
|
navigator: [
|
|
14565
14578
|
{ provider: "openai-codex", model: "gpt-5.6-luna", thinking: "medium" },
|
|
14566
14579
|
{ provider: "xai", model: "grok-4.5", thinking: "high" }
|
|
@@ -14574,6 +14587,9 @@ var init_registry2 = __esm({
|
|
|
14574
14587
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
14575
14588
|
import { homedir } from "node:os";
|
|
14576
14589
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
14590
|
+
function isMenxiaOfficerSeat(value) {
|
|
14591
|
+
return MENXIA_OFFICER_SEATS.includes(value);
|
|
14592
|
+
}
|
|
14577
14593
|
function publicCliConfigPath(home = homedir()) {
|
|
14578
14594
|
return join3(home, ".ak-roles", "public-cli.json");
|
|
14579
14595
|
}
|
|
@@ -14612,6 +14628,21 @@ function setPersistentSeatConfig(config, seat, selection) {
|
|
|
14612
14628
|
}
|
|
14613
14629
|
};
|
|
14614
14630
|
}
|
|
14631
|
+
function clearPersistentSeatConfig(config, seat) {
|
|
14632
|
+
const previous = config.seats[seat];
|
|
14633
|
+
if (previous === void 0) return config;
|
|
14634
|
+
if (seat === "notary" && previous.engine !== void 0) {
|
|
14635
|
+
return {
|
|
14636
|
+
...config,
|
|
14637
|
+
seats: {
|
|
14638
|
+
...config.seats,
|
|
14639
|
+
notary: { engine: previous.engine }
|
|
14640
|
+
}
|
|
14641
|
+
};
|
|
14642
|
+
}
|
|
14643
|
+
const { [seat]: _dropped, ...seats } = config.seats;
|
|
14644
|
+
return { ...config, seats };
|
|
14645
|
+
}
|
|
14615
14646
|
function isEngineAxisSeat(seat) {
|
|
14616
14647
|
return isPublicCallableRole(seat);
|
|
14617
14648
|
}
|
|
@@ -14624,6 +14655,10 @@ function setPersistentSeatEngine(config, seat, engine) {
|
|
|
14624
14655
|
}
|
|
14625
14656
|
if (engine === void 0) {
|
|
14626
14657
|
const { engine: _dropped, ...modelOnly } = previous;
|
|
14658
|
+
if (seatModelOnly(modelOnly) === void 0) {
|
|
14659
|
+
const { [seat]: _row, ...seats } = config.seats;
|
|
14660
|
+
return { ...config, seats };
|
|
14661
|
+
}
|
|
14627
14662
|
return {
|
|
14628
14663
|
...config,
|
|
14629
14664
|
seats: {
|
|
@@ -14652,6 +14687,7 @@ function setAutoResumeLimit(config, limit) {
|
|
|
14652
14687
|
return { ...config, autoResumeLimit: parseAutoResumeLimit(limit) };
|
|
14653
14688
|
}
|
|
14654
14689
|
function seatModelOnly(seat) {
|
|
14690
|
+
if (seat?.provider === void 0 || seat.model === void 0) return void 0;
|
|
14655
14691
|
return seat.thinking === void 0 ? { provider: seat.provider, model: seat.model } : { provider: seat.provider, model: seat.model, thinking: seat.thinking };
|
|
14656
14692
|
}
|
|
14657
14693
|
function validatePublicCliConfigEngines(config, _packageRoot) {
|
|
@@ -14752,6 +14788,23 @@ function parseSeatModelConfig(value, seat) {
|
|
|
14752
14788
|
throw new Error(`config seat ${seat} must be an object`);
|
|
14753
14789
|
}
|
|
14754
14790
|
const raw = value;
|
|
14791
|
+
const hasProvider = raw.provider !== void 0;
|
|
14792
|
+
const hasModel = raw.model !== void 0;
|
|
14793
|
+
if (hasProvider !== hasModel) {
|
|
14794
|
+
throw new Error(`config seat ${seat} requires both provider and model`);
|
|
14795
|
+
}
|
|
14796
|
+
if (raw.engine !== void 0 && typeof raw.engine !== "string") {
|
|
14797
|
+
throw new Error(`config seat ${seat} engine must be a string`);
|
|
14798
|
+
}
|
|
14799
|
+
if (!hasProvider) {
|
|
14800
|
+
if (seat === "notary" && typeof raw.engine === "string") {
|
|
14801
|
+
if (raw.thinking !== void 0) {
|
|
14802
|
+
throw new Error(`config seat ${seat} thinking requires provider/model`);
|
|
14803
|
+
}
|
|
14804
|
+
return { engine: raw.engine };
|
|
14805
|
+
}
|
|
14806
|
+
throw new Error(`config seat ${seat} requires provider`);
|
|
14807
|
+
}
|
|
14755
14808
|
if (typeof raw.provider !== "string" || raw.provider.trim() === "") {
|
|
14756
14809
|
throw new Error(`config seat ${seat} requires provider`);
|
|
14757
14810
|
}
|
|
@@ -14766,14 +14819,9 @@ function parseSeatModelConfig(value, seat) {
|
|
|
14766
14819
|
const parsed = {
|
|
14767
14820
|
provider: raw.provider,
|
|
14768
14821
|
model: raw.model,
|
|
14769
|
-
...raw.thinking === void 0 ? {} : { thinking: raw.thinking }
|
|
14822
|
+
...raw.thinking === void 0 ? {} : { thinking: raw.thinking },
|
|
14823
|
+
...raw.engine === void 0 ? {} : { engine: raw.engine }
|
|
14770
14824
|
};
|
|
14771
|
-
if (raw.engine !== void 0) {
|
|
14772
|
-
if (typeof raw.engine !== "string") {
|
|
14773
|
-
throw new Error(`config seat ${seat} engine must be a string`);
|
|
14774
|
-
}
|
|
14775
|
-
parsed.engine = raw.engine;
|
|
14776
|
-
}
|
|
14777
14825
|
return parsed;
|
|
14778
14826
|
}
|
|
14779
14827
|
function providerConfigured(credentials, provider) {
|
|
@@ -14821,14 +14869,14 @@ function attachEngineAxis(seat, config, invocation) {
|
|
|
14821
14869
|
};
|
|
14822
14870
|
}
|
|
14823
14871
|
function resolveBaseSeat(config, seat, credentials) {
|
|
14824
|
-
const automatic = seat
|
|
14825
|
-
const
|
|
14826
|
-
if (
|
|
14872
|
+
const automatic = isAutomaticConfigurableSeat(seat);
|
|
14873
|
+
const persistentModel = seatModelOnly(config.seats[seat]);
|
|
14874
|
+
if (persistentModel !== void 0) {
|
|
14827
14875
|
return {
|
|
14828
14876
|
seat,
|
|
14829
14877
|
automatic,
|
|
14830
14878
|
source: "persistent",
|
|
14831
|
-
selection:
|
|
14879
|
+
selection: persistentModel,
|
|
14832
14880
|
engineSource: "unconfigured"
|
|
14833
14881
|
};
|
|
14834
14882
|
}
|
|
@@ -14845,7 +14893,7 @@ function resolveBaseSeat(config, seat, credentials) {
|
|
|
14845
14893
|
return { seat, automatic, source: "unconfigured", engineSource: "unconfigured" };
|
|
14846
14894
|
}
|
|
14847
14895
|
function resolveEffectiveSeat(config, seat, credentials, invocation) {
|
|
14848
|
-
const automatic = seat
|
|
14896
|
+
const automatic = isAutomaticConfigurableSeat(seat);
|
|
14849
14897
|
const hasModelInvocation = invocation !== void 0 && (invocation.model !== void 0 || invocation.thinking !== void 0);
|
|
14850
14898
|
let modelSeat;
|
|
14851
14899
|
if (!hasModelInvocation || invocation === void 0) {
|
|
@@ -14906,12 +14954,17 @@ async function loadCredentialProviders(agentDir) {
|
|
|
14906
14954
|
throw error;
|
|
14907
14955
|
}
|
|
14908
14956
|
}
|
|
14909
|
-
var THINKING_LEVELS;
|
|
14957
|
+
var MENXIA_OFFICER_SEATS, THINKING_LEVELS;
|
|
14910
14958
|
var init_config2 = __esm({
|
|
14911
14959
|
"src/public-cli/config.ts"() {
|
|
14912
14960
|
"use strict";
|
|
14913
14961
|
init_engine_material();
|
|
14914
14962
|
init_registry2();
|
|
14963
|
+
MENXIA_OFFICER_SEATS = [
|
|
14964
|
+
"gatekeeper",
|
|
14965
|
+
"inspector",
|
|
14966
|
+
"notary"
|
|
14967
|
+
];
|
|
14915
14968
|
THINKING_LEVELS = /* @__PURE__ */ new Set([
|
|
14916
14969
|
"off",
|
|
14917
14970
|
"minimal",
|
|
@@ -17411,12 +17464,14 @@ var init_option_definitions = __esm({
|
|
|
17411
17464
|
summary: "Persistent seat model, labor-engine, and auto-resume defaults.",
|
|
17412
17465
|
usage: [
|
|
17413
17466
|
"ak-role config set <seat> <provider/model[:thinking]> [<seat> <spec> ...]",
|
|
17467
|
+
"ak-role config unset <gatekeeper|inspector|notary>",
|
|
17414
17468
|
"ak-role config set-engine <seat> <name>",
|
|
17415
17469
|
"ak-role config unset-engine <seat>",
|
|
17416
17470
|
"ak-role config set-auto-resume-limit <N>"
|
|
17417
17471
|
],
|
|
17418
17472
|
examples: [
|
|
17419
17473
|
"ak-role config set judge openai-codex/gpt-5.6-sol:high",
|
|
17474
|
+
"ak-role config unset gatekeeper",
|
|
17420
17475
|
"ak-role config set-engine judge opus",
|
|
17421
17476
|
"ak-role config set-auto-resume-limit 3"
|
|
17422
17477
|
]
|
|
@@ -20062,6 +20117,7 @@ var init_evidence_child_executor = __esm({
|
|
|
20062
20117
|
init_engine_detour_tool();
|
|
20063
20118
|
init_engine_detour();
|
|
20064
20119
|
init_engine_material();
|
|
20120
|
+
init_config2();
|
|
20065
20121
|
init_receipt_delivery_policy();
|
|
20066
20122
|
init_reviewer_construction();
|
|
20067
20123
|
init_stream_idle_guard();
|
|
@@ -28740,9 +28796,9 @@ function requireLegalEngineName(name) {
|
|
|
28740
28796
|
}
|
|
28741
28797
|
}
|
|
28742
28798
|
function requireEngineAxisSeat(seat, verb) {
|
|
28743
|
-
if (seat
|
|
28799
|
+
if (isAutomaticConfigurableSeat(seat)) {
|
|
28744
28800
|
throw new CliUsageError(
|
|
28745
|
-
`config ${verb} refuses
|
|
28801
|
+
`config ${verb} refuses ${seat}: no independent activation path; storing would be silently ineffective`
|
|
28746
28802
|
);
|
|
28747
28803
|
}
|
|
28748
28804
|
if (!isEngineAxisSeat(seat)) {
|
|
@@ -28838,7 +28894,7 @@ function renderHelp() {
|
|
|
28838
28894
|
lines.push(
|
|
28839
28895
|
"",
|
|
28840
28896
|
"Role options: ak-role help <command>",
|
|
28841
|
-
"Persistent config: ak-role config set <seat> <provider/model[:thinking]>",
|
|
28897
|
+
"Persistent config: ak-role config set <seat> <provider/model[:thinking]> | unset <gatekeeper|inspector|notary>",
|
|
28842
28898
|
"Persistent engine (callable roles): ak-role config set-engine <seat> <name> | unset-engine <seat>",
|
|
28843
28899
|
"Effective seats: ak-role roles"
|
|
28844
28900
|
);
|
|
@@ -28879,6 +28935,10 @@ function renderRoles(seats) {
|
|
|
28879
28935
|
return `${lines.join("\n")}
|
|
28880
28936
|
`;
|
|
28881
28937
|
}
|
|
28938
|
+
function renderPersistentSeatModel(selection) {
|
|
28939
|
+
const model = seatModelOnly(selection);
|
|
28940
|
+
return model === void 0 ? "-" : formatModelSpec(model);
|
|
28941
|
+
}
|
|
28882
28942
|
function renderConfig(config) {
|
|
28883
28943
|
const lines = ["seat model engine"];
|
|
28884
28944
|
const keys = Object.keys(config.seats);
|
|
@@ -28889,7 +28949,7 @@ function renderConfig(config) {
|
|
|
28889
28949
|
const selection = config.seats[seat];
|
|
28890
28950
|
if (selection === void 0) continue;
|
|
28891
28951
|
const engine = selection.engine === void 0 ? "-" : selection.engine;
|
|
28892
|
-
lines.push(`${seat} ${
|
|
28952
|
+
lines.push(`${seat} ${renderPersistentSeatModel(selection)} ${engine}`);
|
|
28893
28953
|
}
|
|
28894
28954
|
}
|
|
28895
28955
|
lines.push(`autoResumeLimit ${config.autoResumeLimit ?? AUTO_RESUME_LIMIT}`);
|
|
@@ -28909,8 +28969,10 @@ async function runConfigCommand(args, home, packageRoot2, io) {
|
|
|
28909
28969
|
`);
|
|
28910
28970
|
} else {
|
|
28911
28971
|
const engine = selection.engine === void 0 ? "-" : selection.engine;
|
|
28912
|
-
io.stdout(
|
|
28913
|
-
|
|
28972
|
+
io.stdout(
|
|
28973
|
+
`${args[1]} ${renderPersistentSeatModel(selection)} ${engine}
|
|
28974
|
+
`
|
|
28975
|
+
);
|
|
28914
28976
|
}
|
|
28915
28977
|
return 0;
|
|
28916
28978
|
}
|
|
@@ -28942,6 +29004,26 @@ async function runConfigCommand(args, home, packageRoot2, io) {
|
|
|
28942
29004
|
io.stdout(renderConfig(config));
|
|
28943
29005
|
return 0;
|
|
28944
29006
|
}
|
|
29007
|
+
if (args[0] === "unset") {
|
|
29008
|
+
if (args.length !== 2) {
|
|
29009
|
+
throw new CliUsageError(
|
|
29010
|
+
"usage: ak-role config unset <gatekeeper|inspector|notary>"
|
|
29011
|
+
);
|
|
29012
|
+
}
|
|
29013
|
+
const seat = args[1];
|
|
29014
|
+
if (!isMenxiaOfficerSeat(seat)) {
|
|
29015
|
+
throw new CliUsageError(
|
|
29016
|
+
`config unset serves menxia officer overrides only (gatekeeper|inspector|notary); got ${seat}`
|
|
29017
|
+
);
|
|
29018
|
+
}
|
|
29019
|
+
const config = clearPersistentSeatConfig(
|
|
29020
|
+
await loadAndValidateConfig(home, packageRoot2),
|
|
29021
|
+
seat
|
|
29022
|
+
);
|
|
29023
|
+
await savePublicCliConfig(config, home);
|
|
29024
|
+
io.stdout(renderConfig(config));
|
|
29025
|
+
return 0;
|
|
29026
|
+
}
|
|
28945
29027
|
if (args[0] === "set-engine") {
|
|
28946
29028
|
if (args.length !== 3) {
|
|
28947
29029
|
throw new CliUsageError(
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed public CLI registry — sole source for discoverable commands, seats,
|
|
3
|
+
* and startup model candidates (ADR 0052 / #105).
|
|
4
|
+
*/
|
|
5
|
+
import { PACKAGED_ROLE_REGISTRY, } from "../packaged-role-registry.js";
|
|
6
|
+
/** Package-relative path of the Internal role entrypoint (explicit load only). */
|
|
7
|
+
export const INTERNAL_ROLE_ENTRYPOINT_RELATIVE = "extensions/role-runtime.ts";
|
|
8
|
+
export const PUBLIC_CALLABLE_ROLES = PACKAGED_ROLE_REGISTRY.map((entry) => entry.role);
|
|
9
|
+
/** Automatic attendance seat — configurable, never a caller-selected command. */
|
|
10
|
+
export const AUTOMATIC_NAVIGATOR_SEAT = "navigator";
|
|
11
|
+
/** Automatic menxia seats — configurable model only; never caller commands (#453). */
|
|
12
|
+
export const AUTOMATIC_GATEKEEPER_SEAT = "gatekeeper";
|
|
13
|
+
export const AUTOMATIC_INSPECTOR_SEAT = "inspector";
|
|
14
|
+
/** All automatic configurable seats (no independent public activation command). */
|
|
15
|
+
export const AUTOMATIC_CONFIGURABLE_SEATS = [
|
|
16
|
+
AUTOMATIC_GATEKEEPER_SEAT,
|
|
17
|
+
AUTOMATIC_INSPECTOR_SEAT,
|
|
18
|
+
AUTOMATIC_NAVIGATOR_SEAT,
|
|
19
|
+
];
|
|
20
|
+
export const PUBLIC_CONFIGURABLE_SEATS = [
|
|
21
|
+
...PUBLIC_CALLABLE_ROLES,
|
|
22
|
+
...AUTOMATIC_CONFIGURABLE_SEATS,
|
|
23
|
+
];
|
|
24
|
+
export function isAutomaticConfigurableSeat(value) {
|
|
25
|
+
return AUTOMATIC_CONFIGURABLE_SEATS.includes(value);
|
|
26
|
+
}
|
|
27
|
+
export const PUBLIC_CLI_SUPPORT_COMMANDS = [
|
|
28
|
+
"roles",
|
|
29
|
+
"config",
|
|
30
|
+
"help",
|
|
31
|
+
"resume",
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* Package startup candidates (#11): Codex family first, then Grok 4.5.
|
|
35
|
+
* Selection among candidates is credential-driven at resolve time.
|
|
36
|
+
*/
|
|
37
|
+
const STARTUP_CANDIDATES = {
|
|
38
|
+
judge: [
|
|
39
|
+
{ provider: "openai-codex", model: "gpt-5.6-sol", thinking: "high" },
|
|
40
|
+
{ provider: "xai", model: "grok-4.5", thinking: "high" },
|
|
41
|
+
],
|
|
42
|
+
reviewer: [
|
|
43
|
+
{ provider: "openai-codex", model: "gpt-5.6-sol", thinking: "medium" },
|
|
44
|
+
{ provider: "xai", model: "grok-4.5", thinking: "high" },
|
|
45
|
+
],
|
|
46
|
+
coder: [
|
|
47
|
+
{ provider: "openai-codex", model: "gpt-5.6-luna", thinking: "high" },
|
|
48
|
+
{ provider: "xai", model: "grok-4.5", thinking: "high" },
|
|
49
|
+
],
|
|
50
|
+
fixer: [
|
|
51
|
+
{ provider: "openai-codex", model: "gpt-5.6-luna", thinking: "high" },
|
|
52
|
+
{ provider: "xai", model: "grok-4.5", thinking: "high" },
|
|
53
|
+
],
|
|
54
|
+
collector: [
|
|
55
|
+
{ provider: "openai-codex", model: "gpt-5.6-luna", thinking: "high" },
|
|
56
|
+
{ provider: "xai", model: "grok-4.5", thinking: "high" },
|
|
57
|
+
],
|
|
58
|
+
doctor: [
|
|
59
|
+
{ provider: "openai-codex", model: "gpt-5.6-luna", thinking: "high" },
|
|
60
|
+
{ provider: "xai", model: "grok-4.5", thinking: "high" },
|
|
61
|
+
],
|
|
62
|
+
merger: [
|
|
63
|
+
{ provider: "openai-codex", model: "gpt-5.6-luna", thinking: "high" },
|
|
64
|
+
{ provider: "xai", model: "grok-4.5", thinking: "high" },
|
|
65
|
+
],
|
|
66
|
+
notary: [
|
|
67
|
+
{ provider: "openai-codex", model: "gpt-5.6-luna", thinking: "high" },
|
|
68
|
+
{ provider: "xai", model: "grok-4.5", thinking: "high" },
|
|
69
|
+
],
|
|
70
|
+
// #453: automatic menxia seats have no startup default — unset means inherit parent.
|
|
71
|
+
gatekeeper: [],
|
|
72
|
+
inspector: [],
|
|
73
|
+
navigator: [
|
|
74
|
+
{ provider: "openai-codex", model: "gpt-5.6-luna", thinking: "medium" },
|
|
75
|
+
{ provider: "xai", model: "grok-4.5", thinking: "high" },
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
export function publicStartupCandidates(seat) {
|
|
79
|
+
return STARTUP_CANDIDATES[seat];
|
|
80
|
+
}
|
|
81
|
+
/** Deterministic public commands — discoverable, never LLM-configurable seats. */
|
|
82
|
+
export const PUBLIC_DETERMINISTIC_COMMANDS = ["analyst"];
|
|
83
|
+
/**
|
|
84
|
+
* Typed help surface. Presentation formats these facts; tests must not assert
|
|
85
|
+
* exact help prose or layout (锚定宪法 / ADR 0016 / #105 AC).
|
|
86
|
+
* analyst is a deterministic analysis command on the public CLI — not an LLM seat.
|
|
87
|
+
*/
|
|
88
|
+
export function listHelpCapabilities() {
|
|
89
|
+
const support = PUBLIC_CLI_SUPPORT_COMMANDS.map((name) => ({
|
|
90
|
+
kind: "support",
|
|
91
|
+
name,
|
|
92
|
+
}));
|
|
93
|
+
const roles = PACKAGED_ROLE_REGISTRY.map((entry) => {
|
|
94
|
+
const phases = entry.phases;
|
|
95
|
+
const defaultPhase = phases.length === 1 && phases[0] === null
|
|
96
|
+
? null
|
|
97
|
+
: phases.includes("apply")
|
|
98
|
+
? "apply"
|
|
99
|
+
: (phases[0] ?? null);
|
|
100
|
+
return {
|
|
101
|
+
kind: "role",
|
|
102
|
+
name: entry.role,
|
|
103
|
+
phases,
|
|
104
|
+
defaultPhase,
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
const deterministic = PUBLIC_DETERMINISTIC_COMMANDS.map((name) => ({
|
|
108
|
+
kind: "deterministic",
|
|
109
|
+
name,
|
|
110
|
+
}));
|
|
111
|
+
return [...support, ...roles, ...deterministic];
|
|
112
|
+
}
|
|
113
|
+
export function isPublicCallableRole(value) {
|
|
114
|
+
return PUBLIC_CALLABLE_ROLES.includes(value);
|
|
115
|
+
}
|
|
116
|
+
export function isPublicConfigurableSeat(value) {
|
|
117
|
+
return PUBLIC_CONFIGURABLE_SEATS.includes(value);
|
|
118
|
+
}
|
|
119
|
+
export function isPublicCliSupportCommand(value) {
|
|
120
|
+
return PUBLIC_CLI_SUPPORT_COMMANDS.includes(value);
|
|
121
|
+
}
|
package/package.json
CHANGED
|
@@ -36,6 +36,13 @@ import {
|
|
|
36
36
|
engineSessionMaterialFromOptions,
|
|
37
37
|
type EngineSessionMaterial,
|
|
38
38
|
} from "./package-resources/engine-material.ts";
|
|
39
|
+
import {
|
|
40
|
+
formatModelSpec,
|
|
41
|
+
loadPublicCliConfig,
|
|
42
|
+
resolveMenxiaOfficerModelSelection,
|
|
43
|
+
type MenxiaOfficerSeat,
|
|
44
|
+
} from "./public-cli/config.ts";
|
|
45
|
+
import type { PublicThinkingLevel } from "./public-cli/registry.ts";
|
|
39
46
|
import { createReceiptDeliveryPolicy, NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, RECEIPT_DELIVERY_PROMPT, type NoReceiptLifecycleFacts } from "./receipt-delivery-policy.ts";
|
|
40
47
|
import { REVIEWER_VERIFICATION_BOUNDARY } from "./reviewer-construction.ts";
|
|
41
48
|
import type { ReviewerPromptText } from "./reviewer-prompt-identity.ts";
|
|
@@ -129,6 +136,11 @@ export type InheritedRuntimeOptions = {
|
|
|
129
136
|
readonly signal?: AbortSignal;
|
|
130
137
|
/** When true, wrap provider streams with ADR 0059 idle-only retry. */
|
|
131
138
|
readonly idleRetry?: boolean;
|
|
139
|
+
/**
|
|
140
|
+
* Explicit child model. When set, auth/availability failures do not fall back
|
|
141
|
+
* to the parent session model (#453 menxia overrides).
|
|
142
|
+
*/
|
|
143
|
+
readonly model?: Model<Api>;
|
|
132
144
|
};
|
|
133
145
|
|
|
134
146
|
export type InheritedRuntime = {
|
|
@@ -145,7 +157,8 @@ export type InheritedRuntime = {
|
|
|
145
157
|
*/
|
|
146
158
|
export async function createInheritedRuntime(options: InheritedRuntimeOptions): Promise<InheritedRuntime> {
|
|
147
159
|
const { ModelRuntime } = await import("@earendil-works/pi-coding-agent");
|
|
148
|
-
|
|
160
|
+
// Explicit override wins; never silent-fallback to parent when override is set (#453).
|
|
161
|
+
const activeModel = options.model ?? options.context.model;
|
|
149
162
|
if (activeModel === undefined) throw new Error(`${options.label} requires an active model`);
|
|
150
163
|
const dispatch = await prepareComplianceDispatch(activeModel, options.context, options.label);
|
|
151
164
|
const parentProvider = options.runCompletion === undefined
|
|
@@ -333,9 +346,11 @@ export async function createInheritedRuntime(options: InheritedRuntimeOptions):
|
|
|
333
346
|
return wrapped as ReturnType<Provider["stream"]>;
|
|
334
347
|
};
|
|
335
348
|
|
|
349
|
+
// Provider id must match activeModel.provider so ModelRuntime auth lookup
|
|
350
|
+
// finds this registration when a menxia override changes provider (#453).
|
|
336
351
|
const provider: Provider = options.idleRetry === true || options.runCompletion !== undefined
|
|
337
352
|
? {
|
|
338
|
-
id:
|
|
353
|
+
id: activeModel.provider,
|
|
339
354
|
name: parentProvider?.name ?? options.label,
|
|
340
355
|
auth: {
|
|
341
356
|
apiKey: {
|
|
@@ -716,8 +731,39 @@ export type AuditorRoleOptions = {
|
|
|
716
731
|
signal?: AbortSignal;
|
|
717
732
|
runCompletion?: AuditorCompletion;
|
|
718
733
|
retainResponse?(response: AssistantMessage): void;
|
|
734
|
+
/**
|
|
735
|
+
* Province seat identity only — shared executor owns config load, registry
|
|
736
|
+
* resolve, and loud auth/availability failure (#453 / ADR 0018).
|
|
737
|
+
*/
|
|
738
|
+
menxiaSeat?: MenxiaOfficerSeat;
|
|
719
739
|
};
|
|
720
740
|
|
|
741
|
+
/**
|
|
742
|
+
* Resolve province child model from public-cli persistent config (#453).
|
|
743
|
+
* Own override > gatekeeper override > unset (inherit parent). Availability
|
|
744
|
+
* failures throw — createInheritedRuntime must not silent-fallback to parent.
|
|
745
|
+
*/
|
|
746
|
+
async function resolveMenxiaSeatModelOptions(
|
|
747
|
+
context: ExtensionContext,
|
|
748
|
+
seat: MenxiaOfficerSeat,
|
|
749
|
+
roleLabel: string,
|
|
750
|
+
): Promise<{ model?: Model<Api>; thinkingLevel?: PublicThinkingLevel }> {
|
|
751
|
+
const selection = resolveMenxiaOfficerModelSelection(await loadPublicCliConfig(), seat);
|
|
752
|
+
if (selection === undefined) return {};
|
|
753
|
+
const find = context.modelRegistry.find?.bind(context.modelRegistry);
|
|
754
|
+
if (typeof find !== "function") {
|
|
755
|
+
throw new Error(`${roleLabel} model registry cannot resolve models`);
|
|
756
|
+
}
|
|
757
|
+
const model = find(selection.provider, selection.model);
|
|
758
|
+
if (model === undefined) {
|
|
759
|
+
throw new Error(`${roleLabel} model is unavailable: ${formatModelSpec(selection)}`);
|
|
760
|
+
}
|
|
761
|
+
return {
|
|
762
|
+
model,
|
|
763
|
+
...(selection.thinking === undefined ? {} : { thinkingLevel: selection.thinking }),
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
|
|
721
767
|
/**
|
|
722
768
|
* Auditor lifecycle via the shared in-process helper.
|
|
723
769
|
* Adapter keeps role label / soul / decision tool / result projection only.
|
|
@@ -730,10 +776,19 @@ export async function executeAuditorChild(
|
|
|
730
776
|
const { createRecordSession } = await import("./archivist-record-entry.ts");
|
|
731
777
|
|
|
732
778
|
return withInProcessScratch({ prefix: "ak-auditor-role-" }, async (scratch) => {
|
|
779
|
+
const menxia =
|
|
780
|
+
options.menxiaSeat === undefined
|
|
781
|
+
? {}
|
|
782
|
+
: await resolveMenxiaSeatModelOptions(
|
|
783
|
+
options.context,
|
|
784
|
+
options.menxiaSeat,
|
|
785
|
+
options.roleLabel,
|
|
786
|
+
);
|
|
733
787
|
const inherited = await createInheritedRuntime({
|
|
734
788
|
context: options.context,
|
|
735
789
|
label: options.roleLabel,
|
|
736
790
|
idleRetry: true,
|
|
791
|
+
...(menxia.model === undefined ? {} : { model: menxia.model }),
|
|
737
792
|
...(options.runCompletion === undefined
|
|
738
793
|
? {}
|
|
739
794
|
: { runCompletion: options.runCompletion, injectedSystemPrompt: options.systemPrompt }),
|
|
@@ -792,7 +847,7 @@ export async function executeAuditorChild(
|
|
|
792
847
|
cwd,
|
|
793
848
|
agentDir: scratch,
|
|
794
849
|
model: inherited.model,
|
|
795
|
-
thinkingLevel: options.context.thinkingLevel ?? "off",
|
|
850
|
+
thinkingLevel: menxia.thinkingLevel ?? options.context.thinkingLevel ?? "off",
|
|
796
851
|
modelRuntime: inherited.runtime,
|
|
797
852
|
systemPrompt: options.systemPrompt,
|
|
798
853
|
customTools: [{ ...options.dossierTool, label: options.roleLabel }, tool],
|
package/src/gatekeeper-role.ts
CHANGED
|
@@ -215,9 +215,11 @@ export async function runGatekeeper(options: RunGatekeeperOptions): Promise<Gate
|
|
|
215
215
|
const loadSoul = options.loadSoul ?? defaultLoadSoul;
|
|
216
216
|
let provinceRun: Awaited<ReturnType<typeof executeAuditorChild>>;
|
|
217
217
|
try {
|
|
218
|
+
// Seat identity only — shared executor owns model config/registry/auth (#453 / ADR 0018).
|
|
218
219
|
provinceRun = await executeAuditorChild({
|
|
219
220
|
context: options.context,
|
|
220
221
|
roleLabel: "Gatekeeper",
|
|
222
|
+
menxiaSeat: "gatekeeper",
|
|
221
223
|
systemPrompt: `${await loadSoul("gatekeeper")}\n\n${INVOCATION_OVERLAY}`,
|
|
222
224
|
prompt: "Read the admitted subject with ak_gatekeeper_subject, then dispatch it or submit typed incomplete.",
|
|
223
225
|
tool: createGatekeeperOutputTool(),
|
|
@@ -236,9 +238,11 @@ export async function runGatekeeper(options: RunGatekeeperOptions): Promise<Gate
|
|
|
236
238
|
|
|
237
239
|
const officer = province.officer;
|
|
238
240
|
try {
|
|
241
|
+
const roleLabel = officer === "inspector" ? "Inspector" : "Notary";
|
|
239
242
|
const officerRun = await executeAuditorChild({
|
|
240
243
|
context: options.context,
|
|
241
|
-
roleLabel
|
|
244
|
+
roleLabel,
|
|
245
|
+
menxiaSeat: officer,
|
|
242
246
|
systemPrompt: `${await loadSoul(officer)}\n\n${INVOCATION_OVERLAY}`,
|
|
243
247
|
prompt: "Read the admitted subject with ak_gatekeeper_subject, then submit one typed decision on only your assigned axes.",
|
|
244
248
|
tool: createOfficerDecisionTool(officer === "inspector" ? INSPECTOR_OUTPUT_TOOL : NOTARY_OUTPUT_TOOL),
|
package/src/public-cli/cli.ts
CHANGED
|
@@ -9,14 +9,17 @@ import {
|
|
|
9
9
|
assertLegalEngineName,
|
|
10
10
|
} from "../package-resources/engine-material.ts";
|
|
11
11
|
import {
|
|
12
|
+
clearPersistentSeatConfig,
|
|
12
13
|
effectiveSeatConfigurations,
|
|
13
14
|
formatModelSpec,
|
|
14
15
|
isEngineAxisSeat,
|
|
16
|
+
isMenxiaOfficerSeat,
|
|
15
17
|
loadCredentialProviders,
|
|
16
18
|
loadPublicCliConfig,
|
|
17
19
|
parseModelSpec,
|
|
18
20
|
resolveEffectiveSeat,
|
|
19
21
|
savePublicCliConfig,
|
|
22
|
+
seatModelOnly,
|
|
20
23
|
setAutoResumeLimit,
|
|
21
24
|
setPersistentSeatConfig,
|
|
22
25
|
setPersistentSeatEngine,
|
|
@@ -63,8 +66,8 @@ import { runPublicReviewer, runPublicReviewerResume } from "./reviewer-run.ts";
|
|
|
63
66
|
import { runPublicAnalyst } from "./analyst-run.ts";
|
|
64
67
|
import { AUTO_RESUME_LIMIT, peekRoleRunRole } from "./run-lifecycle.ts";
|
|
65
68
|
import {
|
|
66
|
-
AUTOMATIC_NAVIGATOR_SEAT,
|
|
67
69
|
INTERNAL_ROLE_ENTRYPOINT_RELATIVE,
|
|
70
|
+
isAutomaticConfigurableSeat,
|
|
68
71
|
isPublicCallableRole,
|
|
69
72
|
isPublicCliSupportCommand,
|
|
70
73
|
isPublicConfigurableSeat,
|
|
@@ -351,15 +354,15 @@ function requireLegalEngineName(name: string): string {
|
|
|
351
354
|
|
|
352
355
|
/**
|
|
353
356
|
* Persistent engine axis gate (#391 E1): PUBLIC_CALLABLE_ROLES only.
|
|
354
|
-
*
|
|
357
|
+
* Automatic seats are configurable for model but have no independent activation path.
|
|
355
358
|
*/
|
|
356
359
|
function requireEngineAxisSeat(
|
|
357
360
|
seat: string,
|
|
358
361
|
verb: "set-engine" | "unset-engine",
|
|
359
362
|
): asserts seat is PublicCallableRole {
|
|
360
|
-
if (seat
|
|
363
|
+
if (isAutomaticConfigurableSeat(seat)) {
|
|
361
364
|
throw new CliUsageError(
|
|
362
|
-
`config ${verb} refuses
|
|
365
|
+
`config ${verb} refuses ${seat}: no independent activation path; storing would be silently ineffective`,
|
|
363
366
|
);
|
|
364
367
|
}
|
|
365
368
|
if (!isEngineAxisSeat(seat)) {
|
|
@@ -478,7 +481,7 @@ function renderHelp(): string {
|
|
|
478
481
|
lines.push(
|
|
479
482
|
"",
|
|
480
483
|
"Role options: ak-role help <command>",
|
|
481
|
-
"Persistent config: ak-role config set <seat> <provider/model[:thinking]>",
|
|
484
|
+
"Persistent config: ak-role config set <seat> <provider/model[:thinking]> | unset <gatekeeper|inspector|notary>",
|
|
482
485
|
"Persistent engine (callable roles): ak-role config set-engine <seat> <name> | unset-engine <seat>",
|
|
483
486
|
"Effective seats: ak-role roles",
|
|
484
487
|
);
|
|
@@ -521,6 +524,15 @@ function renderRoles(seats: readonly EffectiveSeat[]): string {
|
|
|
521
524
|
return `${lines.join("\n")}\n`;
|
|
522
525
|
}
|
|
523
526
|
|
|
527
|
+
function renderPersistentSeatModel(selection: {
|
|
528
|
+
provider?: string;
|
|
529
|
+
model?: string;
|
|
530
|
+
thinking?: PublicThinkingLevel;
|
|
531
|
+
}): string {
|
|
532
|
+
const model = seatModelOnly(selection);
|
|
533
|
+
return model === undefined ? "-" : formatModelSpec(model);
|
|
534
|
+
}
|
|
535
|
+
|
|
524
536
|
function renderConfig(config: PublicCliConfig): string {
|
|
525
537
|
const lines: string[] = ["seat\tmodel\tengine"];
|
|
526
538
|
const keys = Object.keys(config.seats) as (keyof typeof config.seats)[];
|
|
@@ -531,7 +543,7 @@ function renderConfig(config: PublicCliConfig): string {
|
|
|
531
543
|
const selection = config.seats[seat];
|
|
532
544
|
if (selection === undefined) continue;
|
|
533
545
|
const engine = selection.engine === undefined ? "-" : selection.engine;
|
|
534
|
-
lines.push(`${seat}\t${
|
|
546
|
+
lines.push(`${seat}\t${renderPersistentSeatModel(selection)}\t${engine}`);
|
|
535
547
|
}
|
|
536
548
|
}
|
|
537
549
|
// #422: show the effective auto-resume ceiling (configured value or default).
|
|
@@ -557,7 +569,9 @@ async function runConfigCommand(
|
|
|
557
569
|
} else {
|
|
558
570
|
const engine =
|
|
559
571
|
selection.engine === undefined ? "-" : selection.engine;
|
|
560
|
-
io.stdout(
|
|
572
|
+
io.stdout(
|
|
573
|
+
`${args[1]}\t${renderPersistentSeatModel(selection)}\t${engine}\n`,
|
|
574
|
+
);
|
|
561
575
|
}
|
|
562
576
|
return 0;
|
|
563
577
|
}
|
|
@@ -594,6 +608,28 @@ async function runConfigCommand(
|
|
|
594
608
|
return 0;
|
|
595
609
|
}
|
|
596
610
|
|
|
611
|
+
// #453: clear menxia officer model override only (engine axis preserved).
|
|
612
|
+
if (args[0] === "unset") {
|
|
613
|
+
if (args.length !== 2) {
|
|
614
|
+
throw new CliUsageError(
|
|
615
|
+
"usage: ak-role config unset <gatekeeper|inspector|notary>",
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
const seat = args[1]!;
|
|
619
|
+
if (!isMenxiaOfficerSeat(seat)) {
|
|
620
|
+
throw new CliUsageError(
|
|
621
|
+
`config unset serves menxia officer overrides only (gatekeeper|inspector|notary); got ${seat}`,
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
const config = clearPersistentSeatConfig(
|
|
625
|
+
await loadAndValidateConfig(home, packageRoot),
|
|
626
|
+
seat,
|
|
627
|
+
);
|
|
628
|
+
await savePublicCliConfig(config, home);
|
|
629
|
+
io.stdout(renderConfig(config));
|
|
630
|
+
return 0;
|
|
631
|
+
}
|
|
632
|
+
|
|
597
633
|
if (args[0] === "set-engine") {
|
|
598
634
|
if (args.length !== 3) {
|
|
599
635
|
throw new CliUsageError(
|