jinzd-ai-cli 0.4.155 → 0.4.157

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.
Files changed (31) hide show
  1. package/dist/{batch-LS3IJVBK.js → batch-ATL6V3UE.js} +2 -2
  2. package/dist/{chat-index-IF4EINLQ.js → chat-index-2I7ZHRE5.js} +2 -2
  3. package/dist/{chunk-E5ICQT3P.js → chunk-3HJW556L.js} +4 -4
  4. package/dist/{chunk-JOJRBV2K.js → chunk-62CD2T5F.js} +1 -1
  5. package/dist/{chunk-D6GJTJQH.js → chunk-77DDRYFM.js} +1 -1
  6. package/dist/{chunk-CIZQZ7CC.js → chunk-A4JROOGF.js} +2 -2
  7. package/dist/{chunk-NFRTSL3N.js → chunk-GRFQ2QD5.js} +1 -1
  8. package/dist/{chunk-JXSWY54M.js → chunk-LLQMVGNP.js} +1 -1
  9. package/dist/{chunk-B3LFGPU2.js → chunk-N3VGZTEJ.js} +1 -1
  10. package/dist/chunk-NZ4X6GUC.js +230 -0
  11. package/dist/{chunk-O6MLS5QO.js → chunk-OJL3PY36.js} +0 -226
  12. package/dist/{hub-ZILVZWI2.js → chunk-Q3ZUDA6S.js} +6 -249
  13. package/dist/{persist-3EBOLHFZ.js → chunk-RUJQ5OUB.js} +1 -2
  14. package/dist/{chunk-IBBYW6PM.js → chunk-V5OQOKU3.js} +1 -1
  15. package/dist/{ci-34ZQH43L.js → ci-E7MDZSB6.js} +3 -3
  16. package/dist/{constants-DQ5VJOGS.js → constants-NL5ETRA5.js} +1 -1
  17. package/dist/{doctor-cli-TSCI4ORL.js → doctor-cli-XJNT745O.js} +6 -6
  18. package/dist/electron-server.js +758 -44
  19. package/dist/hub-RXZ5IDBY.js +260 -0
  20. package/dist/{hub-server-OH7AYQIW.js → hub-server-GSTG5MNE.js} +4 -2
  21. package/dist/index.js +40 -40
  22. package/dist/persist-UI6WRBGB.js +12 -0
  23. package/dist/{run-tests-5KWCHBQS.js → run-tests-I3EBH7O6.js} +2 -2
  24. package/dist/{run-tests-5CJRMOMI.js → run-tests-O46AI32W.js} +1 -1
  25. package/dist/{server-35OQV62B.js → server-4PJJHZWM.js} +142 -32
  26. package/dist/{server-DVIP7NLW.js → server-SVBHOHTC.js} +6 -6
  27. package/dist/{task-orchestrator-AXSS7ROD.js → task-orchestrator-NMX3CYW2.js} +6 -6
  28. package/dist/web/client/app.js +173 -0
  29. package/dist/web/client/index.html +31 -0
  30. package/package.json +1 -1
  31. package/dist/{chunk-U5MY24UZ.js → chunk-MM3F43H6.js} +3 -3
