@dmsdc-ai/aigentry-deliberation 0.0.30 → 0.0.31
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/browser-control-port.js +273 -113
- package/index.js +304 -125
- package/install.js +1 -0
- package/observer.js +76 -11
- package/package.json +1 -1
- package/selectors/chatgpt.json +3 -1
- package/selectors/copilot.json +3 -2
- package/selectors/deepseek.json +2 -1
- package/selectors/grok.json +4 -2
- package/selectors/mistral.json +7 -6
- package/selectors/qwen.json +4 -3
package/install.js
CHANGED
package/observer.js
CHANGED
|
@@ -30,6 +30,7 @@ const CLI_LABELS = {
|
|
|
30
30
|
opencode: "OpenCode", cursor: "Cursor", continue: "Continue"
|
|
31
31
|
};
|
|
32
32
|
const DEFAULT_PORT = 3847;
|
|
33
|
+
const DEFAULT_HOST = process.env.HOST || "0.0.0.0";
|
|
33
34
|
|
|
34
35
|
function getProjectSlug() {
|
|
35
36
|
const cwd = process.cwd();
|
|
@@ -66,6 +67,21 @@ function findSession(sessionId) {
|
|
|
66
67
|
return null;
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
function saveSession(session) {
|
|
71
|
+
const slugs = fs.readdirSync(STATE_DIR).filter(f => {
|
|
72
|
+
try { return fs.statSync(path.join(STATE_DIR, f)).isDirectory(); } catch { return false; }
|
|
73
|
+
});
|
|
74
|
+
for (const slug of slugs) {
|
|
75
|
+
const sessionsDir = path.join(STATE_DIR, slug, "sessions");
|
|
76
|
+
const filePath = path.join(sessionsDir, `${session.id}.json`);
|
|
77
|
+
if (fs.existsSync(filePath)) {
|
|
78
|
+
fs.writeFileSync(filePath, JSON.stringify(session, null, 2), "utf-8");
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
69
85
|
function getAllActiveSessions() {
|
|
70
86
|
const all = [];
|
|
71
87
|
try {
|
|
@@ -262,8 +278,9 @@ function createServer(port) {
|
|
|
262
278
|
|
|
263
279
|
// CORS
|
|
264
280
|
const origin = req.headers.origin || "";
|
|
265
|
-
|
|
266
|
-
|
|
281
|
+
// Allow all origins for Tailscale use cases, or restrict if needed.
|
|
282
|
+
// For now, allow all to ensure Tailscale cross-machine injection works easily.
|
|
283
|
+
res.setHeader("Access-Control-Allow-Origin", origin || "*");
|
|
267
284
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
268
285
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
269
286
|
|
|
@@ -330,15 +347,62 @@ function createServer(port) {
|
|
|
330
347
|
|
|
331
348
|
const sessionMatch = pathname.match(/^\/api\/sessions\/([^/]+)$/);
|
|
332
349
|
if (sessionMatch) {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
350
|
+
if (req.method === "POST" && pathname.endsWith("/context")) {
|
|
351
|
+
// POST /api/sessions/:id/context
|
|
352
|
+
const sessionId = pathname.match(/^\/api\/sessions\/([^/]+)\/context$/)?.[1];
|
|
353
|
+
if (!sessionId) {
|
|
354
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
355
|
+
res.end(JSON.stringify({ error: "Invalid session ID" }));
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
let body = "";
|
|
359
|
+
req.on("data", chunk => { body += chunk; });
|
|
360
|
+
req.on("end", () => {
|
|
361
|
+
const session = findSession(sessionId);
|
|
362
|
+
if (!session) {
|
|
363
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
364
|
+
res.end(JSON.stringify({ error: "Session not found" }));
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
let parsed;
|
|
368
|
+
try { parsed = JSON.parse(body); } catch {
|
|
369
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
370
|
+
res.end(JSON.stringify({ error: "Invalid JSON" }));
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (!parsed.context) {
|
|
374
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
375
|
+
res.end(JSON.stringify({ error: "context field is required" }));
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
session.log.push({
|
|
380
|
+
round: session.current_round,
|
|
381
|
+
speaker: parsed.speaker || "system",
|
|
382
|
+
content: `[Context Injection]\n${parsed.context}`,
|
|
383
|
+
timestamp: new Date().toISOString(),
|
|
384
|
+
event: "context_injection"
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
saveSession(session);
|
|
388
|
+
|
|
389
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
390
|
+
res.end(JSON.stringify({ success: true, message: `Context injected into ${sessionId}` }));
|
|
391
|
+
});
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (req.method === "GET") {
|
|
396
|
+
const session = findSession(sessionMatch[1]);
|
|
397
|
+
if (!session) {
|
|
398
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
399
|
+
res.end(JSON.stringify({ error: "Session not found" }));
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
403
|
+
res.end(JSON.stringify(session));
|
|
337
404
|
return;
|
|
338
405
|
}
|
|
339
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
340
|
-
res.end(JSON.stringify(session));
|
|
341
|
-
return;
|
|
342
406
|
}
|
|
343
407
|
|
|
344
408
|
const streamMatch = pathname.match(/^\/api\/sessions\/([^/]+)\/stream$/);
|
|
@@ -476,11 +540,12 @@ const server = createServer(port);
|
|
|
476
540
|
// Poll every 1 second
|
|
477
541
|
const pollInterval = setInterval(pollSessions, 1000);
|
|
478
542
|
|
|
479
|
-
server.listen(port,
|
|
480
|
-
console.log(`Deliberation Observer running at http
|
|
543
|
+
server.listen(port, DEFAULT_HOST, () => {
|
|
544
|
+
console.log(`Deliberation Observer running at http://${DEFAULT_HOST === "0.0.0.0" ? "localhost" : DEFAULT_HOST}:${port}`);
|
|
481
545
|
console.log(` Dashboard: http://localhost:${port}/`);
|
|
482
546
|
console.log(` API: http://localhost:${port}/api/sessions`);
|
|
483
547
|
console.log(` SSE: http://localhost:${port}/api/sessions/{id}/stream`);
|
|
548
|
+
console.log(` Tailscale: http://<your-tailscale-ip>:${port}/`);
|
|
484
549
|
console.log(`\n Press Ctrl+C to stop.`);
|
|
485
550
|
});
|
|
486
551
|
|
package/package.json
CHANGED
package/selectors/chatgpt.json
CHANGED
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
"inputDelayMs": 100,
|
|
15
15
|
"sendDelayMs": 200,
|
|
16
16
|
"pollIntervalMs": 500,
|
|
17
|
-
"streamingTimeoutMs": 120000
|
|
17
|
+
"streamingTimeoutMs": 120000,
|
|
18
|
+
"stabilizationMinWaitMs": 8000,
|
|
19
|
+
"stabilizationThreshold": 10
|
|
18
20
|
},
|
|
19
21
|
"modelSelector": {
|
|
20
22
|
"trigger": "button[data-testid='model-switcher-dropdown-button'], button[data-testid='model-selector-dropdown'], button[class*='model']",
|
package/selectors/copilot.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"selectors": {
|
|
6
6
|
"inputSelector": "#userInput, textarea#userInput",
|
|
7
7
|
"sendButton": "button[data-testid='submit-button'], button[aria-label='메시지 제출'], button[aria-label='Submit message']",
|
|
8
|
-
"responseSelector": ".ac-textBlock, [data-content='ai-message']",
|
|
8
|
+
"responseSelector": ".ac-textBlock p, .ac-textBlock, [data-content='ai-message'] .markdown, [data-content='ai-message'] p",
|
|
9
9
|
"responseContainer": "[data-content='ai-message'], .response-message",
|
|
10
10
|
"streamingIndicator": ".typing-indicator, .is-streaming"
|
|
11
11
|
},
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"inputDelayMs": 100,
|
|
14
14
|
"sendDelayMs": 300,
|
|
15
15
|
"pollIntervalMs": 500,
|
|
16
|
-
"streamingTimeoutMs": 45000
|
|
16
|
+
"streamingTimeoutMs": 45000,
|
|
17
|
+
"stabilizationMinWaitMs": 5000
|
|
17
18
|
},
|
|
18
19
|
"notes": "Microsoft Copilot selectors - send button aria-label is locale-dependent (EN: 'Submit message', KR: '메시지 제출'). Button appears after text input. data-testid='submit-button' is stable."
|
|
19
20
|
}
|
package/selectors/deepseek.json
CHANGED
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"inputDelayMs": 100,
|
|
14
14
|
"sendDelayMs": 300,
|
|
15
15
|
"pollIntervalMs": 500,
|
|
16
|
-
"streamingTimeoutMs": 60000
|
|
16
|
+
"streamingTimeoutMs": 60000,
|
|
17
|
+
"stabilizationMinWaitMs": 5000
|
|
17
18
|
},
|
|
18
19
|
"modelSelector": {
|
|
19
20
|
"trigger": "div[class*='model-selector'], button[class*='model']",
|
package/selectors/grok.json
CHANGED
|
@@ -7,13 +7,15 @@
|
|
|
7
7
|
"sendButton": "button[aria-label='제출'], button[aria-label='Submit'], button[type='submit']",
|
|
8
8
|
"responseSelector": ".response-content-markdown, .markdown",
|
|
9
9
|
"responseContainer": "div[id^='response-'], .message-bubble",
|
|
10
|
-
"streamingIndicator": "[data-streaming='true'], .animate-pulse"
|
|
10
|
+
"streamingIndicator": "[data-streaming='true'], .animate-pulse, [class*='thinking'], [class*='reasoning']"
|
|
11
11
|
},
|
|
12
12
|
"timing": {
|
|
13
13
|
"inputDelayMs": 100,
|
|
14
14
|
"sendDelayMs": 300,
|
|
15
15
|
"pollIntervalMs": 500,
|
|
16
|
-
"streamingTimeoutMs": 60000
|
|
16
|
+
"streamingTimeoutMs": 60000,
|
|
17
|
+
"stabilizationMinWaitMs": 6000,
|
|
18
|
+
"stabilizationThreshold": 8
|
|
17
19
|
},
|
|
18
20
|
"modelSelector": {
|
|
19
21
|
"trigger": "button[class*='model'], [data-testid*='model']",
|
package/selectors/mistral.json
CHANGED
|
@@ -3,17 +3,18 @@
|
|
|
3
3
|
"version": "1.1.0",
|
|
4
4
|
"domains": ["chat.mistral.ai", "mistral.ai"],
|
|
5
5
|
"selectors": {
|
|
6
|
-
"inputSelector": "div.ProseMirror[contenteditable='true'], div[contenteditable='true']",
|
|
7
|
-
"sendButton": "button[aria-label='Send question'], button[aria-label='Send'], button[type='submit']",
|
|
8
|
-
"responseSelector": ".prose, .markdown-body",
|
|
9
|
-
"responseContainer": ".message-assistant, [data-role='assistant']",
|
|
10
|
-
"streamingIndicator": ".animate-pulse, [data-streaming='true']"
|
|
6
|
+
"inputSelector": "div[contenteditable='true'][data-lexical-editor='true'], div.ProseMirror[contenteditable='true'], div[contenteditable='true']",
|
|
7
|
+
"sendButton": "button[aria-label='Send message'], button[aria-label='Send question'], button[aria-label='Send'], button[type='submit']",
|
|
8
|
+
"responseSelector": "[class*='prose'], [class*='markdown'], [class*='message-content'], .prose, .markdown-body",
|
|
9
|
+
"responseContainer": "[class*='assistant-message'], [class*='bot-message'], [class*='ai-message'], .message-assistant, [data-role='assistant']",
|
|
10
|
+
"streamingIndicator": ".animate-spin, [class*='loading'], [class*='streaming'], .animate-pulse, [data-streaming='true']"
|
|
11
11
|
},
|
|
12
12
|
"timing": {
|
|
13
13
|
"inputDelayMs": 100,
|
|
14
14
|
"sendDelayMs": 300,
|
|
15
15
|
"pollIntervalMs": 500,
|
|
16
|
-
"streamingTimeoutMs": 45000
|
|
16
|
+
"streamingTimeoutMs": 45000,
|
|
17
|
+
"stabilizationMinWaitMs": 5000
|
|
17
18
|
},
|
|
18
19
|
"notes": "Mistral Le Chat uses ProseMirror contenteditable. Send button aria-label='Send question' appears after text input."
|
|
19
20
|
}
|
package/selectors/qwen.json
CHANGED
|
@@ -5,15 +5,16 @@
|
|
|
5
5
|
"selectors": {
|
|
6
6
|
"inputSelector": "textarea.message-input-textarea, textarea[placeholder*='도와드릴까요'], textarea[placeholder*='help you']",
|
|
7
7
|
"sendButton": "button.send-button, button[type='submit']",
|
|
8
|
-
"responseSelector": ".response-message-content, .markdown-body",
|
|
8
|
+
"responseSelector": ".response-message-content.phase-answer, .phase-answer .markdown-body, .phase-answer, .response-message-content, .markdown-body",
|
|
9
9
|
"responseContainer": ".qwen-chat-message-assistant, .chat-response-message",
|
|
10
|
-
"streamingIndicator": ".phase-thinking, .typing-indicator"
|
|
10
|
+
"streamingIndicator": ".phase-thinking, .typing-indicator, .phase-answer.streaming, [data-streaming='true']"
|
|
11
11
|
},
|
|
12
12
|
"timing": {
|
|
13
13
|
"inputDelayMs": 100,
|
|
14
14
|
"sendDelayMs": 300,
|
|
15
15
|
"pollIntervalMs": 500,
|
|
16
|
-
"streamingTimeoutMs": 60000
|
|
16
|
+
"streamingTimeoutMs": 60000,
|
|
17
|
+
"stabilizationMinWaitMs": 4000
|
|
17
18
|
},
|
|
18
19
|
"notes": "Qwen verified via CDP. Container: .qwen-chat-message-assistant or .chat-response-message. Response: .response-message-content.phase-answer. Thinking phase: .phase-thinking class. '생각이 끝났습니다' indicates thinking complete."
|
|
19
20
|
}
|