agent-relay 1.0.6 → 1.0.8
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 +18 -6
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +344 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/daemon/agent-registry.d.ts +60 -0
- package/dist/daemon/agent-registry.d.ts.map +1 -0
- package/dist/daemon/agent-registry.js +158 -0
- package/dist/daemon/agent-registry.js.map +1 -0
- package/dist/daemon/connection.d.ts +11 -1
- package/dist/daemon/connection.d.ts.map +1 -1
- package/dist/daemon/connection.js +31 -2
- package/dist/daemon/connection.js.map +1 -1
- package/dist/daemon/index.d.ts +2 -0
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +2 -0
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/registry.d.ts +9 -0
- package/dist/daemon/registry.d.ts.map +1 -0
- package/dist/daemon/registry.js +9 -0
- package/dist/daemon/registry.js.map +1 -0
- package/dist/daemon/router.d.ts +34 -2
- package/dist/daemon/router.d.ts.map +1 -1
- package/dist/daemon/router.js +111 -1
- package/dist/daemon/router.js.map +1 -1
- package/dist/daemon/server.d.ts +1 -0
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +60 -13
- package/dist/daemon/server.js.map +1 -1
- package/dist/dashboard/public/index.html +625 -16
- package/dist/dashboard/server.d.ts +1 -1
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +125 -7
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/protocol/types.d.ts +15 -1
- package/dist/protocol/types.d.ts.map +1 -1
- package/dist/storage/adapter.d.ts +53 -0
- package/dist/storage/adapter.d.ts.map +1 -1
- package/dist/storage/adapter.js +3 -0
- package/dist/storage/adapter.js.map +1 -1
- package/dist/storage/sqlite-adapter.d.ts +58 -1
- package/dist/storage/sqlite-adapter.d.ts.map +1 -1
- package/dist/storage/sqlite-adapter.js +374 -47
- package/dist/storage/sqlite-adapter.js.map +1 -1
- package/dist/utils/project-namespace.d.ts.map +1 -1
- package/dist/utils/project-namespace.js +22 -1
- package/dist/utils/project-namespace.js.map +1 -1
- package/dist/wrapper/client.d.ts +22 -3
- package/dist/wrapper/client.d.ts.map +1 -1
- package/dist/wrapper/client.js +59 -9
- package/dist/wrapper/client.js.map +1 -1
- package/dist/wrapper/parser.d.ts +110 -4
- package/dist/wrapper/parser.d.ts.map +1 -1
- package/dist/wrapper/parser.js +296 -84
- package/dist/wrapper/parser.js.map +1 -1
- package/dist/wrapper/tmux-wrapper.d.ts +100 -9
- package/dist/wrapper/tmux-wrapper.d.ts.map +1 -1
- package/dist/wrapper/tmux-wrapper.js +441 -83
- package/dist/wrapper/tmux-wrapper.js.map +1 -1
- package/docs/AGENTS.md +27 -27
- package/docs/CHANGELOG.md +1 -1
- package/docs/DESIGN_V2.md +1079 -0
- package/docs/INTEGRATION-GUIDE.md +926 -0
- package/docs/PROPOSAL-trajectories.md +1582 -0
- package/docs/PROTOCOL.md +3 -3
- package/docs/SCALING_ANALYSIS.md +280 -0
- package/docs/TMUX_IMPLEMENTATION_NOTES.md +9 -9
- package/docs/TMUX_IMPROVEMENTS.md +968 -0
- package/docs/competitive-analysis-mcp-agent-mail.md +389 -0
- package/package.json +6 -2
|
@@ -0,0 +1,926 @@
|
|
|
1
|
+
# Agent Infrastructure Integration Guide
|
|
2
|
+
|
|
3
|
+
How to integrate agent-relay, claude-mem, and agent-trajectories into a cohesive stack.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## The Stack at a Glance
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
11
|
+
│ YOUR AGENT (Claude Code, Codex, Gemini, etc.) │
|
|
12
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
13
|
+
│ │ │
|
|
14
|
+
│ ┌────────────┴────────────┐ │
|
|
15
|
+
│ ▼ ▼ │
|
|
16
|
+
│ ┌─────────────────┐ ┌─────────────────┐ │
|
|
17
|
+
│ │ CLAUDE-MEM │ │ AGENT-RELAY │ │
|
|
18
|
+
│ │ (Observations) │ │ (Messaging) │ │
|
|
19
|
+
│ │ │ │ │ │
|
|
20
|
+
│ │ • Tool calls │ │ • ->relay:Agent │ │
|
|
21
|
+
│ │ • Concepts │ │ • Broadcasting │ │
|
|
22
|
+
│ │ • Sessions │ │ • Persistence │ │
|
|
23
|
+
│ └────────┬────────┘ └────────┬────────┘ │
|
|
24
|
+
│ │ │ │
|
|
25
|
+
│ └──────────┬─────────────┘ │
|
|
26
|
+
│ ▼ │
|
|
27
|
+
│ ┌─────────────────────┐ │
|
|
28
|
+
│ │ AGENT-TRAJECTORIES │ │
|
|
29
|
+
│ │ (Narratives) │ │
|
|
30
|
+
│ │ │ │
|
|
31
|
+
│ │ • Task stories │ │
|
|
32
|
+
│ │ • Decisions │ │
|
|
33
|
+
│ │ • Retrospectives │ │
|
|
34
|
+
│ │ • Workspace │ │
|
|
35
|
+
│ └─────────────────────┘ │
|
|
36
|
+
│ │
|
|
37
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Phase 0: Current State (What Exists)
|
|
43
|
+
|
|
44
|
+
### agent-relay (✅ Ready)
|
|
45
|
+
|
|
46
|
+
**What it does:** Real-time agent-to-agent messaging via Unix sockets.
|
|
47
|
+
|
|
48
|
+
**Installation:**
|
|
49
|
+
```bash
|
|
50
|
+
npm install -g agent-relay
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Usage:**
|
|
54
|
+
```bash
|
|
55
|
+
# Start daemon
|
|
56
|
+
agent-relay up
|
|
57
|
+
|
|
58
|
+
# Wrap agent
|
|
59
|
+
agent-relay -n Alice claude
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**What it provides for integration:**
|
|
63
|
+
```typescript
|
|
64
|
+
import { StoredMessage, MessageQuery, StorageAdapter } from 'agent-relay';
|
|
65
|
+
|
|
66
|
+
// Query messages for a time range
|
|
67
|
+
const messages = await storage.getMessages({
|
|
68
|
+
sinceTs: startTime,
|
|
69
|
+
order: 'asc'
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### claude-mem (✅ Exists, needs integration)
|
|
74
|
+
|
|
75
|
+
**What it does:** Captures tool observations with semantic concepts.
|
|
76
|
+
|
|
77
|
+
**Installation:**
|
|
78
|
+
```bash
|
|
79
|
+
# Clone and setup
|
|
80
|
+
git clone https://github.com/thedotmack/claude-mem
|
|
81
|
+
cd claude-mem
|
|
82
|
+
bun install
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**How it works:**
|
|
86
|
+
- Hooks into Claude Code lifecycle (SessionStart, PostToolUse, SessionEnd)
|
|
87
|
+
- Stores observations in SQLite + Chroma (vector search)
|
|
88
|
+
- Provides `mem-search` skill for natural language queries
|
|
89
|
+
|
|
90
|
+
**What it provides for integration:**
|
|
91
|
+
- Tool call history with semantic tags
|
|
92
|
+
- Session continuity
|
|
93
|
+
- Concept-based search
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Phase 1: Install claude-mem
|
|
98
|
+
|
|
99
|
+
### Step 1.1: Clone and Configure
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# From your project root
|
|
103
|
+
git clone https://github.com/thedotmack/claude-mem .claude-mem
|
|
104
|
+
|
|
105
|
+
# Install dependencies
|
|
106
|
+
cd .claude-mem
|
|
107
|
+
bun install
|
|
108
|
+
|
|
109
|
+
# Start the worker service
|
|
110
|
+
bun run start
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Step 1.2: Configure Claude Code Hooks
|
|
114
|
+
|
|
115
|
+
Add to your `~/.claude/settings.json` (or project `.claude/settings.json`):
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"hooks": {
|
|
120
|
+
"SessionStart": [
|
|
121
|
+
{
|
|
122
|
+
"command": "node .claude-mem/hooks/session-start.js",
|
|
123
|
+
"timeout": 5000
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
"PostToolUse": [
|
|
127
|
+
{
|
|
128
|
+
"command": "node .claude-mem/hooks/post-tool-use.js",
|
|
129
|
+
"timeout": 3000
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
"SessionEnd": [
|
|
133
|
+
{
|
|
134
|
+
"command": "node .claude-mem/hooks/session-end.js",
|
|
135
|
+
"timeout": 5000
|
|
136
|
+
}
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Step 1.3: Verify It's Working
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Start a Claude Code session
|
|
146
|
+
claude
|
|
147
|
+
|
|
148
|
+
# Do some work...
|
|
149
|
+
|
|
150
|
+
# Check the web viewer
|
|
151
|
+
open http://localhost:37777
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
You should see observations being captured.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Phase 2: Create agent-trajectories
|
|
159
|
+
|
|
160
|
+
### Step 2.1: Initialize the Project
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Create new repo
|
|
164
|
+
mkdir agent-trajectories
|
|
165
|
+
cd agent-trajectories
|
|
166
|
+
npm init -y
|
|
167
|
+
|
|
168
|
+
# Install dependencies
|
|
169
|
+
npm install better-sqlite3 commander uuid
|
|
170
|
+
npm install -D typescript @types/node @types/better-sqlite3 vitest
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Step 2.2: Project Structure
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
agent-trajectories/
|
|
177
|
+
├── src/
|
|
178
|
+
│ ├── core/
|
|
179
|
+
│ │ ├── types.ts # Trajectory, Chapter, Event types
|
|
180
|
+
│ │ ├── schema.ts # JSON schema for .trajectory format
|
|
181
|
+
│ │ └── trajectory.ts # Trajectory class
|
|
182
|
+
│ │
|
|
183
|
+
│ ├── storage/
|
|
184
|
+
│ │ ├── file-storage.ts # .trajectories/ directory
|
|
185
|
+
│ │ └── sqlite-storage.ts # SQLite for indexing
|
|
186
|
+
│ │
|
|
187
|
+
│ ├── adapters/
|
|
188
|
+
│ │ ├── adapter.ts # TaskSourceAdapter interface
|
|
189
|
+
│ │ ├── beads.ts # Beads integration
|
|
190
|
+
│ │ ├── github.ts # GitHub Issues integration
|
|
191
|
+
│ │ ├── linear.ts # Linear integration
|
|
192
|
+
│ │ └── plain.ts # Standalone trajectories
|
|
193
|
+
│ │
|
|
194
|
+
│ ├── integrations/
|
|
195
|
+
│ │ ├── relay.ts # Import from agent-relay
|
|
196
|
+
│ │ └── claude-mem.ts # Import from claude-mem
|
|
197
|
+
│ │
|
|
198
|
+
│ ├── workspace/
|
|
199
|
+
│ │ ├── decisions.ts # Decision log
|
|
200
|
+
│ │ ├── patterns.ts # Pattern library
|
|
201
|
+
│ │ └── extract.ts # Auto-extraction
|
|
202
|
+
│ │
|
|
203
|
+
│ ├── export/
|
|
204
|
+
│ │ ├── markdown.ts # Notion-style export
|
|
205
|
+
│ │ └── timeline.ts # Linear-style export
|
|
206
|
+
│ │
|
|
207
|
+
│ ├── cli/
|
|
208
|
+
│ │ └── index.ts # CLI commands
|
|
209
|
+
│ │
|
|
210
|
+
│ └── index.ts # Main exports
|
|
211
|
+
│
|
|
212
|
+
├── package.json
|
|
213
|
+
└── tsconfig.json
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Step 2.3: Core Types
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// src/core/types.ts
|
|
220
|
+
|
|
221
|
+
export interface Trajectory {
|
|
222
|
+
id: string;
|
|
223
|
+
version: 1;
|
|
224
|
+
|
|
225
|
+
task: TaskReference;
|
|
226
|
+
|
|
227
|
+
startedAt: string;
|
|
228
|
+
completedAt?: string;
|
|
229
|
+
status: 'active' | 'completed' | 'abandoned';
|
|
230
|
+
|
|
231
|
+
agents: AgentParticipation[];
|
|
232
|
+
chapters: Chapter[];
|
|
233
|
+
retrospective?: Retrospective;
|
|
234
|
+
|
|
235
|
+
commits: string[];
|
|
236
|
+
filesChanged: string[];
|
|
237
|
+
|
|
238
|
+
projectId: string;
|
|
239
|
+
tags: string[];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface TaskReference {
|
|
243
|
+
title: string;
|
|
244
|
+
description?: string;
|
|
245
|
+
source?: {
|
|
246
|
+
system: string; // 'beads' | 'linear' | 'github' | 'plain'
|
|
247
|
+
id: string;
|
|
248
|
+
url?: string;
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export interface Chapter {
|
|
253
|
+
id: string;
|
|
254
|
+
title: string;
|
|
255
|
+
agentName: string;
|
|
256
|
+
startedAt: string;
|
|
257
|
+
endedAt?: string;
|
|
258
|
+
events: TrajectoryEvent[];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export interface TrajectoryEvent {
|
|
262
|
+
ts: number;
|
|
263
|
+
type: EventType;
|
|
264
|
+
content: string;
|
|
265
|
+
raw?: unknown;
|
|
266
|
+
significance?: 'low' | 'medium' | 'high' | 'critical';
|
|
267
|
+
tags?: string[];
|
|
268
|
+
source?: 'relay' | 'claude-mem' | 'manual';
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export type EventType =
|
|
272
|
+
| 'prompt'
|
|
273
|
+
| 'thinking'
|
|
274
|
+
| 'tool_call'
|
|
275
|
+
| 'tool_result'
|
|
276
|
+
| 'message_sent'
|
|
277
|
+
| 'message_received'
|
|
278
|
+
| 'decision'
|
|
279
|
+
| 'observation' // From claude-mem
|
|
280
|
+
| 'error';
|
|
281
|
+
|
|
282
|
+
export interface Retrospective {
|
|
283
|
+
summary: string;
|
|
284
|
+
approach: string;
|
|
285
|
+
decisions: Decision[];
|
|
286
|
+
challenges: string[];
|
|
287
|
+
learnings: string[];
|
|
288
|
+
suggestions: string[];
|
|
289
|
+
confidence: number;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export interface Decision {
|
|
293
|
+
question: string;
|
|
294
|
+
chosen: string;
|
|
295
|
+
alternatives: string[];
|
|
296
|
+
reasoning: string;
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Step 2.4: CLI Commands
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
// src/cli/index.ts
|
|
304
|
+
|
|
305
|
+
import { Command } from 'commander';
|
|
306
|
+
|
|
307
|
+
const program = new Command();
|
|
308
|
+
|
|
309
|
+
program
|
|
310
|
+
.name('trajectory')
|
|
311
|
+
.description('Agent trajectory management')
|
|
312
|
+
.version('1.0.0');
|
|
313
|
+
|
|
314
|
+
// Create new trajectory
|
|
315
|
+
program
|
|
316
|
+
.command('new <title>')
|
|
317
|
+
.description('Start a new trajectory')
|
|
318
|
+
.option('--beads <id>', 'Link to Beads task')
|
|
319
|
+
.option('--linear <id>', 'Link to Linear issue')
|
|
320
|
+
.option('--github <id>', 'Link to GitHub issue')
|
|
321
|
+
.action(async (title, options) => {
|
|
322
|
+
// Implementation
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// Show current trajectory status
|
|
326
|
+
program
|
|
327
|
+
.command('status')
|
|
328
|
+
.description('Show active trajectory')
|
|
329
|
+
.action(async () => {
|
|
330
|
+
// Implementation
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// Add a chapter
|
|
334
|
+
program
|
|
335
|
+
.command('chapter <title>')
|
|
336
|
+
.description('Start a new chapter')
|
|
337
|
+
.action(async (title) => {
|
|
338
|
+
// Implementation
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// Record a decision
|
|
342
|
+
program
|
|
343
|
+
.command('decision <title>')
|
|
344
|
+
.description('Record a decision')
|
|
345
|
+
.option('--chosen <choice>', 'What was chosen')
|
|
346
|
+
.option('--alternatives <alts...>', 'Alternatives considered')
|
|
347
|
+
.option('--reasoning <reason>', 'Why this choice')
|
|
348
|
+
.action(async (title, options) => {
|
|
349
|
+
// Implementation
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// Complete trajectory
|
|
353
|
+
program
|
|
354
|
+
.command('complete')
|
|
355
|
+
.description('Complete the active trajectory')
|
|
356
|
+
.option('--retrospective', 'Prompt for retrospective')
|
|
357
|
+
.action(async (options) => {
|
|
358
|
+
// Implementation
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// Import from sources
|
|
362
|
+
program
|
|
363
|
+
.command('import')
|
|
364
|
+
.description('Import events from external sources')
|
|
365
|
+
.option('--relay', 'Import from agent-relay')
|
|
366
|
+
.option('--claude-mem', 'Import from claude-mem')
|
|
367
|
+
.option('--since <timestamp>', 'Import since timestamp')
|
|
368
|
+
.action(async (options) => {
|
|
369
|
+
// Implementation
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// Export trajectory
|
|
373
|
+
program
|
|
374
|
+
.command('export <id>')
|
|
375
|
+
.description('Export trajectory')
|
|
376
|
+
.option('--format <format>', 'Export format (markdown, json, timeline)')
|
|
377
|
+
.action(async (id, options) => {
|
|
378
|
+
// Implementation
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// Search trajectories
|
|
382
|
+
program
|
|
383
|
+
.command('search <query>')
|
|
384
|
+
.description('Search trajectories')
|
|
385
|
+
.action(async (query) => {
|
|
386
|
+
// Implementation
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
program.parse();
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## Phase 3: agent-relay Integration
|
|
395
|
+
|
|
396
|
+
### Step 3.1: Import Relay Messages
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
// src/integrations/relay.ts
|
|
400
|
+
|
|
401
|
+
import { StoredMessage, MessageQuery } from 'agent-relay';
|
|
402
|
+
import { TrajectoryEvent } from '../core/types.js';
|
|
403
|
+
|
|
404
|
+
interface RelayImportOptions {
|
|
405
|
+
sinceTs: number;
|
|
406
|
+
untilTs?: number;
|
|
407
|
+
agentName?: string;
|
|
408
|
+
topic?: string;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export async function importFromRelay(
|
|
412
|
+
storage: StorageAdapter,
|
|
413
|
+
options: RelayImportOptions
|
|
414
|
+
): Promise<TrajectoryEvent[]> {
|
|
415
|
+
const query: MessageQuery = {
|
|
416
|
+
sinceTs: options.sinceTs,
|
|
417
|
+
order: 'asc',
|
|
418
|
+
limit: 1000
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
if (options.agentName) {
|
|
422
|
+
query.from = options.agentName;
|
|
423
|
+
}
|
|
424
|
+
if (options.topic) {
|
|
425
|
+
query.topic = options.topic;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const messages = await storage.getMessages(query);
|
|
429
|
+
|
|
430
|
+
return messages
|
|
431
|
+
.filter(m => !options.untilTs || m.ts <= options.untilTs)
|
|
432
|
+
.map(messageToEvent);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function messageToEvent(msg: StoredMessage): TrajectoryEvent {
|
|
436
|
+
return {
|
|
437
|
+
ts: msg.ts,
|
|
438
|
+
type: msg.kind === 'thinking' ? 'thinking' :
|
|
439
|
+
msg.to === '*' ? 'message_sent' :
|
|
440
|
+
'message_received',
|
|
441
|
+
content: msg.body,
|
|
442
|
+
raw: {
|
|
443
|
+
from: msg.from,
|
|
444
|
+
to: msg.to,
|
|
445
|
+
kind: msg.kind,
|
|
446
|
+
data: msg.data
|
|
447
|
+
},
|
|
448
|
+
significance: 'medium',
|
|
449
|
+
source: 'relay'
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Step 3.2: Real-time Relay Subscription
|
|
455
|
+
|
|
456
|
+
```typescript
|
|
457
|
+
// src/integrations/relay-listener.ts
|
|
458
|
+
|
|
459
|
+
import { RelayClient } from 'agent-relay';
|
|
460
|
+
import { TrajectoryCapture } from '../capture/trajectory-capture.js';
|
|
461
|
+
|
|
462
|
+
export class RelayListener {
|
|
463
|
+
private client: RelayClient;
|
|
464
|
+
private capture: TrajectoryCapture;
|
|
465
|
+
|
|
466
|
+
constructor(capture: TrajectoryCapture) {
|
|
467
|
+
this.capture = capture;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async connect(agentName: string): Promise<void> {
|
|
471
|
+
this.client = new RelayClient({ agentName });
|
|
472
|
+
|
|
473
|
+
this.client.on('message', (envelope) => {
|
|
474
|
+
this.capture.recordEvent({
|
|
475
|
+
type: envelope.from === agentName ? 'message_sent' : 'message_received',
|
|
476
|
+
content: envelope.payload.body,
|
|
477
|
+
raw: envelope,
|
|
478
|
+
source: 'relay'
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
await this.client.connect();
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
async disconnect(): Promise<void> {
|
|
486
|
+
await this.client.disconnect();
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
## Phase 4: claude-mem Integration
|
|
494
|
+
|
|
495
|
+
### Step 4.1: Query claude-mem Observations
|
|
496
|
+
|
|
497
|
+
```typescript
|
|
498
|
+
// src/integrations/claude-mem.ts
|
|
499
|
+
|
|
500
|
+
import { TrajectoryEvent } from '../core/types.js';
|
|
501
|
+
|
|
502
|
+
interface ClaudeMemObservation {
|
|
503
|
+
id: string;
|
|
504
|
+
timestamp: string;
|
|
505
|
+
type: 'decision' | 'bugfix' | 'feature' | 'refactor' | 'discovery' | 'change';
|
|
506
|
+
content: string;
|
|
507
|
+
concepts: string[];
|
|
508
|
+
files?: string[];
|
|
509
|
+
tokens?: number;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
interface ClaudeMemImportOptions {
|
|
513
|
+
sinceTs: number;
|
|
514
|
+
untilTs?: number;
|
|
515
|
+
types?: string[];
|
|
516
|
+
concepts?: string[];
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const CLAUDE_MEM_API = 'http://localhost:37777';
|
|
520
|
+
|
|
521
|
+
export async function importFromClaudeMem(
|
|
522
|
+
options: ClaudeMemImportOptions
|
|
523
|
+
): Promise<TrajectoryEvent[]> {
|
|
524
|
+
const params = new URLSearchParams({
|
|
525
|
+
since: new Date(options.sinceTs).toISOString(),
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
if (options.untilTs) {
|
|
529
|
+
params.set('until', new Date(options.untilTs).toISOString());
|
|
530
|
+
}
|
|
531
|
+
if (options.types?.length) {
|
|
532
|
+
params.set('types', options.types.join(','));
|
|
533
|
+
}
|
|
534
|
+
if (options.concepts?.length) {
|
|
535
|
+
params.set('concepts', options.concepts.join(','));
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const response = await fetch(`${CLAUDE_MEM_API}/api/observations?${params}`);
|
|
539
|
+
const observations: ClaudeMemObservation[] = await response.json();
|
|
540
|
+
|
|
541
|
+
return observations.map(observationToEvent);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function observationToEvent(obs: ClaudeMemObservation): TrajectoryEvent {
|
|
545
|
+
return {
|
|
546
|
+
ts: new Date(obs.timestamp).getTime(),
|
|
547
|
+
type: 'observation',
|
|
548
|
+
content: obs.content,
|
|
549
|
+
raw: obs,
|
|
550
|
+
significance: mapTypeToSignificance(obs.type),
|
|
551
|
+
tags: obs.concepts,
|
|
552
|
+
source: 'claude-mem'
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function mapTypeToSignificance(type: string): 'low' | 'medium' | 'high' | 'critical' {
|
|
557
|
+
switch (type) {
|
|
558
|
+
case 'decision': return 'high';
|
|
559
|
+
case 'bugfix': return 'high';
|
|
560
|
+
case 'feature': return 'medium';
|
|
561
|
+
case 'discovery': return 'medium';
|
|
562
|
+
case 'refactor': return 'low';
|
|
563
|
+
case 'change': return 'low';
|
|
564
|
+
default: return 'medium';
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Search claude-mem for relevant observations
|
|
569
|
+
export async function searchClaudeMem(query: string): Promise<ClaudeMemObservation[]> {
|
|
570
|
+
const response = await fetch(`${CLAUDE_MEM_API}/api/search`, {
|
|
571
|
+
method: 'POST',
|
|
572
|
+
headers: { 'Content-Type': 'application/json' },
|
|
573
|
+
body: JSON.stringify({ query })
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
return response.json();
|
|
577
|
+
}
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
### Step 4.2: Enrich Trajectories with Observations
|
|
581
|
+
|
|
582
|
+
```typescript
|
|
583
|
+
// src/integrations/enrichment.ts
|
|
584
|
+
|
|
585
|
+
import { Trajectory, Chapter } from '../core/types.js';
|
|
586
|
+
import { importFromClaudeMem } from './claude-mem.js';
|
|
587
|
+
import { importFromRelay } from './relay.js';
|
|
588
|
+
|
|
589
|
+
export async function enrichTrajectory(
|
|
590
|
+
trajectory: Trajectory,
|
|
591
|
+
relayStorage?: StorageAdapter
|
|
592
|
+
): Promise<Trajectory> {
|
|
593
|
+
const startTs = new Date(trajectory.startedAt).getTime();
|
|
594
|
+
const endTs = trajectory.completedAt
|
|
595
|
+
? new Date(trajectory.completedAt).getTime()
|
|
596
|
+
: Date.now();
|
|
597
|
+
|
|
598
|
+
// Import from claude-mem
|
|
599
|
+
const claudeMemEvents = await importFromClaudeMem({
|
|
600
|
+
sinceTs: startTs,
|
|
601
|
+
untilTs: endTs,
|
|
602
|
+
types: ['decision', 'discovery', 'bugfix']
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
// Import from agent-relay (if available)
|
|
606
|
+
let relayEvents = [];
|
|
607
|
+
if (relayStorage) {
|
|
608
|
+
relayEvents = await importFromRelay(relayStorage, {
|
|
609
|
+
sinceTs: startTs,
|
|
610
|
+
untilTs: endTs
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Merge events into chapters by timestamp
|
|
615
|
+
const allEvents = [...claudeMemEvents, ...relayEvents]
|
|
616
|
+
.sort((a, b) => a.ts - b.ts);
|
|
617
|
+
|
|
618
|
+
// Distribute events to appropriate chapters
|
|
619
|
+
for (const event of allEvents) {
|
|
620
|
+
const chapter = findChapterForTimestamp(trajectory.chapters, event.ts);
|
|
621
|
+
if (chapter) {
|
|
622
|
+
chapter.events.push(event);
|
|
623
|
+
chapter.events.sort((a, b) => a.ts - b.ts);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return trajectory;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function findChapterForTimestamp(chapters: Chapter[], ts: number): Chapter | null {
|
|
631
|
+
for (const chapter of chapters) {
|
|
632
|
+
const start = new Date(chapter.startedAt).getTime();
|
|
633
|
+
const end = chapter.endedAt
|
|
634
|
+
? new Date(chapter.endedAt).getTime()
|
|
635
|
+
: Date.now();
|
|
636
|
+
|
|
637
|
+
if (ts >= start && ts <= end) {
|
|
638
|
+
return chapter;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Return last chapter if no match
|
|
643
|
+
return chapters[chapters.length - 1] || null;
|
|
644
|
+
}
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
---
|
|
648
|
+
|
|
649
|
+
## Phase 5: Hook Integration
|
|
650
|
+
|
|
651
|
+
### Step 5.1: Trajectory Hooks for Claude Code
|
|
652
|
+
|
|
653
|
+
```typescript
|
|
654
|
+
// src/hooks/session-start.ts
|
|
655
|
+
|
|
656
|
+
import { TrajectoryStore } from '../storage/trajectory-store.js';
|
|
657
|
+
|
|
658
|
+
async function onSessionStart(): Promise<{ context?: string }> {
|
|
659
|
+
const store = new TrajectoryStore();
|
|
660
|
+
const active = await store.getActive();
|
|
661
|
+
|
|
662
|
+
if (!active) {
|
|
663
|
+
return {};
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const context = `
|
|
667
|
+
## Active Trajectory
|
|
668
|
+
|
|
669
|
+
**Task:** ${active.task.title}
|
|
670
|
+
**Status:** ${active.status}
|
|
671
|
+
**Chapter:** ${active.chapters[active.chapters.length - 1]?.title || 'Starting'}
|
|
672
|
+
|
|
673
|
+
### Key Decisions So Far
|
|
674
|
+
${active.chapters
|
|
675
|
+
.flatMap(c => c.events.filter(e => e.type === 'decision'))
|
|
676
|
+
.map(d => `- ${d.content}`)
|
|
677
|
+
.join('\n') || 'None yet'}
|
|
678
|
+
|
|
679
|
+
### Recent Activity
|
|
680
|
+
${active.chapters[active.chapters.length - 1]?.events
|
|
681
|
+
.slice(-5)
|
|
682
|
+
.map(e => `- [${e.type}] ${e.content.slice(0, 100)}`)
|
|
683
|
+
.join('\n') || 'None'}
|
|
684
|
+
|
|
685
|
+
---
|
|
686
|
+
To record a decision: [[TRAJECTORY:decision]]{"title": "...", "chosen": "...", "alternatives": [...], "reasoning": "..."}[[/TRAJECTORY]]
|
|
687
|
+
To start a new chapter: [[TRAJECTORY:chapter]]{"title": "..."}[[/TRAJECTORY]]
|
|
688
|
+
`.trim();
|
|
689
|
+
|
|
690
|
+
return { context };
|
|
691
|
+
}
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
### Step 5.2: Combined Hook Configuration
|
|
695
|
+
|
|
696
|
+
```json
|
|
697
|
+
// .claude/settings.json
|
|
698
|
+
{
|
|
699
|
+
"hooks": {
|
|
700
|
+
"SessionStart": [
|
|
701
|
+
{
|
|
702
|
+
"command": "node .claude-mem/hooks/session-start.js",
|
|
703
|
+
"timeout": 5000
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
"command": "npx trajectory hook:session-start",
|
|
707
|
+
"timeout": 3000
|
|
708
|
+
}
|
|
709
|
+
],
|
|
710
|
+
"PostToolUse": [
|
|
711
|
+
{
|
|
712
|
+
"command": "node .claude-mem/hooks/post-tool-use.js",
|
|
713
|
+
"timeout": 3000
|
|
714
|
+
}
|
|
715
|
+
],
|
|
716
|
+
"Stop": [
|
|
717
|
+
{
|
|
718
|
+
"command": "npx trajectory hook:stop",
|
|
719
|
+
"timeout": 5000
|
|
720
|
+
}
|
|
721
|
+
],
|
|
722
|
+
"SessionEnd": [
|
|
723
|
+
{
|
|
724
|
+
"command": "node .claude-mem/hooks/session-end.js",
|
|
725
|
+
"timeout": 5000
|
|
726
|
+
},
|
|
727
|
+
{
|
|
728
|
+
"command": "npx trajectory hook:session-end",
|
|
729
|
+
"timeout": 5000
|
|
730
|
+
}
|
|
731
|
+
]
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
### Step 5.3: Stop Hook - Prompt for Retrospective
|
|
737
|
+
|
|
738
|
+
```typescript
|
|
739
|
+
// src/hooks/stop.ts
|
|
740
|
+
|
|
741
|
+
import { TrajectoryStore } from '../storage/trajectory-store.js';
|
|
742
|
+
|
|
743
|
+
async function onStop(): Promise<{ decision: 'allow' | 'block'; reason?: string }> {
|
|
744
|
+
const store = new TrajectoryStore();
|
|
745
|
+
const active = await store.getActive();
|
|
746
|
+
|
|
747
|
+
if (!active) {
|
|
748
|
+
return { decision: 'allow' };
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// Check if trajectory has a retrospective
|
|
752
|
+
if (!active.retrospective) {
|
|
753
|
+
return {
|
|
754
|
+
decision: 'block',
|
|
755
|
+
reason: `
|
|
756
|
+
Active trajectory "${active.task.title}" needs a retrospective before completing.
|
|
757
|
+
|
|
758
|
+
Please output a retrospective:
|
|
759
|
+
|
|
760
|
+
[[TRAJECTORY:retrospective]]
|
|
761
|
+
{
|
|
762
|
+
"summary": "What was accomplished?",
|
|
763
|
+
"approach": "How did you approach it?",
|
|
764
|
+
"decisions": [
|
|
765
|
+
{"question": "Key choice made", "chosen": "What you picked", "alternatives": ["Other options"], "reasoning": "Why"}
|
|
766
|
+
],
|
|
767
|
+
"challenges": ["What was difficult?"],
|
|
768
|
+
"learnings": ["What would you do differently?"],
|
|
769
|
+
"suggestions": ["Improvements for codebase/process?"],
|
|
770
|
+
"confidence": 0.85
|
|
771
|
+
}
|
|
772
|
+
[[/TRAJECTORY]]
|
|
773
|
+
|
|
774
|
+
Or run: trajectory complete --skip-retrospective
|
|
775
|
+
`.trim()
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
return { decision: 'allow' };
|
|
780
|
+
}
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
---
|
|
784
|
+
|
|
785
|
+
## Phase 6: Putting It All Together
|
|
786
|
+
|
|
787
|
+
### Complete Setup Checklist
|
|
788
|
+
|
|
789
|
+
```bash
|
|
790
|
+
# 1. Install agent-relay
|
|
791
|
+
npm install -g agent-relay
|
|
792
|
+
|
|
793
|
+
# 2. Clone and setup claude-mem
|
|
794
|
+
git clone https://github.com/thedotmack/claude-mem .claude-mem
|
|
795
|
+
cd .claude-mem && bun install && bun run start &
|
|
796
|
+
cd ..
|
|
797
|
+
|
|
798
|
+
# 3. Install agent-trajectories (once published)
|
|
799
|
+
npm install -g agent-trajectories
|
|
800
|
+
|
|
801
|
+
# 4. Configure hooks
|
|
802
|
+
cat > .claude/settings.json << 'EOF'
|
|
803
|
+
{
|
|
804
|
+
"hooks": {
|
|
805
|
+
"SessionStart": [
|
|
806
|
+
{"command": "node .claude-mem/hooks/session-start.js", "timeout": 5000},
|
|
807
|
+
{"command": "npx trajectory hook:session-start", "timeout": 3000}
|
|
808
|
+
],
|
|
809
|
+
"PostToolUse": [
|
|
810
|
+
{"command": "node .claude-mem/hooks/post-tool-use.js", "timeout": 3000}
|
|
811
|
+
],
|
|
812
|
+
"Stop": [
|
|
813
|
+
{"command": "npx trajectory hook:stop", "timeout": 5000}
|
|
814
|
+
],
|
|
815
|
+
"SessionEnd": [
|
|
816
|
+
{"command": "node .claude-mem/hooks/session-end.js", "timeout": 5000},
|
|
817
|
+
{"command": "npx trajectory hook:session-end", "timeout": 5000}
|
|
818
|
+
]
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
EOF
|
|
822
|
+
|
|
823
|
+
# 5. Start the relay daemon
|
|
824
|
+
agent-relay up
|
|
825
|
+
|
|
826
|
+
# 6. Start working!
|
|
827
|
+
agent-relay -n Alice claude
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
### Typical Workflow
|
|
831
|
+
|
|
832
|
+
```bash
|
|
833
|
+
# Start a task
|
|
834
|
+
trajectory new "Implement user authentication" --linear ENG-456
|
|
835
|
+
|
|
836
|
+
# Work in Claude Code...
|
|
837
|
+
# - claude-mem captures tool observations automatically
|
|
838
|
+
# - agent-relay captures messages automatically
|
|
839
|
+
# - You can add decisions manually via [[TRAJECTORY:decision]]
|
|
840
|
+
|
|
841
|
+
# Check status
|
|
842
|
+
trajectory status
|
|
843
|
+
|
|
844
|
+
# Start a new chapter when switching focus
|
|
845
|
+
trajectory chapter "Testing"
|
|
846
|
+
|
|
847
|
+
# When done, complete with retrospective
|
|
848
|
+
trajectory complete
|
|
849
|
+
|
|
850
|
+
# View the result
|
|
851
|
+
trajectory export ENG-456 --format markdown
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
### Data Flow Diagram
|
|
855
|
+
|
|
856
|
+
```
|
|
857
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
858
|
+
│ AGENT SESSION │
|
|
859
|
+
├──────────────────────────────────────────────────────────────────┤
|
|
860
|
+
│ │
|
|
861
|
+
│ Agent works... │
|
|
862
|
+
│ │ │
|
|
863
|
+
│ ├──────────────────┬───────────────────┐ │
|
|
864
|
+
│ ▼ ▼ ▼ │
|
|
865
|
+
│ ┌─────────┐ ┌───────────┐ ┌────────────┐ │
|
|
866
|
+
│ │ Tool │ │ ->relay: │ │[[TRAJECTORY│ │
|
|
867
|
+
│ │ Calls │ │ messages │ │ :decision]]│ │
|
|
868
|
+
│ └────┬────┘ └─────┬─────┘ └──────┬─────┘ │
|
|
869
|
+
│ │ │ │ │
|
|
870
|
+
│ ▼ ▼ ▼ │
|
|
871
|
+
│ ┌─────────┐ ┌───────────┐ ┌────────────┐ │
|
|
872
|
+
│ │claude- │ │agent-relay│ │agent- │ │
|
|
873
|
+
│ │mem │ │SQLite │ │trajectories│ │
|
|
874
|
+
│ │SQLite + │ │ │ │.trajectory/│ │
|
|
875
|
+
│ │Chroma │ │ │ │ │ │
|
|
876
|
+
│ └────┬────┘ └─────┬─────┘ └──────┬─────┘ │
|
|
877
|
+
│ │ │ │ │
|
|
878
|
+
│ └────────────┬────┴───────────────────┘ │
|
|
879
|
+
│ ▼ │
|
|
880
|
+
│ ┌─────────────────┐ │
|
|
881
|
+
│ │ trajectory │ │
|
|
882
|
+
│ │ complete │ │
|
|
883
|
+
│ │ │ │
|
|
884
|
+
│ │ Enriches with: │ │
|
|
885
|
+
│ │ - relay msgs │ │
|
|
886
|
+
│ │ - claude-mem │ │
|
|
887
|
+
│ │ observations │ │
|
|
888
|
+
│ └────────┬────────┘ │
|
|
889
|
+
│ ▼ │
|
|
890
|
+
│ ┌─────────────────┐ │
|
|
891
|
+
│ │ .trajectory.json│ │
|
|
892
|
+
│ │ .trajectory.md │ │
|
|
893
|
+
│ │ │ │
|
|
894
|
+
│ │ Complete story │ │
|
|
895
|
+
│ │ of the work │ │
|
|
896
|
+
│ └─────────────────┘ │
|
|
897
|
+
│ │
|
|
898
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
---
|
|
902
|
+
|
|
903
|
+
## Summary: What Each Piece Does
|
|
904
|
+
|
|
905
|
+
| Component | Captures | Storage | Query |
|
|
906
|
+
|-----------|----------|---------|-------|
|
|
907
|
+
| **agent-relay** | Agent messages | SQLite | By time, sender, topic |
|
|
908
|
+
| **claude-mem** | Tool observations | SQLite + Chroma | Semantic search |
|
|
909
|
+
| **agent-trajectories** | Task narratives | Files + SQLite | By task, decision, pattern |
|
|
910
|
+
|
|
911
|
+
| Component | Hooks | Real-time | Export |
|
|
912
|
+
|-----------|-------|-----------|--------|
|
|
913
|
+
| **agent-relay** | Stop (inbox check) | Yes (sockets) | JSON |
|
|
914
|
+
| **claude-mem** | All lifecycle | No | JSON |
|
|
915
|
+
| **agent-trajectories** | Start, Stop, End | Optional | Markdown, JSON, Timeline |
|
|
916
|
+
|
|
917
|
+
---
|
|
918
|
+
|
|
919
|
+
## Next Steps
|
|
920
|
+
|
|
921
|
+
1. **Phase 1:** Get claude-mem working in your project
|
|
922
|
+
2. **Phase 2:** Create agent-trajectories repo with core types
|
|
923
|
+
3. **Phase 3:** Add relay integration
|
|
924
|
+
4. **Phase 4:** Add claude-mem integration
|
|
925
|
+
5. **Phase 5:** Build CLI and hooks
|
|
926
|
+
6. **Phase 6:** Test end-to-end flow
|