@@ -1,4 +1,13 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ DiscussionOrchestrator,
4
+ getPreset,
5
+ resolveRoleProviders
6
+ } from "./chunk-Q3ZUDA6S.js";
7
+ import "./chunk-NZ4X6GUC.js";
8
+ import {
9
+ persistDiscussion
10
+ } from "./chunk-RUJQ5OUB.js";
2
11
  import {
3
12
  AuthManager,
4
13
  TOKEN_EXPIRY_MS
@@ -12,28 +21,7 @@ import {
12
21
  loadDevState,
13
22
  persistToolRound,
14
23
  setupProxy
15
- } from "./chunk-CIZQZ7CC.js";
16
- import {
17
- SessionManager,
18
- getContentText
19
- } from "./chunk-TOTEUETI.js";
20
- import {
21
- CONTENT_ONLY_STREAM_REMINDER,
22
- HALLUCINATION_CORRECTION_MESSAGE,
23
- ProviderRegistry,
24
- TEE_FINAL_USER_NUDGE,
25
- TOOL_CALL_REMINDER,
26
- detectMetaNarration,
27
- detectPseudoToolCalls,
28
- detectsHallucinatedFileOp,
29
- hadPreviousWriteToolCalls,
30
- looksLikeDocumentBody,
31
- stripPseudoToolCalls,
32
- stripToolCallReminder
33
- } from "./chunk-AIZOARZY.js";
34
- import {
35
- ConfigManager
36
- } from "./chunk-IBBYW6PM.js";
24
+ } from "./chunk-A4JROOGF.js";
37
25
  import {
38
26
  ToolExecutor,
39
27
  ToolRegistry,
@@ -51,16 +39,37 @@ import {
51
39
  spawnAgentContext,
52
40
  truncateOutput,
53
41
  undoStack
54
- } from "./chunk-E5ICQT3P.js";
42
+ } from "./chunk-3HJW556L.js";
55
43
  import "./chunk-HDSKW7Q3.js";
56
44
  import "./chunk-ZWVIDFGY.js";
57
- import "./chunk-JOJRBV2K.js";
45
+ import "./chunk-62CD2T5F.js";
46
+ import {
47
+ SessionManager,
48
+ getContentText
49
+ } from "./chunk-TOTEUETI.js";
50
+ import {
51
+ CONTENT_ONLY_STREAM_REMINDER,
52
+ HALLUCINATION_CORRECTION_MESSAGE,
53
+ ProviderRegistry,
54
+ TEE_FINAL_USER_NUDGE,
55
+ TOOL_CALL_REMINDER,
56
+ detectMetaNarration,
57
+ detectPseudoToolCalls,
58
+ detectsHallucinatedFileOp,
59
+ hadPreviousWriteToolCalls,
60
+ looksLikeDocumentBody,
61
+ stripPseudoToolCalls,
62
+ stripToolCallReminder
63
+ } from "./chunk-AIZOARZY.js";
58
64
  import {
59
65
  runTool
60
- } from "./chunk-NFRTSL3N.js";
66
+ } from "./chunk-GRFQ2QD5.js";
61
67
  import {
62
68
  getDangerLevel
63
69
  } from "./chunk-NXXNLLSG.js";
70
+ import {
71
+ ConfigManager
72
+ } from "./chunk-V5OQOKU3.js";
64
73
  import "./chunk-2ZD3YTVM.js";
65
74
  import {
66
75
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -80,16 +89,16 @@ import {
80
89
  SKILLS_DIR_NAME,
81
90
  VERSION,
82
91
  buildUserIdentityPrompt
83
- } from "./chunk-B3LFGPU2.js";
92
+ } from "./chunk-N3VGZTEJ.js";
84
93
  import {
85
94
  formatGitContextForPrompt,
86
95
  getGitContext,
87
96
  getGitRoot
88
97
  } from "./chunk-HOSJZMQS.js";
89
98
  import "./chunk-4BKXL7SM.js";
90
- import "./chunk-U5MY24UZ.js";
91
- import "./chunk-SLSWPBK3.js";
99
+ import "./chunk-MM3F43H6.js";
92
100
  import "./chunk-KHYD3WXE.js";
101
+ import "./chunk-SLSWPBK3.js";
93
102
  import "./chunk-VNNYHW6N.js";
94
103
  import "./chunk-OVWE4E46.js";
95
104
  import "./chunk-PDX44BCA.js";
