society-protocol 1.0.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/LICENSE +21 -0
- package/README.md +111 -0
- package/dist/adapters.d.ts +101 -0
- package/dist/adapters.d.ts.map +1 -0
- package/dist/adapters.js +764 -0
- package/dist/adapters.js.map +1 -0
- package/dist/agents-md.d.ts +59 -0
- package/dist/agents-md.d.ts.map +1 -0
- package/dist/agents-md.js +204 -0
- package/dist/agents-md.js.map +1 -0
- package/dist/autoconfig.d.ts +137 -0
- package/dist/autoconfig.d.ts.map +1 -0
- package/dist/autoconfig.js +452 -0
- package/dist/autoconfig.js.map +1 -0
- package/dist/bootstrap.d.ts +68 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +304 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/bridges/a2a-bridge.d.ts +156 -0
- package/dist/bridges/a2a-bridge.d.ts.map +1 -0
- package/dist/bridges/a2a-bridge.js +337 -0
- package/dist/bridges/a2a-bridge.js.map +1 -0
- package/dist/bridges/mcp-bridge.d.ts +87 -0
- package/dist/bridges/mcp-bridge.d.ts.map +1 -0
- package/dist/bridges/mcp-bridge.js +332 -0
- package/dist/bridges/mcp-bridge.js.map +1 -0
- package/dist/cache.d.ts +130 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +257 -0
- package/dist/cache.js.map +1 -0
- package/dist/capsules.d.ts +23 -0
- package/dist/capsules.d.ts.map +1 -0
- package/dist/capsules.js +75 -0
- package/dist/capsules.js.map +1 -0
- package/dist/cli/commands.d.ts +8 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +263 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/coc.d.ts +121 -0
- package/dist/coc.d.ts.map +1 -0
- package/dist/coc.js +629 -0
- package/dist/coc.js.map +1 -0
- package/dist/coc.test.d.ts +2 -0
- package/dist/coc.test.d.ts.map +1 -0
- package/dist/coc.test.js +80 -0
- package/dist/coc.test.js.map +1 -0
- package/dist/compression.d.ts +125 -0
- package/dist/compression.d.ts.map +1 -0
- package/dist/compression.js +573 -0
- package/dist/compression.js.map +1 -0
- package/dist/cot-stream.d.ts +220 -0
- package/dist/cot-stream.d.ts.map +1 -0
- package/dist/cot-stream.js +673 -0
- package/dist/cot-stream.js.map +1 -0
- package/dist/crypto-wasm.d.ts +100 -0
- package/dist/crypto-wasm.d.ts.map +1 -0
- package/dist/crypto-wasm.js +229 -0
- package/dist/crypto-wasm.js.map +1 -0
- package/dist/federation.d.ts +200 -0
- package/dist/federation.d.ts.map +1 -0
- package/dist/federation.js +691 -0
- package/dist/federation.js.map +1 -0
- package/dist/federation.test.d.ts +2 -0
- package/dist/federation.test.d.ts.map +1 -0
- package/dist/federation.test.js +71 -0
- package/dist/federation.test.js.map +1 -0
- package/dist/gateway/capability-router.d.ts +77 -0
- package/dist/gateway/capability-router.d.ts.map +1 -0
- package/dist/gateway/capability-router.js +222 -0
- package/dist/gateway/capability-router.js.map +1 -0
- package/dist/gateway/demand-spawner.d.ts +155 -0
- package/dist/gateway/demand-spawner.d.ts.map +1 -0
- package/dist/gateway/demand-spawner.js +426 -0
- package/dist/gateway/demand-spawner.js.map +1 -0
- package/dist/identity.d.ts +46 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +102 -0
- package/dist/identity.js.map +1 -0
- package/dist/identity.test.d.ts +2 -0
- package/dist/identity.test.d.ts.map +1 -0
- package/dist/identity.test.js +45 -0
- package/dist/identity.test.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1572 -0
- package/dist/index.js.map +1 -0
- package/dist/integration.d.ts +210 -0
- package/dist/integration.d.ts.map +1 -0
- package/dist/integration.js +1105 -0
- package/dist/integration.js.map +1 -0
- package/dist/integration.test.d.ts +2 -0
- package/dist/integration.test.d.ts.map +1 -0
- package/dist/integration.test.js +155 -0
- package/dist/integration.test.js.map +1 -0
- package/dist/knowledge.d.ts +219 -0
- package/dist/knowledge.d.ts.map +1 -0
- package/dist/knowledge.js +543 -0
- package/dist/knowledge.js.map +1 -0
- package/dist/knowledge.test.d.ts +2 -0
- package/dist/knowledge.test.d.ts.map +1 -0
- package/dist/knowledge.test.js +72 -0
- package/dist/knowledge.test.js.map +1 -0
- package/dist/latent-space.d.ts +178 -0
- package/dist/latent-space.d.ts.map +1 -0
- package/dist/latent-space.js +385 -0
- package/dist/latent-space.js.map +1 -0
- package/dist/lib.d.ts +30 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +30 -0
- package/dist/lib.js.map +1 -0
- package/dist/mcp/server.d.ts +74 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +1392 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/metrics.d.ts +98 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +222 -0
- package/dist/metrics.js.map +1 -0
- package/dist/p2p.d.ts +87 -0
- package/dist/p2p.d.ts.map +1 -0
- package/dist/p2p.js +606 -0
- package/dist/p2p.js.map +1 -0
- package/dist/persona/capabilities.d.ts +17 -0
- package/dist/persona/capabilities.d.ts.map +1 -0
- package/dist/persona/capabilities.js +224 -0
- package/dist/persona/capabilities.js.map +1 -0
- package/dist/persona/domains.d.ts +22 -0
- package/dist/persona/domains.d.ts.map +1 -0
- package/dist/persona/domains.js +176 -0
- package/dist/persona/domains.js.map +1 -0
- package/dist/persona/embeddings.d.ts +40 -0
- package/dist/persona/embeddings.d.ts.map +1 -0
- package/dist/persona/embeddings.js +265 -0
- package/dist/persona/embeddings.js.map +1 -0
- package/dist/persona/engine.d.ts +79 -0
- package/dist/persona/engine.d.ts.map +1 -0
- package/dist/persona/engine.js +1087 -0
- package/dist/persona/engine.js.map +1 -0
- package/dist/persona/index.d.ts +11 -0
- package/dist/persona/index.d.ts.map +1 -0
- package/dist/persona/index.js +11 -0
- package/dist/persona/index.js.map +1 -0
- package/dist/persona/lifecycle.d.ts +17 -0
- package/dist/persona/lifecycle.d.ts.map +1 -0
- package/dist/persona/lifecycle.js +36 -0
- package/dist/persona/lifecycle.js.map +1 -0
- package/dist/persona/retrieval.d.ts +6 -0
- package/dist/persona/retrieval.d.ts.map +1 -0
- package/dist/persona/retrieval.js +122 -0
- package/dist/persona/retrieval.js.map +1 -0
- package/dist/persona/sync.d.ts +15 -0
- package/dist/persona/sync.d.ts.map +1 -0
- package/dist/persona/sync.js +92 -0
- package/dist/persona/sync.js.map +1 -0
- package/dist/persona/types.d.ts +283 -0
- package/dist/persona/types.d.ts.map +1 -0
- package/dist/persona/types.js +2 -0
- package/dist/persona/types.js.map +1 -0
- package/dist/persona/zkp/engine.d.ts +26 -0
- package/dist/persona/zkp/engine.d.ts.map +1 -0
- package/dist/persona/zkp/engine.js +370 -0
- package/dist/persona/zkp/engine.js.map +1 -0
- package/dist/persona/zkp/types.d.ts +39 -0
- package/dist/persona/zkp/types.d.ts.map +1 -0
- package/dist/persona/zkp/types.js +2 -0
- package/dist/persona/zkp/types.js.map +1 -0
- package/dist/planner.d.ts +114 -0
- package/dist/planner.d.ts.map +1 -0
- package/dist/planner.js +522 -0
- package/dist/planner.js.map +1 -0
- package/dist/proactive/checkpoints.d.ts +9 -0
- package/dist/proactive/checkpoints.d.ts.map +1 -0
- package/dist/proactive/checkpoints.js +20 -0
- package/dist/proactive/checkpoints.js.map +1 -0
- package/dist/proactive/engine.d.ts +59 -0
- package/dist/proactive/engine.d.ts.map +1 -0
- package/dist/proactive/engine.js +406 -0
- package/dist/proactive/engine.js.map +1 -0
- package/dist/proactive/scheduler.d.ts +11 -0
- package/dist/proactive/scheduler.d.ts.map +1 -0
- package/dist/proactive/scheduler.js +45 -0
- package/dist/proactive/scheduler.js.map +1 -0
- package/dist/proactive/swarm-controller.d.ts +189 -0
- package/dist/proactive/swarm-controller.d.ts.map +1 -0
- package/dist/proactive/swarm-controller.js +477 -0
- package/dist/proactive/swarm-controller.js.map +1 -0
- package/dist/proactive/swarm-registry.d.ts +13 -0
- package/dist/proactive/swarm-registry.d.ts.map +1 -0
- package/dist/proactive/swarm-registry.js +122 -0
- package/dist/proactive/swarm-registry.js.map +1 -0
- package/dist/proactive/types.d.ts +145 -0
- package/dist/proactive/types.d.ts.map +1 -0
- package/dist/proactive/types.js +25 -0
- package/dist/proactive/types.js.map +1 -0
- package/dist/registry.d.ts +35 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +88 -0
- package/dist/registry.js.map +1 -0
- package/dist/reputation.d.ts +123 -0
- package/dist/reputation.d.ts.map +1 -0
- package/dist/reputation.js +366 -0
- package/dist/reputation.js.map +1 -0
- package/dist/reputation.test.d.ts +5 -0
- package/dist/reputation.test.d.ts.map +1 -0
- package/dist/reputation.test.js +265 -0
- package/dist/reputation.test.js.map +1 -0
- package/dist/rooms.d.ts +96 -0
- package/dist/rooms.d.ts.map +1 -0
- package/dist/rooms.js +410 -0
- package/dist/rooms.js.map +1 -0
- package/dist/sdk/client.d.ts +290 -0
- package/dist/sdk/client.d.ts.map +1 -0
- package/dist/sdk/client.js +1287 -0
- package/dist/sdk/client.js.map +1 -0
- package/dist/sdk/index.d.ts +32 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +70 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/security.d.ts +230 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +652 -0
- package/dist/security.js.map +1 -0
- package/dist/skills/engine.d.ts +262 -0
- package/dist/skills/engine.d.ts.map +1 -0
- package/dist/skills/engine.js +788 -0
- package/dist/skills/engine.js.map +1 -0
- package/dist/skills/engine.test.d.ts +2 -0
- package/dist/skills/engine.test.d.ts.map +1 -0
- package/dist/skills/engine.test.js +134 -0
- package/dist/skills/engine.test.js.map +1 -0
- package/dist/skills/parser.d.ts +129 -0
- package/dist/skills/parser.d.ts.map +1 -0
- package/dist/skills/parser.js +318 -0
- package/dist/skills/parser.js.map +1 -0
- package/dist/social.d.ts +149 -0
- package/dist/social.d.ts.map +1 -0
- package/dist/social.js +401 -0
- package/dist/social.js.map +1 -0
- package/dist/storage-optimized.d.ts +116 -0
- package/dist/storage-optimized.d.ts.map +1 -0
- package/dist/storage-optimized.js +264 -0
- package/dist/storage-optimized.js.map +1 -0
- package/dist/storage.d.ts +584 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +2703 -0
- package/dist/storage.js.map +1 -0
- package/dist/storage.test.d.ts +2 -0
- package/dist/storage.test.d.ts.map +1 -0
- package/dist/storage.test.js +78 -0
- package/dist/storage.test.js.map +1 -0
- package/dist/swp.d.ts +443 -0
- package/dist/swp.d.ts.map +1 -0
- package/dist/swp.js +223 -0
- package/dist/swp.js.map +1 -0
- package/dist/swp.test.d.ts +5 -0
- package/dist/swp.test.d.ts.map +1 -0
- package/dist/swp.test.js +127 -0
- package/dist/swp.test.js.map +1 -0
- package/dist/templates.d.ts +25 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +1048 -0
- package/dist/templates.js.map +1 -0
- package/dist/test-e2e.d.ts +14 -0
- package/dist/test-e2e.d.ts.map +1 -0
- package/dist/test-e2e.js +266 -0
- package/dist/test-e2e.js.map +1 -0
- package/dist/workers/research-worker.d.ts +19 -0
- package/dist/workers/research-worker.d.ts.map +1 -0
- package/dist/workers/research-worker.js +141 -0
- package/dist/workers/research-worker.js.map +1 -0
- package/package.json +110 -0
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Society Protocol — Collaborative Chain-of-Thought (CoT) Streaming v1.0
|
|
3
|
+
*
|
|
4
|
+
* Killer feature: Real-time distributed reasoning between agents.
|
|
5
|
+
* Each agent publishes reasoning tokens, insights, questions and answers
|
|
6
|
+
* via GossipSub, enabling "pair programming between LLMs" — agents can
|
|
7
|
+
* absorb each other's reasoning in real-time and build on it.
|
|
8
|
+
*
|
|
9
|
+
* Key concepts:
|
|
10
|
+
* - CoT Stream: A reasoning session tied to a room/chain/step
|
|
11
|
+
* - Tokens: Typed reasoning chunks (reasoning, observation, hypothesis, etc.)
|
|
12
|
+
* - Insights: Distilled discoveries that can auto-create Knowledge Cards
|
|
13
|
+
* - Questions/Answers: Inter-agent Socratic dialogue
|
|
14
|
+
* - Branches: Divergent reasoning paths explored in parallel
|
|
15
|
+
* - Merges: Synthesis of multiple reasoning branches
|
|
16
|
+
*
|
|
17
|
+
* Architecture:
|
|
18
|
+
* Agent A ──┐ ┌── Agent C (absorbs A+B reasoning)
|
|
19
|
+
* ├── GossipSub topic ─┤
|
|
20
|
+
* Agent B ──┘ └── Agent D (branches from A's insight)
|
|
21
|
+
*/
|
|
22
|
+
import { EventEmitter } from 'events';
|
|
23
|
+
import { ulid } from 'ulid';
|
|
24
|
+
// ─── CoT Stream Engine ──────────────────────────────────────────
|
|
25
|
+
const DEFAULT_CONFIG = {
|
|
26
|
+
maxBufferSize: 100,
|
|
27
|
+
autoCreateCards: true,
|
|
28
|
+
autoCardMinConfidence: 0.7,
|
|
29
|
+
maxStreamsPerRoom: 20,
|
|
30
|
+
batchIntervalMs: 250,
|
|
31
|
+
};
|
|
32
|
+
export class CotStreamEngine extends EventEmitter {
|
|
33
|
+
identity;
|
|
34
|
+
rooms;
|
|
35
|
+
knowledge;
|
|
36
|
+
streams = new Map();
|
|
37
|
+
insights = new Map();
|
|
38
|
+
questions = new Map();
|
|
39
|
+
tokenBuffers = new Map();
|
|
40
|
+
roomStreams = new Map(); // roomId -> streamIds
|
|
41
|
+
batchTimers = new Map();
|
|
42
|
+
config;
|
|
43
|
+
constructor(identity, rooms, knowledge, config) {
|
|
44
|
+
super();
|
|
45
|
+
this.identity = identity;
|
|
46
|
+
this.rooms = rooms;
|
|
47
|
+
this.knowledge = knowledge;
|
|
48
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
49
|
+
this.bindEvents();
|
|
50
|
+
}
|
|
51
|
+
// ─── Stream Lifecycle ────────────────────────────────────────
|
|
52
|
+
/**
|
|
53
|
+
* Start a new reasoning stream — begins broadcasting thinking tokens.
|
|
54
|
+
*/
|
|
55
|
+
async startStream(roomId, goal, options = {}) {
|
|
56
|
+
const streamId = `cot_${ulid()}`;
|
|
57
|
+
// Enforce max streams per room
|
|
58
|
+
const roomStreamSet = this.roomStreams.get(roomId);
|
|
59
|
+
if (roomStreamSet && roomStreamSet.size >= this.config.maxStreamsPerRoom) {
|
|
60
|
+
throw new Error(`Max streams (${this.config.maxStreamsPerRoom}) reached for room ${roomId}`);
|
|
61
|
+
}
|
|
62
|
+
const stream = {
|
|
63
|
+
stream_id: streamId,
|
|
64
|
+
room_id: roomId,
|
|
65
|
+
chain_id: options.chain_id,
|
|
66
|
+
step_id: options.step_id,
|
|
67
|
+
goal,
|
|
68
|
+
model: options.model,
|
|
69
|
+
owner_did: this.identity.did,
|
|
70
|
+
parent_stream_id: options.parent_stream_id,
|
|
71
|
+
status: 'active',
|
|
72
|
+
started_at: Date.now(),
|
|
73
|
+
token_count: 0,
|
|
74
|
+
insight_count: 0,
|
|
75
|
+
question_count: 0,
|
|
76
|
+
branches: [],
|
|
77
|
+
};
|
|
78
|
+
this.streams.set(streamId, stream);
|
|
79
|
+
this.tokenBuffers.set(streamId, []);
|
|
80
|
+
if (!this.roomStreams.has(roomId)) {
|
|
81
|
+
this.roomStreams.set(roomId, new Set());
|
|
82
|
+
}
|
|
83
|
+
this.roomStreams.get(roomId).add(streamId);
|
|
84
|
+
// If this is a branch, register with parent
|
|
85
|
+
if (options.parent_stream_id) {
|
|
86
|
+
const parent = this.streams.get(options.parent_stream_id);
|
|
87
|
+
if (parent) {
|
|
88
|
+
parent.branches.push(streamId);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const body = {
|
|
92
|
+
stream_id: streamId,
|
|
93
|
+
chain_id: options.chain_id,
|
|
94
|
+
step_id: options.step_id,
|
|
95
|
+
goal,
|
|
96
|
+
model: options.model,
|
|
97
|
+
parent_stream_id: options.parent_stream_id,
|
|
98
|
+
};
|
|
99
|
+
await this.rooms.sendMessage(roomId, body, 'cot.stream.start');
|
|
100
|
+
this.emit('stream:started', streamId, stream);
|
|
101
|
+
return streamId;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Emit a reasoning token — the core of collaborative thinking.
|
|
105
|
+
* Tokens are batched for network efficiency.
|
|
106
|
+
*/
|
|
107
|
+
async emitToken(streamId, token, tokenType, options = {}) {
|
|
108
|
+
const stream = this.streams.get(streamId);
|
|
109
|
+
if (!stream || stream.status !== 'active') {
|
|
110
|
+
throw new Error(`Stream ${streamId} not active`);
|
|
111
|
+
}
|
|
112
|
+
stream.token_count++;
|
|
113
|
+
const tokenBody = {
|
|
114
|
+
stream_id: streamId,
|
|
115
|
+
seq: stream.token_count,
|
|
116
|
+
token,
|
|
117
|
+
token_type: tokenType,
|
|
118
|
+
confidence: options.confidence,
|
|
119
|
+
domain: options.domain,
|
|
120
|
+
references: options.references,
|
|
121
|
+
};
|
|
122
|
+
// Buffer tokens and flush in batches for efficiency
|
|
123
|
+
const buffer = this.tokenBuffers.get(streamId);
|
|
124
|
+
buffer.push(tokenBody);
|
|
125
|
+
if (buffer.length >= this.config.maxBufferSize) {
|
|
126
|
+
await this.flushTokenBuffer(streamId);
|
|
127
|
+
}
|
|
128
|
+
else if (!this.batchTimers.has(streamId)) {
|
|
129
|
+
this.batchTimers.set(streamId, setTimeout(() => {
|
|
130
|
+
this.flushTokenBuffer(streamId);
|
|
131
|
+
this.batchTimers.delete(streamId);
|
|
132
|
+
}, this.config.batchIntervalMs));
|
|
133
|
+
}
|
|
134
|
+
this.emit('token:emitted', streamId, tokenBody);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Publish an insight — a distilled discovery from reasoning.
|
|
138
|
+
* Can auto-create a Knowledge Card if configured.
|
|
139
|
+
*/
|
|
140
|
+
async publishInsight(streamId, title, content, insightType, options = {}) {
|
|
141
|
+
const stream = this.streams.get(streamId);
|
|
142
|
+
if (!stream)
|
|
143
|
+
throw new Error(`Stream ${streamId} not found`);
|
|
144
|
+
const insightId = `insight_${ulid()}`;
|
|
145
|
+
const confidence = options.confidence ?? 0.5;
|
|
146
|
+
const insight = {
|
|
147
|
+
insight_id: insightId,
|
|
148
|
+
stream_id: streamId,
|
|
149
|
+
title,
|
|
150
|
+
content,
|
|
151
|
+
insight_type: insightType,
|
|
152
|
+
confidence,
|
|
153
|
+
supporting_evidence: options.supporting_evidence || [],
|
|
154
|
+
author_did: this.identity.did,
|
|
155
|
+
created_at: Date.now(),
|
|
156
|
+
};
|
|
157
|
+
this.insights.set(insightId, insight);
|
|
158
|
+
stream.insight_count++;
|
|
159
|
+
// Auto-create knowledge card if configured and confidence is high enough
|
|
160
|
+
const shouldCreateCard = (options.auto_create_card ?? this.config.autoCreateCards)
|
|
161
|
+
&& confidence >= this.config.autoCardMinConfidence
|
|
162
|
+
&& this.knowledge;
|
|
163
|
+
if (shouldCreateCard) {
|
|
164
|
+
try {
|
|
165
|
+
const card = await this.knowledge.createCard(this.getDefaultSpaceId(stream.room_id), this.insightTypeToKnowledgeType(insightType), title, content, {
|
|
166
|
+
summary: content.slice(0, 200),
|
|
167
|
+
tags: ['cot-insight', insightType],
|
|
168
|
+
domain: [],
|
|
169
|
+
source: {
|
|
170
|
+
type: 'coc',
|
|
171
|
+
id: stream.chain_id,
|
|
172
|
+
context: `CoT Stream ${streamId}`,
|
|
173
|
+
},
|
|
174
|
+
confidence,
|
|
175
|
+
});
|
|
176
|
+
insight.knowledge_card_id = card.id;
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Knowledge pool may not be available; insight still valid
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const body = {
|
|
183
|
+
stream_id: streamId,
|
|
184
|
+
insight_id: insightId,
|
|
185
|
+
title,
|
|
186
|
+
content,
|
|
187
|
+
insight_type: insightType,
|
|
188
|
+
confidence,
|
|
189
|
+
supporting_evidence: options.supporting_evidence || [],
|
|
190
|
+
related_streams: options.related_streams,
|
|
191
|
+
auto_create_card: !!shouldCreateCard,
|
|
192
|
+
};
|
|
193
|
+
await this.rooms.sendMessage(stream.room_id, body, 'cot.stream.insight');
|
|
194
|
+
this.emit('insight:published', streamId, insight);
|
|
195
|
+
return insightId;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Ask a question to other agents in the room — Socratic dialogue.
|
|
199
|
+
*/
|
|
200
|
+
async askQuestion(streamId, question, questionType, options = {}) {
|
|
201
|
+
const stream = this.streams.get(streamId);
|
|
202
|
+
if (!stream)
|
|
203
|
+
throw new Error(`Stream ${streamId} not found`);
|
|
204
|
+
const questionId = `q_${ulid()}`;
|
|
205
|
+
const cotQuestion = {
|
|
206
|
+
question_id: questionId,
|
|
207
|
+
stream_id: streamId,
|
|
208
|
+
question,
|
|
209
|
+
question_type: questionType,
|
|
210
|
+
context: options.context || '',
|
|
211
|
+
asked_by: this.identity.did,
|
|
212
|
+
target_did: options.target_did,
|
|
213
|
+
urgency: options.urgency || 'normal',
|
|
214
|
+
asked_at: Date.now(),
|
|
215
|
+
answered: false,
|
|
216
|
+
answers: [],
|
|
217
|
+
};
|
|
218
|
+
this.questions.set(questionId, cotQuestion);
|
|
219
|
+
stream.question_count++;
|
|
220
|
+
const body = {
|
|
221
|
+
stream_id: streamId,
|
|
222
|
+
question_id: questionId,
|
|
223
|
+
question,
|
|
224
|
+
question_type: questionType,
|
|
225
|
+
context: options.context || '',
|
|
226
|
+
target_did: options.target_did,
|
|
227
|
+
urgency: options.urgency || 'normal',
|
|
228
|
+
};
|
|
229
|
+
await this.rooms.sendMessage(stream.room_id, body, 'cot.stream.question');
|
|
230
|
+
this.emit('question:asked', streamId, cotQuestion);
|
|
231
|
+
return questionId;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Answer a question from another agent.
|
|
235
|
+
*/
|
|
236
|
+
async answerQuestion(streamId, questionId, answer, options = {}) {
|
|
237
|
+
const stream = this.streams.get(streamId);
|
|
238
|
+
if (!stream)
|
|
239
|
+
throw new Error(`Stream ${streamId} not found`);
|
|
240
|
+
const cotQuestion = this.questions.get(questionId);
|
|
241
|
+
if (!cotQuestion)
|
|
242
|
+
throw new Error(`Question ${questionId} not found`);
|
|
243
|
+
const answerId = `a_${ulid()}`;
|
|
244
|
+
const cotAnswer = {
|
|
245
|
+
answer_id: answerId,
|
|
246
|
+
question_id: questionId,
|
|
247
|
+
stream_id: streamId,
|
|
248
|
+
answer,
|
|
249
|
+
confidence: options.confidence ?? 0.5,
|
|
250
|
+
references: options.references || [],
|
|
251
|
+
answered_by: this.identity.did,
|
|
252
|
+
answered_at: Date.now(),
|
|
253
|
+
};
|
|
254
|
+
cotQuestion.answers.push(cotAnswer);
|
|
255
|
+
cotQuestion.answered = true;
|
|
256
|
+
const body = {
|
|
257
|
+
stream_id: streamId,
|
|
258
|
+
question_id: questionId,
|
|
259
|
+
answer,
|
|
260
|
+
confidence: options.confidence ?? 0.5,
|
|
261
|
+
references: options.references,
|
|
262
|
+
};
|
|
263
|
+
await this.rooms.sendMessage(stream.room_id, body, 'cot.stream.answer');
|
|
264
|
+
this.emit('question:answered', streamId, questionId, cotAnswer);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Branch the reasoning — explore a divergent hypothesis in parallel.
|
|
268
|
+
*/
|
|
269
|
+
async branchStream(parentStreamId, hypothesis, branchReason) {
|
|
270
|
+
const parent = this.streams.get(parentStreamId);
|
|
271
|
+
if (!parent)
|
|
272
|
+
throw new Error(`Parent stream ${parentStreamId} not found`);
|
|
273
|
+
// Create a new stream as a branch
|
|
274
|
+
const branchId = await this.startStream(parent.room_id, hypothesis, {
|
|
275
|
+
chain_id: parent.chain_id,
|
|
276
|
+
step_id: parent.step_id,
|
|
277
|
+
model: parent.model,
|
|
278
|
+
parent_stream_id: parentStreamId,
|
|
279
|
+
});
|
|
280
|
+
const body = {
|
|
281
|
+
stream_id: branchId,
|
|
282
|
+
parent_stream_id: parentStreamId,
|
|
283
|
+
branch_reason: branchReason,
|
|
284
|
+
hypothesis,
|
|
285
|
+
};
|
|
286
|
+
await this.rooms.sendMessage(parent.room_id, body, 'cot.stream.branch');
|
|
287
|
+
this.emit('stream:branched', parentStreamId, branchId, hypothesis);
|
|
288
|
+
return branchId;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Merge multiple reasoning branches into a synthesis.
|
|
292
|
+
*/
|
|
293
|
+
async mergeStreams(targetStreamId, sourceStreamIds, synthesis, consensusLevel) {
|
|
294
|
+
const target = this.streams.get(targetStreamId);
|
|
295
|
+
if (!target)
|
|
296
|
+
throw new Error(`Target stream ${targetStreamId} not found`);
|
|
297
|
+
// Mark source streams as merged
|
|
298
|
+
for (const srcId of sourceStreamIds) {
|
|
299
|
+
const src = this.streams.get(srcId);
|
|
300
|
+
if (src) {
|
|
301
|
+
src.status = 'merged';
|
|
302
|
+
src.merged_into = targetStreamId;
|
|
303
|
+
src.ended_at = Date.now();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const body = {
|
|
307
|
+
stream_id: targetStreamId,
|
|
308
|
+
merged_streams: sourceStreamIds,
|
|
309
|
+
synthesis,
|
|
310
|
+
consensus_level: Math.max(0, Math.min(1, consensusLevel)),
|
|
311
|
+
};
|
|
312
|
+
await this.rooms.sendMessage(target.room_id, body, 'cot.stream.merge');
|
|
313
|
+
this.emit('stream:merged', targetStreamId, sourceStreamIds, synthesis);
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* End a reasoning stream.
|
|
317
|
+
*/
|
|
318
|
+
async endStream(streamId, status, summary) {
|
|
319
|
+
const stream = this.streams.get(streamId);
|
|
320
|
+
if (!stream)
|
|
321
|
+
throw new Error(`Stream ${streamId} not found`);
|
|
322
|
+
// Flush any remaining tokens
|
|
323
|
+
await this.flushTokenBuffer(streamId);
|
|
324
|
+
stream.status = status === 'completed' ? 'completed' :
|
|
325
|
+
status === 'paused' ? 'paused' :
|
|
326
|
+
status === 'merged' ? 'merged' : 'abandoned';
|
|
327
|
+
stream.ended_at = Date.now();
|
|
328
|
+
const body = {
|
|
329
|
+
stream_id: streamId,
|
|
330
|
+
status,
|
|
331
|
+
summary,
|
|
332
|
+
insights_generated: stream.insight_count,
|
|
333
|
+
tokens_total: stream.token_count,
|
|
334
|
+
duration_ms: stream.ended_at - stream.started_at,
|
|
335
|
+
};
|
|
336
|
+
await this.rooms.sendMessage(stream.room_id, body, 'cot.stream.end');
|
|
337
|
+
this.emit('stream:ended', streamId, status, summary);
|
|
338
|
+
// Cleanup batch timer
|
|
339
|
+
const timer = this.batchTimers.get(streamId);
|
|
340
|
+
if (timer) {
|
|
341
|
+
clearTimeout(timer);
|
|
342
|
+
this.batchTimers.delete(streamId);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// ─── Context Retrieval ───────────────────────────────────────
|
|
346
|
+
/**
|
|
347
|
+
* Get the full reasoning context for a stream — enables agents
|
|
348
|
+
* to "absorb" another agent's thinking and build on it.
|
|
349
|
+
*/
|
|
350
|
+
getReasoningContext(streamId) {
|
|
351
|
+
const stream = this.streams.get(streamId);
|
|
352
|
+
if (!stream)
|
|
353
|
+
return null;
|
|
354
|
+
const tokens = this.tokenBuffers.get(streamId) || [];
|
|
355
|
+
const streamInsights = Array.from(this.insights.values())
|
|
356
|
+
.filter(i => i.stream_id === streamId);
|
|
357
|
+
const streamQuestions = Array.from(this.questions.values())
|
|
358
|
+
.filter(q => q.stream_id === streamId);
|
|
359
|
+
return {
|
|
360
|
+
stream_id: streamId,
|
|
361
|
+
tokens,
|
|
362
|
+
insights: streamInsights,
|
|
363
|
+
questions: streamQuestions,
|
|
364
|
+
goal: stream.goal,
|
|
365
|
+
branches: stream.branches,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Get all active streams in a room — see what everyone is thinking.
|
|
370
|
+
*/
|
|
371
|
+
getRoomStreams(roomId) {
|
|
372
|
+
const streamIds = this.roomStreams.get(roomId);
|
|
373
|
+
if (!streamIds)
|
|
374
|
+
return [];
|
|
375
|
+
return Array.from(streamIds)
|
|
376
|
+
.map(id => this.streams.get(id))
|
|
377
|
+
.filter((s) => s !== undefined);
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Get active streams for a specific CoC chain.
|
|
381
|
+
*/
|
|
382
|
+
getChainStreams(chainId) {
|
|
383
|
+
return Array.from(this.streams.values())
|
|
384
|
+
.filter(s => s.chain_id === chainId);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Get all unanswered questions targeting this agent.
|
|
388
|
+
*/
|
|
389
|
+
getPendingQuestions() {
|
|
390
|
+
return Array.from(this.questions.values())
|
|
391
|
+
.filter(q => !q.answered && (!q.target_did || q.target_did === this.identity.did));
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Get the reasoning tree starting from a root stream,
|
|
395
|
+
* including all branches and merges.
|
|
396
|
+
*/
|
|
397
|
+
getReasoningTree(rootStreamId) {
|
|
398
|
+
const root = this.streams.get(rootStreamId);
|
|
399
|
+
if (!root)
|
|
400
|
+
return null;
|
|
401
|
+
const branches = [];
|
|
402
|
+
const allInsights = [];
|
|
403
|
+
let maxDepth = 0;
|
|
404
|
+
const traverse = (streamId, depth) => {
|
|
405
|
+
const stream = this.streams.get(streamId);
|
|
406
|
+
if (!stream)
|
|
407
|
+
return;
|
|
408
|
+
maxDepth = Math.max(maxDepth, depth);
|
|
409
|
+
// Collect insights
|
|
410
|
+
for (const insight of this.insights.values()) {
|
|
411
|
+
if (insight.stream_id === streamId) {
|
|
412
|
+
allInsights.push(insight);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// Traverse branches
|
|
416
|
+
for (const branchId of stream.branches) {
|
|
417
|
+
const branch = this.streams.get(branchId);
|
|
418
|
+
if (branch) {
|
|
419
|
+
branches.push(branch);
|
|
420
|
+
traverse(branchId, depth + 1);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
traverse(rootStreamId, 0);
|
|
425
|
+
return { root, branches, insights: allInsights, depth: maxDepth };
|
|
426
|
+
}
|
|
427
|
+
// ─── Event Handling ──────────────────────────────────────────
|
|
428
|
+
bindEvents() {
|
|
429
|
+
// Handle incoming CoT stream events from other agents
|
|
430
|
+
this.rooms.on('cot:stream:start', (_roomId, envelope) => {
|
|
431
|
+
const body = envelope.body;
|
|
432
|
+
this.handleRemoteStreamStart(envelope.room, body, envelope.from.did);
|
|
433
|
+
});
|
|
434
|
+
this.rooms.on('cot:stream:token', (_roomId, envelope) => {
|
|
435
|
+
const body = envelope.body;
|
|
436
|
+
this.handleRemoteToken(body, envelope.from.did);
|
|
437
|
+
});
|
|
438
|
+
this.rooms.on('cot:stream:insight', (_roomId, envelope) => {
|
|
439
|
+
const body = envelope.body;
|
|
440
|
+
this.handleRemoteInsight(body, envelope.from.did);
|
|
441
|
+
});
|
|
442
|
+
this.rooms.on('cot:stream:question', (_roomId, envelope) => {
|
|
443
|
+
const body = envelope.body;
|
|
444
|
+
this.handleRemoteQuestion(body, envelope.from.did);
|
|
445
|
+
});
|
|
446
|
+
this.rooms.on('cot:stream:answer', (_roomId, envelope) => {
|
|
447
|
+
const body = envelope.body;
|
|
448
|
+
this.handleRemoteAnswer(body, envelope.from.did);
|
|
449
|
+
});
|
|
450
|
+
this.rooms.on('cot:stream:branch', (_roomId, envelope) => {
|
|
451
|
+
const body = envelope.body;
|
|
452
|
+
this.handleRemoteBranch(body, envelope.from.did);
|
|
453
|
+
});
|
|
454
|
+
this.rooms.on('cot:stream:merge', (_roomId, envelope) => {
|
|
455
|
+
const body = envelope.body;
|
|
456
|
+
this.handleRemoteMerge(body, envelope.from.did);
|
|
457
|
+
});
|
|
458
|
+
this.rooms.on('cot:stream:end', (_roomId, envelope) => {
|
|
459
|
+
const body = envelope.body;
|
|
460
|
+
this.handleRemoteStreamEnd(body, envelope.from.did);
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
handleRemoteStreamStart(roomId, body, fromDid) {
|
|
464
|
+
if (this.streams.has(body.stream_id))
|
|
465
|
+
return; // Already known
|
|
466
|
+
const stream = {
|
|
467
|
+
stream_id: body.stream_id,
|
|
468
|
+
room_id: roomId,
|
|
469
|
+
chain_id: body.chain_id,
|
|
470
|
+
step_id: body.step_id,
|
|
471
|
+
goal: body.goal,
|
|
472
|
+
model: body.model,
|
|
473
|
+
owner_did: fromDid,
|
|
474
|
+
parent_stream_id: body.parent_stream_id,
|
|
475
|
+
status: 'active',
|
|
476
|
+
started_at: Date.now(),
|
|
477
|
+
token_count: 0,
|
|
478
|
+
insight_count: 0,
|
|
479
|
+
question_count: 0,
|
|
480
|
+
branches: [],
|
|
481
|
+
};
|
|
482
|
+
this.streams.set(body.stream_id, stream);
|
|
483
|
+
this.tokenBuffers.set(body.stream_id, []);
|
|
484
|
+
if (!this.roomStreams.has(roomId)) {
|
|
485
|
+
this.roomStreams.set(roomId, new Set());
|
|
486
|
+
}
|
|
487
|
+
this.roomStreams.get(roomId).add(body.stream_id);
|
|
488
|
+
if (body.parent_stream_id) {
|
|
489
|
+
const parent = this.streams.get(body.parent_stream_id);
|
|
490
|
+
if (parent)
|
|
491
|
+
parent.branches.push(body.stream_id);
|
|
492
|
+
}
|
|
493
|
+
this.emit('stream:remote:started', body.stream_id, stream);
|
|
494
|
+
}
|
|
495
|
+
handleRemoteToken(body, fromDid) {
|
|
496
|
+
const stream = this.streams.get(body.stream_id);
|
|
497
|
+
if (!stream)
|
|
498
|
+
return;
|
|
499
|
+
stream.token_count = Math.max(stream.token_count, body.seq);
|
|
500
|
+
// Store in buffer for context retrieval
|
|
501
|
+
const buffer = this.tokenBuffers.get(body.stream_id);
|
|
502
|
+
if (buffer) {
|
|
503
|
+
buffer.push(body);
|
|
504
|
+
// Keep buffer bounded
|
|
505
|
+
if (buffer.length > this.config.maxBufferSize * 10) {
|
|
506
|
+
buffer.splice(0, buffer.length - this.config.maxBufferSize * 5);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
this.emit('token:received', body.stream_id, body, fromDid);
|
|
510
|
+
}
|
|
511
|
+
handleRemoteInsight(body, fromDid) {
|
|
512
|
+
const insight = {
|
|
513
|
+
insight_id: body.insight_id,
|
|
514
|
+
stream_id: body.stream_id,
|
|
515
|
+
title: body.title,
|
|
516
|
+
content: body.content,
|
|
517
|
+
insight_type: body.insight_type,
|
|
518
|
+
confidence: body.confidence,
|
|
519
|
+
supporting_evidence: body.supporting_evidence,
|
|
520
|
+
author_did: fromDid,
|
|
521
|
+
created_at: Date.now(),
|
|
522
|
+
};
|
|
523
|
+
this.insights.set(body.insight_id, insight);
|
|
524
|
+
const stream = this.streams.get(body.stream_id);
|
|
525
|
+
if (stream)
|
|
526
|
+
stream.insight_count++;
|
|
527
|
+
this.emit('insight:received', body.stream_id, insight);
|
|
528
|
+
}
|
|
529
|
+
handleRemoteQuestion(body, fromDid) {
|
|
530
|
+
const question = {
|
|
531
|
+
question_id: body.question_id,
|
|
532
|
+
stream_id: body.stream_id,
|
|
533
|
+
question: body.question,
|
|
534
|
+
question_type: body.question_type,
|
|
535
|
+
context: body.context,
|
|
536
|
+
asked_by: fromDid,
|
|
537
|
+
target_did: body.target_did,
|
|
538
|
+
urgency: body.urgency,
|
|
539
|
+
asked_at: Date.now(),
|
|
540
|
+
answered: false,
|
|
541
|
+
answers: [],
|
|
542
|
+
};
|
|
543
|
+
this.questions.set(body.question_id, question);
|
|
544
|
+
const stream = this.streams.get(body.stream_id);
|
|
545
|
+
if (stream)
|
|
546
|
+
stream.question_count++;
|
|
547
|
+
// Emit targeted event if this agent is the target
|
|
548
|
+
if (!body.target_did || body.target_did === this.identity.did) {
|
|
549
|
+
this.emit('question:received', body.stream_id, question);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
handleRemoteAnswer(body, fromDid) {
|
|
553
|
+
const question = this.questions.get(body.question_id);
|
|
554
|
+
if (!question)
|
|
555
|
+
return;
|
|
556
|
+
const answer = {
|
|
557
|
+
answer_id: `a_${ulid()}`,
|
|
558
|
+
question_id: body.question_id,
|
|
559
|
+
stream_id: body.stream_id,
|
|
560
|
+
answer: body.answer,
|
|
561
|
+
confidence: body.confidence,
|
|
562
|
+
references: body.references || [],
|
|
563
|
+
answered_by: fromDid,
|
|
564
|
+
answered_at: Date.now(),
|
|
565
|
+
};
|
|
566
|
+
question.answers.push(answer);
|
|
567
|
+
question.answered = true;
|
|
568
|
+
this.emit('answer:received', body.stream_id, body.question_id, answer);
|
|
569
|
+
}
|
|
570
|
+
handleRemoteBranch(body, fromDid) {
|
|
571
|
+
const parent = this.streams.get(body.parent_stream_id);
|
|
572
|
+
if (parent && !parent.branches.includes(body.stream_id)) {
|
|
573
|
+
parent.branches.push(body.stream_id);
|
|
574
|
+
}
|
|
575
|
+
this.emit('stream:remote:branched', body.parent_stream_id, body.stream_id, body.hypothesis);
|
|
576
|
+
}
|
|
577
|
+
handleRemoteMerge(body, fromDid) {
|
|
578
|
+
for (const srcId of body.merged_streams) {
|
|
579
|
+
const src = this.streams.get(srcId);
|
|
580
|
+
if (src) {
|
|
581
|
+
src.status = 'merged';
|
|
582
|
+
src.merged_into = body.stream_id;
|
|
583
|
+
src.ended_at = Date.now();
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
this.emit('stream:remote:merged', body.stream_id, body.merged_streams, body.synthesis);
|
|
587
|
+
}
|
|
588
|
+
handleRemoteStreamEnd(body, fromDid) {
|
|
589
|
+
const stream = this.streams.get(body.stream_id);
|
|
590
|
+
if (!stream)
|
|
591
|
+
return;
|
|
592
|
+
stream.status = body.status === 'completed' ? 'completed' :
|
|
593
|
+
body.status === 'paused' ? 'paused' :
|
|
594
|
+
body.status === 'merged' ? 'merged' : 'abandoned';
|
|
595
|
+
stream.ended_at = Date.now();
|
|
596
|
+
this.emit('stream:remote:ended', body.stream_id, body.status, body.summary);
|
|
597
|
+
}
|
|
598
|
+
// ─── Helpers ─────────────────────────────────────────────────
|
|
599
|
+
async flushTokenBuffer(streamId) {
|
|
600
|
+
const buffer = this.tokenBuffers.get(streamId);
|
|
601
|
+
const stream = this.streams.get(streamId);
|
|
602
|
+
if (!buffer || buffer.length === 0 || !stream)
|
|
603
|
+
return;
|
|
604
|
+
// Only flush tokens we own (don't re-broadcast remote tokens)
|
|
605
|
+
const ownTokens = buffer.filter(t => !this.isRemoteToken(t));
|
|
606
|
+
if (ownTokens.length === 0)
|
|
607
|
+
return;
|
|
608
|
+
// Send each token as its own message for real-time streaming
|
|
609
|
+
for (const token of ownTokens) {
|
|
610
|
+
await this.rooms.sendMessage(stream.room_id, token, 'cot.stream.token');
|
|
611
|
+
}
|
|
612
|
+
// Keep remote tokens in buffer, clear own tokens
|
|
613
|
+
const remoteTokens = buffer.filter(t => this.isRemoteToken(t));
|
|
614
|
+
this.tokenBuffers.set(streamId, remoteTokens);
|
|
615
|
+
}
|
|
616
|
+
isRemoteToken(token) {
|
|
617
|
+
// Tokens we emitted locally don't have a 'from' field yet;
|
|
618
|
+
// remote tokens are added via handleRemoteToken.
|
|
619
|
+
// Use a simple heuristic: check if we've already sent this seq
|
|
620
|
+
return false; // Local tokens are always flushed; buffer keeps all for context
|
|
621
|
+
}
|
|
622
|
+
getDefaultSpaceId(roomId) {
|
|
623
|
+
return `space_${roomId}`;
|
|
624
|
+
}
|
|
625
|
+
insightTypeToKnowledgeType(type) {
|
|
626
|
+
const mapping = {
|
|
627
|
+
discovery: 'finding',
|
|
628
|
+
pattern: 'insight',
|
|
629
|
+
contradiction: 'evidence',
|
|
630
|
+
connection: 'insight',
|
|
631
|
+
gap: 'hypothesis',
|
|
632
|
+
confirmation: 'evidence',
|
|
633
|
+
};
|
|
634
|
+
return mapping[type] || 'insight';
|
|
635
|
+
}
|
|
636
|
+
// ─── Getters ─────────────────────────────────────────────────
|
|
637
|
+
getStream(streamId) {
|
|
638
|
+
return this.streams.get(streamId);
|
|
639
|
+
}
|
|
640
|
+
getInsight(insightId) {
|
|
641
|
+
return this.insights.get(insightId);
|
|
642
|
+
}
|
|
643
|
+
getQuestion(questionId) {
|
|
644
|
+
return this.questions.get(questionId);
|
|
645
|
+
}
|
|
646
|
+
getActiveStreams() {
|
|
647
|
+
return Array.from(this.streams.values())
|
|
648
|
+
.filter(s => s.status === 'active');
|
|
649
|
+
}
|
|
650
|
+
getStats() {
|
|
651
|
+
const questions = Array.from(this.questions.values());
|
|
652
|
+
return {
|
|
653
|
+
totalStreams: this.streams.size,
|
|
654
|
+
activeStreams: Array.from(this.streams.values()).filter(s => s.status === 'active').length,
|
|
655
|
+
totalInsights: this.insights.size,
|
|
656
|
+
totalQuestions: questions.length,
|
|
657
|
+
answeredQuestions: questions.filter(q => q.answered).length,
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
// ─── Cleanup ─────────────────────────────────────────────────
|
|
661
|
+
destroy() {
|
|
662
|
+
for (const timer of this.batchTimers.values()) {
|
|
663
|
+
clearTimeout(timer);
|
|
664
|
+
}
|
|
665
|
+
this.batchTimers.clear();
|
|
666
|
+
this.streams.clear();
|
|
667
|
+
this.insights.clear();
|
|
668
|
+
this.questions.clear();
|
|
669
|
+
this.tokenBuffers.clear();
|
|
670
|
+
this.roomStreams.clear();
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
//# sourceMappingURL=cot-stream.js.map
|