@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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/dist/__tests__/context.test.d.ts +1 -0
  3. package/dist/__tests__/context.test.js +353 -0
  4. package/dist/__tests__/context.test.js.map +1 -0
  5. package/dist/cli/context.d.ts +4 -0
  6. package/dist/cli/context.js +190 -0
  7. package/dist/cli/context.js.map +1 -0
  8. package/dist/cli/index.js +17 -0
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/cli/start.d.ts +1 -0
  11. package/dist/cli/start.js +10 -5
  12. package/dist/cli/start.js.map +1 -1
  13. package/dist/context/handoff.d.ts +48 -0
  14. package/dist/context/handoff.js +88 -0
  15. package/dist/context/handoff.js.map +1 -0
  16. package/dist/context/index.d.ts +7 -0
  17. package/dist/context/index.js +8 -0
  18. package/dist/context/index.js.map +1 -0
  19. package/dist/context/schema.d.ts +82 -0
  20. package/dist/context/schema.js +33 -0
  21. package/dist/context/schema.js.map +1 -0
  22. package/dist/context/storage.d.ts +49 -0
  23. package/dist/context/storage.js +172 -0
  24. package/dist/context/storage.js.map +1 -0
  25. package/dist/core/daemon.d.ts +7 -0
  26. package/dist/core/daemon.js +53 -2
  27. package/dist/core/daemon.js.map +1 -1
  28. package/dist/core/heartbeat.d.ts +6 -0
  29. package/dist/core/heartbeat.js +8 -0
  30. package/dist/core/heartbeat.js.map +1 -1
  31. package/dist/core/injector.d.ts +9 -0
  32. package/dist/core/injector.js +55 -3
  33. package/dist/core/injector.js.map +1 -1
  34. package/dist/core/tmux.d.ts +13 -0
  35. package/dist/core/tmux.js +62 -0
  36. package/dist/core/tmux.js.map +1 -1
  37. package/package.json +11 -11
  38. package/src/__tests__/context.test.ts +464 -0
  39. package/src/cli/context.ts +232 -0
  40. package/src/cli/index.ts +17 -0
  41. package/src/cli/start.ts +11 -9
  42. package/src/context/handoff.ts +122 -0
  43. package/src/context/index.ts +8 -0
  44. package/src/context/schema.ts +111 -0
  45. package/src/context/storage.ts +197 -0
  46. package/src/core/daemon.ts +59 -1
  47. package/src/core/heartbeat.ts +13 -0
  48. package/src/core/injector.ts +74 -30
  49. package/src/core/tmux.ts +75 -0
@@ -1,11 +1,10 @@
1
- import { sendKeys } from "./tmux.js";
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
- agentName: string,
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
+ }