@@ -535,6 +544,10 @@ var SessionHandler = class _SessionHandler {
535
544
  abortController = null;
536
545
  userInterjection = null;
537
546
  processing = false;
547
+ /** P4: active hub discussion orchestrator (web room), if any. */
548
+ hubOrchestrator = null;
549
+ /** P4b: pending between-rounds human-steer prompts (requestId → resolver). */
550
+ pendingHubReview = /* @__PURE__ */ new Map();
538
551
  /** Pending ask_user promises */
539
552
  pendingAskUser = /* @__PURE__ */ new Map();
540
553
  /** Pending auto-pause promises */
@@ -673,6 +686,19 @@ var SessionHandler = class _SessionHandler {
673
686
  this.abortController.abort();
674
687
  }
675
688
  return;
689
+ case "hub_start":
690
+ return this.handleHubStart(msg);
691
+ case "hub_abort":
692
+ this.hubOrchestrator?.abort();
693
+ return;
694
+ case "hub_steer": {
695
+ const resolve3 = this.pendingHubReview.get(msg.requestId);
696
+ if (resolve3) {
697
+ this.pendingHubReview.delete(msg.requestId);
698
+ resolve3({ action: msg.action, message: msg.message });
699
+ }
700
+ return;
701
+ }
676
702
  case "interjection":
677
703
  this.userInterjection = msg.content;
678
704
  return;
@@ -763,6 +789,90 @@ var SessionHandler = class _SessionHandler {
763
789
  }
764
790
  }
765
791
  // ── Chat handling ────────────────────────────────────────────────
792
+ /**
793
+ * P4: run a multi-agent hub discussion in the browser room. Bridges the
794
+ * UI-agnostic DiscussionOrchestrator (onEvent) to WebSocket hub_event
795
+ * messages, then persists the result (P3) so it can be replayed.
796
+ */
797
+ async handleHubStart(msg) {
798
+ if (this.processing || this.hubOrchestrator) {
799
+ this.send({ type: "error", message: "Already running a request. Abort first." });
800
+ return;
801
+ }
802
+ const topic = (msg.topic ?? "").trim();
803
+ if (!topic) {
804
+ this.send({ type: "error", message: "Hub topic is required." });
805
+ return;
806
+ }
807
+ const preset = getPreset(msg.preset ?? "brainstorm");
808
+ if (!preset) {
809
+ this.send({ type: "error", message: `Unknown preset "${msg.preset}".` });
810
+ return;
811
+ }
812
+ const defaultProvider = this.currentProvider;
813
+ const defaultModel = this.currentModel;
814
+ const resolution = resolveRoleProviders(
815
+ preset.roles,
816
+ {
817
+ has: (id) => this.providers.has(id),
818
+ defaultModelFor: (id) => this.providers.has(id) ? this.providers.get(id).info.defaultModel : void 0
819
+ },
820
+ defaultProvider,
821
+ defaultModel,
822
+ this.providers.listAvailable().map((p) => p.info.id),
823
+ msg.mix
824
+ );
825
+ const roles = resolution.roles;
826
+ for (const w of resolution.warnings) this.send({ type: "info", message: w });
827
+ const config = {
828
+ mode: "discuss",
829
+ roles,
830
+ defaultProvider,
831
+ defaultModel,
832
+ maxRounds: msg.maxRounds && msg.maxRounds > 0 ? Math.min(msg.maxRounds, 20) : 6,
833
+ voteConverge: msg.vote === true,
834
+ humanSteer: msg.steer === true
835
+ };
836
+ this.send({
837
+ type: "hub_started",
838
+ topic,
839
+ maxRounds: config.maxRounds,
840
+ roles: roles.map((r) => ({ id: r.id, name: r.name, color: r.color, provider: r.provider, model: r.model }))
841
+ });
842
+ const orchestrator = new DiscussionOrchestrator(config, this.providers);
843
+ this.hubOrchestrator = orchestrator;
844
+ orchestrator.onEvent = (event) => this.send({ type: "hub_event", event });
845
+ if (config.humanSteer) {
846
+ orchestrator.onRoundReview = ({ round, maxRounds }) => new Promise((resolve3) => {
847
+ const requestId = `hubrev_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
848
+ this.pendingHubReview.set(requestId, resolve3);
849
+ this.send({ type: "hub_review", requestId, round, maxRounds });
850
+ });
851
+ }
852
+ this.processing = true;
853
+ try {
854
+ const state = await orchestrator.run(topic);
855
+ let sessionId;
856
+ let saved = false;
857
+ if (state.messages.length > 0) {
858
+ try {
859
+ const res = await persistDiscussion(state, this.config, defaultProvider, defaultModel);
860
+ sessionId = res.id;
861
+ saved = true;
862
+ } catch (err) {
863
+ this.send({ type: "info", message: `Could not save discussion: ${err.message}` });
864
+ }
865
+ }
866
+ this.send({ type: "hub_done", sessionId, saved });
867
+ if (saved) this.sendSessionList();
868
+ } catch (err) {
869
+ this.send({ type: "error", message: `Hub error: ${err.message}` });
870
+ this.send({ type: "hub_done", saved: false });
871
+ } finally {
872
+ this.processing = false;
873
+ this.hubOrchestrator = null;
874
+ }
875
+ }
766
876
  async handleChat(content, images) {
767
877
  if (this.processing) {
768
878
  this.send({ type: "error", message: "Already processing a request. Use abort first." });
@@ -2465,7 +2575,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2465
2575
  case "test": {
2466
2576
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2467
2577
  try {
2468
- const { executeTests } = await import("./run-tests-5KWCHBQS.js");
2578
+ const { executeTests } = await import("./run-tests-I3EBH7O6.js");
2469
2579
  const argStr = args.join(" ").trim();
2470
2580
  let testArgs = {};
2471
2581
  if (argStr) {
@@ -2989,7 +3099,7 @@ Add .md files to create commands.` });
2989
3099
  return;
2990
3100
  }
2991
3101
  try {
2992
- const { searchChatMemory, loadChatIndex } = await import("./chat-index-IF4EINLQ.js");
3102
+ const { searchChatMemory, loadChatIndex } = await import("./chat-index-2I7ZHRE5.js");
2993
3103
  const loaded = loadChatIndex();
2994
3104
  if (!loaded || loaded.idx.chunks.length === 0) {
2995
3105
  this.send({ type: "memory_hits", query: q, hits: [], indexMissing: true });
@@ -3025,7 +3135,7 @@ Add .md files to create commands.` });
3025
3135
  }
3026
3136
  async handleMemoryStatus() {
3027
3137
  try {
3028
- const { getChatIndexStatus } = await import("./chat-index-IF4EINLQ.js");
3138
+ const { getChatIndexStatus } = await import("./chat-index-2I7ZHRE5.js");
3029
3139
  const s = getChatIndexStatus();
3030
3140
  this.send({
3031
3141
  type: "memory_status",
@@ -3050,7 +3160,7 @@ Add .md files to create commands.` });
3050
3160
  type: "info",
3051
3161
  message: full ? "\u{1F9E0} Rebuilding chat memory index (this may take a while on first run \u2014 ~117 MB embedder)." : "\u{1F9E0} Refreshing chat memory index (incremental)\u2026"
3052
3162
  });
