twining-mcp 1.4.0 → 1.4.2
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/README.md +119 -106
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/dashboard/public/app.js +140 -9
- package/dist/dashboard/public/index.html +19 -1
- package/dist/dashboard/public/style.css +123 -3
- package/dist/embeddings/search.d.ts.map +1 -1
- package/dist/embeddings/search.js +5 -2
- package/dist/embeddings/search.js.map +1 -1
- package/dist/engine/archiver.d.ts.map +1 -1
- package/dist/engine/archiver.js +15 -11
- package/dist/engine/archiver.js.map +1 -1
- package/dist/engine/blackboard.d.ts +6 -1
- package/dist/engine/blackboard.d.ts.map +1 -1
- package/dist/engine/blackboard.js +16 -0
- package/dist/engine/blackboard.js.map +1 -1
- package/dist/engine/context-assembler.d.ts +6 -0
- package/dist/engine/context-assembler.d.ts.map +1 -1
- package/dist/engine/context-assembler.js +69 -14
- package/dist/engine/context-assembler.js.map +1 -1
- package/dist/engine/verify.d.ts +8 -2
- package/dist/engine/verify.d.ts.map +1 -1
- package/dist/engine/verify.js +27 -9
- package/dist/engine/verify.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +3 -1
- package/dist/server.js.map +1 -1
- package/dist/storage/decision-store.d.ts.map +1 -1
- package/dist/storage/decision-store.js +8 -18
- package/dist/storage/decision-store.js.map +1 -1
- package/dist/storage/handoff-store.d.ts +1 -1
- package/dist/storage/handoff-store.d.ts.map +1 -1
- package/dist/storage/handoff-store.js +45 -17
- package/dist/storage/handoff-store.js.map +1 -1
- package/dist/tools/blackboard-tools.d.ts.map +1 -1
- package/dist/tools/blackboard-tools.js +4 -1
- package/dist/tools/blackboard-tools.js.map +1 -1
- package/dist/tools/context-tools.d.ts.map +1 -1
- package/dist/tools/context-tools.js +3 -0
- package/dist/tools/context-tools.js.map +1 -1
- package/dist/tools/graph-tools.d.ts.map +1 -1
- package/dist/tools/graph-tools.js +6 -4
- package/dist/tools/graph-tools.js.map +1 -1
- package/dist/tools/lifecycle-tools.d.ts.map +1 -1
- package/dist/tools/lifecycle-tools.js +3 -0
- package/dist/tools/lifecycle-tools.js.map +1 -1
- package/dist/utils/types.d.ts +1 -0
- package/dist/utils/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/dashboard/public/app.js +140 -9
- package/src/dashboard/public/index.html +19 -1
- package/src/dashboard/public/style.css +123 -3
package/README.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<strong>
|
|
7
|
-
|
|
6
|
+
<strong>Your AI agents forget everything. Twining remembers.</strong><br>
|
|
7
|
+
Persistent project memory for Claude Code and other MCP clients.
|
|
8
8
|
</p>
|
|
9
9
|
|
|
10
10
|
<p align="center">
|
|
@@ -14,172 +14,185 @@
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
## The Problem
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
You spend two hours with Claude Code making architectural decisions. You choose PostgreSQL over MongoDB. You settle on JWT for auth. You flag a race condition in the payment module. Then the session ends.
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
- **Decision Tracking** — Record decisions with rationale, alternatives, confidence, dependency chains, and git commit linking
|
|
23
|
-
- **Context Assembly** — Build tailored context packages for a task within a token budget, with handoff results and agent suggestions
|
|
24
|
-
- **Knowledge Graph** — Lightweight entity-relation graph with traversal, search, and full state export
|
|
25
|
-
- **Semantic Search** — Local embeddings via all-MiniLM-L6-v2 with automatic keyword fallback
|
|
26
|
-
- **Agent Coordination** — Registry with capability-based discovery, delegation matching, structured handoffs, and liveness tracking
|
|
27
|
-
- **Web Dashboard** — Embedded HTTP server with stats, search, decision timeline, interactive graph visualization, and agent coordination views
|
|
28
|
-
- **Archiving** — Move stale entries to archive while preserving decision records
|
|
21
|
+
Tomorrow you start a new session. Claude has no idea what happened. The decisions are gone. The warnings are gone. The rationale is gone. You re-explain everything — or worse, Claude silently contradicts yesterday's choices.
|
|
29
22
|
|
|
30
|
-
|
|
23
|
+
This gets worse with multiple agents. Agent A decides on REST. Agent B picks gRPC for the same service. Neither knows the other exists. You find out when the code doesn't compile.
|
|
31
24
|
|
|
32
|
-
|
|
25
|
+
**Context windows are ephemeral. Your project's decisions shouldn't be.**
|
|
33
26
|
|
|
34
|
-
|
|
27
|
+
## How Twining Fixes It
|
|
28
|
+
|
|
29
|
+
Twining is an MCP server that gives your AI agents persistent project memory. Decisions survive context resets. New sessions start informed. Multi-agent work stays coordinated.
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
# Install in 10 seconds
|
|
35
33
|
claude mcp add twining -- npx -y twining-mcp --project .
|
|
36
34
|
```
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
**Record a decision with rationale:**
|
|
37
|
+
```
|
|
38
|
+
> Use twining_decide to record: chose PostgreSQL over MongoDB for ACID compliance
|
|
39
|
+
```
|
|
40
|
+
Twining captures the decision, rationale, alternatives considered, confidence level, and affected files — as a structured record, not chat history.
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
claude mcp add twining -s project -- npx -y twining-mcp --project .
|
|
42
|
+
**Start a new session. Get caught up instantly:**
|
|
42
43
|
```
|
|
44
|
+
> Use twining_assemble for the database module
|
|
45
|
+
```
|
|
46
|
+
Twining scores every decision, warning, and finding by relevance to your task, then fills a token budget in priority order. You get exactly the context you need — no firehose, no re-explaining.
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
**Ask why things are the way they are:**
|
|
49
|
+
```
|
|
50
|
+
> Use twining_why src/auth/middleware.ts
|
|
51
|
+
```
|
|
52
|
+
Returns the full decision chain for any file: what was decided, when, why, what alternatives were rejected, and which commit implemented it.
|
|
45
53
|
|
|
46
|
-
|
|
54
|
+
## Why Not Just Use CLAUDE.md?
|
|
47
55
|
|
|
48
|
-
|
|
49
|
-
{
|
|
50
|
-
"mcpServers": {
|
|
51
|
-
"twining": {
|
|
52
|
-
"command": "npx",
|
|
53
|
-
"args": ["-y", "twining-mcp", "--project", "."]
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
```
|
|
56
|
+
CLAUDE.md is static. You write it once and update it manually. It doesn't capture decisions *as they happen*, doesn't track rationale or alternatives, doesn't detect conflicts between agents, and can't selectively assemble context within a token budget.
|
|
58
57
|
|
|
59
|
-
|
|
58
|
+
Twining is dynamic. Every `twining_decide` call records a structured decision. Every `twining_post` shares a finding or warning. Every `twining_assemble` scores relevance and delivers precisely what the current task needs. The `.twining/` directory is your project's living institutional memory.
|
|
60
59
|
|
|
61
|
-
|
|
60
|
+
## Why Not an Orchestrator?
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
> Use twining_post to share a finding about the auth module
|
|
65
|
-
> Use twining_assemble to get context for the refactoring task
|
|
66
|
-
> Use twining_decide to record the database choice with rationale
|
|
67
|
-
> Use twining_why to understand why we chose PostgreSQL
|
|
68
|
-
> Use twining_agents to see which agents are available
|
|
69
|
-
```
|
|
62
|
+
Orchestrators (like agent swarms and hierarchical coordinators) route work by *assigning tasks*. Twining coordinates by *sharing state*. The difference matters:
|
|
70
63
|
|
|
71
|
-
|
|
64
|
+
- **Orchestrators** hold coordination context in their own context window — a single point of failure that degrades as the window fills
|
|
65
|
+
- **Twining's blackboard** persists coordination state outside any agent's window, surviving context resets without information loss
|
|
72
66
|
|
|
73
|
-
|
|
67
|
+
Agents self-select into work by reading the blackboard. No central bottleneck. No relay that drops context. Every agent sees every other agent's decisions and warnings, directly.
|
|
74
68
|
|
|
75
|
-
|
|
69
|
+
## Quick Start
|
|
76
70
|
|
|
77
|
-
###
|
|
71
|
+
### Add to Claude Code
|
|
78
72
|
|
|
79
|
-
|
|
73
|
+
```bash
|
|
74
|
+
claude mcp add twining -- npx -y twining-mcp --project .
|
|
75
|
+
```
|
|
80
76
|
|
|
81
|
-
|
|
77
|
+
Or scope to a single project:
|
|
82
78
|
|
|
83
|
-
```bash
|
|
84
|
-
|
|
79
|
+
```bash
|
|
80
|
+
claude mcp add twining -s project -- npx -y twining-mcp --project .
|
|
85
81
|
```
|
|
86
82
|
|
|
87
|
-
|
|
83
|
+
### Manual Configuration
|
|
84
|
+
|
|
85
|
+
Add to your `.mcp.json`:
|
|
88
86
|
|
|
89
87
|
```json
|
|
90
88
|
{
|
|
91
89
|
"mcpServers": {
|
|
92
90
|
"twining": {
|
|
93
|
-
"command": "
|
|
94
|
-
"args": ["--project", "
|
|
91
|
+
"command": "npx",
|
|
92
|
+
"args": ["-y", "twining-mcp", "--project", "."]
|
|
95
93
|
}
|
|
96
94
|
}
|
|
97
95
|
}
|
|
98
96
|
```
|
|
99
97
|
|
|
100
|
-
|
|
98
|
+
### Get the Most Out of It
|
|
101
99
|
|
|
102
|
-
|
|
100
|
+
Add Twining instructions to your project's `CLAUDE.md` so agents use it automatically. See **[docs/CLAUDE_TEMPLATE.md](docs/CLAUDE_TEMPLATE.md)** for a ready-to-copy template.
|
|
103
101
|
|
|
104
|
-
###
|
|
102
|
+
### Dashboard
|
|
105
103
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
| `twining_read` | Read blackboard entries with optional filters by type, scope, or agent |
|
|
110
|
-
| `twining_query` | Semantic search across blackboard entries, with keyword fallback |
|
|
111
|
-
| `twining_recent` | Get the most recent blackboard entries |
|
|
104
|
+
A web dashboard starts automatically at `http://localhost:24282` — browse decisions, blackboard entries, knowledge graph, and agent state. Configurable via `TWINING_DASHBOARD_PORT`.
|
|
105
|
+
|
|
106
|
+
## What's Inside
|
|
112
107
|
|
|
113
|
-
### Decisions
|
|
108
|
+
### Persistent Decisions
|
|
114
109
|
|
|
115
|
-
| Tool |
|
|
110
|
+
| Tool | What It Does |
|
|
111
|
+
|------|-------------|
|
|
112
|
+
| `twining_decide` | Record a decision with rationale, alternatives, confidence, and traceability |
|
|
113
|
+
| `twining_why` | Get the full decision chain for any file or scope |
|
|
114
|
+
| `twining_trace` | Trace a decision's dependency chain upstream and downstream |
|
|
115
|
+
| `twining_reconsider` | Flag a decision for reconsideration with impact analysis |
|
|
116
|
+
| `twining_override` | Override a decision, optionally creating a replacement |
|
|
117
|
+
| `twining_search_decisions` | Search decisions by keyword or semantic similarity |
|
|
118
|
+
| `twining_link_commit` | Link a git commit to a decision |
|
|
119
|
+
| `twining_commits` | Find decisions by git commit |
|
|
120
|
+
|
|
121
|
+
### Shared Blackboard
|
|
122
|
+
|
|
123
|
+
| Tool | What It Does |
|
|
116
124
|
|------|-------------|
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
|
125
|
-
|
|
126
|
-
### Context & Lifecycle
|
|
127
|
-
|
|
128
|
-
| Tool | Description |
|
|
125
|
+
| `twining_post` | Share a finding, warning, need, or question with all agents |
|
|
126
|
+
| `twining_read` | Read entries filtered by type, scope, or agent |
|
|
127
|
+
| `twining_query` | Semantic search across all entries |
|
|
128
|
+
| `twining_recent` | Get the latest entries |
|
|
129
|
+
|
|
130
|
+
### Context Assembly
|
|
131
|
+
|
|
132
|
+
| Tool | What It Does |
|
|
129
133
|
|------|-------------|
|
|
130
134
|
| `twining_assemble` | Build tailored context for a task within a token budget |
|
|
131
|
-
| `twining_summarize` |
|
|
132
|
-
| `twining_what_changed` |
|
|
133
|
-
| `twining_status` |
|
|
134
|
-
| `twining_archive` |
|
|
135
|
-
| `twining_export` | Export full
|
|
135
|
+
| `twining_summarize` | High-level summary of project state |
|
|
136
|
+
| `twining_what_changed` | What changed since a given point in time |
|
|
137
|
+
| `twining_status` | Health check — entry counts, warnings, agent status |
|
|
138
|
+
| `twining_archive` | Move stale entries to archive |
|
|
139
|
+
| `twining_export` | Export full state as markdown |
|
|
136
140
|
|
|
137
141
|
### Knowledge Graph
|
|
138
142
|
|
|
139
|
-
| Tool |
|
|
143
|
+
| Tool | What It Does |
|
|
140
144
|
|------|-------------|
|
|
141
|
-
| `twining_add_entity` | Add or update
|
|
142
|
-
| `twining_add_relation` | Add a relation between
|
|
143
|
-
| `twining_neighbors` | Traverse
|
|
144
|
-
| `twining_graph_query` | Search
|
|
145
|
+
| `twining_add_entity` | Add or update an entity |
|
|
146
|
+
| `twining_add_relation` | Add a relation between entities |
|
|
147
|
+
| `twining_neighbors` | Traverse from an entity up to depth 3 |
|
|
148
|
+
| `twining_graph_query` | Search by name or property |
|
|
149
|
+
|
|
150
|
+
Decisions auto-populate the graph: `twining_decide` creates file and function entities with `decided_by` relations for every affected file.
|
|
145
151
|
|
|
146
152
|
### Agent Coordination
|
|
147
153
|
|
|
148
|
-
| Tool |
|
|
154
|
+
| Tool | What It Does |
|
|
149
155
|
|------|-------------|
|
|
150
|
-
| `twining_agents` | List
|
|
151
|
-
| `twining_discover` | Find agents matching required capabilities
|
|
152
|
-
| `twining_delegate` | Post a delegation request
|
|
153
|
-
| `twining_handoff` |
|
|
156
|
+
| `twining_agents` | List agents with capabilities and liveness |
|
|
157
|
+
| `twining_discover` | Find agents matching required capabilities |
|
|
158
|
+
| `twining_delegate` | Post a delegation request with capability requirements |
|
|
159
|
+
| `twining_handoff` | Hand off work with results and auto-assembled context |
|
|
154
160
|
| `twining_acknowledge` | Acknowledge receipt of a handoff |
|
|
155
161
|
|
|
156
|
-
##
|
|
162
|
+
## How It Works
|
|
157
163
|
|
|
158
|
-
|
|
164
|
+
All state lives in `.twining/` as plain files — JSONL for the blackboard, JSON for decisions, graph, agents, and handoffs. Everything is `jq`-queryable, `grep`-able, and git-diffable. No database. No cloud. No accounts.
|
|
159
165
|
|
|
160
|
-
|
|
161
|
-
- **Engine Layer** — Business logic for each domain: blackboard posting/querying, decision recording/tracing, graph traversal, context assembly with token budgeting, agent coordination with capability-based discovery and delegation, and archiving.
|
|
162
|
-
- **Embeddings Layer** — Lazy-loaded local embeddings using `@huggingface/transformers` with all-MiniLM-L6-v2. Falls back to keyword search if model loading fails. The server never fails to start because of embedding issues.
|
|
163
|
-
- **Dashboard Layer** — Embedded HTTP server running alongside MCP stdio transport. Vanilla HTML/CSS/JS with cytoscape.js for graph visualization and vis-timeline for decision timelines. Read-only observer of Twining state.
|
|
164
|
-
- **Tools Layer** — MCP tool definitions that map 1:1 to the tool surface. Each tool validates input with Zod and returns structured results.
|
|
166
|
+
**Architecture layers:**
|
|
165
167
|
|
|
166
|
-
|
|
168
|
+
- **Storage** — File-backed stores with locking for concurrent access
|
|
169
|
+
- **Engine** — Decision tracking, blackboard, graph traversal, context assembly with token budgeting, agent coordination
|
|
170
|
+
- **Embeddings** — Local all-MiniLM-L6-v2 via `@huggingface/transformers`, lazy-loaded, with keyword fallback. The server never fails to start because of embedding issues.
|
|
171
|
+
- **Dashboard** — Read-only web UI with cytoscape.js graph visualization and vis-timeline
|
|
172
|
+
- **Tools** — MCP tool definitions validated with Zod, mapping 1:1 to the tool surface
|
|
167
173
|
|
|
168
|
-
See [TWINING-DESIGN-SPEC.md](TWINING-DESIGN-SPEC.md) for the full
|
|
174
|
+
See [TWINING-DESIGN-SPEC.md](TWINING-DESIGN-SPEC.md) for the full specification.
|
|
169
175
|
|
|
170
|
-
##
|
|
176
|
+
## FAQ
|
|
171
177
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
178
|
+
**Does Twining slow down Claude Code?**
|
|
179
|
+
No. It's a local MCP server — tool calls are local file reads/writes. Semantic search loads lazily on first use.
|
|
180
|
+
|
|
181
|
+
**Can I use it with Cursor, Windsurf, or other MCP clients?**
|
|
182
|
+
Yes. Twining is a standard MCP server. Any MCP host can connect to it.
|
|
175
183
|
|
|
176
|
-
|
|
177
|
-
|
|
184
|
+
**Where does my data go?**
|
|
185
|
+
Nowhere. All state is local in `.twining/`. No telemetry, no cloud, no external calls.
|
|
178
186
|
|
|
179
|
-
|
|
180
|
-
|
|
187
|
+
**Is Twining an agent orchestrator?**
|
|
188
|
+
No. It's a coordination state layer. It captures what agents decided and why, and makes that knowledge available to future agents. Use it alongside orchestrators, agent teams, or standalone sessions.
|
|
181
189
|
|
|
182
|
-
|
|
190
|
+
## Development
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
npm install # Install dependencies
|
|
194
|
+
npm run build # Build
|
|
195
|
+
npm test # Run tests (444 tests)
|
|
183
196
|
npm run test:watch
|
|
184
197
|
```
|
|
185
198
|
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,eAAO,MAAM,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,eAAO,MAAM,cAAc,EAAE,aAiC5B,CAAC;AA+BF;;;GAGG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CAc5D"}
|
package/dist/config.js
CHANGED
|
@@ -17,9 +17,10 @@ export const DEFAULT_CONFIG = {
|
|
|
17
17
|
default_max_tokens: 4000,
|
|
18
18
|
priority_weights: {
|
|
19
19
|
recency: 0.3,
|
|
20
|
-
relevance: 0.
|
|
20
|
+
relevance: 0.3,
|
|
21
21
|
decision_confidence: 0.2,
|
|
22
22
|
warning_boost: 0.1,
|
|
23
|
+
graph_connectivity: 0.1,
|
|
23
24
|
},
|
|
24
25
|
},
|
|
25
26
|
conflict_resolution: "human",
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,IAAI,MAAM,SAAS,CAAC;AAG3B,MAAM,CAAC,MAAM,cAAc,GAAkB;IAC3C,OAAO,EAAE,CAAC;IACV,YAAY,EAAE,EAAE;IAChB,eAAe,EAAE,kBAAkB;IACnC,OAAO,EAAE;QACP,sBAAsB,EAAE,IAAI;QAC5B,8BAA8B,EAAE,IAAI;QACpC,qCAAqC,EAAE,GAAG;KAC3C;IACD,gBAAgB,EAAE;QAChB,kBAAkB,EAAE,IAAI;QACxB,gBAAgB,EAAE;YAChB,OAAO,EAAE,GAAG;YACZ,SAAS,EAAE,GAAG;YACd,mBAAmB,EAAE,GAAG;YACxB,aAAa,EAAE,GAAG;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,IAAI,MAAM,SAAS,CAAC;AAG3B,MAAM,CAAC,MAAM,cAAc,GAAkB;IAC3C,OAAO,EAAE,CAAC;IACV,YAAY,EAAE,EAAE;IAChB,eAAe,EAAE,kBAAkB;IACnC,OAAO,EAAE;QACP,sBAAsB,EAAE,IAAI;QAC5B,8BAA8B,EAAE,IAAI;QACpC,qCAAqC,EAAE,GAAG;KAC3C;IACD,gBAAgB,EAAE;QAChB,kBAAkB,EAAE,IAAI;QACxB,gBAAgB,EAAE;YAChB,OAAO,EAAE,GAAG;YACZ,SAAS,EAAE,GAAG;YACd,mBAAmB,EAAE,GAAG;YACxB,aAAa,EAAE,GAAG;YAClB,kBAAkB,EAAE,GAAG;SACxB;KACF;IACD,mBAAmB,EAAE,OAAO;IAC5B,MAAM,EAAE;QACN,QAAQ,EAAE;YACR,aAAa,EAAE,MAAM,EAAE,YAAY;YACnC,aAAa,EAAE,OAAO,EAAE,aAAa;SACtC;KACF;IACD,WAAW,EAAE;QACX,QAAQ,EAAE;YACR,OAAO,EAAE,MAAM,EAAQ,YAAY;YACnC,SAAS,EAAE,OAAO,EAAK,aAAa;YACpC,MAAM,EAAE,QAAQ,EAAO,UAAU;SAClC;KACF;CACF,CAAC;AAEF,4DAA4D;AAC5D,SAAS,SAAS,CAChB,MAA+B,EAC/B,MAA+B;IAE/B,MAAM,MAAM,GAA4B,EAAE,GAAG,MAAM,EAAE,CAAC;IACtD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,SAAS,KAAK,SAAS;YAAE,SAAS;QACtC,IACE,SAAS,KAAK,IAAI;YAClB,OAAO,SAAS,KAAK,QAAQ;YAC7B,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;YACzB,SAAS,KAAK,IAAI;YAClB,OAAO,SAAS,KAAK,QAAQ;YAC7B,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EACzB,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CACrB,SAAoC,EACpC,SAAoC,CACrC,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,UAAkB;IAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IACvD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;IAC/B,CAAC;IACD,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC1E,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;IAC/B,CAAC;IACD,OAAO,SAAS,CACd,cAAoD,EACpD,MAAiC,CACN,CAAC;AAChC,CAAC"}
|
|
@@ -134,6 +134,8 @@ function fetchBlackboard() {
|
|
|
134
134
|
state.connected = true;
|
|
135
135
|
updateConnectionIndicator();
|
|
136
136
|
renderBlackboard();
|
|
137
|
+
renderActivityBreakdown();
|
|
138
|
+
renderRecentActivity();
|
|
137
139
|
})
|
|
138
140
|
.catch(function() {
|
|
139
141
|
state.connected = false;
|
|
@@ -281,7 +283,8 @@ function fetchHandoffDetail(id) {
|
|
|
281
283
|
function refreshData() {
|
|
282
284
|
fetchStatus();
|
|
283
285
|
var tab = state.activeTab;
|
|
284
|
-
if (tab === "
|
|
286
|
+
if (tab === "stats") fetchBlackboard();
|
|
287
|
+
else if (tab === "blackboard") fetchBlackboard();
|
|
285
288
|
else if (tab === "decisions") fetchDecisions();
|
|
286
289
|
else if (tab === "graph") fetchGraph();
|
|
287
290
|
else if (tab === "search" && state.search.query) fetchSearch();
|
|
@@ -457,6 +460,78 @@ function renderStatus() {
|
|
|
457
460
|
if (msgEl) {
|
|
458
461
|
msgEl.style.display = (s.initialized === false) ? "block" : "none";
|
|
459
462
|
}
|
|
463
|
+
|
|
464
|
+
renderActivityBreakdown();
|
|
465
|
+
renderRecentActivity();
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function renderActivityBreakdown() {
|
|
469
|
+
var container = document.getElementById('activity-breakdown');
|
|
470
|
+
if (!container) return;
|
|
471
|
+
|
|
472
|
+
var entries = state.blackboard.data || [];
|
|
473
|
+
var typeCounts = {};
|
|
474
|
+
|
|
475
|
+
for (var i = 0; i < entries.length; i++) {
|
|
476
|
+
var type = entries[i].entry_type || 'unknown';
|
|
477
|
+
typeCounts[type] = (typeCounts[type] || 0) + 1;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
clearElement(container);
|
|
481
|
+
|
|
482
|
+
var types = Object.keys(typeCounts).sort(function(a, b) {
|
|
483
|
+
return typeCounts[b] - typeCounts[a];
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
for (var j = 0; j < types.length; j++) {
|
|
487
|
+
var type = types[j];
|
|
488
|
+
var count = typeCounts[type];
|
|
489
|
+
|
|
490
|
+
var item = el('div', 'activity-item');
|
|
491
|
+
var label = el('div', 'activity-item-label', type);
|
|
492
|
+
var countEl = el('div', 'activity-item-count', String(count));
|
|
493
|
+
|
|
494
|
+
item.appendChild(label);
|
|
495
|
+
item.appendChild(countEl);
|
|
496
|
+
container.appendChild(item);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function renderRecentActivity() {
|
|
501
|
+
var container = document.getElementById('recent-activity');
|
|
502
|
+
if (!container) return;
|
|
503
|
+
|
|
504
|
+
var entries = state.blackboard.data || [];
|
|
505
|
+
var sorted = entries.slice().sort(function(a, b) {
|
|
506
|
+
return new Date(b.timestamp) - new Date(a.timestamp);
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
var recent = sorted.slice(0, 10);
|
|
510
|
+
clearElement(container);
|
|
511
|
+
|
|
512
|
+
if (recent.length === 0) {
|
|
513
|
+
container.textContent = 'No recent activity';
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
for (var i = 0; i < recent.length; i++) {
|
|
518
|
+
var entry = recent[i];
|
|
519
|
+
|
|
520
|
+
var entryDiv = el('div', 'activity-entry');
|
|
521
|
+
|
|
522
|
+
var header = el('div', 'activity-entry-header');
|
|
523
|
+
var typeSpan = el('div', 'activity-entry-type', entry.entry_type || 'unknown');
|
|
524
|
+
var timeSpan = el('div', 'activity-entry-time', formatTimestamp(entry.timestamp));
|
|
525
|
+
|
|
526
|
+
header.appendChild(typeSpan);
|
|
527
|
+
header.appendChild(timeSpan);
|
|
528
|
+
|
|
529
|
+
var summary = el('div', 'activity-entry-summary', truncate(entry.summary || '', 80));
|
|
530
|
+
|
|
531
|
+
entryDiv.appendChild(header);
|
|
532
|
+
entryDiv.appendChild(summary);
|
|
533
|
+
container.appendChild(entryDiv);
|
|
534
|
+
}
|
|
460
535
|
}
|
|
461
536
|
|
|
462
537
|
/* ========== Render: Blackboard ========== */
|
|
@@ -1841,7 +1916,9 @@ function initTimeline() {
|
|
|
1841
1916
|
orientation: { axis: 'top' },
|
|
1842
1917
|
selectable: true,
|
|
1843
1918
|
tooltip: { followMouse: true },
|
|
1844
|
-
margin: { item: 10 }
|
|
1919
|
+
margin: { item: { horizontal: 10, vertical: 20 } },
|
|
1920
|
+
stack: true,
|
|
1921
|
+
maxHeight: 600
|
|
1845
1922
|
};
|
|
1846
1923
|
|
|
1847
1924
|
window.timelineInstance = new vis.Timeline(container, window.timelineDataSet, options);
|
|
@@ -1938,12 +2015,14 @@ function buildGraphStyles() {
|
|
|
1938
2015
|
'label': 'data(label)',
|
|
1939
2016
|
'text-valign': 'bottom',
|
|
1940
2017
|
'text-halign': 'center',
|
|
1941
|
-
'font-size': '
|
|
1942
|
-
'width':
|
|
1943
|
-
'height':
|
|
2018
|
+
'font-size': '11px',
|
|
2019
|
+
'width': 40,
|
|
2020
|
+
'height': 40,
|
|
1944
2021
|
'color': textColor,
|
|
1945
|
-
'text-margin-y':
|
|
1946
|
-
'background-color': '#6b7280'
|
|
2022
|
+
'text-margin-y': 6,
|
|
2023
|
+
'background-color': '#6b7280',
|
|
2024
|
+
'text-wrap': 'wrap',
|
|
2025
|
+
'text-max-width': '120px'
|
|
1947
2026
|
}
|
|
1948
2027
|
},
|
|
1949
2028
|
{
|
|
@@ -2075,10 +2154,19 @@ function initGraphVis() {
|
|
|
2075
2154
|
window.cyInstance = cytoscape({
|
|
2076
2155
|
container: document.getElementById('graph-canvas'),
|
|
2077
2156
|
elements: elements,
|
|
2078
|
-
layout: {
|
|
2157
|
+
layout: {
|
|
2158
|
+
name: 'cose',
|
|
2159
|
+
animate: true,
|
|
2160
|
+
animationDuration: 500,
|
|
2161
|
+
nodeRepulsion: function() { return 12000; },
|
|
2162
|
+
idealEdgeLength: function() { return 100; },
|
|
2163
|
+
nodeOverlap: 20,
|
|
2164
|
+
padding: 40
|
|
2165
|
+
},
|
|
2079
2166
|
style: buildGraphStyles(),
|
|
2080
2167
|
minZoom: 0.2,
|
|
2081
|
-
maxZoom: 5
|
|
2168
|
+
maxZoom: 5,
|
|
2169
|
+
wheelSensitivity: 0.2
|
|
2082
2170
|
});
|
|
2083
2171
|
|
|
2084
2172
|
// Click node to show detail and expand neighbors
|
|
@@ -2106,6 +2194,49 @@ function initGraphVis() {
|
|
|
2106
2194
|
});
|
|
2107
2195
|
|
|
2108
2196
|
renderGraphLegend();
|
|
2197
|
+
setupGraphControls();
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
function setupGraphControls() {
|
|
2201
|
+
var zoomIn = document.getElementById('graph-zoom-in');
|
|
2202
|
+
var zoomOut = document.getElementById('graph-zoom-out');
|
|
2203
|
+
var fit = document.getElementById('graph-fit');
|
|
2204
|
+
var reset = document.getElementById('graph-reset');
|
|
2205
|
+
|
|
2206
|
+
if (zoomIn) {
|
|
2207
|
+
zoomIn.onclick = function() {
|
|
2208
|
+
if (window.cyInstance) window.cyInstance.zoom(window.cyInstance.zoom() * 1.2);
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
if (zoomOut) {
|
|
2213
|
+
zoomOut.onclick = function() {
|
|
2214
|
+
if (window.cyInstance) window.cyInstance.zoom(window.cyInstance.zoom() * 0.8);
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
if (fit) {
|
|
2219
|
+
fit.onclick = function() {
|
|
2220
|
+
if (window.cyInstance) window.cyInstance.fit(null, 40);
|
|
2221
|
+
};
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
if (reset) {
|
|
2225
|
+
reset.onclick = function() {
|
|
2226
|
+
if (window.cyInstance) {
|
|
2227
|
+
var layout = window.cyInstance.layout({
|
|
2228
|
+
name: 'cose',
|
|
2229
|
+
animate: true,
|
|
2230
|
+
animationDuration: 500,
|
|
2231
|
+
nodeRepulsion: function() { return 12000; },
|
|
2232
|
+
idealEdgeLength: function() { return 100; },
|
|
2233
|
+
nodeOverlap: 20,
|
|
2234
|
+
padding: 40
|
|
2235
|
+
});
|
|
2236
|
+
layout.run();
|
|
2237
|
+
}
|
|
2238
|
+
};
|
|
2239
|
+
}
|
|
2109
2240
|
}
|
|
2110
2241
|
|
|
2111
2242
|
function expandNeighbors(nodeId) {
|
|
@@ -115,6 +115,17 @@
|
|
|
115
115
|
<div class="stat-value" id="stat-total-handoffs">--</div>
|
|
116
116
|
</div>
|
|
117
117
|
</div>
|
|
118
|
+
|
|
119
|
+
<div class="stats-section">
|
|
120
|
+
<h2 class="stats-section-title">Activity Breakdown</h2>
|
|
121
|
+
<div id="activity-breakdown" class="activity-breakdown"></div>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<div class="stats-section">
|
|
125
|
+
<h2 class="stats-section-title">Recent Activity</h2>
|
|
126
|
+
<div id="recent-activity" class="recent-activity"></div>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
118
129
|
<div id="uninitialized-msg" style="display:none">No data yet. Twining will populate as agents use MCP tools.</div>
|
|
119
130
|
</div>
|
|
120
131
|
|
|
@@ -213,7 +224,14 @@
|
|
|
213
224
|
<div class="view-visual" id="graph-visual-view" style="display:none">
|
|
214
225
|
<div class="content-area">
|
|
215
226
|
<div class="list-panel">
|
|
216
|
-
<div id="graph-canvas"
|
|
227
|
+
<div id="graph-canvas">
|
|
228
|
+
<div class="graph-controls">
|
|
229
|
+
<button id="graph-zoom-in" title="Zoom in" aria-label="Zoom in">+</button>
|
|
230
|
+
<button id="graph-zoom-out" title="Zoom out" aria-label="Zoom out">−</button>
|
|
231
|
+
<button id="graph-fit" title="Fit to screen" aria-label="Fit to screen">⊡</button>
|
|
232
|
+
<button id="graph-reset" title="Reset layout" aria-label="Reset layout">↻</button>
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
217
235
|
<div class="graph-legend" id="graph-legend"></div>
|
|
218
236
|
</div>
|
|
219
237
|
<div class="detail-panel" id="graph-visual-detail">
|