@faviovazquez/deliberate 0.1.0
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/BRAINSTORM.md +300 -0
- package/CHANGELOG.md +26 -0
- package/LICENSE +21 -0
- package/README.md +229 -0
- package/SKILL.md +365 -0
- package/agents/adversarial-strategist.md +96 -0
- package/agents/assumption-breaker.md +93 -0
- package/agents/bias-detector.md +95 -0
- package/agents/classifier.md +92 -0
- package/agents/emergence-reader.md +95 -0
- package/agents/first-principles.md +95 -0
- package/agents/formal-verifier.md +95 -0
- package/agents/incentive-mapper.md +95 -0
- package/agents/inverter.md +95 -0
- package/agents/pragmatic-builder.md +95 -0
- package/agents/reframer.md +95 -0
- package/agents/resilience-anchor.md +95 -0
- package/agents/risk-analyst.md +95 -0
- package/agents/specialists/design-lens.md +96 -0
- package/agents/specialists/ml-intuition.md +96 -0
- package/agents/specialists/safety-frontier.md +96 -0
- package/agents/systems-thinker.md +95 -0
- package/bin/cli.js +69 -0
- package/configs/defaults.yaml +54 -0
- package/configs/provider-model-slots.example.yaml +88 -0
- package/install.sh +210 -0
- package/package.json +54 -0
- package/scripts/detect-platform.sh +70 -0
- package/scripts/frame-template.html +517 -0
- package/scripts/helper.js +339 -0
- package/scripts/release.sh +131 -0
- package/scripts/start-server.sh +274 -0
- package/scripts/stop-server.sh +42 -0
- package/templates/brainstorm-output.md +60 -0
- package/templates/deliberation-output.md +64 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# deliberate visual companion server
|
|
5
|
+
# Watches a directory for HTML files and serves the newest one to a browser.
|
|
6
|
+
# User interactions (clicks, selections) are recorded as JSONL events.
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
PROJECT_DIR=""
|
|
10
|
+
HOST="127.0.0.1"
|
|
11
|
+
URL_HOST=""
|
|
12
|
+
PORT=0
|
|
13
|
+
FOREGROUND=false
|
|
14
|
+
|
|
15
|
+
usage() {
|
|
16
|
+
cat <<EOF
|
|
17
|
+
Usage: start-server.sh [OPTIONS]
|
|
18
|
+
|
|
19
|
+
Options:
|
|
20
|
+
--project-dir DIR Project root for persistent storage (recommended)
|
|
21
|
+
--host HOST Bind address (default: 127.0.0.1)
|
|
22
|
+
--url-host HOST Hostname for printed URL (default: same as --host)
|
|
23
|
+
--port PORT Port to listen on (default: random available)
|
|
24
|
+
--foreground Run in foreground (for Codex, Gemini CLI)
|
|
25
|
+
-h, --help Show this help
|
|
26
|
+
EOF
|
|
27
|
+
exit 0
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
while [[ $# -gt 0 ]]; do
|
|
31
|
+
case "$1" in
|
|
32
|
+
--project-dir) PROJECT_DIR="$2"; shift 2 ;;
|
|
33
|
+
--host) HOST="$2"; shift 2 ;;
|
|
34
|
+
--url-host) URL_HOST="$2"; shift 2 ;;
|
|
35
|
+
--port) PORT="$2"; shift 2 ;;
|
|
36
|
+
--foreground) FOREGROUND=true; shift ;;
|
|
37
|
+
-h|--help) usage ;;
|
|
38
|
+
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
39
|
+
esac
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
# Find available port if not specified
|
|
43
|
+
if [[ "$PORT" == "0" ]]; then
|
|
44
|
+
PORT=$(python3 -c "import socket; s=socket.socket(); s.bind(('',0)); print(s.getsockname()[1]); s.close()" 2>/dev/null || echo "52341")
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
[[ -z "$URL_HOST" ]] && URL_HOST="$HOST"
|
|
48
|
+
[[ "$URL_HOST" == "0.0.0.0" ]] && URL_HOST="localhost"
|
|
49
|
+
|
|
50
|
+
# Create session directory
|
|
51
|
+
TIMESTAMP=$(date +%s)
|
|
52
|
+
PID_PREFIX="$$"
|
|
53
|
+
|
|
54
|
+
if [[ -n "$PROJECT_DIR" ]]; then
|
|
55
|
+
SESSION_DIR="$PROJECT_DIR/.deliberate/companion/${PID_PREFIX}-${TIMESTAMP}"
|
|
56
|
+
else
|
|
57
|
+
SESSION_DIR="/tmp/deliberate-companion/${PID_PREFIX}-${TIMESTAMP}"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
SCREEN_DIR="$SESSION_DIR/content"
|
|
61
|
+
STATE_DIR="$SESSION_DIR/state"
|
|
62
|
+
|
|
63
|
+
mkdir -p "$SCREEN_DIR" "$STATE_DIR"
|
|
64
|
+
|
|
65
|
+
# Write the frame template and helper to the session
|
|
66
|
+
cp "$SCRIPT_DIR/frame-template.html" "$SESSION_DIR/frame-template.html"
|
|
67
|
+
cp "$SCRIPT_DIR/helper.js" "$SESSION_DIR/helper.js"
|
|
68
|
+
|
|
69
|
+
# Create the server script (Node.js)
|
|
70
|
+
cat > "$SESSION_DIR/server.js" << 'SERVEREOF'
|
|
71
|
+
const http = require('http');
|
|
72
|
+
const fs = require('fs');
|
|
73
|
+
const path = require('path');
|
|
74
|
+
|
|
75
|
+
const args = process.argv.slice(2);
|
|
76
|
+
const HOST = args[0] || '127.0.0.1';
|
|
77
|
+
const PORT = parseInt(args[1] || '52341', 10);
|
|
78
|
+
const SCREEN_DIR = args[2];
|
|
79
|
+
const STATE_DIR = args[3];
|
|
80
|
+
const SESSION_DIR = args[4];
|
|
81
|
+
|
|
82
|
+
const FRAME_TEMPLATE = fs.readFileSync(path.join(SESSION_DIR, 'frame-template.html'), 'utf8');
|
|
83
|
+
const HELPER_JS = fs.readFileSync(path.join(SESSION_DIR, 'helper.js'), 'utf8');
|
|
84
|
+
|
|
85
|
+
let inactivityTimer = null;
|
|
86
|
+
const INACTIVITY_TIMEOUT = 30 * 60 * 1000; // 30 minutes
|
|
87
|
+
|
|
88
|
+
function resetInactivityTimer() {
|
|
89
|
+
if (inactivityTimer) clearTimeout(inactivityTimer);
|
|
90
|
+
inactivityTimer = setTimeout(() => {
|
|
91
|
+
console.log('Inactivity timeout reached. Shutting down.');
|
|
92
|
+
fs.writeFileSync(path.join(STATE_DIR, 'server-stopped'), Date.now().toString());
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}, INACTIVITY_TIMEOUT);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getNewestFile() {
|
|
98
|
+
try {
|
|
99
|
+
const files = fs.readdirSync(SCREEN_DIR)
|
|
100
|
+
.filter(f => f.endsWith('.html'))
|
|
101
|
+
.map(f => ({
|
|
102
|
+
name: f,
|
|
103
|
+
mtime: fs.statSync(path.join(SCREEN_DIR, f)).mtimeMs
|
|
104
|
+
}))
|
|
105
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
106
|
+
return files.length > 0 ? files[0].name : null;
|
|
107
|
+
} catch (e) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function wrapInFrame(content, filename) {
|
|
113
|
+
// If content is a full HTML document, just inject the helper script
|
|
114
|
+
if (content.trim().startsWith('<!DOCTYPE') || content.trim().startsWith('<html')) {
|
|
115
|
+
return content.replace('</body>', `<script>${HELPER_JS}</script></body>`);
|
|
116
|
+
}
|
|
117
|
+
// Otherwise, wrap in frame template
|
|
118
|
+
return FRAME_TEMPLATE
|
|
119
|
+
.replace('{{CONTENT}}', content)
|
|
120
|
+
.replace('{{HELPER_JS}}', HELPER_JS)
|
|
121
|
+
.replace('{{FILENAME}}', filename || 'deliberate');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function getMimeType(ext) {
|
|
125
|
+
const types = {
|
|
126
|
+
'.html': 'text/html',
|
|
127
|
+
'.css': 'text/css',
|
|
128
|
+
'.js': 'application/javascript',
|
|
129
|
+
'.json': 'application/json',
|
|
130
|
+
'.png': 'image/png',
|
|
131
|
+
'.jpg': 'image/jpeg',
|
|
132
|
+
'.svg': 'image/svg+xml'
|
|
133
|
+
};
|
|
134
|
+
return types[ext] || 'text/plain';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const server = http.createServer((req, res) => {
|
|
138
|
+
resetInactivityTimer();
|
|
139
|
+
|
|
140
|
+
// CORS headers
|
|
141
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
142
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
143
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
144
|
+
|
|
145
|
+
if (req.method === 'OPTIONS') {
|
|
146
|
+
res.writeHead(204);
|
|
147
|
+
res.end();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// POST /events -- record user interaction
|
|
152
|
+
if (req.method === 'POST' && req.url === '/events') {
|
|
153
|
+
let body = '';
|
|
154
|
+
req.on('data', chunk => body += chunk);
|
|
155
|
+
req.on('end', () => {
|
|
156
|
+
const eventsFile = path.join(STATE_DIR, 'events');
|
|
157
|
+
fs.appendFileSync(eventsFile, body + '\n');
|
|
158
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
159
|
+
res.end('{"status":"ok"}');
|
|
160
|
+
});
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// GET /events -- read events
|
|
165
|
+
if (req.method === 'GET' && req.url === '/events') {
|
|
166
|
+
const eventsFile = path.join(STATE_DIR, 'events');
|
|
167
|
+
if (fs.existsSync(eventsFile)) {
|
|
168
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
169
|
+
res.end(fs.readFileSync(eventsFile, 'utf8'));
|
|
170
|
+
} else {
|
|
171
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
172
|
+
res.end('');
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// GET /status -- health check
|
|
178
|
+
if (req.url === '/status') {
|
|
179
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
180
|
+
res.end(JSON.stringify({ status: 'running', newest: getNewestFile() }));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// GET / -- serve newest HTML file wrapped in frame
|
|
185
|
+
if (req.url === '/' || req.url === '/index.html') {
|
|
186
|
+
const newest = getNewestFile();
|
|
187
|
+
if (!newest) {
|
|
188
|
+
const waiting = `
|
|
189
|
+
<div style="display:flex;align-items:center;justify-content:center;min-height:60vh;flex-direction:column">
|
|
190
|
+
<h2 style="color:var(--text-primary,#e0e0e0);font-family:system-ui">deliberate</h2>
|
|
191
|
+
<p style="color:var(--text-secondary,#999);font-family:system-ui">Waiting for content...</p>
|
|
192
|
+
</div>`;
|
|
193
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
194
|
+
res.end(wrapInFrame(waiting, 'waiting'));
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const content = fs.readFileSync(path.join(SCREEN_DIR, newest), 'utf8');
|
|
198
|
+
// Clear events when new screen is served
|
|
199
|
+
const eventsFile = path.join(STATE_DIR, 'events');
|
|
200
|
+
if (fs.existsSync(eventsFile)) fs.unlinkSync(eventsFile);
|
|
201
|
+
|
|
202
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
203
|
+
res.end(wrapInFrame(content, newest));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Serve static files from screen_dir
|
|
208
|
+
const filePath = path.join(SCREEN_DIR, req.url.slice(1));
|
|
209
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
210
|
+
const ext = path.extname(filePath);
|
|
211
|
+
res.writeHead(200, { 'Content-Type': getMimeType(ext) });
|
|
212
|
+
res.end(fs.readFileSync(filePath));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
res.writeHead(404);
|
|
217
|
+
res.end('Not found');
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
server.listen(PORT, HOST, () => {
|
|
221
|
+
const info = {
|
|
222
|
+
type: 'server-started',
|
|
223
|
+
port: PORT,
|
|
224
|
+
url: `http://${args[5] || HOST}:${PORT}`,
|
|
225
|
+
screen_dir: SCREEN_DIR,
|
|
226
|
+
state_dir: STATE_DIR,
|
|
227
|
+
session_dir: SESSION_DIR
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// Write server info for retrieval
|
|
231
|
+
fs.writeFileSync(path.join(STATE_DIR, 'server-info'), JSON.stringify(info, null, 2));
|
|
232
|
+
|
|
233
|
+
// Output to stdout for the launching script
|
|
234
|
+
console.log(JSON.stringify(info));
|
|
235
|
+
|
|
236
|
+
resetInactivityTimer();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
process.on('SIGTERM', () => {
|
|
240
|
+
fs.writeFileSync(path.join(STATE_DIR, 'server-stopped'), Date.now().toString());
|
|
241
|
+
process.exit(0);
|
|
242
|
+
});
|
|
243
|
+
process.on('SIGINT', () => {
|
|
244
|
+
fs.writeFileSync(path.join(STATE_DIR, 'server-stopped'), Date.now().toString());
|
|
245
|
+
process.exit(0);
|
|
246
|
+
});
|
|
247
|
+
SERVEREOF
|
|
248
|
+
|
|
249
|
+
# Auto-detect environment
|
|
250
|
+
IS_CODEX=false
|
|
251
|
+
[[ -n "${CODEX_CI:-}" ]] && IS_CODEX=true
|
|
252
|
+
|
|
253
|
+
# Launch the server
|
|
254
|
+
if [[ "$FOREGROUND" == "true" ]] || [[ "$IS_CODEX" == "true" ]]; then
|
|
255
|
+
# Foreground mode: blocks the shell
|
|
256
|
+
exec node "$SESSION_DIR/server.js" "$HOST" "$PORT" "$SCREEN_DIR" "$STATE_DIR" "$SESSION_DIR" "$URL_HOST"
|
|
257
|
+
else
|
|
258
|
+
# Background mode: detach and return immediately
|
|
259
|
+
nohup node "$SESSION_DIR/server.js" "$HOST" "$PORT" "$SCREEN_DIR" "$STATE_DIR" "$SESSION_DIR" "$URL_HOST" > /dev/null 2>&1 &
|
|
260
|
+
SERVER_PID=$!
|
|
261
|
+
|
|
262
|
+
# Wait briefly for the server to start and write server-info
|
|
263
|
+
for i in 1 2 3 4 5; do
|
|
264
|
+
if [[ -f "$STATE_DIR/server-info" ]]; then
|
|
265
|
+
cat "$STATE_DIR/server-info"
|
|
266
|
+
exit 0
|
|
267
|
+
fi
|
|
268
|
+
sleep 0.5
|
|
269
|
+
done
|
|
270
|
+
|
|
271
|
+
# If server-info wasn't written, something went wrong
|
|
272
|
+
echo '{"type":"error","message":"Server failed to start within 2.5 seconds"}' >&2
|
|
273
|
+
exit 1
|
|
274
|
+
fi
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Stop a deliberate visual companion server session
|
|
5
|
+
|
|
6
|
+
if [[ $# -lt 1 ]]; then
|
|
7
|
+
echo "Usage: stop-server.sh SESSION_DIR" >&2
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
SESSION_DIR="$1"
|
|
12
|
+
STATE_DIR="$SESSION_DIR/state"
|
|
13
|
+
|
|
14
|
+
if [[ ! -d "$SESSION_DIR" ]]; then
|
|
15
|
+
echo "Session directory not found: $SESSION_DIR" >&2
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Read server info to find the process
|
|
20
|
+
if [[ -f "$STATE_DIR/server-info" ]]; then
|
|
21
|
+
# Find the node process serving this session
|
|
22
|
+
SERVER_JS="$SESSION_DIR/server.js"
|
|
23
|
+
PIDS=$(pgrep -f "$SERVER_JS" 2>/dev/null || true)
|
|
24
|
+
|
|
25
|
+
if [[ -n "$PIDS" ]]; then
|
|
26
|
+
echo "$PIDS" | xargs kill 2>/dev/null || true
|
|
27
|
+
echo "Server stopped."
|
|
28
|
+
else
|
|
29
|
+
echo "No running server found for this session."
|
|
30
|
+
fi
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Mark as stopped
|
|
34
|
+
echo "$(date +%s)" > "$STATE_DIR/server-stopped"
|
|
35
|
+
|
|
36
|
+
# Clean up /tmp sessions only
|
|
37
|
+
if [[ "$SESSION_DIR" == /tmp/* ]]; then
|
|
38
|
+
rm -rf "$SESSION_DIR"
|
|
39
|
+
echo "Temporary session cleaned up."
|
|
40
|
+
else
|
|
41
|
+
echo "Session files preserved at: $SESSION_DIR"
|
|
42
|
+
fi
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Brainstorm Record
|
|
2
|
+
|
|
3
|
+
## Metadata
|
|
4
|
+
- **Date**: {{DATE}}
|
|
5
|
+
- **Agents**: {{AGENTS}}
|
|
6
|
+
- **Topic**: {{TOPIC}}
|
|
7
|
+
- **Visual Companion**: {{VISUAL_COMPANION}}
|
|
8
|
+
- **Platform**: {{PLATFORM}}
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Context Summary
|
|
13
|
+
|
|
14
|
+
{{CONTEXT_SUMMARY}}
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Clarifying Questions & Answers
|
|
19
|
+
|
|
20
|
+
{{CLARIFYING_QA}}
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Divergent Phase: Ideas Generated
|
|
25
|
+
|
|
26
|
+
{{DIVERGENT_IDEAS}}
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Cross-Pollination
|
|
31
|
+
|
|
32
|
+
{{CROSS_POLLINATION}}
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Convergence: Top Directions
|
|
37
|
+
|
|
38
|
+
{{CONVERGENCE}}
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Final Design
|
|
43
|
+
|
|
44
|
+
### Overview
|
|
45
|
+
{{DESIGN_OVERVIEW}}
|
|
46
|
+
|
|
47
|
+
### Architecture / Structure
|
|
48
|
+
{{DESIGN_ARCHITECTURE}}
|
|
49
|
+
|
|
50
|
+
### Key Decisions
|
|
51
|
+
{{DESIGN_DECISIONS}}
|
|
52
|
+
|
|
53
|
+
### Scope Boundaries (what we're NOT building)
|
|
54
|
+
{{SCOPE_BOUNDARIES}}
|
|
55
|
+
|
|
56
|
+
### Risks and Mitigations
|
|
57
|
+
{{DESIGN_RISKS}}
|
|
58
|
+
|
|
59
|
+
### Agent Attribution
|
|
60
|
+
{{AGENT_ATTRIBUTION}}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Deliberation Record
|
|
2
|
+
|
|
3
|
+
## Metadata
|
|
4
|
+
- **Date**: {{DATE}}
|
|
5
|
+
- **Mode**: {{MODE}}
|
|
6
|
+
- **Agents**: {{AGENTS}}
|
|
7
|
+
- **Question**: {{QUESTION}}
|
|
8
|
+
- **Profile**: {{PROFILE}}
|
|
9
|
+
- **Platform**: {{PLATFORM}}
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Problem Restatement
|
|
14
|
+
|
|
15
|
+
{{PROBLEM_RESTATEMENT}}
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Round 1: Independent Analysis
|
|
20
|
+
|
|
21
|
+
{{ROUND_1_OUTPUTS}}
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Round 2: Cross-Examination
|
|
26
|
+
|
|
27
|
+
{{ROUND_2_OUTPUTS}}
|
|
28
|
+
|
|
29
|
+
### Enforcement Notes
|
|
30
|
+
{{ENFORCEMENT_NOTES}}
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Round 3: Crystallization
|
|
35
|
+
|
|
36
|
+
{{ROUND_3_OUTPUTS}}
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Verdict
|
|
41
|
+
|
|
42
|
+
### Verdict Type
|
|
43
|
+
{{VERDICT_TYPE}}
|
|
44
|
+
|
|
45
|
+
### Consensus Position
|
|
46
|
+
{{CONSENSUS_POSITION}}
|
|
47
|
+
|
|
48
|
+
### Key Insights by Agent
|
|
49
|
+
{{KEY_INSIGHTS}}
|
|
50
|
+
|
|
51
|
+
### Points of Agreement
|
|
52
|
+
{{AGREEMENT_POINTS}}
|
|
53
|
+
|
|
54
|
+
### Points of Disagreement
|
|
55
|
+
{{DISAGREEMENT_POINTS}}
|
|
56
|
+
|
|
57
|
+
### Minority Report
|
|
58
|
+
{{MINORITY_REPORT}}
|
|
59
|
+
|
|
60
|
+
### Recommended Next Steps
|
|
61
|
+
{{NEXT_STEPS}}
|
|
62
|
+
|
|
63
|
+
### Unresolved Questions
|
|
64
|
+
{{UNRESOLVED_QUESTIONS}}
|