3053
- const { buildChatIndex } = await import("./chat-index-IF4EINLQ.js");
3163
+ const { buildChatIndex } = await import("./chat-index-2I7ZHRE5.js");
3054
3164
  const stats = await buildChatIndex({
3055
3165
  full,
3056
3166
  onProgress: (p) => {
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ToolRegistry
4
- } from "./chunk-E5ICQT3P.js";
4
+ } from "./chunk-3HJW556L.js";
5
5
  import "./chunk-HDSKW7Q3.js";
6
6
  import "./chunk-ZWVIDFGY.js";
7
- import "./chunk-JOJRBV2K.js";
7
+ import "./chunk-62CD2T5F.js";
8
8
  import {
9
9
  runTool
10
- } from "./chunk-NFRTSL3N.js";
10
+ } from "./chunk-GRFQ2QD5.js";
11
11
  import {
12
12
  getDangerLevel,
13
13
  schemaToJsonSchema
@@ -15,11 +15,11 @@ import {
15
15
  import "./chunk-2ZD3YTVM.js";
16
16
  import {
17
17
  VERSION
18
- } from "./chunk-B3LFGPU2.js";
18
+ } from "./chunk-N3VGZTEJ.js";
19
19
  import "./chunk-4BKXL7SM.js";
20
- import "./chunk-U5MY24UZ.js";
21
- import "./chunk-SLSWPBK3.js";
20
+ import "./chunk-MM3F43H6.js";
22
21
  import "./chunk-KHYD3WXE.js";
22
+ import "./chunk-SLSWPBK3.js";
23
23
  import "./chunk-VNNYHW6N.js";
24
24
  import "./chunk-OVWE4E46.js";
25
25
  import "./chunk-PDX44BCA.js";
@@ -3,24 +3,24 @@ import {
3
3
  ToolRegistry,
4
4
  googleSearchContext,
5
5
  truncateOutput
6
- } from "./chunk-E5ICQT3P.js";
6
+ } from "./chunk-3HJW556L.js";
7
7
  import "./chunk-HDSKW7Q3.js";
