multiarena 0.1.3 → 0.1.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.1.4
4
+
5
+ ### 关键升级
6
+
7
+ - **团队共享上下文** — 团队模式新增 `teamMessages` 共享消息线程,所有模型读写同一份对话历史。审议引擎从共享历史读取上下文而非零基础构建,每轮产出自动 push 回共享数组
8
+ - **私有思考阶段(Think Phase)** — 审议每轮之前模型先进行私有分析:审视当前文档优劣、识别问题、规划修改方案,再基于分析结果动手修改。思考内容不进入共享上下文(其他模型不可见),防止锚定偏差,同时拦截幻觉
9
+ - **"继续修改"Bug 修复** — 审议完成后在团队概览输入新要求,新一轮审议通过共享上下文自然看到上一轮全部产出和用户新指令,不再出现"缺失必要信息"错误
10
+ - **上下文管理文档** — 新增 `docs/multiarena-context-and-prompts.md`,详细记录模型上下文管理架构和系统提示词设计
11
+
12
+ ### 关键 Bug 修复
13
+
14
+ - 删除 `[团队审议结果]` 手动注入逻辑 — 共享上下文已包含所有审议产出,无需事后打补丁
15
+ - 团队私聊(Tab 到模型)使用 `teamMessages` 作为上下文,与审议共享同一消息线程
16
+
17
+ ### 测试
18
+
19
+ - 新增"继续修改"端到端测试(第一轮审议产出 → 用户追加消息 → 第二轮审议能访问完整上文)
20
+ - 全部 30 个测试文件、312 个测试用例通过
21
+
22
+ ---
23
+
3
24
  ## v0.1.3
4
25
 
5
26
  ### 关键升级
@@ -1,4 +1,5 @@
1
1
  import type { ModelConfig } from "../config/types.js";
2
+ import type { Message } from "../provider/types.js";
2
3
  export type RoundRole = "draft" | "revise" | "polish" | "review";
