@dmsdc-ai/aigentry-deliberation 0.0.22 → 0.0.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.
Files changed (3) hide show
  1. package/README.md +52 -0
  2. package/index.js +26 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -17,6 +17,12 @@ MCP Deliberation Server — Multi-session AI deliberation with smart speaker ord
17
17
  - **Cross-platform**: macOS (tmux + Terminal.app), Windows (Windows Terminal), Linux
18
18
  - **Obsidian archiving**: Auto-archive deliberation results to Obsidian vault
19
19
  - **Session monitoring**: Real-time tmux/terminal monitoring
20
+ - **Vote enforcement**: Automatic [AGREE]/[DISAGREE]/[CONDITIONAL] vote marker requirement
21
+ - **Dynamic CLI timeout**: Smart cold-start handling (180s first turn, 120s subsequent)
22
+ - **Runtime logging**: Session lifecycle event logging for observability
23
+ - **Resilient browser automation**: 5-stage degradation state machine with 60s SLO
24
+ - **Model routing**: Dynamic per-provider model selection based on prompt analysis
25
+ - **Role drift detection**: Structural heading markers + keyword analysis for accurate role inference
20
26
 
21
27
  ## Installation
22
28
 
@@ -121,6 +127,34 @@ Claude Code, Codex CLI, Gemini CLI의 MCP 설정을 자동 점검하고 문제
121
127
  | `researcher` | Data, benchmarks, references |
122
128
  | `free` | No role constraint (default) |
123
129
 
130
+ ### Supported CLI Speakers
131
+
132
+ | CLI | Command | Status |
133
+ |-----|---------|--------|
134
+ | Claude Code | `claude` | ✅ Tested |
135
+ | Codex CLI | `codex` | ✅ Tested |
136
+ | Gemini CLI | `gemini` | ✅ Tested |
137
+ | Aider | `aider` | 🔧 Supported |
138
+ | Cursor Agent | `cursor` | 🔧 Supported |
139
+ | OpenCode | `opencode` | 🔧 Supported |
140
+ | Continue | `continue` | 🔧 Supported |
141
+
142
+ ### Supported Browser LLMs
143
+
144
+ | Provider | Transport | Status |
145
+ |----------|-----------|--------|
146
+ | ChatGPT | CDP / Clipboard | ✅ Tested |
147
+ | Claude Web | CDP / Clipboard | ✅ Tested |
148
+ | Gemini Web | CDP / Clipboard | ✅ Tested |
149
+ | DeepSeek | CDP / Clipboard | ✅ Tested |
150
+ | Qwen | CDP / Clipboard | ✅ Tested |
151
+ | Poe | CDP / Clipboard | ✅ Tested |
152
+ | Copilot | CDP / Clipboard | 🔧 Supported |
153
+ | Perplexity | CDP / Clipboard | 🔧 Supported |
154
+ | Mistral | CDP / Clipboard | 🔧 Supported |
155
+ | Grok | CDP / Clipboard | 🔧 Supported |
156
+ | HuggingChat | CDP / Clipboard | 🔧 Supported |
157
+
124
158
  ### deliberation-gate (Superpowers Integration)
125
159
 
