@akagilnc/pi-workflow-roles 0.1.3712 → 0.1.3716

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 CHANGED
@@ -59,10 +59,14 @@ ak-role config unset-engine judge
59
59
  ak-role config set-host judge grok-build
60
60
  ak-role config unset-host judge
61
61
  ak-role config set-auto-resume-limit 3
62
+ ak-role config set-provider-alias xai hermes xai-oauth
63
+ ak-role config unset-provider-alias xai hermes
62
64
  ```
63
65
 
64
66
  **Host axis (invocation-insensible after default):** `--host` is a global public option on every callable role and on `resume`. Resolution is invocation `--host` → persistent seat host (`config set-host`) → package default (`pi`). After `config set-host <seat> <name>`, the same command face used with Pi runs that seat on the named host with zero extra flags and zero caller-side changes; bare `resume` follows the same table. All public callable roles and their institutional sub-legs (soul audit, doctor audit, navigator, reviewer evidence children) are host-neutral on the shared in-process institutional session seam.
65
67
 
68
+ **Provider host alias (#778):** seat rows keep one provider name; `config set-provider-alias <provider> <host> <alias>` registers an owner map so the same row hands the host the name it recognizes. Unregistered pairs pass the seat provider through unchanged — the package never invents pairs.
69
+
66
70
  For Gate officers (`gatekeeper` / `inspector` / `notary`) resolution is officer pin → province (`gatekeeper`) pin → inherit parent session; an explicit selection that fails is loud and does not fall back. Configuration usage and refusal text are owned by `ak-role config` / `ak-role help config`. The persistent file is machine-wide and shared across CLI builds: seat keys this build does not know are skipped on read (not an error); unknown field-level keys on known seats keep their existing tolerance.
67
71
 
68
72
  Receipts are typed, so callers compose roles without parsing prose; ordering and stopping stay caller-owned ([ADR 0010](docs/adr/0010-callers-own-role-composition-and-repetition.md)). Programmatic consumers derive contracts from the exported schemas in `src/package-contracts/`, not from this guide.
package/README.zh-CN.md CHANGED
@@ -59,10 +59,14 @@ ak-role config unset-engine judge
59
59
  ak-role config set-host judge grok-build
60
60
  ak-role config unset-host judge
61
61
  ak-role config set-auto-resume-limit 3
62
+ ak-role config set-provider-alias xai hermes xai-oauth
63
+ ak-role config unset-provider-alias xai hermes
62
64
  ```
63
65
 
64
66
  **宿主轴(配置默认 host 后调用无感):** `--host` 为全局公开旗,全部可调用角色与 `resume` 受理。解析序为调用 `--host` → 席位持久 host(`config set-host`)→ 包默认(`pi`)。`config set-host <seat> <name>` 之后,与 Pi 完全相同的命令面即可在该席跑命名宿主——零额外旗、零调用侧改动;裸 `resume` 同序取表。全部 public callable 角色及其机构子腿(审刑审计、太医审计、游奕使、御史台证据子腿)在共享进程内机构子会话接缝上均为宿主中立。
65
67
 