8
8
  import "./chunk-ZWVIDFGY.js";
9
- import "./chunk-JOJRBV2K.js";
9
+ import "./chunk-62CD2T5F.js";
10
10
  import {
11
11
  runTool
12
- } from "./chunk-NFRTSL3N.js";
12
+ } from "./chunk-GRFQ2QD5.js";
13
13
  import {
14
14
  getDangerLevel
15
15
  } from "./chunk-NXXNLLSG.js";
16
16
  import "./chunk-2ZD3YTVM.js";
17
17
  import {
18
18
  SUBAGENT_ALLOWED_TOOLS
19
- } from "./chunk-B3LFGPU2.js";
19
+ } from "./chunk-N3VGZTEJ.js";
20
20
  import "./chunk-4BKXL7SM.js";
21
- import "./chunk-U5MY24UZ.js";
22
- import "./chunk-SLSWPBK3.js";
21
+ import "./chunk-MM3F43H6.js";
23
22
  import "./chunk-KHYD3WXE.js";
23
+ import "./chunk-SLSWPBK3.js";
24
24
  import "./chunk-VNNYHW6N.js";
25
25
  import "./chunk-OVWE4E46.js";
26
26
  import "./chunk-PDX44BCA.js";
@@ -199,6 +199,10 @@ function handleServerMessage(msg) {
199
199
  case 'memory_rebuild_done': renderMemoryRebuildDone(msg); break;
200
200
  case 'auth_required': showAuthScreen(msg.hasUsers); break;
201
201
  case 'auth_result': handleAuthResult(msg); break;
202
+ case 'hub_started': hubStarted(msg); break;
203
+ case 'hub_event': hubEvent(msg.event); break;
204
+ case 'hub_done': hubDone(msg); break;
205
+ case 'hub_review': hubReview(msg); break;
202
206
  case 'info': addInfoMessage(msg.message); break;
203
207
  case 'error': addErrorMessage(msg.message); hideRoundProgress(); clearAllToolTimers(); setProcessing(false); break;
204
208
  case 'round_progress': handleRoundProgress(msg); break;
@@ -3109,3 +3113,172 @@ document.querySelectorAll('.sidebar-tab').forEach(btn => {
3109
3113
  btn.addEventListener('click', () => initMemoryPanel());
3110
3114
  }
3111
3115
  });
