@agentmeshhq/agent 0.1.13 → 0.1.15

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 (89) hide show
  1. package/dist/__tests__/loader.test.js +44 -1
  2. package/dist/__tests__/loader.test.js.map +1 -1
  3. package/dist/__tests__/runner.test.js.map +1 -1
  4. package/dist/__tests__/sandbox.test.js.map +1 -1
  5. package/dist/__tests__/watchdog.test.d.ts +1 -0
  6. package/dist/__tests__/watchdog.test.js +290 -0
  7. package/dist/__tests__/watchdog.test.js.map +1 -0
  8. package/dist/cli/attach.js +20 -1
  9. package/dist/cli/attach.js.map +1 -1
  10. package/dist/cli/build.js +8 -2
  11. package/dist/cli/build.js.map +1 -1
  12. package/dist/cli/context.js.map +1 -1
  13. package/dist/cli/deploy.js +1 -1
  14. package/dist/cli/deploy.js.map +1 -1
  15. package/dist/cli/init.js +1 -1
  16. package/dist/cli/init.js.map +1 -1
  17. package/dist/cli/list.js +3 -3
  18. package/dist/cli/list.js.map +1 -1
  19. package/dist/cli/local.js +5 -3
  20. package/dist/cli/local.js.map +1 -1
  21. package/dist/cli/migrate.js +1 -1
  22. package/dist/cli/migrate.js.map +1 -1
  23. package/dist/cli/nudge.js +16 -3
  24. package/dist/cli/nudge.js.map +1 -1
  25. package/dist/cli/restart.js.map +1 -1
  26. package/dist/cli/slack.js +1 -1
  27. package/dist/cli/slack.js.map +1 -1
  28. package/dist/cli/stop.js +13 -5
  29. package/dist/cli/stop.js.map +1 -1
  30. package/dist/cli/test.js +1 -1
  31. package/dist/cli/test.js.map +1 -1
  32. package/dist/cli/token.js +2 -2
  33. package/dist/cli/token.js.map +1 -1
  34. package/dist/config/loader.d.ts +5 -1
  35. package/dist/config/loader.js +27 -2
  36. package/dist/config/loader.js.map +1 -1
  37. package/dist/config/schema.d.ts +13 -0
  38. package/dist/core/daemon.d.ts +32 -1
  39. package/dist/core/daemon.js +395 -19
  40. package/dist/core/daemon.js.map +1 -1
  41. package/dist/core/injector.d.ts +2 -2
  42. package/dist/core/injector.js +23 -4
  43. package/dist/core/injector.js.map +1 -1
  44. package/dist/core/runner.d.ts +1 -1
  45. package/dist/core/runner.js +44 -1
  46. package/dist/core/runner.js.map +1 -1
  47. package/dist/core/sandbox.d.ts +11 -0
  48. package/dist/core/sandbox.js +34 -2
  49. package/dist/core/sandbox.js.map +1 -1
  50. package/dist/core/tmux.d.ts +9 -0
  51. package/dist/core/tmux.js +105 -11
  52. package/dist/core/tmux.js.map +1 -1
  53. package/dist/core/watchdog.d.ts +41 -0
  54. package/dist/core/watchdog.js +198 -0
  55. package/dist/core/watchdog.js.map +1 -0
  56. package/dist/core/websocket.js +1 -1
  57. package/dist/core/websocket.js.map +1 -1
  58. package/dist/index.d.ts +5 -5
  59. package/dist/index.js +5 -5
  60. package/dist/index.js.map +1 -1
  61. package/package.json +1 -1
  62. package/src/__tests__/loader.test.ts +52 -4
  63. package/src/__tests__/runner.test.ts +1 -2
  64. package/src/__tests__/sandbox.test.ts +1 -1
  65. package/src/__tests__/watchdog.test.ts +368 -0
  66. package/src/cli/attach.ts +22 -1
  67. package/src/cli/build.ts +12 -4
  68. package/src/cli/context.ts +0 -1
  69. package/src/cli/deploy.ts +7 -5
  70. package/src/cli/init.ts +7 -19
  71. package/src/cli/list.ts +6 -10
  72. package/src/cli/local.ts +21 -12
  73. package/src/cli/migrate.ts +6 -4
  74. package/src/cli/nudge.ts +29 -14
  75. package/src/cli/restart.ts +1 -1
  76. package/src/cli/slack.ts +16 -15
  77. package/src/cli/stop.ts +14 -5
  78. package/src/cli/test.ts +5 -3
  79. package/src/cli/token.ts +4 -4
  80. package/src/config/loader.ts +29 -2
  81. package/src/config/schema.ts +14 -0
  82. package/src/core/daemon.ts +474 -24
  83. package/src/core/injector.ts +27 -4
  84. package/src/core/runner.ts +49 -1
  85. package/src/core/sandbox.ts +47 -2
  86. package/src/core/tmux.ts +135 -12
  87. package/src/core/watchdog.ts +238 -0
  88. package/src/core/websocket.ts +2 -2
  89. package/src/index.ts +6 -5