3
4
  export interface DeliberationRoundConfig {
4
5
  modelName: string;
@@ -6,7 +7,7 @@ export interface DeliberationRoundConfig {
6
7
  config: ModelConfig;
7
8
  }
8
9
  export interface DeliberationProgress {
9
- type: "round_start" | "text" | "round_end" | "done" | "error";
10
+ type: "think_start" | "think_text" | "think_end" | "round_start" | "text" | "round_end" | "done" | "error";
10
11
  round: number;
11
12
  totalRounds: number;
12
13
  modelName?: string;
@@ -36,7 +37,7 @@ export interface DeliberationResult {
36
37
  * round output for context. No central synthesizer — the document
37
38
  * emerges through sequential refinement.
38
39
  */
39
- export declare function runDeliberation(task: string, roundConfigs: DeliberationRoundConfig[], constraint?: string, worktreePath?: string): AsyncGenerator<DeliberationProgress>;
40
+ export declare function runDeliberation(sharedMessages: Message[], roundConfigs: DeliberationRoundConfig[], constraint?: string, worktreePath?: string): AsyncGenerator<DeliberationProgress>;
40
41
  /** Build round configs with mirror pattern: A→B→C→B→A. */
41
42
  export declare function autoAssignRounds(modelNames: string[], models: Record<string, ModelConfig>): DeliberationRoundConfig[];
42
43
  /** Human-readable label for a round role. */
@@ -98,6 +98,61 @@ ${previousDocument}
98
98
  return "";
99
99
  }
100
100
  }
101
+ /**
102
+ * Build a private "think" prompt for the given role.
103
+ * The model analyses the current state before acting — this output is NOT
104
+ * shared with other models, but is fed back into the same model's main
105
+ * round system prompt so its public output is informed by private reasoning.
106
+ */
107
+ function buildThinkPrompt(role, task, isFirstRound, constraint, previousDocument) {
108
+ const docBlock = previousDocument
109
+ ? `\n\n## 当前文档(请仔细分析)\n${previousDocument}`
110
+ : "";
111
+ const taskBlock = `\n\n## 用户任务/反馈\n${task}`;
112
+ const constraintBlock = constraint
113
+ ? `\n\n## 约束文档\n${constraint}`
114
+ : "";
115
+ switch (role) {
116
+ case "draft":
117
+ return `你即将以**起草者**的身份撰写一份初稿。在此之前,请先进行私有分析:
118
+ ${taskBlock}${constraintBlock}
119
+
120
+ 请简短回答以下问题(用你自己的话,不要长篇大论):
121
+ 1. 任务的核心目标是什么?需要覆盖哪些关键要点?
122
+ 2. 文档应该是什么结构?(章节/段落规划)
123
+ 3. 有什么需要特别注意的约束或陷阱?
124
+ 4. 有什么地方信息不足,需要合理假设的?`;
125
+ case "revise":
126
+ return `你即将以**修订者**的身份修改一份文档。在此之前,请先进行私有分析:
127
+ ${taskBlock}${constraintBlock}${docBlock}
128
+
129
+ 请简短回答以下问题:
130
+ 1. 这份文档的优点是什么?哪些部分写得不错?
131
+ 2. 存在哪些问题?(偏离任务、遗漏要点、逻辑不严谨、表达不清、潜在的事实错误或幻觉)
132
+ 3. 对照约束文档,哪些地方违反了约束?
133
+ 4. 你计划做哪些具体修改?按优先级列出。${isFirstRound ? "\n注意:这是第一轮修订,你看到的是初稿。重点关注初稿是否忠实地回应了用户的任务。" : ""}`;
134
+ case "polish":
135
+ return `你即将以**润色者**的身份最终润色一份文档。在此之前,请先进行私有分析:
136
+ ${taskBlock}${constraintBlock}${docBlock}
137
+
138
+ 请简短回答以下问题:
139
+ 1. 文档的整体语言质量如何?(流畅度、可读性、语气一致性)
140
+ 2. 有哪些表达可以更优雅或更精准?
141
+ 3. 对照约束文档,还有什么需要修正的?
142
+ 4. 你独有的补充见解是什么?(如果有的话)`;
143
+ case "review":
144
+ return `你即将以**终审者**的身份做最终审查。在此之前,请先进行私有分析:
145
+ ${taskBlock}${constraintBlock}${docBlock}
146
+
147
+ 请简短回答以下问题:
148
+ 1. 经过多轮修改后,文档是否偏离了用户的原始意图?
149
+ 2. 逐条对照约束文档检查——还有违规项吗?
150
+ 3. 有没有任何模型引入了事实错误或幻觉?
151
+ 4. 最终交付前,还有什么必须清理或修复的?`;
152
+ default:
153
+ return "";
154
+ }
155
+ }
101
156
  /**
102
157
  * Run the R2D2 (Round-Robin Deliberative Drafting) pipeline.
103
158
  *
@@ -107,14 +162,17 @@ ${previousDocument}
107
162
  * round output for context. No central synthesizer — the document
108
163
  * emerges through sequential refinement.
109
164
  */
110
- export async function* runDeliberation(task, roundConfigs, constraint, worktreePath) {
165
+ export async function* runDeliberation(sharedMessages, roundConfigs, constraint, worktreePath) {
111
166
  const documents = [];
112
167
  const totalRounds = roundConfigs.length;
168
+ // Derive the task from the last user message in the shared context.
169
+ const lastUser = [...sharedMessages].reverse().find((m) => m.role === "user");
170
+ const task = lastUser?.content ?? "";
113
171
  for (let i = 0; i < roundConfigs.length; i++) {
114
172
  const rc = roundConfigs[i];
115
173
  const previousDocument = i > 0 ? documents[i - 1] : undefined;
116
174
  const draftAuthor = i > 0 ? roundConfigs[0].modelName : undefined;
117
- const systemPrompt = buildSystemPrompt(rc.role, task, i === roundConfigs.length - 1, constraint, previousDocument, draftAuthor);
175
+ const isFinalRound = i === roundConfigs.length - 1;
118
176
  yield {
119
177
  type: "round_start",
120
178
  round: i + 1,
@@ -122,14 +180,74 @@ export async function* runDeliberation(task, roundConfigs, constraint, worktreeP
122
180
  modelName: rc.modelName,
123
181
  role: rc.role,
124
182
  };
125
- const messages = [
126
- {
127
- role: "user",
128
- content: rc.role === "draft"
129
- ? `请起草以下文档:\n\n${task}`
130
- : "请根据你的角色要求和上述文档内容,输出修改后的完整文档。不要输出任何前言或后记,直接输出文档内容。",
131
- },
132
- ];
183
+ // ── Private Think Phase ──────────────────────────────────────
184
+ // The model analyses the current state privately before acting.
185
+ // This output is NOT shared with other models — it only informs
186
+ // this model's own main round via the system prompt.
187
+ let thinkOutput = "";
188
+ const thinkPrompt = buildThinkPrompt(rc.role, task, i === 0, constraint, previousDocument);
189
+ if (thinkPrompt) {
190
+ yield {
191
+ type: "think_start",
192
+ round: i + 1,
193
+ totalRounds,
194
+ modelName: rc.modelName,
195
+ role: rc.role,
196
+ };
197
+ try {
198
+ const thinkStream = runTurn({
199
+ modelName: rc.modelName,
200
+ config: rc.config,
201
+ messages: [{ role: "user", content: "请按上述要求进行分析。用简洁的语言回答,不要长篇大论。" }],
202
+ systemPrompt: thinkPrompt,
203
+ tools: [],
204
+ registry: new ToolRegistry(),
205
+ permission: new PermissionManager(),
206
+ worktreePath: worktreePath ?? process.cwd(),
207
+ });
208
+ for await (const event of thinkStream) {
209
+ if (event.type === "text") {
210
+ thinkOutput += event.content;
211
+ yield {
212
+ type: "think_text",
213
+ round: i + 1,
214
+ totalRounds,
215
+ modelName: rc.modelName,
216
+ role: rc.role,
217
+ content: event.content,
218
+ };
219
+ }
220
+ else if (event.type === "error") {
221
+ // Think failure is non-fatal — proceed without think context
222
+ thinkOutput = "";
223
+ break;
224
+ }
225
+ }
226
+ }
227
+ catch {
228
+ thinkOutput = "";
229
+ }
230
+ yield {
231
+ type: "think_end",
232
+ round: i + 1,
233
+ totalRounds,
234
+ modelName: rc.modelName,
235
+ role: rc.role,
236
+ };
237
+ }
238
+ // ── Main Round System Prompt (informed by private think) ──────
239
+ const thinkBlock = thinkOutput
240
+ ? `\n\n## 你的私有分析结果\n以下是你刚才对当前状态的分析。请基于这些洞察来完成你的任务:\n\n${thinkOutput}`
241
+ : "";
242
+ const systemPrompt = buildSystemPrompt(rc.role, task, isFinalRound, constraint, previousDocument, draftAuthor) + thinkBlock;
243
+ // Build messages from the shared context plus this round's role instruction.
244
+ const instruction = {
245
+ role: "user",
246
+ content: rc.role === "draft"
247
+ ? `请起草以下文档:\n\n${task}`
248
+ : "请根据你的角色要求和上述文档内容,输出修改后的完整文档。不要输出任何前言或后记,直接输出文档内容。",
249
+ };
250
+ const messages = [...sharedMessages, instruction];
133
251
  let buffer = "";
134
252
  try {
135
253
  const stream = runTurn({
@@ -180,6 +298,7 @@ export async function* runDeliberation(task, roundConfigs, constraint, worktreeP
180
298
  return;
181
299
  }
182
300
  documents.push(buffer);
301
+ sharedMessages.push({ role: "assistant", content: buffer });
183
302
  // Extract revision annotations for the process summary
184
303
  const revisionMatches = buffer.match(/\[修订:\s*([^\]]+?)\]/g) ?? [];
185
304
  const changeSamples = revisionMatches
@@ -16,12 +16,14 @@ export interface SessionSnapshot {
16
16
  }>;
17
17
  targetMode: TargetMode;
18
18
  worktreeBase: string;
19
+ teamMessages?: Message[];
19
20
  }
20
21
  export declare class Session {
21
22
  private state;
22
23
  constructor(config: ArenaConfig, worktreeBase: string, snapshot?: SessionSnapshot);
23
24
  get models(): ModelState[];
24
25
  get targetMode(): TargetMode;
26
+ get teamMessages(): Message[];
25
27
  /** Add a user message to the target model(s). Returns affected models. */
26
28
  addUserMessage(content: string): ModelState[];
27
29
  /** Append assistant response to a model's history */
@@ -10,6 +10,7 @@ export class Session {
10
10
  })),
11
11
  targetMode: snapshot.targetMode,
12
12
  worktreeBase: snapshot.worktreeBase,
13
+ teamMessages: snapshot.teamMessages ?? [],
13
14
  };
14
15
  }
15
16
  else {
@@ -30,6 +31,7 @@ export class Session {
30
31
  models,
31
32
  targetMode: { type: "broadcast" },
32
33
  worktreeBase,
34
+ teamMessages: [],
33
35
  };
34
36
  }
35
37
  }
@@ -39,6 +41,9 @@ export class Session {
39
41
  get targetMode() {
40
42
  return this.state.targetMode;
41
43
  }
44
+ get teamMessages() {
45
+ return this.state.teamMessages;
46
+ }
42
47
  /** Add a user message to the target model(s). Returns affected models. */
43
48
  addUserMessage(content) {
44
49
  if (this.state.targetMode.type === "broadcast") {
@@ -136,6 +141,7 @@ export class Session {
136
141
  })),
137
142
  targetMode: this.state.targetMode,
138
143
  worktreeBase: this.state.worktreeBase,
144
+ teamMessages: [...this.state.teamMessages],
139
145
  };
140
146
  }
141
147
  findModel(name) {
package/dist/ui/app.js CHANGED
@@ -62,6 +62,7 @@ export const App = ({ sessionId: initialSessionId }) => {
62
62
  // ── Deliberation state ─────────────────────────────────────────
63
63
  const [deliberationProgress, setDeliberationProgress] = useState(null);
64
64
  const [deliberationDocument, setDeliberationDocument] = useState("");
65
+ const [deliberationThinkText, setDeliberationThinkText] = useState("");
65
66
  const [deliberationRounds, setDeliberationRounds] = useState([]);
66
67
  const [deliberationScrollOffset, setDeliberationScrollOffset] = useState(0);
67
68
  const deliberatingRef = useRef(false);
@@ -377,7 +378,7 @@ export const App = ({ sessionId: initialSessionId }) => {
377
378
  }
378
379
  });
379
380
  // ── Deliberation runner ───────────────────────────────────────
380
- const runDeliberationPipeline = useCallback(async (task) => {
381
+ const runDeliberationPipeline = useCallback(async () => {
381
382
  if (deliberatingRef.current)
382
383
  return;
383
384
  deliberatingRef.current = true;
@@ -434,12 +435,24 @@ export const App = ({ sessionId: initialSessionId }) => {
434
435
  setDeliberationRounds([]);
435
436
  setDeliberationScrollOffset(0);
436
437
  let doc = "";
437
- const stream = runDeliberation(task, roundConfigs, constraint);
438
+ const stream = runDeliberation(session.teamMessages, roundConfigs, constraint);
438
439
  for await (const event of stream) {
439
440
  setDeliberationProgress(event);
440
- if (event.type === "round_start") {
441
+ if (event.type === "think_start") {
442
+ setDeliberationThinkText("");
443
+ setDeliberationDocument("");
444
+ }
445
+ else if (event.type === "think_text" && event.content) {
446
+ setDeliberationThinkText((prev) => prev + event.content);
447
+ }
448
+ else if (event.type === "think_end") {
449
+ // Think done — keep think text in state for UI reference,
450
+ // main round will populate deliberationDocument next.
451
+ }
452
+ else if (event.type === "round_start") {
441
453
  doc = "";
442
454
  setDeliberationDocument("");
455
+ setDeliberationThinkText("");
443
456
  setDeliberationRounds((prev) => [
444
457
  ...prev,
445
458
  { round: event.round, modelName: event.modelName, role: event.role },
@@ -467,17 +480,6 @@ export const App = ({ sessionId: initialSessionId }) => {
467
480
  return;
468
481
  }
469
482
  }
470
- // Keep the final document visible; user presses Esc to dismiss
471
- // Inject the final document into each model's message history
472
- // so they can discuss it when the user switches to chat mode.
473
- if (doc) {
474
- const contextMsg = `[团队审议结果]\n\n以下是你与其他模型协作完成的最终文档。用户可以就此文档与你讨论。\n\n---\n${doc}\n---`;
475
- for (const m of session.models) {
476
- if (!m.muted) {
477
- m.messages.push({ role: "user", content: contextMsg });
478
- }
479
- }
480
- }
481
483
  deliberatingRef.current = false;
482
484
  }, [session.models, config]);
483
485
  // ── Merge runner ──────────────────────────────────────────────
@@ -572,18 +574,71 @@ export const App = ({ sessionId: initialSessionId }) => {
572
574
  const r = reduceSubmitInTeam(currentModeState(), isOverview());
573
575
  if (r.action === "block")
574
576
  return;
577
+ inputHistoryRef.current.push(trimmed);
578
+ historyIdxRef.current = -1;
579
+ setInput("");
580
+ // All team interactions share session.teamMessages as context.
581
+ session.teamMessages.push({ role: "user", content: trimmed });
575
582
  if (r.action === "deliberate") {
576
- inputHistoryRef.current.push(trimmed);
577
- historyIdxRef.current = -1;
578
- setInput("");
579
583
  setDeliberationDocument("");
580
584
  // Reset target to overview so OutputArea shows deliberation progress
581
585
  session.setTarget({ type: "broadcast" });
582
586
  setTargetVersion((v) => v + 1);
583
- runDeliberationPipeline(trimmed);
587
+ runDeliberationPipeline();
588
+ return;
589
+ }
590
+ // r.action === "route_normally" — team directed chat.
591
+ // The user is drilling into a specific model. Use shared team context.
592
+ const targetModel = session.targetMode.type === "directed"
593
+ ? session.targetMode.modelName
594
+ : null;
595
+ if (!targetModel)
596
+ return;
597
+ const tm = session.models.find((m) => m.name === targetModel && !m.muted);
598
+ if (!tm)
584
599
  return;
600
+ const tmc = config.models[targetModel];
601
+ if (!tmc) {
602
+ tm.buffer = `[Error: No config for model "${targetModel}"]`;
603
+ setModelStates([...session.models]);
604
+ return;
605
+ }
606
+ // Worktree setup
607
+ const taskId = Date.now().toString(36);
608
+ const wtManager = new WorktreeManager(process.cwd());
609
+ await wtManager.setup(taskId, [targetModel]);
610
+ const wtPath = wtManager.getWorktreePath(targetModel) ?? process.cwd();
611
+ tm.isStreaming = true;
612
+ tm.buffer = "";
613
+ setModelStates([...session.models]);
614
+ const stream = runTurn({
615
+ modelName: targetModel,
616
+ config: tmc,
617
+ messages: session.teamMessages,
618
+ systemPrompt: makeSystemPrompt(targetModel, tmc.provider),
619
+ tools: toolRegistry.getDefinitions(),
620
+ registry: toolRegistry,
621
+ permission: permissionManager,
622
+ worktreePath: wtPath,
623
+ });
624
+ for await (const event of stream) {
625
+ if (event.type === "text") {
626
+ tm.buffer += event.content;
627
+ }
628
+ else if (event.type === "done") {
629
+ tm.usage.input += event.usage.input;
630
+ tm.usage.output += event.usage.output;
631
+ tm.isStreaming = false;
632
+ }
633
+ else if (event.type === "error") {
634
+ tm.buffer += `\n[Error: ${event.message}]`;
635
+ tm.isStreaming = false;
636
+ }
637
+ setModelStates([...session.models]);
585
638
  }
586
- // r.action === "route_normally" — send as directed/broadcast message
639
+ await wtManager.cleanup(taskId);
640
+ saveCurrentSession();
641
+ return;
587
642
  }
588
643
  // ── Team mode toggle ────────────────────────────────────
589
644
  if (trimmed === "/team" || trimmed === "/t") {
@@ -718,6 +773,6 @@ broadcast = true`;
718
773
  "\u26A0 ",
719
774
  w.message))))),
720
775
  React.createElement(Box, { flexGrow: 1 },
721
- React.createElement(OutputArea, { models: modelStates, targetMode: session.targetMode, scrollOffsets: scrollOffsets, comparisonModel: comparisonModel, terminalWidth: terminalWidth, deliberationProgress: deliberationProgress, deliberationDocument: deliberationDocument, deliberationRounds: deliberationRounds, teamMode: teamMode, deliberationScrollOffset: deliberationScrollOffset })),
776
+ React.createElement(OutputArea, { models: modelStates, targetMode: session.targetMode, scrollOffsets: scrollOffsets, comparisonModel: comparisonModel, terminalWidth: terminalWidth, deliberationProgress: deliberationProgress, deliberationDocument: deliberationDocument, deliberationThinkText: deliberationThinkText, deliberationRounds: deliberationRounds, teamMode: teamMode, deliberationScrollOffset: deliberationScrollOffset })),
722
777
  React.createElement(InputBar, { models: modelStates, activeModelName: activeModelName, prefix: targetPrefix, value: input, onChange: handleInputChange, onSubmit: handleSubmit })));
723
778
  };
@@ -10,6 +10,7 @@ export interface RoundSummary {
10
10
  interface Props {
11
11
  progress: DeliberationProgress;
12
12
  document: string;
13
+ thinkText?: string;
13
14
  rounds: RoundSummary[];
14
15
  scrollOffset?: number;
15
16
  }
@@ -11,11 +11,12 @@ function spinner(frame) {
11
11
  const chars = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
12
12
  return chars[frame % chars.length] ?? ".";
13
13
  }
14
- export const DeliberationView = ({ progress, document, rounds, scrollOffset = 0 }) => {
14
+ export const DeliberationView = ({ progress, document, thinkText = "", rounds, scrollOffset = 0 }) => {
15
15
  const role = progress.role ?? "draft";
16
16
  const roleColor = ROLE_COLORS[role];
17
17
  const isActive = progress.type !== "done" && progress.type !== "error";
18
18
  const isDone = progress.type === "done";
19
+ const isThinking = progress.type === "think_start" || progress.type === "think_text";
19
20
  const spin = spinner(Date.now() % 10);
20
21
  const allLines = document ? document.split("\n") : [];
21
22
  const lines = allLines.slice(scrollOffset);
@@ -40,12 +41,17 @@ export const DeliberationView = ({ progress, document, rounds, scrollOffset = 0
40
41
  React.createElement(Box, { flexDirection: "row" },
41
42
  React.createElement(Text, { dimColor: true }, r.modelName))));
42
43
  })),
43
- isActive && (React.createElement(Box, { marginBottom: 1 },
44
- React.createElement(Text, { color: roleColor },
45
- roundLabel(role),
46
- "\u4E2D \u2014 ",
47
- progress.modelName,
48
- " \u2014 \u6B63\u5728\u751F\u6210\u2026"))),
44
+ isActive && (React.createElement(Box, { marginBottom: 1 }, isThinking ? (React.createElement(Text, { dimColor: true },
45
+ "\uD83D\uDCAD \u601D\u8003\u4E2D \u2014 ",
46
+ progress.modelName,
47
+ " \u2014 \u5206\u6790\u5F53\u524D\u72B6\u6001\u2026")) : (React.createElement(Text, { color: roleColor },
48
+ roundLabel(role),
49
+ "\u4E2D \u2014 ",
50
+ progress.modelName,
51
+ " \u2014 \u6B63\u5728\u751F\u6210\u2026")))),
52
+ thinkText && isThinking && (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
53
+ React.createElement(Text, { dimColor: true }, "\u2500\u2500 \u79C1\u6709\u5206\u6790 \u2500\u2500"),
54
+ thinkText.split("\n").map((line, i) => (React.createElement(Text, { key: i, dimColor: true }, line || " "))))),
49
55
  progress.type === "error" && (React.createElement(Box, { marginBottom: 1 },
50
56
  React.createElement(Text, { color: "red" },
51
57
  "\u9519\u8BEF\uFF1A",
@@ -15,6 +15,7 @@ interface Props {
15
15
  terminalWidth: number;
16
16
  deliberationProgress?: DeliberationProgress | null;
17
17
  deliberationDocument?: string;
18
+ deliberationThinkText?: string;
18
19
  deliberationRounds?: RoundSummary[];
19
20
  teamMode?: boolean;
20
21
  deliberationScrollOffset?: number;
@@ -3,7 +3,7 @@ import { Box, Text } from "ink";
3
3
  import { BroadcastSummary } from "./BroadcastSummary.js";
4
4
  import { ModelDetail } from "./ModelDetail.js";
5
5
  import { DeliberationView } from "./DeliberationView.js";
6
- export const OutputArea = ({ models, targetMode, scrollOffsets, comparisonModel, terminalWidth, deliberationProgress, deliberationDocument, deliberationRounds, teamMode, deliberationScrollOffset = 0, }) => {
6
+ export const OutputArea = ({ models, targetMode, scrollOffsets, comparisonModel, terminalWidth, deliberationProgress, deliberationDocument, deliberationThinkText, deliberationRounds, teamMode, deliberationScrollOffset = 0, }) => {
7
7
  // ── Team mode ──────────────────────────────────────────────────
8
8
  if (teamMode) {
9
9
  // During active deliberation (running): always show deliberation view
@@ -11,12 +11,12 @@ export const OutputArea = ({ models, targetMode, scrollOffsets, comparisonModel,
11
11
  deliberationProgress.type !== "done" &&
12
12
  deliberationProgress.type !== "error";
13
13
  if (isDeliberating) {
14
- return (React.createElement(DeliberationView, { progress: deliberationProgress, scrollOffset: deliberationScrollOffset, document: deliberationDocument ?? "", rounds: deliberationRounds ?? [] }));
14
+ return (React.createElement(DeliberationView, { progress: deliberationProgress, scrollOffset: deliberationScrollOffset, document: deliberationDocument ?? "", thinkText: deliberationThinkText ?? "", rounds: deliberationRounds ?? [] }));
15
15
  }
16
16
  // Team overview (broadcast target): show deliberation result or idle prompt
17
17
  if (targetMode.type === "broadcast") {
18
18
  if (deliberationProgress) {
19
- return (React.createElement(DeliberationView, { progress: deliberationProgress, document: deliberationDocument ?? "", rounds: deliberationRounds ?? [], scrollOffset: deliberationScrollOffset }));
19
+ return (React.createElement(DeliberationView, { progress: deliberationProgress, document: deliberationDocument ?? "", thinkText: deliberationThinkText ?? "", rounds: deliberationRounds ?? [], scrollOffset: deliberationScrollOffset }));
20
20
  }
21
21
  return (React.createElement(Box, { flexDirection: "column", flexGrow: 1, padding: 1 },
22
22
  React.createElement(Text, { bold: true }, "\u56E2\u961F\u6A21\u5F0F"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multiarena",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Terminal-native multi-model content generation — N models collaborate to produce the best document, analysis, script, or strategy",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -31,7 +31,7 @@
31
31
  ],
32
32
  "repository": {
33
33
  "type": "git",
34
- "url": "https://github.com/timgunnar/multiarena"
34
+ "url": "git+https://github.com/timgunnar/multiarena.git"
35
35
  },
36
36
  "scripts": {
37
37
  "prebuild": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",