68
+ **Provider 宿主别名(#778):** 席位行只写一份 provider 名;`config set-provider-alias <provider> <host> <alias>` 由 owner 在席位表登记映射,使同一行在指定宿主下交出该宿主认得的名。未登记的 (provider, host) 原样透传——包内不内置任何对照。
69
+
66
70
  门下省官席解析顺序:官自钉 → 省钉(`gatekeeper`)→ 继承父 session;显式指定失败响亮、不回退。配置用法与拒绝文案以 `ak-role config`/`ak-role help config` 为准。持久配置是全机共享单文件、多 CLI 版本同读:本构建不认识的席位键读时跳过(不报错);已知席位上的未知字段沿用现行容忍。
67
71
 
68
72
  回执是 typed 的,调用者不必解析散文即可组合角色;顺序与停止归调用者([ADR 0010](docs/adr/0010-callers-own-role-composition-and-repetition.md))。编程消费者从 `src/package-contracts/` 导出推导契约,不从本文。
@@ -6074,14 +6074,17 @@ var init_option_definitions = __esm({
6074
6074
  "ak-role config unset-engine <seat>",
6075
6075
  "ak-role config set-host <seat> <name>",
6076
6076
  "ak-role config unset-host <seat>",
6077
- "ak-role config set-auto-resume-limit <N>"
6077
+ "ak-role config set-auto-resume-limit <N>",
6078
+ "ak-role config set-provider-alias <provider> <host> <alias>",
6079
+ "ak-role config unset-provider-alias <provider> <host>"
6078
6080
  ],
6079
6081
  examples: [
6080
6082
  "ak-role config set judge openai-codex/gpt-5.6-sol:high",
6081
6083
  "ak-role config unset gatekeeper",
6082
6084
  "ak-role config set-engine judge opus",
6083
6085
  "ak-role config set-host judge grok-build",
6084
- "ak-role config set-auto-resume-limit 3"
6086
+ "ak-role config set-auto-resume-limit 3",
6087
+ "ak-role config set-provider-alias xai hermes xai-oauth"
6085
6088
  ]
6086
6089
  },
6087
6090
  help: {
@@ -8819,6 +8822,7 @@ var init_institutional_resolution = __esm({
8819
8822
  var config_exports = {};
8820
8823
  __export(config_exports, {
8821
8824
  GATE_OFFICER_SEATS: () => GATE_OFFICER_SEATS,
8825
+ applyProviderHostAlias: () => applyProviderHostAlias,
8822
8826
  buildSeatModelCliArgs: () => buildSeatModelCliArgs2,
8823
8827
  clearPersistentSeatConfig: () => clearPersistentSeatConfig,
8824
8828
  credentialProvidersFromAuthData: () => credentialProvidersFromAuthData,
@@ -8839,6 +8843,8 @@ __export(config_exports, {
8839
8843
  setPersistentSeatConfig: () => setPersistentSeatConfig,
8840
8844
  setPersistentSeatEngine: () => setPersistentSeatEngine,
8841
8845
  setPersistentSeatHost: () => setPersistentSeatHost,
8846
+ setProviderHostAlias: () => setProviderHostAlias,
8847
+ unsetProviderHostAlias: () => unsetProviderHostAlias,
8842
8848
  validatePublicCliConfigAxes: () => validatePublicCliConfigAxes
8843
8849
  });
8844
8850
  import { mkdir as mkdir2, readFile as readFile10, writeFile as writeFile5 } from "node:fs/promises";
@@ -8966,6 +8972,50 @@ function parseAutoResumeLimit(value) {
8966
8972
  function setAutoResumeLimit(config, limit) {
8967
8973
  return { ...config, autoResumeLimit: parseAutoResumeLimit(limit) };
8968
8974
  }
8975
+ function requireNonEmptyToken(value, label) {
8976
+ const trimmed = value.trim();
8977
+ if (trimmed === "") {
8978
+ throw new Error(`${label} must be a non-empty string`);
8979
+ }
8980
+ return trimmed;
8981
+ }
8982
+ function setProviderHostAlias(config, provider, host, alias) {
8983
+ const from = requireNonEmptyToken(provider, "provider");
8984
+ const hostName = requireNonEmptyToken(host, "host");
8985
+ const to = requireNonEmptyToken(alias, "alias");
8986
+ const previous = config.providerAliases ?? {};
8987
+ return {
8988
+ ...config,
8989
+ providerAliases: {
8990
+ ...previous,
8991
+ [from]: {
8992
+ ...previous[from] ?? {},
8993
+ [hostName]: to
8994
+ }
8995
+ }
8996
+ };
8997
+ }
8998
+ function unsetProviderHostAlias(config, provider, host) {
8999
+ const from = requireNonEmptyToken(provider, "provider");
9000
+ const hostName = requireNonEmptyToken(host, "host");
9001
+ const previous = config.providerAliases;
9002
+ if (previous === void 0 || previous[from] === void 0) return config;
9003
+ const { [hostName]: _dropped, ...restHosts } = previous[from];
9004
+ const nextForProvider = Object.keys(restHosts).length === 0 ? void 0 : restHosts;
9005
+ const { [from]: _provider, ...restProviders } = previous;
9006
+ const nextAliases = nextForProvider === void 0 ? restProviders : { ...restProviders, [from]: nextForProvider };
9007
+ if (Object.keys(nextAliases).length === 0) {
9008
+ const { providerAliases: _gone, ...rest } = config;
9009
+ return rest;
9010
+ }
9011
+ return { ...config, providerAliases: nextAliases };
9012
+ }
9013
+ function applyProviderHostAlias(selection, host, aliases) {
9014
+ if (selection === void 0 || aliases === void 0) return selection;
9015
+ const mapped = aliases[selection.provider]?.[host];
9016
+ if (mapped === void 0) return selection;
9017
+ return { ...selection, provider: mapped };
9018
+ }
8969
9019
  function validatePublicCliConfigAxes(config, _packageRoot) {
8970
9020
  for (const seat of Object.keys(config.seats)) {
8971
9021
  const row = config.seats[seat];
@@ -9035,9 +9085,44 @@ function serializePublicCliConfig(config) {
9035
9085
  ...config.unknownSeats ?? {},
9036
9086
  ...config.seats
9037
9087
  },
9038
- ...config.autoResumeLimit === void 0 ? {} : { autoResumeLimit: config.autoResumeLimit }
9088
+ ...config.autoResumeLimit === void 0 ? {} : { autoResumeLimit: config.autoResumeLimit },
9089
+ ...config.providerAliases === void 0 || Object.keys(config.providerAliases).length === 0 ? {} : { providerAliases: config.providerAliases }
9039
9090
  };
9040
9091
  }
9092
+ function parseProviderHostAliases(value) {
9093
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
9094
+ throw new Error("public CLI config.providerAliases must be an object");
9095
+ }
9096
+ const out = {};
9097
+ for (const [provider, byHost] of Object.entries(value)) {
9098
+ if (provider.trim() === "") {
9099
+ throw new Error("public CLI config.providerAliases provider key must be non-empty");
9100
+ }
9101
+ if (byHost === null || typeof byHost !== "object" || Array.isArray(byHost)) {
9102
+ throw new Error(
9103
+ `public CLI config.providerAliases[${provider}] must be an object`
9104
+ );
9105
+ }
9106
+ const hosts = {};
9107
+ for (const [host, alias] of Object.entries(byHost)) {
9108
+ if (host.trim() === "") {
9109
+ throw new Error(
9110
+ `public CLI config.providerAliases[${provider}] host key must be non-empty`
9111
+ );
9112
+ }
9113
+ if (typeof alias !== "string" || alias.trim() === "") {
9114
+ throw new Error(
9115
+ `public CLI config.providerAliases[${provider}][${host}] must be a non-empty string`
9116
+ );
9117
+ }
9118
+ hosts[host] = alias;
9119
+ }
9120
+ if (Object.keys(hosts).length > 0) {
9121
+ out[provider] = hosts;
9122
+ }
9123
+ }
9124
+ return out;
9125
+ }
9041
9126
  function parsePublicCliConfig(value) {
9042
9127
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
9043
9128
  throw new Error("public CLI config must be an object");
@@ -9047,6 +9132,13 @@ function parsePublicCliConfig(value) {
9047
9132
  if (record4.autoResumeLimit !== void 0) {
9048
9133
  autoResumeLimit = parseAutoResumeLimit(record4.autoResumeLimit);
9049
9134
  }
9135
+ let providerAliases;
9136
+ if (record4.providerAliases !== void 0) {
9137
+ const parsed = parseProviderHostAliases(record4.providerAliases);
9138
+ if (Object.keys(parsed).length > 0) {
9139
+ providerAliases = parsed;
9140
+ }
9141
+ }
9050
9142
  const unknownSeats = {};
9051
9143
  if (record4.unknownSeats !== void 0) {
9052
9144
  if (record4.unknownSeats === null || typeof record4.unknownSeats !== "object" || Array.isArray(record4.unknownSeats)) {
@@ -9057,6 +9149,7 @@ function parsePublicCliConfig(value) {
9057
9149
  const withOpaque = (seats2) => ({
9058
9150
  seats: seats2,
9059
9151
  ...autoResumeLimit === void 0 ? {} : { autoResumeLimit },
9152
+ ...providerAliases === void 0 ? {} : { providerAliases },
9060
9153
  ...Object.keys(unknownSeats).length === 0 ? {} : { unknownSeats }
9061
9154
  });
9062
9155
  if (record4.seats === void 0) {
@@ -9233,7 +9326,21 @@ function resolveEffectiveSeat(config, seat, credentials, invocation) {
9233
9326
  };
9234
9327
  }
9235
9328
  }
9236
- return attachHostAxis(attachEngineAxis(modelSeat, config, invocation), config, invocation);
9329
+ const withAxes = attachHostAxis(
9330
+ attachEngineAxis(modelSeat, config, invocation),
9331
+ config,
9332
+ invocation
9333
+ );
9334
+ const selection = applyProviderHostAlias(
9335
+ withAxes.selection,
9336
+ withAxes.host,
9337
+ config.providerAliases
9338
+ );
9339
+ if (selection === void 0) {
9340
+ const { selection: _dropped, ...rest } = withAxes;
9341
+ return rest;
9342
+ }
9343
+ return { ...withAxes, selection };
9237
9344
  }
9238
9345
  function effectiveSeatConfigurations(config, credentials, invocation) {
9239
9346
  return PUBLIC_CONFIGURABLE_SEATS.map(
@@ -139,6 +139,50 @@ function parseAutoResumeLimit(value) {
139
139
  function setAutoResumeLimit(config, limit) {
140
140
  return { ...config, autoResumeLimit: parseAutoResumeLimit(limit) };
141
141
  }
142
+ function requireNonEmptyToken(value, label) {
143
+ const trimmed = value.trim();
144
+ if (trimmed === "") {
145
+ throw new Error(`${label} must be a non-empty string`);
146
+ }
147
+ return trimmed;
148
+ }
149
+ function setProviderHostAlias(config, provider, host, alias) {
150
+ const from = requireNonEmptyToken(provider, "provider");
151
+ const hostName = requireNonEmptyToken(host, "host");
152
+ const to = requireNonEmptyToken(alias, "alias");
153
+ const previous = config.providerAliases ?? {};
154
+ return {
155
+ ...config,
156
+ providerAliases: {
157
+ ...previous,
158
+ [from]: {
159
+ ...previous[from] ?? {},
160
+ [hostName]: to
161
+ }
162
+ }
163
+ };
164
+ }
165
+ function unsetProviderHostAlias(config, provider, host) {
166
+ const from = requireNonEmptyToken(provider, "provider");
167
+ const hostName = requireNonEmptyToken(host, "host");
168
+ const previous = config.providerAliases;
169
+ if (previous === void 0 || previous[from] === void 0) return config;
170
+ const { [hostName]: _dropped, ...restHosts } = previous[from];
171
+ const nextForProvider = Object.keys(restHosts).length === 0 ? void 0 : restHosts;
172
+ const { [from]: _provider, ...restProviders } = previous;
173
+ const nextAliases = nextForProvider === void 0 ? restProviders : { ...restProviders, [from]: nextForProvider };
174
+ if (Object.keys(nextAliases).length === 0) {
175
+ const { providerAliases: _gone, ...rest } = config;
176
+ return rest;
177
+ }
178
+ return { ...config, providerAliases: nextAliases };
179
+ }
180
+ function applyProviderHostAlias(selection, host, aliases) {
181
+ if (selection === void 0 || aliases === void 0) return selection;
182
+ const mapped = aliases[selection.provider]?.[host];
183
+ if (mapped === void 0) return selection;
184
+ return { ...selection, provider: mapped };
185
+ }
142
186
  function validatePublicCliConfigAxes(config, _packageRoot) {
143
187
  for (const seat of Object.keys(config.seats)) {
144
188
  const row = config.seats[seat];
@@ -208,9 +252,44 @@ function serializePublicCliConfig(config) {
208
252
  ...config.unknownSeats ?? {},
209
253
  ...config.seats
210
254
  },
211
- ...config.autoResumeLimit === void 0 ? {} : { autoResumeLimit: config.autoResumeLimit }
255
+ ...config.autoResumeLimit === void 0 ? {} : { autoResumeLimit: config.autoResumeLimit },
256
+ ...config.providerAliases === void 0 || Object.keys(config.providerAliases).length === 0 ? {} : { providerAliases: config.providerAliases }
212
257
  };
213
258
  }
259
+ function parseProviderHostAliases(value) {
260
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
261
+ throw new Error("public CLI config.providerAliases must be an object");
262
+ }
263
+ const out = {};
264
+ for (const [provider, byHost] of Object.entries(value)) {
265
+ if (provider.trim() === "") {
266
+ throw new Error("public CLI config.providerAliases provider key must be non-empty");
267
+ }
268
+ if (byHost === null || typeof byHost !== "object" || Array.isArray(byHost)) {
269
+ throw new Error(
270
+ `public CLI config.providerAliases[${provider}] must be an object`
271
+ );
272
+ }
273
+ const hosts = {};
274
+ for (const [host, alias] of Object.entries(byHost)) {
275
+ if (host.trim() === "") {
276
+ throw new Error(
277
+ `public CLI config.providerAliases[${provider}] host key must be non-empty`
278
+ );
279
+ }
280
+ if (typeof alias !== "string" || alias.trim() === "") {
281
+ throw new Error(
282
+ `public CLI config.providerAliases[${provider}][${host}] must be a non-empty string`
283
+ );
284
+ }
285
+ hosts[host] = alias;
286
+ }
287
+ if (Object.keys(hosts).length > 0) {
288
+ out[provider] = hosts;
289
+ }
290
+ }
291
+ return out;
292
+ }
214
293
  function parsePublicCliConfig(value) {
215
294
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
216
295
  throw new Error("public CLI config must be an object");
@@ -220,6 +299,13 @@ function parsePublicCliConfig(value) {
220
299
  if (record.autoResumeLimit !== void 0) {
221
300
  autoResumeLimit = parseAutoResumeLimit(record.autoResumeLimit);
222
301
  }
302
+ let providerAliases;
303
+ if (record.providerAliases !== void 0) {
304
+ const parsed = parseProviderHostAliases(record.providerAliases);
305
+ if (Object.keys(parsed).length > 0) {
306
+ providerAliases = parsed;
307
+ }
308
+ }
223
309
  const unknownSeats = {};
224
310
  if (record.unknownSeats !== void 0) {
225
311
  if (record.unknownSeats === null || typeof record.unknownSeats !== "object" || Array.isArray(record.unknownSeats)) {
@@ -230,6 +316,7 @@ function parsePublicCliConfig(value) {
230
316
  const withOpaque = (seats2) => ({
231
317
  seats: seats2,
232
318
  ...autoResumeLimit === void 0 ? {} : { autoResumeLimit },
319
+ ...providerAliases === void 0 ? {} : { providerAliases },
233
320
  ...Object.keys(unknownSeats).length === 0 ? {} : { unknownSeats }
234
321
  });
235
322
  if (record.seats === void 0) {
@@ -406,7 +493,21 @@ function resolveEffectiveSeat(config, seat, credentials, invocation) {
406
493
  };
407
494
  }
408
495
  }
409
- return attachHostAxis(attachEngineAxis(modelSeat, config, invocation), config, invocation);
496
+ const withAxes = attachHostAxis(
497
+ attachEngineAxis(modelSeat, config, invocation),
498
+ config,
499
+ invocation
500
+ );
501
+ const selection = applyProviderHostAlias(
502
+ withAxes.selection,
503
+ withAxes.host,
504
+ config.providerAliases
505
+ );
506
+ if (selection === void 0) {
507
+ const { selection: _dropped, ...rest } = withAxes;
508
+ return rest;
509
+ }
510
+ return { ...withAxes, selection };
410
511
  }
411
512
  function effectiveSeatConfigurations(config, credentials, invocation) {
412
513
  return PUBLIC_CONFIGURABLE_SEATS.map(
@@ -439,6 +540,7 @@ async function loadCredentialProviders(agentDir) {
439
540
  }
440
541
  export {
441
542
  GATE_OFFICER_SEATS,
543
+ applyProviderHostAlias,
442
544
  buildSeatModelCliArgs,
443
545
  clearPersistentSeatConfig,
444
546
  credentialProvidersFromAuthData,
@@ -459,5 +561,7 @@ export {
459
561
  setPersistentSeatConfig,
460
562
  setPersistentSeatEngine,
461
563
  setPersistentSeatHost,
564
+ setProviderHostAlias,
565
+ unsetProviderHostAlias,
462
566
  validatePublicCliConfigAxes
463
567
  };
@@ -15584,6 +15584,50 @@ function parseAutoResumeLimit(value) {
15584
15584
  function setAutoResumeLimit(config, limit) {
15585
15585
  return { ...config, autoResumeLimit: parseAutoResumeLimit(limit) };
15586
15586
  }
15587
+ function requireNonEmptyToken(value, label) {
15588
+ const trimmed = value.trim();
15589
+ if (trimmed === "") {
15590
+ throw new Error(`${label} must be a non-empty string`);
15591
+ }
15592
+ return trimmed;
15593
+ }
15594
+ function setProviderHostAlias(config, provider, host, alias) {
15595
+ const from = requireNonEmptyToken(provider, "provider");
15596
+ const hostName = requireNonEmptyToken(host, "host");
15597
+ const to = requireNonEmptyToken(alias, "alias");
15598
+ const previous = config.providerAliases ?? {};
15599
+ return {
15600
+ ...config,
15601
+ providerAliases: {
15602
+ ...previous,
15603
+ [from]: {
15604
+ ...previous[from] ?? {},
15605
+ [hostName]: to
15606
+ }
15607
+ }
15608
+ };
15609
+ }
15610
+ function unsetProviderHostAlias(config, provider, host) {
15611
+ const from = requireNonEmptyToken(provider, "provider");
15612
+ const hostName = requireNonEmptyToken(host, "host");
15613
+ const previous = config.providerAliases;
15614
+ if (previous === void 0 || previous[from] === void 0) return config;
15615
+ const { [hostName]: _dropped, ...restHosts } = previous[from];
15616
+ const nextForProvider = Object.keys(restHosts).length === 0 ? void 0 : restHosts;
15617
+ const { [from]: _provider, ...restProviders } = previous;
15618
+ const nextAliases = nextForProvider === void 0 ? restProviders : { ...restProviders, [from]: nextForProvider };
15619
+ if (Object.keys(nextAliases).length === 0) {
15620
+ const { providerAliases: _gone, ...rest } = config;
15621
+ return rest;
15622
+ }
15623
+ return { ...config, providerAliases: nextAliases };
15624
+ }
15625
+ function applyProviderHostAlias(selection, host, aliases) {
15626
+ if (selection === void 0 || aliases === void 0) return selection;
15627
+ const mapped = aliases[selection.provider]?.[host];
15628
+ if (mapped === void 0) return selection;
15629
+ return { ...selection, provider: mapped };
15630
+ }
15587
15631
  function validatePublicCliConfigAxes(config, _packageRoot) {
15588
15632
  for (const seat of Object.keys(config.seats)) {
15589
15633
  const row = config.seats[seat];
@@ -15643,9 +15687,44 @@ function serializePublicCliConfig(config) {
15643
15687
  ...config.unknownSeats ?? {},
15644
15688
  ...config.seats
15645
15689
  },
15646
- ...config.autoResumeLimit === void 0 ? {} : { autoResumeLimit: config.autoResumeLimit }
15690
+ ...config.autoResumeLimit === void 0 ? {} : { autoResumeLimit: config.autoResumeLimit },
15691
+ ...config.providerAliases === void 0 || Object.keys(config.providerAliases).length === 0 ? {} : { providerAliases: config.providerAliases }
15647
15692
  };
15648
15693
  }
15694
+ function parseProviderHostAliases(value) {
15695
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
15696
+ throw new Error("public CLI config.providerAliases must be an object");
15697
+ }
15698
+ const out = {};
15699
+ for (const [provider, byHost] of Object.entries(value)) {
15700
+ if (provider.trim() === "") {
15701
+ throw new Error("public CLI config.providerAliases provider key must be non-empty");
15702
+ }
15703
+ if (byHost === null || typeof byHost !== "object" || Array.isArray(byHost)) {
15704
+ throw new Error(
15705
+ `public CLI config.providerAliases[${provider}] must be an object`
15706
+ );
15707
+ }
15708
+ const hosts = {};
15709
+ for (const [host, alias] of Object.entries(byHost)) {
15710
+ if (host.trim() === "") {
15711
+ throw new Error(
15712
+ `public CLI config.providerAliases[${provider}] host key must be non-empty`
15713
+ );
15714
+ }
15715
+ if (typeof alias !== "string" || alias.trim() === "") {
15716
+ throw new Error(
15717
+ `public CLI config.providerAliases[${provider}][${host}] must be a non-empty string`
15718
+ );
15719
+ }
15720
+ hosts[host] = alias;
15721
+ }
15722
+ if (Object.keys(hosts).length > 0) {
15723
+ out[provider] = hosts;
15724
+ }
15725
+ }
15726
+ return out;
15727
+ }
15649
15728
  function parsePublicCliConfig(value) {
15650
15729
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
15651
15730
  throw new Error("public CLI config must be an object");
@@ -15655,6 +15734,13 @@ function parsePublicCliConfig(value) {
15655
15734
  if (record4.autoResumeLimit !== void 0) {
15656
15735
  autoResumeLimit = parseAutoResumeLimit(record4.autoResumeLimit);
15657
15736
  }
15737
+ let providerAliases;
15738
+ if (record4.providerAliases !== void 0) {
15739
+ const parsed = parseProviderHostAliases(record4.providerAliases);
15740
+ if (Object.keys(parsed).length > 0) {
15741
+ providerAliases = parsed;
15742
+ }
15743
+ }
15658
15744
  const unknownSeats = {};
15659
15745
  if (record4.unknownSeats !== void 0) {
15660
15746
  if (record4.unknownSeats === null || typeof record4.unknownSeats !== "object" || Array.isArray(record4.unknownSeats)) {
@@ -15665,6 +15751,7 @@ function parsePublicCliConfig(value) {
15665
15751
  const withOpaque = (seats2) => ({
15666
15752
  seats: seats2,
15667
15753
  ...autoResumeLimit === void 0 ? {} : { autoResumeLimit },
15754
+ ...providerAliases === void 0 ? {} : { providerAliases },
15668
15755
  ...Object.keys(unknownSeats).length === 0 ? {} : { unknownSeats }
15669
15756
  });
15670
15757
  if (record4.seats === void 0) {
@@ -15841,7 +15928,21 @@ function resolveEffectiveSeat(config, seat, credentials, invocation) {
15841
15928
  };
15842
15929
  }
15843
15930
  }
15844
- return attachHostAxis(attachEngineAxis(modelSeat, config, invocation), config, invocation);
15931
+ const withAxes = attachHostAxis(
15932
+ attachEngineAxis(modelSeat, config, invocation),
15933
+ config,
15934
+ invocation
15935
+ );
15936
+ const selection = applyProviderHostAlias(
15937
+ withAxes.selection,
15938
+ withAxes.host,
15939
+ config.providerAliases
15940
+ );
15941
+ if (selection === void 0) {
15942
+ const { selection: _dropped, ...rest } = withAxes;
15943
+ return rest;
15944
+ }
15945
+ return { ...withAxes, selection };
15845
15946
  }
15846
15947
  function effectiveSeatConfigurations(config, credentials, invocation) {
15847
15948
  return PUBLIC_CONFIGURABLE_SEATS.map(
@@ -19489,14 +19590,17 @@ var init_option_definitions = __esm({
19489
19590
  "ak-role config unset-engine <seat>",
19490
19591
  "ak-role config set-host <seat> <name>",
19491
19592
  "ak-role config unset-host <seat>",
19492
- "ak-role config set-auto-resume-limit <N>"
19593
+ "ak-role config set-auto-resume-limit <N>",
19594
+ "ak-role config set-provider-alias <provider> <host> <alias>",
19595
+ "ak-role config unset-provider-alias <provider> <host>"
19493
19596
  ],
19494
19597
  examples: [
19495
19598
  "ak-role config set judge openai-codex/gpt-5.6-sol:high",
19496
19599
  "ak-role config unset gatekeeper",
19497
19600
  "ak-role config set-engine judge opus",
19498
19601
  "ak-role config set-host judge grok-build",
19499
- "ak-role config set-auto-resume-limit 3"
19602
+ "ak-role config set-auto-resume-limit 3",
19603
+ "ak-role config set-provider-alias xai hermes xai-oauth"
19500
19604
  ]
19501
19605
  },
19502
19606
  help: {
@@ -31595,6 +31699,7 @@ function renderHelp() {
31595
31699
  "Persistent config: ak-role config set <seat> <provider/model[:thinking]> | unset <gatekeeper|inspector|notary>",
31596
31700
  "Persistent engine (callable roles): ak-role config set-engine <seat> <name> | unset-engine <seat>",
31597
31701
  "Persistent host (callable roles): ak-role config set-host <seat> <name> | unset-host <seat>",
31702
+ "Provider host alias: ak-role config set-provider-alias <provider> <host> <alias> | unset-provider-alias <provider> <host>",
31598
31703
  "Host resolution: --host \u2192 persistent seat host \u2192 pi (resume uses the same order; #617)",
31599
31704
  "Effective seats: ak-role roles"
31600
31705
  );
@@ -31694,6 +31799,15 @@ function renderConfig(config) {
31694
31799
  }
31695
31800
  }
31696
31801
  lines.push(`autoResumeLimit ${config.autoResumeLimit ?? AUTO_RESUME_LIMIT}`);
31802
+ const aliases = config.providerAliases;
31803
+ if (aliases !== void 0) {
31804
+ for (const provider of Object.keys(aliases).sort()) {
31805
+ const byHost = aliases[provider];
31806
+ for (const host of Object.keys(byHost).sort()) {
31807
+ lines.push(`providerAlias ${provider} ${host} ${byHost[host]}`);
31808
+ }
31809
+ }
31810
+ }
31697
31811
  return `${lines.join("\n")}
31698
31812
  `;
31699
31813
  }
@@ -31843,6 +31957,44 @@ async function runConfigCommand(args, home, packageRoot2, io) {
31843
31957
  io.stdout(renderConfig(config));
31844
31958
  return 0;
31845
31959
  }
31960
+ if (args[0] === "set-provider-alias") {
31961
+ if (args.length !== 4) {
31962
+ throw new CliUsageError(
31963
+ "usage: ak-role config set-provider-alias <provider> <host> <alias>"
31964
+ );
31965
+ }
31966
+ let config = await loadAndValidateConfig(home, packageRoot2);
31967
+ try {
31968
+ config = setProviderHostAlias(config, args[1], args[2], args[3]);
31969
+ } catch (error) {
31970
+ throw new CliUsageError(
31971
+ error instanceof Error ? error.message : String(error),
31972
+ { cause: error }
31973
+ );
31974
+ }
31975
+ await savePublicCliConfig(config, home);
31976
+ io.stdout(renderConfig(config));
31977
+ return 0;
31978
+ }
31979
+ if (args[0] === "unset-provider-alias") {
31980
+ if (args.length !== 3) {
31981
+ throw new CliUsageError(
31982
+ "usage: ak-role config unset-provider-alias <provider> <host>"
31983
+ );
31984
+ }
31985
+ let config = await loadAndValidateConfig(home, packageRoot2);
31986
+ try {
31987
+ config = unsetProviderHostAlias(config, args[1], args[2]);
31988
+ } catch (error) {
31989
+ throw new CliUsageError(
31990
+ error instanceof Error ? error.message : String(error),
31991
+ { cause: error }
31992
+ );
31993
+ }
31994
+ await savePublicCliConfig(config, home);
31995
+ io.stdout(renderConfig(config));
31996
+ return 0;
31997
+ }
31846
31998
  throw new CliUsageError(`unknown config subcommand: ${args[0]}`);
31847
31999
  }
31848
32000
  async function runAkRole(argv, env) {
@@ -1077,6 +1077,8 @@ const SUPPORT_COMMAND_HELP = {
1077
1077
  "ak-role config set-host <seat> <name>",
1078
1078
  "ak-role config unset-host <seat>",
1079
1079
  "ak-role config set-auto-resume-limit <N>",
1080
+ "ak-role config set-provider-alias <provider> <host> <alias>",
1081
+ "ak-role config unset-provider-alias <provider> <host>",
1080
1082
  ],
1081
1083
  examples: [
1082
1084
  "ak-role config set judge openai-codex/gpt-5.6-sol:high",
@@ -1084,6 +1086,7 @@ const SUPPORT_COMMAND_HELP = {
1084
1086
  "ak-role config set-engine judge opus",
1085
1087
  "ak-role config set-host judge grok-build",
1086
1088
  "ak-role config set-auto-resume-limit 3",
1089
+ "ak-role config set-provider-alias xai hermes xai-oauth",
1087
1090
  ],
1088
1091
  },
1089
1092
  help: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.3712",
3
+ "version": "0.1.3716",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -28,6 +28,8 @@ import {
28
28
  setPersistentSeatConfig,
29
29
  setPersistentSeatEngine,
30
30
  setPersistentSeatHost,
31
+ setProviderHostAlias,
32
+ unsetProviderHostAlias,
31
33
  validatePublicCliConfigAxes,
32
34
  type CredentialProviders,
33
35
  type EffectiveSeat,
@@ -769,6 +771,7 @@ function renderHelp(): string {
769
771
  "Persistent config: ak-role config set <seat> <provider/model[:thinking]> | unset <gatekeeper|inspector|notary>",
770
772
  "Persistent engine (callable roles): ak-role config set-engine <seat> <name> | unset-engine <seat>",
771
773
  "Persistent host (callable roles): ak-role config set-host <seat> <name> | unset-host <seat>",
774
+ "Provider host alias: ak-role config set-provider-alias <provider> <host> <alias> | unset-provider-alias <provider> <host>",
772
775
  "Host resolution: --host → persistent seat host → pi (resume uses the same order; #617)",
773
776
  "Effective seats: ak-role roles",
774
777
  );
@@ -909,6 +912,16 @@ function renderConfig(config: PublicCliConfig): string {
909
912
  }
910
913
  // #422: show the effective auto-resume ceiling (configured value or default).
911
914
  lines.push(`autoResumeLimit\t${config.autoResumeLimit ?? AUTO_RESUME_LIMIT}`);
915
+ // #778: owner-registered provider→host aliases (disk face; seats stay as written).
916
+ const aliases = config.providerAliases;
917
+ if (aliases !== undefined) {
918
+ for (const provider of Object.keys(aliases).sort()) {
919
+ const byHost = aliases[provider]!;
920
+ for (const host of Object.keys(byHost).sort()) {
921
+ lines.push(`providerAlias\t${provider}\t${host}\t${byHost[host]}`);
922
+ }
923
+ }
924
+ }
912
925
  return `${lines.join("\n")}\n`;
913
926
  }
914
927
 
@@ -1081,6 +1094,47 @@ async function runConfigCommand(
1081
1094
  return 0;
1082
1095
  }
1083
1096
 
1097
+ // #778: owner-registered provider→host alias (opaque strings; no host catalog).
1098
+ if (args[0] === "set-provider-alias") {
1099
+ if (args.length !== 4) {
1100
+ throw new CliUsageError(
1101
+ "usage: ak-role config set-provider-alias <provider> <host> <alias>",
1102
+ );
1103
+ }
1104
+ let config = await loadAndValidateConfig(home, packageRoot);
1105
+ try {
1106
+ config = setProviderHostAlias(config, args[1]!, args[2]!, args[3]!);
1107
+ } catch (error) {
1108
+ throw new CliUsageError(
1109
+ error instanceof Error ? error.message : String(error),
1110
+ { cause: error },
1111
+ );
1112
+ }
1113
+ await savePublicCliConfig(config, home);
1114
+ io.stdout(renderConfig(config));
1115
+ return 0;
1116
+ }
1117
+
1118
+ if (args[0] === "unset-provider-alias") {
1119
+ if (args.length !== 3) {
1120
+ throw new CliUsageError(
1121
+ "usage: ak-role config unset-provider-alias <provider> <host>",
1122
+ );
1123
+ }
1124
+ let config = await loadAndValidateConfig(home, packageRoot);
1125
+ try {
1126
+ config = unsetProviderHostAlias(config, args[1]!, args[2]!);
1127
+ } catch (error) {
1128
+ throw new CliUsageError(
1129
+ error instanceof Error ? error.message : String(error),
1130
+ { cause: error },
1131
+ );
1132
+ }
1133
+ await savePublicCliConfig(config, home);
1134
+ io.stdout(renderConfig(config));
1135
+ return 0;
1136
+ }
1137
+
1084
1138
  throw new CliUsageError(`unknown config subcommand: ${args[0]}`);
1085
1139
  }
1086
1140
 
@@ -54,6 +54,15 @@ export type PersistentSeatConfig = {
54
54
  host?: string;
55
55
  };
56
56
 
57
+ /**
58
+ * #778: owner-registered provider name by host. Keyed seat-table provider →
59
+ * host → host-facing provider. Package code carries the map only — no built-in
60
+ * pairs. Unregistered (provider, host) pairs pass the seat provider through.
61
+ */
62
+ export type ProviderHostAliases = Readonly<
63
+ Record<string, Readonly<Record<string, string>>>
64
+ >;
65
+
57
66
  export type PublicCliConfig = {
58
67
  seats: Partial<Record<PublicConfigurableSeat, PersistentSeatConfig>>;
59
68
  /**
@@ -63,6 +72,11 @@ export type PublicCliConfig = {
63
72
  * bound (ADR 0035).
64
73
  */
65
74
  autoResumeLimit?: number;
75
+ /**
76
+ * #778: owner-registered provider→host aliases. Sibling of `seats`; never
77
+ * invents pairs. Absent or empty means pure pass-through.
78
+ */
79
+ providerAliases?: ProviderHostAliases;
66
80
  /**
67
81
  * #592: opaque seat rows this build does not own. Carried through every
68
82
  * parse→save cycle so a write never silently erases neighboring-line rows
@@ -293,6 +307,81 @@ export function setAutoResumeLimit(
293
307
  return { ...config, autoResumeLimit: parseAutoResumeLimit(limit) };
294
308
  }
295
309
 
310
+ function requireNonEmptyToken(value: string, label: string): string {
311
+ const trimmed = value.trim();
312
+ if (trimmed === "") {
313
+ throw new Error(`${label} must be a non-empty string`);
314
+ }
315
+ return trimmed;
316
+ }
317
+
318
+ /**
319
+ * #778: register one owner provider→host alias. Opaque strings only — no host
320
+ * catalog check and no built-in provider pairs. Sibling keys (seats included)
321
+ * are preserved.
322
+ */
323
+ export function setProviderHostAlias(
324
+ config: PublicCliConfig,
325
+ provider: string,
326
+ host: string,
327
+ alias: string,
328
+ ): PublicCliConfig {
329
+ const from = requireNonEmptyToken(provider, "provider");
330
+ const hostName = requireNonEmptyToken(host, "host");
331
+ const to = requireNonEmptyToken(alias, "alias");
332
+ const previous = config.providerAliases ?? {};
333
+ return {
334
+ ...config,
335
+ providerAliases: {
336
+ ...previous,
337
+ [from]: {
338
+ ...(previous[from] ?? {}),
339
+ [hostName]: to,
340
+ },
341
+ },
342
+ };
343
+ }
344
+
345
+ /** #778: drop one owner provider→host alias. Empty map collapses to absent. */
346
+ export function unsetProviderHostAlias(
347
+ config: PublicCliConfig,
348
+ provider: string,
349
+ host: string,
350
+ ): PublicCliConfig {
351
+ const from = requireNonEmptyToken(provider, "provider");
352
+ const hostName = requireNonEmptyToken(host, "host");
353
+ const previous = config.providerAliases;
354
+ if (previous === undefined || previous[from] === undefined) return config;
355
+ const { [hostName]: _dropped, ...restHosts } = previous[from]!;
356
+ const nextForProvider =
357
+ Object.keys(restHosts).length === 0 ? undefined : restHosts;
358
+ const { [from]: _provider, ...restProviders } = previous;
359
+ const nextAliases =
360
+ nextForProvider === undefined
361
+ ? restProviders
362
+ : { ...restProviders, [from]: nextForProvider };
363
+ if (Object.keys(nextAliases).length === 0) {
364
+ const { providerAliases: _gone, ...rest } = config;
365
+ return rest;
366
+ }
367
+ return { ...config, providerAliases: nextAliases };
368
+ }
369
+
370
+ /**
371
+ * #778: project seat-table provider through owner aliases for one host.
372
+ * Unregistered pairs return the seat selection unchanged (pass-through).
373
+ */
374
+ export function applyProviderHostAlias(
375
+ selection: SeatModelConfig | undefined,
376
+ host: string,
377
+ aliases: ProviderHostAliases | undefined,
378
+ ): SeatModelConfig | undefined {
379
+ if (selection === undefined || aliases === undefined) return selection;
380
+ const mapped = aliases[selection.provider]?.[host];
381
+ if (mapped === undefined) return selection;
382
+ return { ...selection, provider: mapped };
383
+ }
384
+
296
385
  /**
297
386
  * Config-parse seam: persistent call axes belong to PUBLIC_CALLABLE_ROLES;
298
387
  * engine names need only path-safety syntax (no closed material catalog;
@@ -387,6 +476,7 @@ export function buildSeatModelCliArgs(model: SeatModelConfig | undefined): strin
387
476
  function serializePublicCliConfig(config: PublicCliConfig): {
388
477
  seats: Record<string, unknown>;
389
478
  autoResumeLimit?: number;
479
+ providerAliases?: ProviderHostAliases;
390
480
  } {
391
481
  return {
392
482
  // Known seats win on any key clash; by construction the two maps are
@@ -398,9 +488,48 @@ function serializePublicCliConfig(config: PublicCliConfig): {
398
488
  ...(config.autoResumeLimit === undefined
399
489
  ? {}
400
490
  : { autoResumeLimit: config.autoResumeLimit }),
491
+ ...(config.providerAliases === undefined ||
492
+ Object.keys(config.providerAliases).length === 0
493
+ ? {}
494
+ : { providerAliases: config.providerAliases }),
401
495
  };
402
496
  }
403
497
 
498
+ function parseProviderHostAliases(value: unknown): ProviderHostAliases {
499
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
500
+ throw new Error("public CLI config.providerAliases must be an object");
501
+ }
502
+ const out: Record<string, Record<string, string>> = {};
503
+ for (const [provider, byHost] of Object.entries(value as Record<string, unknown>)) {
504
+ if (provider.trim() === "") {
505
+ throw new Error("public CLI config.providerAliases provider key must be non-empty");
506
+ }
507
+ if (byHost === null || typeof byHost !== "object" || Array.isArray(byHost)) {
508
+ throw new Error(
509
+ `public CLI config.providerAliases[${provider}] must be an object`,
510
+ );
511
+ }
512
+ const hosts: Record<string, string> = {};
513
+ for (const [host, alias] of Object.entries(byHost as Record<string, unknown>)) {
514
+ if (host.trim() === "") {
515
+ throw new Error(
516
+ `public CLI config.providerAliases[${provider}] host key must be non-empty`,
517
+ );
518
+ }
519
+ if (typeof alias !== "string" || alias.trim() === "") {
520
+ throw new Error(
521
+ `public CLI config.providerAliases[${provider}][${host}] must be a non-empty string`,
522
+ );
523
+ }
524
+ hosts[host] = alias;
525
+ }
526
+ if (Object.keys(hosts).length > 0) {
527
+ out[provider] = hosts;
528
+ }
529
+ }
530
+ return out;
531
+ }
532
+
404
533
  function parsePublicCliConfig(value: unknown): PublicCliConfig {
405
534
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
406
535
  throw new Error("public CLI config must be an object");
@@ -408,6 +537,7 @@ function parsePublicCliConfig(value: unknown): PublicCliConfig {
408
537
  const record = value as {
409
538
  seats?: unknown;
410
539
  autoResumeLimit?: unknown;
540
+ providerAliases?: unknown;
411
541
  unknownSeats?: unknown;
412
542
  };
413
543
  // #422 round-trip preservation: the sibling top-level key must survive every
@@ -416,6 +546,15 @@ function parsePublicCliConfig(value: unknown): PublicCliConfig {
416
546
  if (record.autoResumeLimit !== undefined) {
417
547
  autoResumeLimit = parseAutoResumeLimit(record.autoResumeLimit);
418
548
  }
549
+ // #778: same survival duty as autoResumeLimit — owner aliases must not vanish
550
+ // on an unrelated seat write.
551
+ let providerAliases: ProviderHostAliases | undefined;
552
+ if (record.providerAliases !== undefined) {
553
+ const parsed = parseProviderHostAliases(record.providerAliases);
554
+ if (Object.keys(parsed).length > 0) {
555
+ providerAliases = parsed;
556
+ }
557
+ }
419
558
  // #592: opaque bucket may already be present on an in-memory round-trip
420
559
  // (load→set→save calls parse again). Carry it before seats iteration so a
421
560
  // disk-shaped seats map and a memory-shaped bucket both survive.
@@ -433,6 +572,7 @@ function parsePublicCliConfig(value: unknown): PublicCliConfig {
433
572
  const withOpaque = (seats: PublicCliConfig["seats"]): PublicCliConfig => ({
434
573
  seats,
435
574
  ...(autoResumeLimit === undefined ? {} : { autoResumeLimit }),
575
+ ...(providerAliases === undefined ? {} : { providerAliases }),
436
576
  ...(Object.keys(unknownSeats).length === 0 ? {} : { unknownSeats }),
437
577
  });
438
578
  if (record.seats === undefined) {
@@ -678,7 +818,23 @@ export function resolveEffectiveSeat(
678
818
  }
679
819
  }
680
820
 
681
- return attachHostAxis(attachEngineAxis(modelSeat, config, invocation), config, invocation);
821
+ const withAxes = attachHostAxis(
822
+ attachEngineAxis(modelSeat, config, invocation),
823
+ config,
824
+ invocation,
825
+ );
826
+ // #778: host-facing provider is the seat-table value after owner aliases.
827
+ // Disk seats stay as written; only the effective selection is projected.
828
+ const selection = applyProviderHostAlias(
829
+ withAxes.selection,
830
+ withAxes.host,
831
+ config.providerAliases,
832
+ );
833
+ if (selection === undefined) {
834
+ const { selection: _dropped, ...rest } = withAxes;
835
+ return rest;
836
+ }
837
+ return { ...withAxes, selection };
682
838
  }
683
839
 
684
840
  export function effectiveSeatConfigurations(
@@ -1307,6 +1307,8 @@ const SUPPORT_COMMAND_HELP = {
1307
1307
  "ak-role config set-host <seat> <name>",
1308
1308
  "ak-role config unset-host <seat>",
1309
1309
  "ak-role config set-auto-resume-limit <N>",
1310
+ "ak-role config set-provider-alias <provider> <host> <alias>",
1311
+ "ak-role config unset-provider-alias <provider> <host>",
1310
1312
  ],
1311
1313
  examples: [
1312
1314
  "ak-role config set judge openai-codex/gpt-5.6-sol:high",
@@ -1314,6 +1316,7 @@ const SUPPORT_COMMAND_HELP = {
1314
1316
  "ak-role config set-engine judge opus",
1315
1317
  "ak-role config set-host judge grok-build",
1316
1318
  "ak-role config set-auto-resume-limit 3",
1319
+ "ak-role config set-provider-alias xai hermes xai-oauth",
1317
1320
  ],
1318
1321
  },
1319
1322
  help: {