privateboard 0.1.22 → 0.1.23

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/server.js CHANGED
@@ -700,6 +700,22 @@ CREATE INDEX IF NOT EXISTS idx_qd_archive_room_cell
700
700
  }
701
701
  });
702
702
 
703
+ // src/storage/migrations/039_remap_removed_models.sql
704
+ var remap_removed_models_default;
705
+ var init_remap_removed_models = __esm({
706
+ "src/storage/migrations/039_remap_removed_models.sql"() {
707
+ remap_removed_models_default = "-- 039_remap_removed_models.sql\n--\n-- Remap stored modelV strings for the six modelVs deleted from the\n-- registry on 2026-05-17 (B.AI catalog reconcile + xAI removal):\n--\n-- opus-4-6 \u2192 opus-4-7 (Anthropic flagship peer)\n-- gpt-5-5-pro \u2192 gpt-5-5 (drop the -pro suffix; same family)\n-- kimi-2-6 \u2192 kimi-k2-6 (current Kimi model \xB7 OR slug `moonshotai/kimi-k2.6`)\n-- grok-4-3 \u2192 sonnet-4-6 (no Grok in registry; safe default)\n-- grok-4-1-fast \u2192 haiku-4-5 (fast-tier peer)\n-- grok-4-20 \u2192 opus-4-7 (long-context peer; was 2M ctx)\n--\n-- Two live tables hold modelV: agents.model_v (every agent record) and\n-- prefs.default_model_v (the user's default-model choice). Historical\n-- snapshot tables (room_summaries.model_v, retired_token_usage.model_v,\n-- usage_daily.model_v) are NOT touched \u2014 they record what actually ran\n-- at the time and rewriting them would lie about history.\n--\n-- Idempotent \xB7 `WHERE model_v = '<old>'` is a no-op on second run.\n\nUPDATE agents SET model_v = 'opus-4-7' WHERE model_v = 'opus-4-6';\nUPDATE agents SET model_v = 'gpt-5-5' WHERE model_v = 'gpt-5-5-pro';\nUPDATE agents SET model_v = 'kimi-k2-6' WHERE model_v = 'kimi-2-6';\nUPDATE agents SET model_v = 'sonnet-4-6' WHERE model_v = 'grok-4-3';\nUPDATE agents SET model_v = 'haiku-4-5' WHERE model_v = 'grok-4-1-fast';\nUPDATE agents SET model_v = 'opus-4-7' WHERE model_v = 'grok-4-20';\n\nUPDATE prefs SET default_model_v = 'opus-4-7' WHERE default_model_v = 'opus-4-6';\nUPDATE prefs SET default_model_v = 'gpt-5-5' WHERE default_model_v = 'gpt-5-5-pro';\nUPDATE prefs SET default_model_v = 'kimi-k2-6' WHERE default_model_v = 'kimi-2-6';\nUPDATE prefs SET default_model_v = 'sonnet-4-6' WHERE default_model_v = 'grok-4-3';\nUPDATE prefs SET default_model_v = 'haiku-4-5' WHERE default_model_v = 'grok-4-1-fast';\nUPDATE prefs SET default_model_v = 'opus-4-7' WHERE default_model_v = 'grok-4-20';\n";
708
+ }
709
+ });
710
+
711
+ // src/storage/migrations/040_remap_kimi_k2_5_to_k2_6.sql
712
+ var remap_kimi_k2_5_to_k2_6_default;
713
+ var init_remap_kimi_k2_5_to_k2_6 = __esm({
714
+ "src/storage/migrations/040_remap_kimi_k2_5_to_k2_6.sql"() {
715
+ remap_kimi_k2_5_to_k2_6_default = "-- 040_remap_kimi_k2_5_to_k2_6.sql\n--\n-- Bump stored `kimi-k2-5` rows to `kimi-k2-6` after the Kimi registry\n-- entry was renamed (OpenRouter exposes the slug as\n-- `moonshotai/kimi-k2.6`; the prior `moonshotai/kimi-k2.5` slug 404s).\n--\n-- This complements 039_remap_removed_models.sql: 039 was edited to\n-- land users directly on `kimi-k2-6` for first-time runs, but any DB\n-- that already ran the previous 039 (which mapped to `kimi-k2-5`) is\n-- left holding the now-unknown `kimi-k2-5` string. This migration\n-- fixes those. Idempotent \xB7 WHERE clause is a no-op on second run.\n--\n-- Live tables only (`agents.model_v`, `prefs.default_model_v`).\n-- Historical snapshot tables are not touched \u2014 they record what\n-- actually ran at the time.\n\nUPDATE agents SET model_v = 'kimi-k2-6' WHERE model_v = 'kimi-k2-5';\nUPDATE prefs SET default_model_v = 'kimi-k2-6' WHERE default_model_v = 'kimi-k2-5';\n";
716
+ }
717
+ });
718
+
703
719
  // src/storage/db.ts