3116
+
3117
+ // ── Hub multi-agent room (P4) ─────────────────────────────────────
3118
+ // DaisyUI semantic colors map for role legend dots / borders.
3119
+ const HUB_COLORS = { cyan: '#22d3ee', green: '#4ade80', red: '#f87171', magenta: '#e879f9', yellow: '#facc15', blue: '#60a5fa', white: '#e5e7eb' };
3120
+ let hubRoles = {}; // roleId → { name, color, provider, model }
3121
+ let hubStreamEl = null; // current streaming agent message element
3122
+ let hubStreamRoleId = null;
3123
+
3124
+ function hubColor(c) { return HUB_COLORS[c] || '#9ca3af'; }
3125
+
3126
+ window.startHub = function () {
3127
+ const topic = (document.getElementById('hub-topic').value || '').trim();
3128
+ if (!topic) { document.getElementById('hub-status').textContent = 'Enter a topic first.'; return; }
3129
+ send({
3130
+ type: 'hub_start',
3131
+ topic,
3132
+ preset: document.getElementById('hub-preset').value,
3133
+ maxRounds: parseInt(document.getElementById('hub-rounds').value, 10) || 6,
3134
+ mix: document.getElementById('hub-mix').checked,
3135
+ vote: document.getElementById('hub-vote').checked,
3136
+ steer: document.getElementById('hub-steer').checked,
3137
+ });
3138
+ document.getElementById('hub-start-btn').classList.add('hidden');
3139
+ document.getElementById('hub-abort-btn').classList.remove('hidden');
3140
+ document.getElementById('hub-status').textContent = 'Starting…';
3141
+ };
3142
+
3143
+ window.abortHub = function () {
3144
+ send({ type: 'hub_abort' });
3145
+ document.getElementById('hub-status').textContent = 'Stopping after current turn…';
3146
+ };
3147
+
3148
+ function hubStarted(msg) {
3149
+ hubRoles = {};
3150
+ for (const r of msg.roles) hubRoles[r.id] = r;
3151
+ hubStreamEl = null; hubStreamRoleId = null;
3152
+
3153
+ const legend = msg.roles.map(r => {
3154
+ const backend = r.provider ? `${r.provider}${r.model ? ':' + r.model : ''}` : '';
3155
+ return `<span class="inline-flex items-center gap-1 mr-2">
3156
+ <span style="width:.6rem;height:.6rem;border-radius:9999px;background:${hubColor(r.color)};display:inline-block"></span>
3157
+ <b>${escapeHtml(r.name)}</b><span class="opacity-50">${backend ? ' · ' + escapeHtml(backend) : ''}</span></span>`;
3158
+ }).join('');
3159
+
3160
+ const el = document.createElement('div');
3161
+ el.className = 'hub-banner border border-base-content/10 rounded-lg p-3 my-2 bg-base-200';
3162
+ el.innerHTML = `<div class="font-bold mb-1">🏛 ${escapeHtml(msg.topic)}</div>
3163
+ <div class="text-xs opacity-70 mb-1">up to ${msg.maxRounds} rounds</div>
3164
+ <div class="text-xs">${legend}</div>`;
3165
+ messagesEl.appendChild(el);
3166
+ scrollToBottom();
3167
+ }
3168
+
3169
+ function hubEvent(ev) {
3170
+ switch (ev.type) {
3171
+ case 'round_start': {
3172
+ const el = document.createElement('div');
3173
+ el.className = 'hub-round text-xs opacity-50 text-center my-2';
3174
+ el.textContent = `── Round ${ev.round}/${ev.maxRounds} ──`;
3175
+ messagesEl.appendChild(el);
3176
+ scrollToBottom();
3177
+ break;
3178
+ }
3179
+ case 'agent_token': {
3180
+ if (hubStreamRoleId !== ev.roleId || !hubStreamEl) {
3181
+ const role = hubRoles[ev.roleId] || { name: ev.roleId, color: 'white' };
3182
+ hubStreamEl = document.createElement('div');
3183
+ hubStreamEl.className = 'hub-msg border-l-2 pl-2 my-2';
3184
+ hubStreamEl.style.borderColor = hubColor(role.color);
3185
+ hubStreamEl.innerHTML = `<div class="text-xs font-bold mb-1" style="color:${hubColor(role.color)}">${escapeHtml(role.name)}</div><div class="hub-body text-sm"></div>`;
3186
+ messagesEl.appendChild(hubStreamEl);
3187
+ hubStreamRoleId = ev.roleId;
3188
+ hubStreamEl._raw = '';
3189
+ }
3190
+ hubStreamEl._raw += ev.token;
3191
+ renderMarkdown(hubStreamEl.querySelector('.hub-body'), hubStreamEl._raw);
3192
+ scrollToBottom();
3193
+ break;
3194
+ }
3195
+ case 'agent_spoke':
3196
+ // Streaming already rendered the body; finalize the stream slot.
3197
+ hubStreamEl = null; hubStreamRoleId = null;
3198
+ break;
3199
+ case 'agent_passed': {
3200
+ const role = hubRoles[ev.roleId] || { name: ev.roleId };
3201
+ const el = document.createElement('div');
3202
+ el.className = 'text-xs opacity-40 italic pl-2 my-1';
3203
+ el.textContent = `${role.name} passed`;
3204
+ messagesEl.appendChild(el);
3205
+ hubStreamEl = null; hubStreamRoleId = null;
3206
+ break;
3207
+ }
3208
+ case 'converge_vote': {
3209
+ const el = document.createElement('div');
3210
+ el.className = 'text-xs text-center opacity-60 my-1';
3211
+ el.textContent = `🗳 Convergence vote: ${ev.converged}/${ev.total} marked [CONVERGED]`;
3212
+ messagesEl.appendChild(el);
3213
+ break;
3214
+ }
3215
+ case 'discussion_end': {
3216
+ const labels = { consensus: 'Consensus reached', vote_converged: '✓ Converged by vote', max_rounds: 'Max rounds reached', human_stop: 'Stopped', user_interrupt: 'Interrupted' };
3217
+ const el = document.createElement('div');
3218
+ el.className = 'text-xs text-center font-semibold my-2 text-success';
3219
+ el.textContent = labels[ev.reason] || ev.reason;
3220
+ messagesEl.appendChild(el);
3221
+ break;
3222
+ }
3223
+ case 'summary': {
3224
+ const el = document.createElement('div');
3225
+ el.className = 'hub-summary border border-success/40 rounded-lg p-3 my-2 bg-success/5';
3226
+ el.innerHTML = `<div class="font-bold mb-1">📋 Summary</div><div class="hub-body text-sm"></div>`;
3227
+ messagesEl.appendChild(el);
3228
+ renderMarkdown(el.querySelector('.hub-body'), ev.content);
3229
+ scrollToBottom();
3230
+ break;
3231
+ }
3232
+ case 'error': {
3233
+ addErrorMessage(`Hub${ev.roleId ? ' (' + ev.roleId + ')' : ''}: ${ev.message}`);
3234
+ hubStreamEl = null; hubStreamRoleId = null;
3235
+ break;
3236
+ }
3237
+ }
3238
+ }
3239
+
3240
+ function hubDone(msg) {
3241
+ document.getElementById('hub-start-btn').classList.remove('hidden');
3242
+ document.getElementById('hub-abort-btn').classList.add('hidden');
3243
+ const status = document.getElementById('hub-status');
3244
+ if (msg.saved && msg.sessionId) {
3245
+ status.innerHTML = `Saved. <a class="link link-primary" onclick="send({type:'command',name:'session',args:['load','${msg.sessionId}']})">Open session</a>`;
3246
+ } else {
3247
+ status.textContent = msg.saved ? 'Saved to history.' : 'Done (not saved).';
3248
+ }
3249
+ hubStreamEl = null; hubStreamRoleId = null;
3250
+ scrollToBottom();
3251
+ }
3252
+
3253
+ function hubReview(msg) {
3254
+ // Inline between-rounds steering card. Submitting guidance continues with it
3255
+ // injected; Continue (empty) proceeds; Stop ends early and jumps to summary.
3256
+ const card = document.createElement('div');
3257
+ card.className = 'hub-review border border-info/40 rounded-lg p-3 my-2 bg-info/5';
3258
+ card.innerHTML = `
3259
+ <div class="text-sm font-semibold mb-2">🧭 Round ${msg.round}/${msg.maxRounds} done — steer the next round?</div>
3260
+ <input type="text" class="input input-bordered input-sm w-full mb-2" placeholder="Optional guidance for all agents…">
3261
+ <div class="flex gap-2">
3262
+ <button class="btn btn-primary btn-sm flex-1">Continue ▶</button>
3263
+ <button class="btn btn-error btn-outline btn-sm">Stop ⏹</button>
3264
+ </div>`;
3265
+ const input = card.querySelector('input');
3266
+ const [contBtn, stopBtn] = card.querySelectorAll('button');
3267
+ let answered = false;
3268
+ const finish = (action) => {
3269
+ if (answered) return;
3270
+ answered = true;
3271
+ const message = input.value.trim();
3272
+ send({ type: 'hub_steer', requestId: msg.requestId, action, message: message || undefined });
3273
+ card.querySelectorAll('input,button').forEach(e => e.disabled = true);
3274
+ card.querySelector('.text-sm').textContent = action === 'stop'
3275
+ ? '🧭 Stopping…'
3276
+ : (message ? `🧭 Steered: ${message}` : '🧭 Continuing…');
3277
+ };
3278
+ contBtn.addEventListener('click', () => finish('continue'));
3279
+ stopBtn.addEventListener('click', () => finish('stop'));
3280
+ input.addEventListener('keydown', (e) => { if (e.key === 'Enter') finish('continue'); });
3281
+ messagesEl.appendChild(card);
3282
+ scrollToBottom();
3283
+ setTimeout(() => input.focus(), 50);
3284
+ }
@@ -186,6 +186,37 @@
186
186
  <div class="text-xs opacity-40 text-center py-4">Type a query above to recall past chats.</div>