126
160
  Inserts multi-AI verification gates at key [superpowers](https://github.com/obra/superpowers) workflow decision points.
@@ -143,6 +177,24 @@ cp skills/deliberation-gate/SKILL.md ~/.claude/skills/deliberation-gate/SKILL.md
143
177
 
144
178
  **RFC:** [Prerequisites header for tool-dependent skills](https://github.com/obra/superpowers/issues/589)
145
179
 
180
+ ## What's New
181
+
182
+ ### v0.0.23
183
+ - **Vote enforcement**: Turn prompts now require [AGREE]/[DISAGREE]/[CONDITIONAL] markers for reliable consensus measurement
184
+ - **Dynamic CLI timeout**: First CLI invocation gets 180s (cold-start buffer), subsequent turns use default 120s
185
+ - **Runtime logging**: INFO-level lifecycle logging (SESSION_CREATED, TURN, CLI_TURN, SYNTHESIZED) to `runtime.log`
186
+ - **Role inference improvement**: Structural heading markers (e.g., `## 조사 결과` → researcher) with +5 weight prevent false role drift detection
187
+
188
+ ### v0.0.22
189
+ - **Security**: CDP `--remote-allow-origins` restricted to `127.0.0.1:9222` (was `*`)
190
+ - **Security**: Observer CORS restricted to localhost allowlist, server bound to `127.0.0.1`
191
+ - **Performance**: Async sleep for Chrome CDP initialization (was blocking event loop)
192
+ - **Bug fix**: Fabrication guard uses `detectCallerSpeaker()` instead of hardcoded `"claude"`
193
+ - **Bug fix**: CLI reviewer uses per-CLI invocation flags via `CLI_INVOCATION_HINTS`
194
+ - **Bug fix**: Windows monitor state directory path corrected
195
+ - **Memory**: SSE client Map cleanup on disconnect prevents memory leak
196
+ - **Code quality**: Removed unreachable dead code in browser-control-port
197
+
146
198
  ## aigentry Ecosystem
147
199
 
148
200
  aigentry-deliberation is one component of the unified aigentry platform. All packages work together to make AI decisions transparent and auditable.
package/index.js CHANGED
@@ -218,12 +218,25 @@ const ROLE_KEYWORDS = {
218
218
  researcher: /사례|데이터|연구|벤치마크|비교|논문|참고/,
219
219
  };
220
220
 
221
+ const ROLE_HEADING_MARKERS = {
222
+ critic: /^##?\s*(Critic|비판|약점|심각도|위험\s*분석)/m,
223
+ implementer: /^##?\s*(코드\s*스케치|구현|Implementation|제안\s*코드)/m,
224
+ mediator: /^##?\s*(합의|종합|중재|Consensus|Mediation)/m,
225
+ researcher: /^##?\s*(조사\s*결과|비교\s*분석|Research|사례\s*연구|근거)/m,
226
+ };
227
+
221
228
  function inferSuggestedRole(text) {
222
229
  const scores = {};
223
230
  for (const [role, pattern] of Object.entries(ROLE_KEYWORDS)) {
224
231
  const matches = (text.match(new RegExp(pattern, "g")) || []).length;
225
232
  if (matches > 0) scores[role] = matches;
226
233
  }
234
+ // Structural heading markers get extra weight (equivalent to 5 keyword matches)
235
+ for (const [role, pattern] of Object.entries(ROLE_HEADING_MARKERS)) {
236
+ if (pattern.test(text)) {
237
+ scores[role] = (scores[role] || 0) + 5;
238
+ }
239
+ }
227
240
  if (Object.keys(scores).length === 0) return "free";
228
241
  return Object.entries(scores).sort((a, b) => b[1] - a[1])[0][0];
229
242
  }
@@ -2237,6 +2250,7 @@ ${recent}
2237
2250
  [response_rule]
2238
2251
  - 위 토론 맥락을 반영해 ${speaker}의 이번 턴 응답만 작성
2239
2252
  - 마크다운 본문만 출력 (불필요한 머리말/꼬리말 금지)${speakerRole !== "free" ? `\n- 배정된 역할(${speakerRole})의 관점에서 분석하고 응답` : ""}
2253
+ - 응답 마지막에 반드시 [AGREE], [DISAGREE], 또는 [CONDITIONAL: 사유] 중 하나를 포함
2240
2254
  [/response_rule]
2241
2255
  [/deliberation_turn_request]
2242
2256
  `;
@@ -2305,6 +2319,7 @@ function submitDeliberationTurn({ session_id, speaker, content, turn_id, channel
2305
2319
  suggested_next_role: suggestedRole !== "free" ? suggestedRole : undefined,
2306
2320
  role_drift: roleDrift || undefined,
2307
2321
  });
2322
+ appendRuntimeLog("INFO", `TURN: ${state.id} | R${state.current_round} | speaker: ${normalizedSpeaker} | votes: ${votes.length > 0 ? votes.map(v => v.vote).join(",") : "none"} | channel: ${channel_used || "respond"}`);
2308
2323
 
2309
2324
  state.current_speaker = selectNextSpeaker(state);
2310
2325
 
@@ -2611,6 +2626,7 @@ server.tool(
2611
2626
  return ` - \`${p.speaker}\`: ${transport} (${p.type})`;
2612
2627
  }).join("\n");