704
720
  var db_exports = {};
705
721
  __export(db_exports, {
@@ -795,6 +811,8 @@ var init_db = __esm({
795
811
  init_negative_space();
796
812
  init_topic_branches();
797
813
  init_qd_archive();
814
+ init_remap_removed_models();
815
+ init_remap_kimi_k2_5_to_k2_6();
798
816
  MIGRATIONS = [
799
817
  { name: "001_init.sql", sql: init_default },
800
818
  { name: "002_default_opus.sql", sql: default_opus_default },
@@ -833,7 +851,9 @@ var init_db = __esm({
833
851
  { name: "035_topic_rec_tag.sql", sql: topic_rec_tag_default },
834
852
  { name: "036_negative_space.sql", sql: negative_space_default },
835
853
  { name: "037_topic_branches.sql", sql: topic_branches_default },
836
- { name: "038_qd_archive.sql", sql: qd_archive_default }
854
+ { name: "038_qd_archive.sql", sql: qd_archive_default },
855
+ { name: "039_remap_removed_models.sql", sql: remap_removed_models_default },
856
+ { name: "040_remap_kimi_k2_5_to_k2_6.sql", sql: remap_kimi_k2_5_to_k2_6_default }
837
857
  ];
838
858
  _db = null;
839
859
  }
@@ -973,16 +993,6 @@ var MODELS = {
973
993
  contextBudget: 2e5,
974
994
  deck: "balanced \xB7 default"
975
995
  },
976
- "opus-4-6": {
977
- v: "opus-4-6",
978
- provider: "anthropic",
979
- directApiId: "claude-opus-4-6",
980
- openrouterId: "anthropic/claude-opus-4.6",
981
- baiId: "claude-opus-4.6",
982
- displayName: "Opus 4.6",
983
- contextBudget: 1e6,
984
- deck: "deep reasoning \xB7 1M ctx"
985
- },
986
996
  "opus-4-7": {
987
997
  v: "opus-4-7",
988
998
  provider: "anthropic",
@@ -1047,26 +1057,12 @@ var MODELS = {
1047
1057
  provider: "openai",
1048
1058
  directApiId: "gpt-5.4-mini",
1049
1059
  openrouterId: "openai/gpt-5.4-mini",
1050
- // No baiId · B.AI's catalog reports "No available channel for
1051
- // model gpt-5-4-mini" on the OneAPI distributor — the mini
1052
- // variant isn't routed there. The full `gpt-5-4` IS available on
1053
- // B.AI; pick that or use direct / OR for the mini tier.
1060
+ baiId: "gpt-5.4-mini",
1054
1061
  displayName: "GPT-5.4 Mini",
1055
1062
  contextBudget: 4e5,
1056
1063
  deck: "fast \xB7 400k ctx"
1057
1064
  },
1058
- // ── OpenAI · OR-only previews (Pro / Codex) ──
1059
- "gpt-5-5-pro": {
1060
- v: "gpt-5-5-pro",
1061
- provider: "openai",
1062
- directApiId: "gpt-5.5-pro",
1063
- openrouterId: "openai/gpt-5.5-pro",
1064
- baiId: "gpt-5.5-pro",
1065
- displayName: "GPT-5.5 Pro",
1066
- contextBudget: 1e6,
1067
- deck: "deep reasoning \xB7 1M ctx",
1068
- viaUniversalOnly: true
1069
- },
1065
+ // ── OpenAI · OR-only previews (Codex) ──
1070
1066
  "codex-5-4": {
1071
1067
  v: "codex-5-4",
1072
1068
  provider: "openai",
@@ -1118,42 +1114,13 @@ var MODELS = {
1118
1114
  contextBudget: 1e6,
1119
1115
  deck: "fast \xB7 1M ctx"
1120
1116
  },
1121
- // ── xAI · current frontier (4.3 / 4.1 Fast direct + 4.20 big-ctx OR) ──
1122
- // Replaced the legacy grok-4 / grok-4-mini entries 4.3 is xAI's
1123
- // current "most intelligent and fastest" model per their docs; 4.1
1124
- // Fast is the new cheap tier. 4.20 stays OR-only since it's a 2M-ctx
1125
- // preview that the direct SDK hasn't acknowledged yet.
1126
- "grok-4-3": {
1127
- v: "grok-4-3",
1128
- provider: "xai",
1129
- directApiId: "grok-4.3",
1130
- openrouterId: "x-ai/grok-4.3",
1131
- baiId: "grok-4.3",
1132
- displayName: "Grok 4.3",
1133
- contextBudget: 1e6,
1134
- deck: "flagship \xB7 1M ctx"
1135
- },
1136
- "grok-4-1-fast": {
1137
- v: "grok-4-1-fast",
1138
- provider: "xai",
1139
- directApiId: "grok-4.1-fast",
1140
- openrouterId: "x-ai/grok-4.1-fast",
1141
- baiId: "grok-4.1-fast",
1142
- displayName: "Grok 4.1 Fast",
1143
- contextBudget: 256e3,
1144
- deck: "fast \xB7 256k ctx"
1145
- },
1146
- "grok-4-20": {
1147
- v: "grok-4-20",
1148
- provider: "xai",
1149
- directApiId: "grok-4.20",
1150
- openrouterId: "x-ai/grok-4.20",
1151
- baiId: "grok-4.20",
1152
- displayName: "Grok 4.20",
1153
- contextBudget: 2e6,
1154
- deck: "2M ctx \xB7 big context",
1155
- viaUniversalOnly: true
1156
- },
1117
+ // ── xAI · all Grok entries removed (2026-05-17) ──
1118
+ // B.AI's catalog lists no Grok models, the user base on B.AI saw
1119
+ // every grok-* call 503 with "no available channel". Direct xAI key
1120
+ // route and OpenRouter route still exist in the adapter (the `xai`
1121
+ // Provider type is retained), but no modelV currently maps to them.
1122
+ // Re-add a `grok-*` entry here if/when B.AI begins routing xAI again
1123
+ // or if the product re-introduces direct xAI as a first-class path.
1157
1124
  // ── DeepSeek (OR-only · no @ai-sdk/deepseek shipped) ──
1158
1125
  "deepseek-v4-pro": {
1159
1126
  v: "deepseek-v4-pro",
@@ -1193,20 +1160,49 @@ var MODELS = {
1193
1160
  deck: "Zhipu flagship \xB7 200k ctx",
1194
1161
  viaUniversalOnly: true
1195
1162
  },
1196
- // ── Moonshot · Kimi family · OR + B.AI only ──
1197
- // OpenRouter catalog convention: `moonshotai/kimi-…`. B.AI uses
1198
- // hyphenated lowercase: `kimi-2-6`. No direct @ai-sdk client ·
1163
+ // ── Moonshot · Kimi family · OR + B.AI ──
1164
+ // OpenRouter catalog convention: `moonshotai/kimi-k2.6` (the leading
1165
+ // `k` is part of the slug — `moonshotai/kimi-2.6` 404s). B.AI's
1166
+ // siliconflow distributor still ships the older `kimi-k2.5` channel
1167
+ // (per 2026-05-17 catalog snapshot), so the B.AI route serves K2.5
1168
+ // until B.AI picks up the newer build. No direct @ai-sdk client ·
1199
1169
  // viaUniversalOnly skips the direct path.
1200
- "kimi-2-6": {
1201
- v: "kimi-2-6",
1170
+ "kimi-k2-6": {
1171
+ v: "kimi-k2-6",
1202
1172
  provider: "moonshot",
1203
- directApiId: "kimi-2.6",
1204
- openrouterId: "moonshotai/kimi-2.6",
1205
- baiId: "kimi-2.6",
1206
- displayName: "Kimi 2.6",
1173
+ directApiId: "kimi-k2.6",
1174
+ openrouterId: "moonshotai/kimi-k2.6",
1175
+ baiId: "kimi-k2.5",
1176
+ displayName: "Kimi K2.6",
1207
1177
  contextBudget: 256e3,
1208
1178
  deck: "Moonshot \xB7 long-context",
1209
1179
  viaUniversalOnly: true
1180
+ },
1181
+ // ── MiniMax · M-series · OR + B.AI ──
1182
+ // No direct @ai-sdk client · viaUniversalOnly skips the direct path.
1183
+ // OpenRouter catalog slug: `minimax/minimax-mX.Y`. B.AI uses bare
1184
+ // model id: `minimax-mX.Y` (siliconflow / paratera distributors).
1185
+ "minimax-m2-7": {
1186
+ v: "minimax-m2-7",
1187
+ provider: "minimax",
1188
+ directApiId: "minimax-m2.7",
1189
+ openrouterId: "minimax/minimax-m2.7",
1190
+ baiId: "minimax-m2.7",
1191
+ displayName: "MiniMax M2.7",
1192
+ contextBudget: 245e3,
1193
+ deck: "MiniMax flagship \xB7 long-context",
1194
+ viaUniversalOnly: true
1195
+ },
1196
+ "minimax-m2-5": {
1197
+ v: "minimax-m2-5",
1198
+ provider: "minimax",
1199
+ directApiId: "minimax-m2.5",
1200
+ openrouterId: "minimax/minimax-m2.5",
1201
+ baiId: "minimax-m2.5",
1202
+ displayName: "MiniMax M2.5",
1203
+ contextBudget: 245e3,
1204
+ deck: "MiniMax prior \xB7 long-context",
1205
+ viaUniversalOnly: true
1210
1206
  }
1211
1207
  };
1212
1208
  function getModel(v) {
@@ -2044,7 +2040,6 @@ function parseAbility2(raw) {
2044
2040
  }
2045
2041
  var ALLOWED_MODELS = /* @__PURE__ */ new Set([
2046
2042
  "sonnet-4-6",
2047
- "opus-4-6",
2048
2043
  "opus-4-7",
2049
2044
  "haiku-4-5",
2050
2045
  "gpt-5-5",
@@ -2053,10 +2048,12 @@ var ALLOWED_MODELS = /* @__PURE__ */ new Set([
2053
2048
  "gemini-3-1",
2054
2049
  "gemini-3-flash",
2055
2050
  "gemini-3-1-flash",
2056
- "grok-4-3",
2057
- "grok-4-1-fast",
2058
2051
  "deepseek-v4-pro",
2059
- "deepseek-v4-flash"
2052
+ "deepseek-v4-flash",
2053
+ "glm-5-1",
2054
+ "kimi-k2-6",
2055
+ "minimax-m2-7",
2056
+ "minimax-m2-5"
2060
2057
  ]);
2061
2058
  function clamp(s, max) {
2062
2059
  if (s.length <= max) return s;
@@ -3316,8 +3313,9 @@ var PRIMARY_BY_CARRIER = {
3316
3313
  bai: "haiku-4-5",
3317
3314
  anthropic: "haiku-4-5",
3318
3315
  openai: "gpt-5-4-mini",
3319
- google: "gemini-3-1-flash",
3320
- xai: "grok-4-1-fast"
3316
+ google: "gemini-3-1-flash"
3317
+ // xai · no primary (no LLM modelV in registry as of 2026-05-17). The
3318
+ // adapter / availability layer skip xai when this key is absent.
3321
3319
  };
3322
3320
  var CARRIER_PRIORITY = ["openrouter", "bai", "anthropic", "openai", "google", "xai"];
3323
3321
  function reachableModelVs() {
@@ -3459,14 +3457,22 @@ var PROVIDER_FLAGSHIP = {
3459
3457
  anthropic: "opus-4-7",
3460
3458
  openai: "gpt-5-5",
3461
3459
  google: "gemini-3-flash",
3462
- xai: "grok-4-3",
3460
+ // xai · no LLM modelV currently in the registry (all grok-* entries
3461
+ // removed 2026-05-17 when B.AI dropped xAI). Keep the key so the
3462
+ // Record type stays exhaustive; the resolver naturally skips it.
3463
+ xai: null,
3463
3464
  deepseek: "deepseek-v4-pro",
3464
3465
  zhipu: "glm-5-1",
3465
- moonshot: "kimi-2-6",
3466
+ moonshot: "kimi-k2-6",
3466
3467
  openrouter: "opus-4-7",
3467
3468
  bai: "opus-4-7",
3468
3469
  brave: null,
3469
3470
  tavily: null,
3471
+ // minimax has LLM models now (minimax-m2-7 / m2-5) but no direct
3472
+ // @ai-sdk path · only the B.AI route works. The Provider Record here
3473
+ // refers to *direct* providers, so `null` is correct: a user with
3474
+ // only a minimax voice key shouldn't try to use it as their LLM
3475
+ // flagship. B.AI carrier still picks up the MiniMax LLMs naturally.
3470
3476
  minimax: null,
3471
3477
  elevenlabs: null
3472
3478
  };
@@ -3474,13 +3480,14 @@ var PROVIDER_FAST = {
3474
3480
  anthropic: "haiku-4-5",
3475
3481
  openai: "gpt-5-4-mini",
3476
3482
  google: "gemini-3-1-flash",
3477
- xai: "grok-4-1-fast",
3483
+ // xai · no LLM modelV in the registry (see PROVIDER_FLAGSHIP note).
3484
+ xai: null,
3478
3485
  deepseek: "deepseek-v4-flash",
3479
3486
  // GLM / Kimi · no separate fast/flash tier in our registry yet, so
3480
3487
  // both providers' "fast pick" falls back to the same flagship that
3481
3488
  // PROVIDER_FLAGSHIP names. Reachability-via-OR/B.AI carries it.
3482
3489
  zhipu: "glm-5-1",
3483
- moonshot: "kimi-2-6",
3490
+ moonshot: "kimi-k2-6",
3484
3491
  openrouter: "opus-4-6-fast",
3485
3492
  bai: "haiku-4-5",
3486
3493
  brave: null,
@@ -3495,28 +3502,26 @@ var FAST_POOL_BY_CARRIER = {
3495
3502
  "gpt-5-4-mini",
3496
3503
  "gemini-3-flash",
3497
3504
  "gemini-3-1-flash",
3498
- "grok-4-1-fast",
3499
3505
  "deepseek-v4-flash"
3500
3506
  ],
3501
- // B.AI carries the same brand-spanning fast catalog as OpenRouter ·
3502
- // identical pool gives a B.AI-only user the same visibly-mixed
3503
- // director cast (different brand badges per seat) that the OpenRouter
3504
- // path produces. Members are filtered against reachability inside
3505
- // `pickRandomFastModel`, so models without a baiId fall out naturally
3506
- // if B.AI ends up not carrying one of them in practice.
3507
+ // B.AI carries the same brand-spanning fast catalog as OpenRouter
3508
+ // (minus Grok · B.AI dropped xAI 2026-05) · identical pool gives a
3509
+ // B.AI-only user the same visibly-mixed director cast (different
3510
+ // brand badges per seat) that the OpenRouter path produces. Members
3511
+ // are filtered against reachability inside `pickRandomFastModel`, so
3512
+ // models without a baiId fall out naturally if B.AI ends up not
3513
+ // carrying one of them in practice.
3507
3514
  bai: [
3508
- "opus-4-6-fast",
3509
3515
  "haiku-4-5",
3510
3516
  "gpt-5-4-mini",
3511
3517
  "gemini-3-flash",
3512
3518
  "gemini-3-1-flash",
3513
- "grok-4-1-fast",
3514
3519
  "deepseek-v4-flash"
3515
3520
  ],
3516
3521
  anthropic: ["opus-4-6-fast", "haiku-4-5"],
3517
3522
  openai: ["gpt-5-4-mini"],
3518
- google: ["gemini-3-flash", "gemini-3-1-flash"],
3519
- xai: ["grok-4-1-fast"]
3523
+ google: ["gemini-3-flash", "gemini-3-1-flash"]
3524
+ // xai · no fast pool (no LLM modelV in registry).
3520
3525
  };
3521
3526
  function pickRandomFastModel(carrier) {
3522
3527
  if (!carrier) return null;
@@ -3537,13 +3542,13 @@ var FLAGSHIP_TIER = /* @__PURE__ */ new Set([
3537
3542
  // Google
3538
3543
  "gemini-3-1",
3539
3544
  "gemini-3-flash",
3540
- // xAI
3541
- "grok-4-3",
3545
+ // xAI · no flagship in registry currently.
3542
3546
  // DeepSeek
3543
3547
  "deepseek-v4-pro",
3544
- // Zhipu · Moonshot · single flagship each (both OR + B.AI routed).
3548
+ // Zhipu · Moonshot · MiniMax · single flagship each (B.AI routed).
3545
3549
  "glm-5-1",
3546
- "kimi-2-6"
3550
+ "kimi-k2-6",
3551
+ "minimax-m2-7"
3547
3552
  ]);
3548
3553
  function effectiveDefaultModel() {
3549
3554
  const prefs = getPrefs();
@@ -3596,17 +3601,15 @@ var CHEAP_BY_CARRIER = {
3596
3601
  anthropic: "sonnet-4-6",
3597
3602
  // only direct-routable Claude
3598
3603
  openai: "gpt-5-4-mini",
3599
- google: "gemini-3-1-flash",
3604
+ google: "gemini-3-1-flash"
3600
3605
  // 3.1 Flash Lite · cheapest direct-routable Gemini
3601
- xai: "grok-4-1-fast"
3602
- // 4.1 Fast · cheapest direct-routable Grok
3606
+ // xai · no cheap tier (no LLM modelV in registry).
3603
3607
  };
3604
3608
  var UTILITY_PREFERENCE = [
3605
3609
  "haiku-4-5",
3606
3610
  "gpt-5-4-mini",
3607
3611
  "gemini-3-1-flash",
3608
- "gemini-3-flash",
3609
- "grok-4-1-fast"
3612
+ "gemini-3-flash"
3610
3613
  ];
3611
3614
  function utilityModelFor(fallback = null) {
3612
3615
  const reachable = new Set(reachableModels().map((m) => m.modelV));
@@ -15818,6 +15821,19 @@ function setKeyPointVote(id, vote) {
15818
15821
  function pickRouterModel() {
15819
15822
  return utilityModelFor();
15820
15823
  }
15824
+ function authorLabel(m, cast) {
15825
+ if (m.authorKind === "user") return "USER";
15826
+ if (m.authorKind === "system") return "system";
15827
+ if (!m.authorId) return m.authorKind === "agent" ? "director" : m.authorKind;
15828
+ const fromCast = cast?.find((a2) => a2.id === m.authorId);
15829
+ if (fromCast) return `${fromCast.name} (${fromCast.handle})`;
15830
+ const a = getAgent(m.authorId);
15831
+ if (a) {
15832
+ const kindTag = a.roleKind === "moderator" ? "chair" : "director";
15833
+ return `${a.name} (${a.handle}, ${kindTag})`;
15834
+ }
15835
+ return m.authorKind === "agent" ? "director" : m.authorKind;
15836
+ }
15821
15837
  var MAX_PICKS = 2;
15822
15838
  async function pickChairClarifyDecision(opts) {
15823
15839
  const prompt = latestUserPrompt(opts.history);
@@ -15895,10 +15911,7 @@ async function pickRoundWrap(opts) {
15895
15911
  if (meta?.kind === "tool-use" || meta?.kind === "tool-preamble") return false;
15896
15912
  if (meta?.kind === "round-open" || meta?.kind === "round-prompt") return false;
15897
15913
  return true;
15898
- }).map((m) => {
15899
- const who = m.authorKind === "user" ? "USER" : m.authorId || "agent";
15900
- return `[${who}] ${m.body.trim().slice(0, 600)}`;
15901
- }).join("\n\n");
15914
+ }).map((m) => `[${authorLabel(m)}] ${m.body.trim().slice(0, 600)}`).join("\n\n");
15902
15915
  const sys = {
15903
15916
  role: "system",
15904
15917
  content: [
@@ -16002,10 +16015,7 @@ ${extras.join("\n")}` : baseRow;
16002
16015
  if (meta?.kind === "tool-use" || meta?.kind === "tool-preamble") return false;
16003
16016
  if (meta?.kind === "round-open" || meta?.kind === "round-prompt") return false;
16004
16017
  return true;
16005
- }).map((m) => {
16006
- const who = m.authorKind === "user" ? "USER" : m.authorId || "agent";
16007
- return `[${who}] ${m.body.trim().slice(0, 600)}`;
16008
- }).join("\n\n");
16018
+ }).map((m) => `[${authorLabel(m, candidates)}] ${m.body.trim().slice(0, 600)}`).join("\n\n");
16009
16019
  const decision1Block = mode === "dissent-gap" ? [
16010
16020
  "DECISION 1 \xB7 Next speaker (DISSENT-GAP MODE).",
16011
16021
  "The room is converging on a single frame \u2014 for THIS pick, the chair",
@@ -16067,6 +16077,13 @@ ${extras.join("\n")}` : baseRow;
16067
16077
  "the SPECIFIC pattern + the load-bearing piece worth pinning down.",
16068
16078
  "No greeting, no signature.",
16069
16079
  "",
16080
+ "NAMING \xB7 When `intervention` or `rationale` references a director,",
16081
+ "use their DISPLAY NAME (the part before the parenthesis in the",
16082
+ 'roster \u2014 e.g. "Maya", not "fk7wvt1bep62"). The opaque id is for',
16083
+ "the `agent_id` JSON field ONLY; it must NEVER appear inside any",
16084
+ "user-facing prose text. The transcript above labels speakers by",
16085
+ "name already; mirror that format.",
16086
+ "",
16070
16087
  "LANGUAGE \xB7 the chair note must follow the room's DOMINANT",
16071
16088
  "language detected from the recent transcript (most recent",
16072
16089
  "messages weight highest). If most directors and the user are",
@@ -16140,16 +16157,26 @@ ${extras.join("\n")}` : baseRow;
16140
16157
  const obj = parsed;
16141
16158
  const validIds = new Set(candidates.map((c) => c.id));
16142
16159
  const id = typeof obj.agent_id === "string" && validIds.has(obj.agent_id) ? obj.agent_id : null;
16143
- const rationale = typeof obj.rationale === "string" ? obj.rationale.trim().slice(0, 200) : "";
16160
+ const rationale = typeof obj.rationale === "string" ? replaceLeakedIds(obj.rationale.trim(), candidates).slice(0, 200) : "";
16144
16161
  let intervention = null;
16145
16162
  if (typeof obj.intervention === "string") {
16146
- const t = obj.intervention.trim();
16163
+ const t = replaceLeakedIds(obj.intervention.trim(), candidates);
16147
16164
  if (t.length > 0 && t.toLowerCase() !== "null" && t.toLowerCase() !== "none") {
16148
16165
  intervention = t.slice(0, 280);
16149
16166
  }
16150
16167
  }
16151
16168
  return { agentId: id, rationale, intervention };
16152
16169
  }
16170
+ function replaceLeakedIds(text, candidates) {
16171
+ if (!text) return text;
16172
+ return text.replace(/(?<![A-Za-z0-9])[0-9a-hjkmnpqrstvwxyz]{12}(?![A-Za-z0-9])/g, (id) => {
16173
+ const fromCast = candidates.find((c) => c.id === id);
16174
+ if (fromCast) return fromCast.name;
16175
+ const a = getAgent(id);
16176
+ if (a) return a.name;
16177
+ return id;
16178
+ });
16179
+ }
16153
16180
  async function pickChairWebSearch(opts) {
16154
16181
  const prompt = latestUserPrompt(opts.history);
16155
16182
  if (!prompt) return null;
@@ -22775,7 +22802,7 @@ function voicesRouter() {
22775
22802
  init_paths();
22776
22803
 
22777
22804
  // src/version.ts
22778
- var VERSION = "0.1.22";
22805
+ var VERSION = "0.1.23";
22779
22806
 
22780
22807
  // src/server.ts
22781
22808
  function createApp() {