package/src/cli/nudge.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { loadConfig, getAgentState, loadState } from "../config/loader.js";
2
- import { sendKeys, sessionExists, getSessionName } from "../core/tmux.js";
3
- import { registerAgent } from "../core/registry.js";
1
+ import { spawnSync } from "node:child_process";
4
2
  import pc from "picocolors";
3
+ import { getAgentState, loadConfig, loadState } from "../config/loader.js";
4
+ import { registerAgent } from "../core/registry.js";
5
+ import { getSessionName, sendKeys, sessionExists } from "../core/tmux.js";
5
6
 
6
7
  export async function nudge(name: string, message: string): Promise<void> {
7
8
  const config = loadConfig();
@@ -24,6 +25,23 @@ export async function nudge(name: string, message: string): Promise<void> {
24
25
  // Check if this is a local agent
25
26
  const localAgent = getAgentState(name);
26
27
 
28
+ // Sandbox agent - route nudge through docker exec
29
+ if (localAgent?.sandboxContainer) {
30
+ const result = spawnSync(
31
+ "docker",
32
+ ["exec", localAgent.sandboxContainer, "agentmesh", "nudge", name, message],
33
+ { encoding: "utf-8", stdio: "pipe" },
34
+ );
35
+
36
+ if (result.status === 0) {
37
+ console.log(pc.green(`Nudged "${name}" in sandbox.`));
38
+ } else {
39
+ console.log(pc.red(`Failed to nudge "${name}" in sandbox: ${result.stderr}`));
40
+ }
41
+ return;
42
+ }
43
+
44
+ // Local agent with tmux session on host
27
45
  if (localAgent && sessionExists(getSessionName(name))) {
28
46
  // Local nudge via tmux send-keys
29
47
  const formatted = `[AgentMesh] Nudge from CLI:
@@ -60,17 +78,14 @@ ${message}`;
60
78
  model: "cli",
61
79
  });
62
80
 
63
- const response = await fetch(
64
- `${config.hubUrl}/api/v1/agents/${agentState.agentId}/nudge`,
65
- {
66
- method: "POST",
67
- headers: {
68
- Authorization: `Bearer ${registration.token}`,
69
- "Content-Type": "application/json",
70
- },
71
- body: JSON.stringify({ message }),
72
- }
73
- );
81
+ const response = await fetch(`${config.hubUrl}/api/v1/agents/${agentState.agentId}/nudge`, {
82
+ method: "POST",
83
+ headers: {
84
+ Authorization: `Bearer ${registration.token}`,
85
+ "Content-Type": "application/json",
86
+ },
87
+ body: JSON.stringify({ message }),
88
+ });
74
89
 
75
90
  if (response.ok) {
76
91
  console.log(pc.green(`Nudged "${name}" via API.`));
@@ -1,6 +1,6 @@
1
1
  import pc from "picocolors";
2
2
  import { getAgentState, loadConfig } from "../config/loader.js";
3
- import { destroySession, getSessionName, sessionExists } from "../core/tmux.js";
3
+ import { getSessionName, sessionExists } from "../core/tmux.js";
4
4
  import { start } from "./start.js";
5
5
  import { stop } from "./stop.js";
6
6
 
package/src/cli/slack.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { loadConfig, getAgentState } from "../config/loader.js";
2
1
  import pc from "picocolors";
2
+ import { getAgentState, loadConfig } from "../config/loader.js";
3
3
 
4
4
  export async function slack(
5
5
  action: string,
6
6
  channel: string | undefined,
7
7
  message: string | undefined,
8
- options: { name?: string }
8
+ options: { name?: string },
9
9
  ): Promise<void> {
10
10
  const config = loadConfig();
11
11
 
@@ -16,12 +16,16 @@ export async function slack(
16
16
 
17
17
  if (action === "respond" || action === "reply") {
18
18
  if (!channel) {
19
- console.log(pc.red("Channel is required. Usage: agentmesh slack respond <channel> <message>"));
19
+ console.log(
20
+ pc.red("Channel is required. Usage: agentmesh slack respond <channel> <message>"),
21
+ );
20
22
  process.exit(1);
21
23
  }
22
24
 
23
25
  if (!message) {
24
- console.log(pc.red("Message is required. Usage: agentmesh slack respond <channel> <message>"));
26
+ console.log(
27
+ pc.red("Message is required. Usage: agentmesh slack respond <channel> <message>"),
28
+ );
25
29
  process.exit(1);
26
30
  }
27
31
 
@@ -36,17 +40,14 @@ export async function slack(
36
40
  }
37
41
 
38
42
  try {
39
- const response = await fetch(
40
- `${config.hubUrl}/api/v1/integrations/slack/respond`,
41
- {
42
- method: "POST",
43
- headers: {
44
- Authorization: `Bearer ${token}`,
45
- "Content-Type": "application/json",
46
- },
47
- body: JSON.stringify({ channel, text: message }),
48
- }
49
- );
43
+ const response = await fetch(`${config.hubUrl}/api/v1/integrations/slack/respond`, {
44
+ method: "POST",
45
+ headers: {
46
+ Authorization: `Bearer ${token}`,
47
+ "Content-Type": "application/json",
48
+ },
49
+ body: JSON.stringify({ channel, text: message }),
50
+ });
50
51
 
51
52
  if (response.ok) {
52
53
  console.log(pc.green(`Message sent to Slack channel ${channel}`));
package/src/cli/stop.ts CHANGED
@@ -1,6 +1,7 @@
1
- import { destroySession } from "../core/tmux.js";
2
- import { removeAgentFromState, getAgentState } from "../config/loader.js";
1
+ import { spawnSync } from "node:child_process";
3
2
  import pc from "picocolors";
3
+ import { getAgentState, removeAgentFromState } from "../config/loader.js";
4
+ import { destroySession } from "../core/tmux.js";
4
5
 
5
6
  export async function stop(name: string): Promise<void> {
6
7
  if (!name) {
@@ -32,8 +33,16 @@ export async function stop(name: string): Promise<void> {
32
33
  console.log(pc.green(`Destroyed tmux session for "${name}"`));
33
34
  }
34
35
 
35
- // Remove from state
36
- removeAgentFromState(name);
36
+ // Clean up Docker sandbox container if it exists
37
+ if (state.sandboxContainer) {
38
+ console.log(`Stopping sandbox container: ${state.sandboxContainer}`);
39
+ spawnSync("docker", ["stop", "-t", "5", state.sandboxContainer], { encoding: "utf-8" });
40
+ spawnSync("docker", ["rm", "-f", state.sandboxContainer], { encoding: "utf-8" });
41
+ console.log(pc.green(`Removed sandbox container`));
42
+ }
43
+
44
+ // Remove from state but preserve agentId for re-registration
45
+ removeAgentFromState(name, true);
37
46
 
38
- console.log(pc.green(`Agent "${name}" stopped.`));
47
+ console.log(pc.green(`Agent "${name}" stopped (agentId preserved for restart).`));
39
48
  }
package/src/cli/test.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { spawnSync, execSync } from "node:child_process";
1
+ import { execSync, spawnSync } from "node:child_process";
2
2
  import path from "node:path";
3
3
  import pc from "picocolors";
4
4
 
@@ -14,14 +14,16 @@ function findProjectRoot(): string {
14
14
  dir = path.dirname(dir);
15
15
  }
16
16
  }
17
- throw new Error("Could not find AgentMesh project root. Make sure you're in the agentmesh repository.");
17
+ throw new Error(
18
+ "Could not find AgentMesh project root. Make sure you're in the agentmesh repository.",
19
+ );
18
20
  }
19
21
 
20
22
  function isLocalStackRunning(): boolean {
21
23
  try {
22
24
  const result = execSync(
23
25
  'docker inspect --format="{{.State.Running}}" agentmesh_postgres 2>/dev/null',
24
- { encoding: "utf-8" }
26
+ { encoding: "utf-8" },
25
27
  ).trim();
26
28
  return result === "true";
27
29
  } catch {
package/src/cli/token.ts CHANGED
@@ -39,9 +39,9 @@ export async function token(action: string, agentName?: string): Promise<void> {
39
39
  }
40
40
 
41
41
  async function showToken(
42
- config: ReturnType<typeof loadConfig>,
42
+ _config: ReturnType<typeof loadConfig>,
43
43
  agentName?: string,
44
- envAgentId?: string,
44
+ _envAgentId?: string,
45
45
  envToken?: string,
46
46
  ): Promise<void> {
47
47
  // If running as agent, show env token
@@ -134,9 +134,9 @@ async function refreshToken(
134
134
  }
135
135
 
136
136
  async function tokenInfo(
137
- config: ReturnType<typeof loadConfig>,
137
+ _config: ReturnType<typeof loadConfig>,
138
138
  agentName?: string,
139
- envAgentId?: string,
139
+ _envAgentId?: string,
140
140
  envToken?: string,
141
141
  ): Promise<void> {
142
142
  // Get token to inspect
@@ -58,12 +58,39 @@ export function addAgentToState(agent: AgentState): void {
58
58
  saveState(state);
59
59
  }
60
60
 
61
- export function removeAgentFromState(name: string): void {
61
+ export function removeAgentFromState(name: string, preserveAgentId = false): void {
62
62
  const state = loadState();
63
- state.agents = state.agents.filter((a) => a.name !== name);
63
+
64
+ if (preserveAgentId) {
65
+ // Keep minimal state with agentId for re-registration
66
+ const existing = state.agents.find((a) => a.name === name);
67
+ if (existing) {
68
+ state.agents = state.agents.filter((a) => a.name !== name);
69
+ // Save only the agentId and restartCount for next startup
70
+ state.agents.push({
71
+ name: existing.name,
72
+ agentId: existing.agentId,
73
+ pid: 0,
74
+ tmuxSession: "",
75
+ startedAt: existing.startedAt,
76
+ restartCount: existing.restartCount,
77
+ status: "stopped",
78
+ });
79
+ }
80
+ } else {
81
+ state.agents = state.agents.filter((a) => a.name !== name);
82
+ }
83
+
64
84
  saveState(state);
65
85
  }
66
86
 
87
+ /**
88
+ * Reset restart count for an agent (called on manual start or after stable period)
89
+ */
90
+ export function resetAgentRestartCount(name: string): void {
91
+ updateAgentInState(name, { restartCount: 0, stuckSince: undefined, status: "running" });
92
+ }
93
+
67
94
  export function getAgentState(name: string): AgentState | undefined {
68
95
  const state = loadState();
69
96
  return state.agents.find((a) => a.name === name);
@@ -32,6 +32,8 @@ export const STATE_PATH = `${process.env.HOME}/.agentmesh/state.json`;
32
32
 
33
33
  export type RunnerType = "opencode" | "claude" | "custom";
34
34
 
35
+ export type AgentStatus = "running" | "stopped" | "failed" | "stuck";
36
+
35
37
  export interface AgentState {
36
38
  name: string;
37
39
  agentId: string;
@@ -45,6 +47,18 @@ export interface AgentState {
45
47
  runtimeModel?: string;
46
48
  /** The runner type (opencode, claude, custom) */
47
49
  runnerType?: RunnerType;
50
+ /** Docker container name if running in sandbox mode */
51
+ sandboxContainer?: string;
52
+ /** Number of auto-restart attempts since last manual start */
53
+ restartCount?: number;
54
+ /** ISO timestamp of last auto-restart */
55
+ lastRestartAt?: string;
56
+ /** ISO timestamp of last detected activity */
57
+ lastActivityAt?: string;
58
+ /** ISO timestamp when agent was detected as stuck */
59
+ stuckSince?: string;
60
+ /** Current agent status */
61
+ status?: AgentStatus;
48
62
  }
49
63
 
50
64
  export interface State {