2613
2628
 
2629
+ appendRuntimeLog("INFO", `SESSION_CREATED: ${sessionId} | topic: ${topic.slice(0, 60)} | speakers: ${speakerOrder.join(",")} | rounds: ${rounds}`);
2614
2630
  return {
2615
2631
  content: [{
2616
2632
  type: "text",
@@ -2989,6 +3005,10 @@ server.tool(
2989
3005
  }] };
2990
3006
  }
2991
3007
 
3008
+ // Dynamic timeout: first turn gets extra time for cold-start
3009
+ const speakerPriorTurns = state.log.filter(e => e.speaker === speaker).length;
3010
+ const effectiveTimeout = speakerPriorTurns === 0 ? Math.max(timeout_sec, 180) : timeout_sec;
3011
+
2992
3012
  const hint = CLI_INVOCATION_HINTS[speaker];
2993
3013
  if (!hint) {
2994
3014
  return { content: [{ type: "text", text: `speaker "${speaker}"에 대한 CLI 호출 정보가 없습니다. CLI_INVOCATION_HINTS에 등록되지 않은 speaker입니다.` }] };
@@ -3039,8 +3059,8 @@ server.tool(
3039
3059
 
3040
3060
  const timer = setTimeout(() => {
3041
3061
  child.kill("SIGTERM");
3042
- reject(new Error(`CLI 타임아웃 (${timeout_sec}초)`));
3043
- }, timeout_sec * 1000);
3062
+ reject(new Error(`CLI 타임아웃 (${effectiveTimeout}초)`));
3063
+ }, effectiveTimeout * 1000);
3044
3064
 
3045
3065
  child.stdout.on("data", (data) => { stdout += data.toString(); });
3046
3066
  child.stderr.on("data", (data) => { stderr += data.toString(); });
@@ -3072,6 +3092,7 @@ server.tool(
3072
3092
  });
3073
3093
 
3074
3094
  const elapsedMs = Date.now() - startTime;
3095
+ appendRuntimeLog("INFO", `CLI_TURN: ${resolved} | speaker: ${speaker} | cli: ${hint.cmd} | elapsed: ${elapsedMs}ms | response_len: ${response.length}`);
3075
3096
 
3076
3097
  if (!response) {
3077
3098
  return { content: [{ type: "text", text: `⚠️ CLI "${speaker}"가 빈 응답을 반환했습니다.` }] };
@@ -3233,6 +3254,8 @@ server.tool(
3233
3254
  return lockedResult;
3234
3255
  }
3235
3256
 
3257
+ appendRuntimeLog("INFO", `SYNTHESIZED: ${resolved} | turns: ${state.log.length} | rounds: ${state.max_rounds}`);
3258
+
3236
3259
  // 토론 종료 즉시 모니터 터미널(물리 Terminal 포함) 강제 종료
3237
3260
  closeMonitorTerminal(state.id, getSessionWindowIds(state));
3238
3261
 
@@ -3758,4 +3781,4 @@ if (__entryFile && path.resolve(__currentFile) === __entryFile) {
3758
3781
  }
3759
3782
 
3760
3783
  // ── Test exports (used by vitest) ──
3761
- export { selectNextSpeaker, loadRolePrompt, inferSuggestedRole, parseVotes, ROLE_KEYWORDS, loadRolePresets, applyRolePreset, detectDegradationLevels, formatDegradationReport, DEGRADATION_TIERS };
3784
+ export { selectNextSpeaker, loadRolePrompt, inferSuggestedRole, parseVotes, ROLE_KEYWORDS, ROLE_HEADING_MARKERS, loadRolePresets, applyRolePreset, detectDegradationLevels, formatDegradationReport, DEGRADATION_TIERS };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dmsdc-ai/aigentry-deliberation",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "description": "MCP Deliberation Server — Multi-session AI deliberation with smart speaker ordering and persona roles",
5
5
  "type": "module",
6
6
  "license": "MIT",