agent-relay 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/LICENSE +22 -0
- package/PROTOCOL.md +319 -0
- package/README.md +791 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +1591 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/daemon/connection.d.ts +60 -0
- package/dist/daemon/connection.d.ts.map +1 -0
- package/dist/daemon/connection.js +245 -0
- package/dist/daemon/connection.js.map +1 -0
- package/dist/daemon/index.d.ts +4 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +4 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/router.d.ts +72 -0
- package/dist/daemon/router.d.ts.map +1 -0
- package/dist/daemon/router.js +183 -0
- package/dist/daemon/router.js.map +1 -0
- package/dist/daemon/server.d.ts +52 -0
- package/dist/daemon/server.d.ts.map +1 -0
- package/dist/daemon/server.js +186 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/dashboard/public/index.html +690 -0
- package/dist/dashboard/server.d.ts +2 -0
- package/dist/dashboard/server.d.ts.map +1 -0
- package/dist/dashboard/server.js +220 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/games/index.d.ts +2 -0
- package/dist/games/index.d.ts.map +1 -0
- package/dist/games/index.js +2 -0
- package/dist/games/index.js.map +1 -0
- package/dist/games/tictactoe.d.ts +24 -0
- package/dist/games/tictactoe.d.ts.map +1 -0
- package/dist/games/tictactoe.js +160 -0
- package/dist/games/tictactoe.js.map +1 -0
- package/dist/hooks/inbox-check/hook.d.ts +28 -0
- package/dist/hooks/inbox-check/hook.d.ts.map +1 -0
- package/dist/hooks/inbox-check/hook.js +97 -0
- package/dist/hooks/inbox-check/hook.js.map +1 -0
- package/dist/hooks/inbox-check/index.d.ts +8 -0
- package/dist/hooks/inbox-check/index.d.ts.map +1 -0
- package/dist/hooks/inbox-check/index.js +8 -0
- package/dist/hooks/inbox-check/index.js.map +1 -0
- package/dist/hooks/inbox-check/types.d.ts +31 -0
- package/dist/hooks/inbox-check/types.d.ts.map +1 -0
- package/dist/hooks/inbox-check/types.js +5 -0
- package/dist/hooks/inbox-check/types.js.map +1 -0
- package/dist/hooks/inbox-check/utils.d.ts +44 -0
- package/dist/hooks/inbox-check/utils.d.ts.map +1 -0
- package/dist/hooks/inbox-check/utils.js +107 -0
- package/dist/hooks/inbox-check/utils.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/framing.d.ts +32 -0
- package/dist/protocol/framing.d.ts.map +1 -0
- package/dist/protocol/framing.js +71 -0
- package/dist/protocol/framing.js.map +1 -0
- package/dist/protocol/index.d.ts +3 -0
- package/dist/protocol/index.d.ts.map +1 -0
- package/dist/protocol/index.js +3 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/types.d.ts +104 -0
- package/dist/protocol/types.d.ts.map +1 -0
- package/dist/protocol/types.js +6 -0
- package/dist/protocol/types.js.map +1 -0
- package/dist/state/agent-state.d.ts +40 -0
- package/dist/state/agent-state.d.ts.map +1 -0
- package/dist/state/agent-state.js +120 -0
- package/dist/state/agent-state.js.map +1 -0
- package/dist/storage/adapter.d.ts +29 -0
- package/dist/storage/adapter.d.ts.map +1 -0
- package/dist/storage/adapter.js +2 -0
- package/dist/storage/adapter.js.map +1 -0
- package/dist/storage/sqlite-adapter.d.ts +15 -0
- package/dist/storage/sqlite-adapter.d.ts.map +1 -0
- package/dist/storage/sqlite-adapter.js +116 -0
- package/dist/storage/sqlite-adapter.js.map +1 -0
- package/dist/supervisor/inbox.d.ts +38 -0
- package/dist/supervisor/inbox.d.ts.map +1 -0
- package/dist/supervisor/inbox.js +162 -0
- package/dist/supervisor/inbox.js.map +1 -0
- package/dist/supervisor/index.d.ts +10 -0
- package/dist/supervisor/index.d.ts.map +1 -0
- package/dist/supervisor/index.js +10 -0
- package/dist/supervisor/index.js.map +1 -0
- package/dist/supervisor/spawner.d.ts +54 -0
- package/dist/supervisor/spawner.d.ts.map +1 -0
- package/dist/supervisor/spawner.js +282 -0
- package/dist/supervisor/spawner.js.map +1 -0
- package/dist/supervisor/state.d.ts +132 -0
- package/dist/supervisor/state.d.ts.map +1 -0
- package/dist/supervisor/state.js +465 -0
- package/dist/supervisor/state.js.map +1 -0
- package/dist/supervisor/supervisor.d.ts +67 -0
- package/dist/supervisor/supervisor.d.ts.map +1 -0
- package/dist/supervisor/supervisor.js +263 -0
- package/dist/supervisor/supervisor.js.map +1 -0
- package/dist/supervisor/types.d.ts +139 -0
- package/dist/supervisor/types.d.ts.map +1 -0
- package/dist/supervisor/types.js +12 -0
- package/dist/supervisor/types.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/name-generator.d.ts +17 -0
- package/dist/utils/name-generator.d.ts.map +1 -0
- package/dist/utils/name-generator.js +52 -0
- package/dist/utils/name-generator.js.map +1 -0
- package/dist/webhook/spawner.d.ts +79 -0
- package/dist/webhook/spawner.d.ts.map +1 -0
- package/dist/webhook/spawner.js +288 -0
- package/dist/webhook/spawner.js.map +1 -0
- package/dist/wrapper/client.d.ts +72 -0
- package/dist/wrapper/client.d.ts.map +1 -0
- package/dist/wrapper/client.js +306 -0
- package/dist/wrapper/client.js.map +1 -0
- package/dist/wrapper/inbox.d.ts +37 -0
- package/dist/wrapper/inbox.d.ts.map +1 -0
- package/dist/wrapper/inbox.js +73 -0
- package/dist/wrapper/inbox.js.map +1 -0
- package/dist/wrapper/index.d.ts +4 -0
- package/dist/wrapper/index.d.ts.map +1 -0
- package/dist/wrapper/index.js +7 -0
- package/dist/wrapper/index.js.map +1 -0
- package/dist/wrapper/parser.d.ts +94 -0
- package/dist/wrapper/parser.d.ts.map +1 -0
- package/dist/wrapper/parser.js +360 -0
- package/dist/wrapper/parser.js.map +1 -0
- package/dist/wrapper/pty-wrapper.d.ts +125 -0
- package/dist/wrapper/pty-wrapper.d.ts.map +1 -0
- package/dist/wrapper/pty-wrapper.js +494 -0
- package/dist/wrapper/pty-wrapper.js.map +1 -0
- package/dist/wrapper/tmux-wrapper.d.ts +131 -0
- package/dist/wrapper/tmux-wrapper.d.ts.map +1 -0
- package/dist/wrapper/tmux-wrapper.js +427 -0
- package/dist/wrapper/tmux-wrapper.js.map +1 -0
- package/install.sh +69 -0
- package/package.json +82 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
3
|
+
import http from 'http';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import { SqliteStorageAdapter } from '../storage/sqlite-adapter.js';
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
export async function startDashboard(port, dataDir, dbPath) {
|
|
11
|
+
console.log('Starting dashboard...');
|
|
12
|
+
console.log('__dirname:', __dirname);
|
|
13
|
+
const publicDir = path.join(__dirname, 'public');
|
|
14
|
+
console.log('Public dir:', publicDir);
|
|
15
|
+
const storage = dbPath
|
|
16
|
+
? new SqliteStorageAdapter({ dbPath })
|
|
17
|
+
: undefined;
|
|
18
|
+
process.on('uncaughtException', (err) => {
|
|
19
|
+
console.error('Uncaught Exception:', err);
|
|
20
|
+
});
|
|
21
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
22
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
23
|
+
});
|
|
24
|
+
const app = express();
|
|
25
|
+
const server = http.createServer(app);
|
|
26
|
+
const wss = new WebSocketServer({ server, path: '/ws' });
|
|
27
|
+
if (storage) {
|
|
28
|
+
await storage.init();
|
|
29
|
+
}
|
|
30
|
+
// Serve static files from public directory
|
|
31
|
+
app.use(express.static(publicDir));
|
|
32
|
+
app.use(express.json());
|
|
33
|
+
const getTeamData = () => {
|
|
34
|
+
const teamPath = path.join(dataDir, 'team.json');
|
|
35
|
+
if (!fs.existsSync(teamPath))
|
|
36
|
+
return null;
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(fs.readFileSync(teamPath, 'utf-8'));
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
console.error('Failed to read team.json', e);
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const parseInbox = (agentName) => {
|
|
46
|
+
const inboxPath = path.join(dataDir, agentName, 'inbox.md');
|
|
47
|
+
if (!fs.existsSync(inboxPath))
|
|
48
|
+
return [];
|
|
49
|
+
try {
|
|
50
|
+
const content = fs.readFileSync(inboxPath, 'utf-8');
|
|
51
|
+
const messages = [];
|
|
52
|
+
// Split by "## Message from "
|
|
53
|
+
const parts = content.split('## Message from ');
|
|
54
|
+
parts.forEach((part, index) => {
|
|
55
|
+
if (!part.trim())
|
|
56
|
+
return;
|
|
57
|
+
const firstLineEnd = part.indexOf('\n');
|
|
58
|
+
if (firstLineEnd === -1)
|
|
59
|
+
return;
|
|
60
|
+
const header = part.substring(0, firstLineEnd).trim(); // "Sender | Timestamp" or just "Sender"
|
|
61
|
+
const body = part.substring(firstLineEnd).trim();
|
|
62
|
+
// Handle potential " | " in header
|
|
63
|
+
let sender = header;
|
|
64
|
+
let timestamp = new Date().toISOString();
|
|
65
|
+
if (header.includes('|')) {
|
|
66
|
+
const split = header.split('|');
|
|
67
|
+
sender = split[0].trim();
|
|
68
|
+
timestamp = split.slice(1).join('|').trim();
|
|
69
|
+
}
|
|
70
|
+
messages.push({
|
|
71
|
+
from: sender,
|
|
72
|
+
to: agentName,
|
|
73
|
+
content: body,
|
|
74
|
+
timestamp: timestamp,
|
|
75
|
+
id: `${agentName}-${index}-${Date.now()}`
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
return messages;
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
console.error(`Failed to read inbox for ${agentName}`, e);
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const mapStoredMessages = (rows) => rows
|
|
86
|
+
.map((row) => ({
|
|
87
|
+
from: row.from,
|
|
88
|
+
to: row.to,
|
|
89
|
+
content: row.body,
|
|
90
|
+
timestamp: new Date(row.ts).toISOString(),
|
|
91
|
+
id: row.id,
|
|
92
|
+
}));
|
|
93
|
+
const getMessages = async (agents) => {
|
|
94
|
+
if (storage) {
|
|
95
|
+
const rows = await storage.getMessages({ limit: 500, order: 'desc' });
|
|
96
|
+
// Dashboard expects oldest first
|
|
97
|
+
return mapStoredMessages(rows).reverse();
|
|
98
|
+
}
|
|
99
|
+
// Fallback to file-based inbox parsing
|
|
100
|
+
let allMessages = [];
|
|
101
|
+
agents.forEach((a) => {
|
|
102
|
+
const msgs = parseInbox(a.name);
|
|
103
|
+
allMessages = [...allMessages, ...msgs];
|
|
104
|
+
});
|
|
105
|
+
return allMessages;
|
|
106
|
+
};
|
|
107
|
+
const getAllData = async () => {
|
|
108
|
+
const team = getTeamData();
|
|
109
|
+
if (!team)
|
|
110
|
+
return { agents: [], messages: [], activity: [] };
|
|
111
|
+
const agentsMap = new Map();
|
|
112
|
+
const allMessages = await getMessages(team.agents);
|
|
113
|
+
// Initialize agents from config
|
|
114
|
+
team.agents.forEach((a) => {
|
|
115
|
+
agentsMap.set(a.name, {
|
|
116
|
+
name: a.name,
|
|
117
|
+
role: a.role,
|
|
118
|
+
cli: a.cli,
|
|
119
|
+
messageCount: 0,
|
|
120
|
+
status: 'Idle'
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
// Update inbox counts if fallback mode; if storage, count messages addressed to agent
|
|
124
|
+
if (storage) {
|
|
125
|
+
for (const msg of allMessages) {
|
|
126
|
+
const agent = agentsMap.get(msg.to);
|
|
127
|
+
if (agent) {
|
|
128
|
+
agent.messageCount = (agent.messageCount ?? 0) + 1;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// Sort by timestamp
|
|
134
|
+
allMessages.sort((a, b) => {
|
|
135
|
+
return new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime();
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// Derive status from messages sent BY agents
|
|
139
|
+
// We scan all messages; if M is from A, we check if it is a STATUS message
|
|
140
|
+
allMessages.forEach(m => {
|
|
141
|
+
const agent = agentsMap.get(m.from);
|
|
142
|
+
if (agent) {
|
|
143
|
+
agent.lastActive = m.timestamp;
|
|
144
|
+
if (m.content.startsWith('STATUS:')) {
|
|
145
|
+
agent.status = m.content.substring(7).trim(); // remove "STATUS:"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
agents: Array.from(agentsMap.values()),
|
|
151
|
+
messages: allMessages,
|
|
152
|
+
activity: allMessages // For now, activity log is just the message log
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
const broadcastData = async () => {
|
|
156
|
+
const data = await getAllData();
|
|
157
|
+
const payload = JSON.stringify(data);
|
|
158
|
+
wss.clients.forEach(client => {
|
|
159
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
160
|
+
client.send(payload);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
app.get('/api/data', (req, res) => {
|
|
165
|
+
getAllData().then((data) => res.json(data)).catch((err) => {
|
|
166
|
+
console.error('Failed to fetch dashboard data', err);
|
|
167
|
+
res.status(500).json({ error: 'Failed to load data' });
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
// Watch for changes
|
|
171
|
+
if (storage) {
|
|
172
|
+
setInterval(() => {
|
|
173
|
+
broadcastData().catch((err) => console.error('Broadcast failed', err));
|
|
174
|
+
}, 1000);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
let fsWait = null;
|
|
178
|
+
try {
|
|
179
|
+
if (fs.existsSync(dataDir)) {
|
|
180
|
+
console.log(`Watching ${dataDir} for changes...`);
|
|
181
|
+
fs.watch(dataDir, { recursive: true }, (eventType, filename) => {
|
|
182
|
+
if (filename && (filename.endsWith('inbox.md') || filename.endsWith('team.json'))) {
|
|
183
|
+
// Debounce
|
|
184
|
+
if (fsWait)
|
|
185
|
+
return;
|
|
186
|
+
fsWait = setTimeout(() => {
|
|
187
|
+
fsWait = null;
|
|
188
|
+
broadcastData();
|
|
189
|
+
}, 100);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
console.warn(`Data directory ${dataDir} does not exist yet.`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch (e) {
|
|
198
|
+
console.error('Watch failed:', e);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return new Promise((resolve, reject) => {
|
|
202
|
+
try {
|
|
203
|
+
server.listen(port, () => {
|
|
204
|
+
console.log(`Dashboard running at http://localhost:${port}`);
|
|
205
|
+
console.log(`Monitoring: ${dataDir}`);
|
|
206
|
+
// We do NOT resolve here to keep the process alive
|
|
207
|
+
// But we must resolve if the user sends SIGINT?
|
|
208
|
+
// The main process handles SIGINT.
|
|
209
|
+
});
|
|
210
|
+
server.on('error', (err) => {
|
|
211
|
+
console.error('Server error:', err);
|
|
212
|
+
reject(err);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
reject(e);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/games/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/games/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tic-Tac-Toe Setup Helpers
|
|
3
|
+
*
|
|
4
|
+
* This is a lightweight “game module” focused on generating instructions and
|
|
5
|
+
* directory setup for manual or scripted agent runs.
|
|
6
|
+
*
|
|
7
|
+
* It intentionally does NOT spawn agent processes (that stays in CLI tooling),
|
|
8
|
+
* and it uses the inbox CLI commands (`inbox-poll`, `inbox-write`) so it works
|
|
9
|
+
* with any agent runtime.
|
|
10
|
+
*/
|
|
11
|
+
export interface TicTacToeSetupOptions {
|
|
12
|
+
dataDir: string;
|
|
13
|
+
playerX?: string;
|
|
14
|
+
playerO?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface TicTacToeSetupResult {
|
|
17
|
+
dataDir: string;
|
|
18
|
+
playerX: string;
|
|
19
|
+
playerO: string;
|
|
20
|
+
instructionsXPath: string;
|
|
21
|
+
instructionsOPath: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function setupTicTacToe(options: TicTacToeSetupOptions): TicTacToeSetupResult;
|
|
24
|
+
//# sourceMappingURL=tictactoe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tictactoe.d.ts","sourceRoot":"","sources":["../../src/games/tictactoe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAwID,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,oBAAoB,CAmBnF"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tic-Tac-Toe Setup Helpers
|
|
3
|
+
*
|
|
4
|
+
* This is a lightweight “game module” focused on generating instructions and
|
|
5
|
+
* directory setup for manual or scripted agent runs.
|
|
6
|
+
*
|
|
7
|
+
* It intentionally does NOT spawn agent processes (that stays in CLI tooling),
|
|
8
|
+
* and it uses the inbox CLI commands (`inbox-poll`, `inbox-write`) so it works
|
|
9
|
+
* with any agent runtime.
|
|
10
|
+
*/
|
|
11
|
+
import fs from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
function instructionsForPlayerX(dataDir, playerX, playerO) {
|
|
14
|
+
return `# Tic-Tac-Toe Autonomous Game Protocol
|
|
15
|
+
|
|
16
|
+
You are **${playerX}** (X). You play FIRST. Your opponent is **${playerO}** (O).
|
|
17
|
+
|
|
18
|
+
## Board Positions
|
|
19
|
+
\`\`\`
|
|
20
|
+
1 | 2 | 3
|
|
21
|
+
-----------
|
|
22
|
+
4 | 5 | 6
|
|
23
|
+
-----------
|
|
24
|
+
7 | 8 | 9
|
|
25
|
+
\`\`\`
|
|
26
|
+
|
|
27
|
+
## Commands Available
|
|
28
|
+
You have these \`agent-relay\` commands to communicate:
|
|
29
|
+
|
|
30
|
+
**Wait for opponent's message (blocking):**
|
|
31
|
+
\`\`\`bash
|
|
32
|
+
agent-relay inbox-poll -n ${playerX} -d ${dataDir} --clear
|
|
33
|
+
\`\`\`
|
|
34
|
+
|
|
35
|
+
**Send a move to opponent:**
|
|
36
|
+
\`\`\`bash
|
|
37
|
+
agent-relay inbox-write -t ${playerO} -f ${playerX} -m "MOVE: X at position N" -d ${dataDir}
|
|
38
|
+
\`\`\`
|
|
39
|
+
|
|
40
|
+
## PROTOCOL (follow EXACTLY)
|
|
41
|
+
|
|
42
|
+
### Since you're X, you go FIRST:
|
|
43
|
+
1. Make your first move to position 5 (center)
|
|
44
|
+
2. Send it to opponent:
|
|
45
|
+
\`\`\`bash
|
|
46
|
+
agent-relay inbox-write -t ${playerO} -f ${playerX} -m "MOVE: X at position 5" -d ${dataDir}
|
|
47
|
+
\`\`\`
|
|
48
|
+
|
|
49
|
+
### Then enter the game loop:
|
|
50
|
+
1. **WAIT** for opponent's response (this will block until they respond):
|
|
51
|
+
\`\`\`bash
|
|
52
|
+
agent-relay inbox-poll -n ${playerX} -d ${dataDir} --clear
|
|
53
|
+
\`\`\`
|
|
54
|
+
|
|
55
|
+
2. **UPDATE** your mental board state with opponent's move
|
|
56
|
+
|
|
57
|
+
3. **CHECK** for win/draw. If game over, send result and announce:
|
|
58
|
+
\`\`\`bash
|
|
59
|
+
agent-relay inbox-write -t ${playerO} -f ${playerX} -m "GAME OVER: X wins!" -d ${dataDir}
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
4. **MAKE** your next move and send it:
|
|
63
|
+
\`\`\`bash
|
|
64
|
+
agent-relay inbox-write -t ${playerO} -f ${playerX} -m "MOVE: X at position N" -d ${dataDir}
|
|
65
|
+
\`\`\`
|
|
66
|
+
|
|
67
|
+
5. **REPEAT** from step 1 until game over
|
|
68
|
+
|
|
69
|
+
## Rules
|
|
70
|
+
- Valid moves: positions 1-9 that are empty
|
|
71
|
+
- Win: 3 in a row (horizontal, vertical, or diagonal)
|
|
72
|
+
- Draw: all 9 positions filled with no winner
|
|
73
|
+
|
|
74
|
+
## CRITICAL
|
|
75
|
+
- NEVER stop mid-game
|
|
76
|
+
- ALWAYS use the inbox-poll command to wait (it blocks until opponent responds)
|
|
77
|
+
- Keep track of the board state
|
|
78
|
+
- Announce result when game ends
|
|
79
|
+
|
|
80
|
+
## START NOW
|
|
81
|
+
Make your FIRST MOVE to position 5, then wait for opponent's response.
|
|
82
|
+
`;
|
|
83
|
+
}
|
|
84
|
+
function instructionsForPlayerO(dataDir, playerX, playerO) {
|
|
85
|
+
return `# Tic-Tac-Toe Autonomous Game Protocol
|
|
86
|
+
|
|
87
|
+
You are **${playerO}** (O). ${playerX} plays first, so you WAIT for their move.
|
|
88
|
+
|
|
89
|
+
## Board Positions
|
|
90
|
+
\`\`\`
|
|
91
|
+
1 | 2 | 3
|
|
92
|
+
-----------
|
|
93
|
+
4 | 5 | 6
|
|
94
|
+
-----------
|
|
95
|
+
7 | 8 | 9
|
|
96
|
+
\`\`\`
|
|
97
|
+
|
|
98
|
+
## Commands Available
|
|
99
|
+
You have these \`agent-relay\` commands to communicate:
|
|
100
|
+
|
|
101
|
+
**Wait for opponent's message (blocking):**
|
|
102
|
+
\`\`\`bash
|
|
103
|
+
agent-relay inbox-poll -n ${playerO} -d ${dataDir} --clear
|
|
104
|
+
\`\`\`
|
|
105
|
+
|
|
106
|
+
**Send a move to opponent:**
|
|
107
|
+
\`\`\`bash
|
|
108
|
+
agent-relay inbox-write -t ${playerX} -f ${playerO} -m "MOVE: O at position N" -d ${dataDir}
|
|
109
|
+
\`\`\`
|
|
110
|
+
|
|
111
|
+
## PROTOCOL (follow EXACTLY)
|
|
112
|
+
|
|
113
|
+
### Since you're O, you go SECOND. Start by waiting:
|
|
114
|
+
1. **WAIT** for opponent's first move (this will block until they move):
|
|
115
|
+
\`\`\`bash
|
|
116
|
+
agent-relay inbox-poll -n ${playerO} -d ${dataDir} --clear
|
|
117
|
+
\`\`\`
|
|
118
|
+
|
|
119
|
+
2. **UPDATE** your mental board state with opponent's move
|
|
120
|
+
|
|
121
|
+
3. **CHECK** for win/draw. If game over, announce result.
|
|
122
|
+
|
|
123
|
+
4. **MAKE** your response move and send it:
|
|
124
|
+
\`\`\`bash
|
|
125
|
+
agent-relay inbox-write -t ${playerX} -f ${playerO} -m "MOVE: O at position N" -d ${dataDir}
|
|
126
|
+
\`\`\`
|
|
127
|
+
|
|
128
|
+
5. **WAIT** for opponent's next move (back to step 1)
|
|
129
|
+
|
|
130
|
+
## Rules
|
|
131
|
+
- Valid moves: positions 1-9 that are empty
|
|
132
|
+
- Win: 3 in a row (horizontal, vertical, or diagonal)
|
|
133
|
+
- Draw: all 9 positions filled with no winner
|
|
134
|
+
|
|
135
|
+
## CRITICAL
|
|
136
|
+
- NEVER stop mid-game
|
|
137
|
+
- ALWAYS use the inbox-poll command to wait (it blocks until opponent responds)
|
|
138
|
+
- Keep track of the board state
|
|
139
|
+
- Announce result when game ends
|
|
140
|
+
|
|
141
|
+
## START NOW
|
|
142
|
+
Run the inbox-poll command to WAIT for ${playerX}'s first move.
|
|
143
|
+
`;
|
|
144
|
+
}
|
|
145
|
+
export function setupTicTacToe(options) {
|
|
146
|
+
const dataDir = options.dataDir;
|
|
147
|
+
const playerX = options.playerX ?? 'PlayerX';
|
|
148
|
+
const playerO = options.playerO ?? 'PlayerO';
|
|
149
|
+
fs.mkdirSync(path.join(dataDir, playerX), { recursive: true });
|
|
150
|
+
fs.mkdirSync(path.join(dataDir, playerO), { recursive: true });
|
|
151
|
+
// Clear inboxes
|
|
152
|
+
fs.writeFileSync(path.join(dataDir, playerX, 'inbox.md'), '', 'utf-8');
|
|
153
|
+
fs.writeFileSync(path.join(dataDir, playerO, 'inbox.md'), '', 'utf-8');
|
|
154
|
+
const instructionsXPath = path.join(dataDir, playerX, 'GAME_INSTRUCTIONS.md');
|
|
155
|
+
const instructionsOPath = path.join(dataDir, playerO, 'GAME_INSTRUCTIONS.md');
|
|
156
|
+
fs.writeFileSync(instructionsXPath, instructionsForPlayerX(dataDir, playerX, playerO), 'utf-8');
|
|
157
|
+
fs.writeFileSync(instructionsOPath, instructionsForPlayerO(dataDir, playerX, playerO), 'utf-8');
|
|
158
|
+
return { dataDir, playerX, playerO, instructionsXPath, instructionsOPath };
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=tictactoe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tictactoe.js","sourceRoot":"","sources":["../../src/games/tictactoe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAgB7B,SAAS,sBAAsB,CAAC,OAAe,EAAE,OAAe,EAAE,OAAe;IAC/E,OAAO;;YAEG,OAAO,8CAA8C,OAAO;;;;;;;;;;;;;;;;4BAgB5C,OAAO,OAAO,OAAO;;;;;6BAKpB,OAAO,OAAO,OAAO,kCAAkC,OAAO;;;;;;;;;gCAS3D,OAAO,OAAO,OAAO,kCAAkC,OAAO;;;;;;+BAM/D,OAAO,OAAO,OAAO;;;;;;;gCAOpB,OAAO,OAAO,OAAO,+BAA+B,OAAO;;;;;gCAK3D,OAAO,OAAO,OAAO,kCAAkC,OAAO;;;;;;;;;;;;;;;;;;CAkB7F,CAAC;AACF,CAAC;AAED,SAAS,sBAAsB,CAAC,OAAe,EAAE,OAAe,EAAE,OAAe;IAC/E,OAAO;;YAEG,OAAO,WAAW,OAAO;;;;;;;;;;;;;;;;4BAgBT,OAAO,OAAO,OAAO;;;;;6BAKpB,OAAO,OAAO,OAAO,kCAAkC,OAAO;;;;;;;;+BAQ5D,OAAO,OAAO,OAAO;;;;;;;;;gCASpB,OAAO,OAAO,OAAO,kCAAkC,OAAO;;;;;;;;;;;;;;;;;yCAiBrD,OAAO;CAC/C,CAAC;AACF,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAA8B;IAC3D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAChC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,SAAS,CAAC;IAC7C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,SAAS,CAAC;IAE7C,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/D,gBAAgB;IAChB,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;IACvE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;IAEvE,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,sBAAsB,CAAC,CAAC;IAC9E,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,sBAAsB,CAAC,CAAC;IAE9E,EAAE,CAAC,aAAa,CAAC,iBAAiB,EAAE,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IAChG,EAAE,CAAC,aAAa,CAAC,iBAAiB,EAAE,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IAEhG,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,CAAC;AAC7E,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Agent Relay Inbox Check Hook
|
|
4
|
+
*
|
|
5
|
+
* A Claude Code Stop hook that checks for unread messages in the agent-relay inbox.
|
|
6
|
+
* When messages are present, it blocks Claude from stopping and instructs it to
|
|
7
|
+
* read and respond to the messages.
|
|
8
|
+
*
|
|
9
|
+
* This enables autonomous agent-to-agent communication without human intervention.
|
|
10
|
+
*
|
|
11
|
+
* Usage in .claude/settings.json:
|
|
12
|
+
* {
|
|
13
|
+
* "hooks": {
|
|
14
|
+
* "Stop": [{
|
|
15
|
+
* "hooks": [{
|
|
16
|
+
* "type": "command",
|
|
17
|
+
* "command": "node /path/to/agent-relay/dist/hooks/inbox-check/hook.js"
|
|
18
|
+
* }]
|
|
19
|
+
* }]
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* Environment Variables:
|
|
24
|
+
* - AGENT_RELAY_NAME: The agent's name (set by agent-relay wrapper)
|
|
25
|
+
* - AGENT_RELAY_INBOX_DIR: Custom inbox directory (default: /tmp/agent-relay)
|
|
26
|
+
*/
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../../src/hooks/inbox-check/hook.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Agent Relay Inbox Check Hook
|
|
4
|
+
*
|
|
5
|
+
* A Claude Code Stop hook that checks for unread messages in the agent-relay inbox.
|
|
6
|
+
* When messages are present, it blocks Claude from stopping and instructs it to
|
|
7
|
+
* read and respond to the messages.
|
|
8
|
+
*
|
|
9
|
+
* This enables autonomous agent-to-agent communication without human intervention.
|
|
10
|
+
*
|
|
11
|
+
* Usage in .claude/settings.json:
|
|
12
|
+
* {
|
|
13
|
+
* "hooks": {
|
|
14
|
+
* "Stop": [{
|
|
15
|
+
* "hooks": [{
|
|
16
|
+
* "type": "command",
|
|
17
|
+
* "command": "node /path/to/agent-relay/dist/hooks/inbox-check/hook.js"
|
|
18
|
+
* }]
|
|
19
|
+
* }]
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* Environment Variables:
|
|
24
|
+
* - AGENT_RELAY_NAME: The agent's name (set by agent-relay wrapper)
|
|
25
|
+
* - AGENT_RELAY_INBOX_DIR: Custom inbox directory (default: /tmp/agent-relay)
|
|
26
|
+
*/
|
|
27
|
+
import { readFileSync } from 'node:fs';
|
|
28
|
+
import { DEFAULT_INBOX_DIR, getAgentName, getInboxPath, hasUnreadMessages, countMessages, buildBlockReason } from './utils.js';
|
|
29
|
+
/**
|
|
30
|
+
* Read hook input from stdin
|
|
31
|
+
*/
|
|
32
|
+
function readStdin() {
|
|
33
|
+
try {
|
|
34
|
+
const input = readFileSync(0, 'utf-8');
|
|
35
|
+
return JSON.parse(input);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Output hook result as JSON
|
|
43
|
+
*/
|
|
44
|
+
function outputResult(result) {
|
|
45
|
+
console.log(JSON.stringify(result));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Exit the hook process
|
|
49
|
+
*/
|
|
50
|
+
function exitHook(code) {
|
|
51
|
+
process.exit(code);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Main hook execution
|
|
55
|
+
*/
|
|
56
|
+
async function main() {
|
|
57
|
+
try {
|
|
58
|
+
// Read stdin (required by Claude Code hook protocol, but we only use env vars)
|
|
59
|
+
readStdin();
|
|
60
|
+
// Get agent name from env
|
|
61
|
+
const agentName = getAgentName();
|
|
62
|
+
// If no agent name configured, allow stop (not in relay mode)
|
|
63
|
+
if (!agentName) {
|
|
64
|
+
outputResult({ decision: 'approve' });
|
|
65
|
+
exitHook(0);
|
|
66
|
+
}
|
|
67
|
+
// Get inbox configuration
|
|
68
|
+
const inboxDir = process.env.AGENT_RELAY_INBOX_DIR || DEFAULT_INBOX_DIR;
|
|
69
|
+
const inboxPath = getInboxPath({ inboxDir, agentName });
|
|
70
|
+
// Check for unread messages
|
|
71
|
+
if (hasUnreadMessages(inboxPath)) {
|
|
72
|
+
const messageCount = countMessages(inboxPath);
|
|
73
|
+
const reason = buildBlockReason(inboxPath, messageCount);
|
|
74
|
+
// Log to stderr for visibility
|
|
75
|
+
console.error(`[agent-relay] Found ${messageCount} unread message(s), blocking stop`);
|
|
76
|
+
// Block stop and provide reason
|
|
77
|
+
outputResult({
|
|
78
|
+
decision: 'block',
|
|
79
|
+
reason
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
// No messages, allow stop
|
|
84
|
+
outputResult({ decision: 'approve' });
|
|
85
|
+
}
|
|
86
|
+
exitHook(0);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
// On error, allow stop to avoid blocking user
|
|
90
|
+
console.error('[agent-relay] Hook error:', error);
|
|
91
|
+
outputResult({ decision: 'approve' });
|
|
92
|
+
exitHook(0);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Run the hook
|
|
96
|
+
main();
|
|
97
|
+
//# sourceMappingURL=hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.js","sourceRoot":"","sources":["../../../src/hooks/inbox-check/hook.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,SAAS,SAAS;IAChB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,MAAkB;IACtC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AACtC,CAAC;AAED;;GAEG;AACH,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,+EAA+E;QAC/E,SAAS,EAAE,CAAC;QAEZ,0BAA0B;QAC1B,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;QAEjC,8DAA8D;QAC9D,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,YAAY,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;YACtC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACd,CAAC;QAED,0BAA0B;QAC1B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,iBAAiB,CAAC;QACxE,MAAM,SAAS,GAAG,YAAY,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;QAExD,4BAA4B;QAC5B,IAAI,iBAAiB,CAAC,SAAS,CAAC,EAAE,CAAC;YACjC,MAAM,YAAY,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,gBAAgB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;YAEzD,+BAA+B;YAC/B,OAAO,CAAC,KAAK,CAAC,uBAAuB,YAAY,mCAAmC,CAAC,CAAC;YAEtF,gCAAgC;YAChC,YAAY,CAAC;gBACX,QAAQ,EAAE,OAAO;gBACjB,MAAM;aACP,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,0BAA0B;YAC1B,YAAY,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;QACxC,CAAC;QAED,QAAQ,CAAC,CAAC,CAAC,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,8CAA8C;QAC9C,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;QAClD,YAAY,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;QACtC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACd,CAAC;AACH,CAAC;AAED,eAAe;AACf,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/inbox-check/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/hooks/inbox-check/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for Agent Relay Inbox Check Hook
|
|
3
|
+
*/
|
|
4
|
+
export interface HookInput {
|
|
5
|
+
/** The hook event type */
|
|
6
|
+
hook_event_name?: string;
|
|
7
|
+
/** Working directory */
|
|
8
|
+
workingDirectory?: string;
|
|
9
|
+
/** Session ID */
|
|
10
|
+
session_id?: string;
|
|
11
|
+
/** Stop reason from Claude */
|
|
12
|
+
stop_reason?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface HookOutput {
|
|
15
|
+
/** Decision: "approve" to allow stop, "block" to continue */
|
|
16
|
+
decision: 'approve' | 'block';
|
|
17
|
+
/** Reason for the decision (shown to Claude if blocked) */
|
|
18
|
+
reason?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface InboxMessage {
|
|
21
|
+
from: string;
|
|
22
|
+
timestamp: string;
|
|
23
|
+
body: string;
|
|
24
|
+
}
|
|
25
|
+
export interface InboxConfig {
|
|
26
|
+
/** Base directory for inbox files */
|
|
27
|
+
inboxDir: string;
|
|
28
|
+
/** Agent name (from env var or config) */
|
|
29
|
+
agentName?: string;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/hooks/inbox-check/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,SAAS;IACxB,0BAA0B;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wBAAwB;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,6DAA6D;IAC7D,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC;IAC9B,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/hooks/inbox-check/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|