187
187
  </div>
188
188
  </div>
189
+
190
+ <!-- Hub tab (P4) — multi-agent brainstorm room -->
191
+ <div id="tab-hub" class="sidebar-tab-content flex flex-col flex-1 overflow-hidden hidden">
192
+ <div class="p-2 flex flex-col gap-2 overflow-y-auto">
193
+ <p class="text-xs opacity-60 leading-snug">Several AI agents discuss a topic in the chat area, then a structured summary is saved to history (open it and hit 🎬 to replay).</p>
194
+ <textarea id="hub-topic" class="textarea textarea-bordered textarea-sm w-full" rows="2" placeholder="Discussion topic…"></textarea>
195
+ <label class="text-xs opacity-70">Preset
196
+ <select id="hub-preset" class="select select-bordered select-xs w-full mt-1">
197
+ <option value="brainstorm">brainstorm — 创意者·分析师·执行者</option>
198
+ <option value="tech-review">tech-review — 架构师·开发者·安全</option>
199
+ <option value="code-review">code-review — 质量·性能·测试</option>
200
+ <option value="debate">debate — 正方·反方·主持人</option>
201
+ </select>
202
+ </label>
203
+ <label class="text-xs opacity-70">Max rounds
204
+ <input id="hub-rounds" type="number" min="1" max="20" value="6" class="input input-bordered input-xs w-full mt-1">
205
+ </label>
206
+ <label class="flex items-center gap-2 text-xs cursor-pointer">
207
+ <input id="hub-mix" type="checkbox" class="checkbox checkbox-xs"> Mix providers (Claude vs GPT vs DeepSeek…)
208
+ </label>
209
+ <label class="flex items-center gap-2 text-xs cursor-pointer">
210
+ <input id="hub-vote" type="checkbox" class="checkbox checkbox-xs" checked> Convergence voting (2/3 ends early)
211
+ </label>
212
+ <label class="flex items-center gap-2 text-xs cursor-pointer">
213
+ <input id="hub-steer" type="checkbox" class="checkbox checkbox-xs"> Human steering (pause each round to guide/stop)
214
+ </label>
215
+ <button id="hub-start-btn" class="btn btn-primary btn-sm w-full" onclick="startHub()">🏛 Start discussion</button>
216
+ <button id="hub-abort-btn" class="btn btn-error btn-outline btn-xs w-full hidden" onclick="abortHub()">⏹ Stop</button>
217
+ <div id="hub-status" class="text-xs opacity-60"></div>
218
+ </div>
219
+ </div>
189
220
  </aside>
190
221
  <!-- Sidebar resize handle -->
191
222
  <div id="sidebar-resize" class="sidebar-resize-handle" title="Drag to resize sidebar"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.155",
3
+ "version": "0.4.157",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- redactString
4
- } from "./chunk-SLSWPBK3.js";
5
2
  import {
6
3
  EMBEDDING_DIM,
7
4
  embed,
8
5
  embedOne
9
6
  } from "./chunk-KHYD3WXE.js";
7
+ import {
8
+ redactString
9
+ } from "./chunk-SLSWPBK3.js";
10
10
 
11
11
  // src/memory/chat-index.ts
12
12
  import fs from "fs";