@agentmeshhq/agent 0.1.7 → 0.1.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/LICENSE +21 -0
- package/dist/__tests__/context.test.d.ts +1 -0
- package/dist/__tests__/context.test.js +353 -0
- package/dist/__tests__/context.test.js.map +1 -0
- package/dist/cli/context.d.ts +4 -0
- package/dist/cli/context.js +190 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/cli/index.js +17 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/start.d.ts +1 -0
- package/dist/cli/start.js +10 -5
- package/dist/cli/start.js.map +1 -1
- package/dist/context/handoff.d.ts +48 -0
- package/dist/context/handoff.js +88 -0
- package/dist/context/handoff.js.map +1 -0
- package/dist/context/index.d.ts +7 -0
- package/dist/context/index.js +8 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/schema.d.ts +82 -0
- package/dist/context/schema.js +33 -0
- package/dist/context/schema.js.map +1 -0
- package/dist/context/storage.d.ts +49 -0
- package/dist/context/storage.js +172 -0
- package/dist/context/storage.js.map +1 -0
- package/dist/core/daemon.d.ts +7 -0
- package/dist/core/daemon.js +53 -2
- package/dist/core/daemon.js.map +1 -1
- package/dist/core/heartbeat.d.ts +6 -0
- package/dist/core/heartbeat.js +8 -0
- package/dist/core/heartbeat.js.map +1 -1
- package/dist/core/injector.d.ts +9 -0
- package/dist/core/injector.js +55 -3
- package/dist/core/injector.js.map +1 -1
- package/dist/core/tmux.d.ts +13 -0
- package/dist/core/tmux.js +62 -0
- package/dist/core/tmux.js.map +1 -1
- package/package.json +11 -11
- package/src/__tests__/context.test.ts +464 -0
- package/src/cli/context.ts +232 -0
- package/src/cli/index.ts +17 -0
- package/src/cli/start.ts +11 -9
- package/src/context/handoff.ts +122 -0
- package/src/context/index.ts +8 -0
- package/src/context/schema.ts +111 -0
- package/src/context/storage.ts +197 -0
- package/src/core/daemon.ts +59 -1
- package/src/core/heartbeat.ts +13 -0
- package/src/core/injector.ts +74 -30
- package/src/core/tmux.ts +75 -0
package/src/core/injector.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { formatHandoffContextSummary, parseHandoffContext } from "../context/handoff.js";
|
|
2
|
+
import type { AgentContext } from "../context/schema.js";
|
|
2
3
|
import type { InboxItem } from "./registry.js";
|
|
4
|
+
import { sendKeys } from "./tmux.js";
|
|
3
5
|
import type { WebSocketEvent } from "./websocket.js";
|
|
4
6
|
|
|
5
|
-
export function injectStartupMessage(
|
|
6
|
-
agentName: string,
|
|
7
|
-
pendingCount: number
|
|
8
|
-
): void {
|
|
7
|
+
export function injectStartupMessage(agentName: string, pendingCount: number): void {
|
|
9
8
|
if (pendingCount === 0) {
|
|
10
9
|
const message = `[AgentMesh] Connected and ready. No pending items in inbox.`;
|
|
11
10
|
sendKeys(agentName, message);
|
|
@@ -18,10 +17,7 @@ Use agentmesh_check_inbox to see them, or agentmesh_accept_handoff to start work
|
|
|
18
17
|
sendKeys(agentName, message);
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
export function injectHandoffReceived(
|
|
22
|
-
agentName: string,
|
|
23
|
-
event: WebSocketEvent
|
|
24
|
-
): void {
|
|
20
|
+
export function injectHandoffReceived(agentName: string, event: WebSocketEvent): void {
|
|
25
21
|
const fromName =
|
|
26
22
|
(event.from_agent as { display_name?: string })?.display_name ||
|
|
27
23
|
(event.from_agent_id as string) ||
|
|
@@ -41,14 +37,9 @@ Accept this handoff and begin work.`;
|
|
|
41
37
|
sendKeys(agentName, message);
|
|
42
38
|
}
|
|
43
39
|
|
|
44
|
-
export function injectNudge(
|
|
45
|
-
agentName: string,
|
|
46
|
-
event: WebSocketEvent
|
|
47
|
-
): void {
|
|
40
|
+
export function injectNudge(agentName: string, event: WebSocketEvent): void {
|
|
48
41
|
const fromName =
|
|
49
|
-
(event.from as { name?: string })?.name ||
|
|
50
|
-
(event.from_name as string) ||
|
|
51
|
-
"Someone";
|
|
42
|
+
(event.from as { name?: string })?.name || (event.from_name as string) || "Someone";
|
|
52
43
|
const message = (event.message as string) || "Check your inbox";
|
|
53
44
|
|
|
54
45
|
const formatted = `[AgentMesh] Nudge from ${fromName}:
|
|
@@ -57,12 +48,8 @@ ${message}`;
|
|
|
57
48
|
sendKeys(agentName, formatted);
|
|
58
49
|
}
|
|
59
50
|
|
|
60
|
-
export function injectBlockerResolved(
|
|
61
|
-
|
|
62
|
-
event: WebSocketEvent
|
|
63
|
-
): void {
|
|
64
|
-
const description =
|
|
65
|
-
(event.description as string) || "A blocker has been resolved";
|
|
51
|
+
export function injectBlockerResolved(agentName: string, event: WebSocketEvent): void {
|
|
52
|
+
const description = (event.description as string) || "A blocker has been resolved";
|
|
66
53
|
const resolvedBy =
|
|
67
54
|
(event.resolved_by as { display_name?: string })?.display_name ||
|
|
68
55
|
(event.resolved_by_name as string) ||
|
|
@@ -78,10 +65,7 @@ You can now proceed with your work.`;
|
|
|
78
65
|
sendKeys(agentName, message);
|
|
79
66
|
}
|
|
80
67
|
|
|
81
|
-
export function injectInboxItems(
|
|
82
|
-
agentName: string,
|
|
83
|
-
items: InboxItem[]
|
|
84
|
-
): void {
|
|
68
|
+
export function injectInboxItems(agentName: string, items: InboxItem[]): void {
|
|
85
69
|
if (items.length === 0) {
|
|
86
70
|
sendKeys(agentName, "[AgentMesh] Your inbox is empty.");
|
|
87
71
|
return;
|
|
@@ -101,10 +85,7 @@ export function injectInboxItems(
|
|
|
101
85
|
sendKeys(agentName, message);
|
|
102
86
|
}
|
|
103
87
|
|
|
104
|
-
export function handleWebSocketEvent(
|
|
105
|
-
agentName: string,
|
|
106
|
-
event: WebSocketEvent
|
|
107
|
-
): void {
|
|
88
|
+
export function handleWebSocketEvent(agentName: string, event: WebSocketEvent): void {
|
|
108
89
|
switch (event.type) {
|
|
109
90
|
case "handoff_received":
|
|
110
91
|
case "handoff.received":
|
|
@@ -126,3 +107,66 @@ export function handleWebSocketEvent(
|
|
|
126
107
|
break;
|
|
127
108
|
}
|
|
128
109
|
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Injects restored context from a previous session
|
|
113
|
+
*/
|
|
114
|
+
export function injectRestoredContext(agentName: string, context: AgentContext): void {
|
|
115
|
+
const parts: string[] = ["[AgentMesh] Restored context from previous session:"];
|
|
116
|
+
|
|
117
|
+
// Working state
|
|
118
|
+
if (context.workingState.workdir) {
|
|
119
|
+
parts.push(`Working directory: ${context.workingState.workdir}`);
|
|
120
|
+
}
|
|
121
|
+
if (context.workingState.gitBranch) {
|
|
122
|
+
parts.push(`Git branch: ${context.workingState.gitBranch}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Tasks
|
|
126
|
+
const activeTasks = context.tasks.tasks.filter(
|
|
127
|
+
(t) => t.status === "in_progress" || t.status === "pending",
|
|
128
|
+
);
|
|
129
|
+
if (activeTasks.length > 0) {
|
|
130
|
+
parts.push("");
|
|
131
|
+
parts.push("Active tasks:");
|
|
132
|
+
for (const task of activeTasks.slice(0, 5)) {
|
|
133
|
+
const statusIcon = task.status === "in_progress" ? ">" : "-";
|
|
134
|
+
parts.push(` ${statusIcon} ${task.content}`);
|
|
135
|
+
}
|
|
136
|
+
if (activeTasks.length > 5) {
|
|
137
|
+
parts.push(` ... and ${activeTasks.length - 5} more`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Current goal
|
|
142
|
+
if (context.tasks.currentGoal) {
|
|
143
|
+
parts.push("");
|
|
144
|
+
parts.push(`Goal: ${context.tasks.currentGoal}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Accomplishments
|
|
148
|
+
if (context.conversation.accomplishments.length > 0) {
|
|
149
|
+
parts.push("");
|
|
150
|
+
parts.push("Recent accomplishments:");
|
|
151
|
+
for (const acc of context.conversation.accomplishments.slice(0, 3)) {
|
|
152
|
+
parts.push(` - ${acc}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const message = parts.join("\n");
|
|
157
|
+
sendKeys(agentName, message);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Injects context received from a handoff
|
|
162
|
+
*/
|
|
163
|
+
export function injectHandoffContext(agentName: string, contextString: string): void {
|
|
164
|
+
const context = parseHandoffContext(contextString);
|
|
165
|
+
if (!context) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const summary = formatHandoffContextSummary(context);
|
|
170
|
+
const message = `[AgentMesh] Context from previous agent:\n\n${summary}`;
|
|
171
|
+
sendKeys(agentName, message);
|
|
172
|
+
}
|
package/src/core/tmux.ts
CHANGED
|
@@ -176,3 +176,78 @@ export function getSessionInfo(agentName: string): { exists: boolean; command?:
|
|
|
176
176
|
return { exists: true };
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
|
+
|
|
180
|
+
export interface SessionContext {
|
|
181
|
+
workdir: string;
|
|
182
|
+
gitBranch?: string;
|
|
183
|
+
gitStatus?: string;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Captures current working state from the tmux session
|
|
188
|
+
*/
|
|
189
|
+
export function captureSessionContext(agentName: string): SessionContext | null {
|
|
190
|
+
const sessionName = getSessionName(agentName);
|
|
191
|
+
|
|
192
|
+
if (!sessionExists(sessionName)) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
// Get current working directory from the pane
|
|
198
|
+
const workdir = execSync(`tmux display-message -t "${sessionName}" -p "#{pane_current_path}"`, {
|
|
199
|
+
encoding: "utf-8",
|
|
200
|
+
}).trim();
|
|
201
|
+
|
|
202
|
+
const context: SessionContext = { workdir };
|
|
203
|
+
|
|
204
|
+
// Try to get git info if in a git repo
|
|
205
|
+
try {
|
|
206
|
+
const gitBranch = execSync("git rev-parse --abbrev-ref HEAD 2>/dev/null", {
|
|
207
|
+
encoding: "utf-8",
|
|
208
|
+
cwd: workdir,
|
|
209
|
+
}).trim();
|
|
210
|
+
if (gitBranch) {
|
|
211
|
+
context.gitBranch = gitBranch;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const gitStatus = execSync("git status --short 2>/dev/null", {
|
|
215
|
+
encoding: "utf-8",
|
|
216
|
+
cwd: workdir,
|
|
217
|
+
}).trim();
|
|
218
|
+
if (gitStatus) {
|
|
219
|
+
// Truncate if too long
|
|
220
|
+
context.gitStatus =
|
|
221
|
+
gitStatus.length > 500 ? `${gitStatus.substring(0, 500)}...` : gitStatus;
|
|
222
|
+
}
|
|
223
|
+
} catch {
|
|
224
|
+
// Not a git repo or git not available
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return context;
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.error(`Failed to capture session context: ${error}`);
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Captures recent output from the tmux pane (last N lines)
|
|
236
|
+
*/
|
|
237
|
+
export function captureSessionOutput(agentName: string, lines = 100): string | null {
|
|
238
|
+
const sessionName = getSessionName(agentName);
|
|
239
|
+
|
|
240
|
+
if (!sessionExists(sessionName)) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
const output = execSync(`tmux capture-pane -t "${sessionName}" -p -S -${lines}`, {
|
|
246
|
+
encoding: "utf-8",
|
|
247
|
+
});
|
|
248
|
+
return output;
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error(`Failed to capture session output: ${error}`);
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
}
|