crewly 1.0.0 → 1.0.1

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 (105) hide show
  1. package/config/constants.ts +40 -2
  2. package/config/skills/_common/lib.sh +2 -1
  3. package/config/skills/agent/computer-use/execute.sh +151 -0
  4. package/config/skills/agent/computer-use/instructions.md +60 -0
  5. package/config/skills/agent/computer-use/skill.json +22 -0
  6. package/config/skills/agent/heartbeat/execute.sh +22 -0
  7. package/config/skills/agent/heartbeat/instructions.md +23 -0
  8. package/config/skills/agent/heartbeat/skill.json +20 -0
  9. package/config/skills/orchestrator/schedule-check/execute.sh +1 -1
  10. package/config/skills/orchestrator/send-pdf-to-slack/execute.sh +28 -5
  11. package/config/skills/orchestrator/send-pdf-to-slack/instructions.md +49 -10
  12. package/config/skills/orchestrator/send-pdf-to-slack/skill.json +1 -1
  13. package/config/skills/orchestrator/subscribe-event/execute.sh +2 -2
  14. package/dist/backend/backend/src/constants.d.ts +24 -2
  15. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  16. package/dist/backend/backend/src/constants.js +13 -3
  17. package/dist/backend/backend/src/constants.js.map +1 -1
  18. package/dist/backend/backend/src/controllers/session/session.controller.d.ts.map +1 -1
  19. package/dist/backend/backend/src/controllers/session/session.controller.js +16 -7
  20. package/dist/backend/backend/src/controllers/session/session.controller.js.map +1 -1
  21. package/dist/backend/backend/src/controllers/team/team.controller.d.ts.map +1 -1
  22. package/dist/backend/backend/src/controllers/team/team.controller.js +27 -0
  23. package/dist/backend/backend/src/controllers/team/team.controller.js.map +1 -1
  24. package/dist/backend/backend/src/index.d.ts +7 -0
  25. package/dist/backend/backend/src/index.d.ts.map +1 -1
  26. package/dist/backend/backend/src/index.js +131 -0
  27. package/dist/backend/backend/src/index.js.map +1 -1
  28. package/dist/backend/backend/src/middleware/agent-heartbeat.middleware.d.ts +3 -2
  29. package/dist/backend/backend/src/middleware/agent-heartbeat.middleware.d.ts.map +1 -1
  30. package/dist/backend/backend/src/middleware/agent-heartbeat.middleware.js +8 -2
  31. package/dist/backend/backend/src/middleware/agent-heartbeat.middleware.js.map +1 -1
  32. package/dist/backend/backend/src/services/agent/agent-heartbeat-monitor.service.d.ts +185 -0
  33. package/dist/backend/backend/src/services/agent/agent-heartbeat-monitor.service.d.ts.map +1 -0
  34. package/dist/backend/backend/src/services/agent/agent-heartbeat-monitor.service.js +508 -0
  35. package/dist/backend/backend/src/services/agent/agent-heartbeat-monitor.service.js.map +1 -0
  36. package/dist/backend/backend/src/services/agent/agent-heartbeat.service.d.ts +5 -5
  37. package/dist/backend/backend/src/services/agent/agent-heartbeat.service.d.ts.map +1 -1
  38. package/dist/backend/backend/src/services/agent/agent-heartbeat.service.js +3 -3
  39. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  40. package/dist/backend/backend/src/services/agent/agent-registration.service.js +36 -13
  41. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  42. package/dist/backend/backend/src/services/agent/disk-cleanup.service.d.ts +24 -3
  43. package/dist/backend/backend/src/services/agent/disk-cleanup.service.d.ts.map +1 -1
  44. package/dist/backend/backend/src/services/agent/disk-cleanup.service.js +15 -3
  45. package/dist/backend/backend/src/services/agent/disk-cleanup.service.js.map +1 -1
  46. package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts.map +1 -1
  47. package/dist/backend/backend/src/services/agent/idle-detection.service.js +18 -0
  48. package/dist/backend/backend/src/services/agent/idle-detection.service.js.map +1 -1
  49. package/dist/backend/backend/src/services/agent/pty-activity-tracker.service.d.ts +7 -2
  50. package/dist/backend/backend/src/services/agent/pty-activity-tracker.service.d.ts.map +1 -1
  51. package/dist/backend/backend/src/services/agent/pty-activity-tracker.service.js +11 -3
  52. package/dist/backend/backend/src/services/agent/pty-activity-tracker.service.js.map +1 -1
  53. package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.d.ts +42 -0
  54. package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.d.ts.map +1 -1
  55. package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.js +190 -1
  56. package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.js.map +1 -1
  57. package/dist/backend/backend/src/services/agent/tmux.service.js +2 -2
  58. package/dist/backend/backend/src/services/agent/tmux.service.js.map +1 -1
  59. package/dist/backend/backend/src/services/monitoring/activity-monitor.service.d.ts.map +1 -1
  60. package/dist/backend/backend/src/services/monitoring/activity-monitor.service.js +13 -19
  61. package/dist/backend/backend/src/services/monitoring/activity-monitor.service.js.map +1 -1
  62. package/dist/backend/backend/src/services/orchestrator/orchestrator-heartbeat-monitor.service.d.ts +129 -0
  63. package/dist/backend/backend/src/services/orchestrator/orchestrator-heartbeat-monitor.service.d.ts.map +1 -0
  64. package/dist/backend/backend/src/services/orchestrator/orchestrator-heartbeat-monitor.service.js +253 -0
  65. package/dist/backend/backend/src/services/orchestrator/orchestrator-heartbeat-monitor.service.js.map +1 -0
  66. package/dist/backend/backend/src/services/session/pty/pty-session-backend.d.ts.map +1 -1
  67. package/dist/backend/backend/src/services/session/pty/pty-session-backend.js +5 -1
  68. package/dist/backend/backend/src/services/session/pty/pty-session-backend.js.map +1 -1
  69. package/dist/backend/backend/src/services/session/session-state-persistence.d.ts +4 -1
  70. package/dist/backend/backend/src/services/session/session-state-persistence.d.ts.map +1 -1
  71. package/dist/backend/backend/src/services/session/session-state-persistence.js +3 -1
  72. package/dist/backend/backend/src/services/session/session-state-persistence.js.map +1 -1
  73. package/dist/backend/backend/src/services/workflow/scheduler.service.d.ts +1 -0
  74. package/dist/backend/backend/src/services/workflow/scheduler.service.d.ts.map +1 -1
  75. package/dist/backend/backend/src/services/workflow/scheduler.service.js +1 -0
  76. package/dist/backend/backend/src/services/workflow/scheduler.service.js.map +1 -1
  77. package/dist/backend/backend/src/types/index.d.ts +2 -0
  78. package/dist/backend/backend/src/types/index.d.ts.map +1 -1
  79. package/dist/backend/backend/src/types/index.js.map +1 -1
  80. package/dist/backend/backend/src/types/scheduler.types.d.ts +17 -0
  81. package/dist/backend/backend/src/types/scheduler.types.d.ts.map +1 -1
  82. package/dist/backend/backend/src/types/scheduler.types.js.map +1 -1
  83. package/dist/backend/config/constants.d.ts +36 -2
  84. package/dist/backend/config/constants.d.ts.map +1 -1
  85. package/dist/backend/config/constants.js +37 -2
  86. package/dist/backend/config/constants.js.map +1 -1
  87. package/dist/backend/config/index.d.ts +1 -1
  88. package/dist/cli/cli/src/constants.d.ts +1 -1
  89. package/dist/cli/cli/src/index.js +1 -1
  90. package/dist/cli/cli/src/index.js.map +1 -1
  91. package/dist/cli/config/constants.d.ts +36 -2
  92. package/dist/cli/config/constants.d.ts.map +1 -1
  93. package/dist/cli/config/constants.js +37 -2
  94. package/dist/cli/config/constants.js.map +1 -1
  95. package/dist/cli/config/index.d.ts +1 -1
  96. package/frontend/dist/assets/{index-77b6a2a0.js → index-4c56763b.js} +31 -31
  97. package/frontend/dist/assets/{index-5ddf71c8.css → index-c1dd0b10.css} +1 -1
  98. package/frontend/dist/index.html +2 -2
  99. package/package.json +8 -4
  100. package/config/constants.test.ts +0 -469
  101. package/config/quality-gates/default-gates.test.ts +0 -246
  102. package/config/skills/agent/nano-banana-image/.env +0 -2
  103. package/config/skills/agent/nano-banana-image/.env.example +0 -6
  104. package/config/skills/nano-banana-image/.env +0 -2
  105. package/config/skills/nano-banana-image/.env.example +0 -6
@@ -346,8 +346,8 @@ export const MESSAGE_CONSTANTS = {
346
346
  * Environment variable names used across domains
347
347
  */
348
348
  export const ENV_CONSTANTS = {
349
- /** Session name (legacy: kept for compatibility with older agents) */
350
- TMUX_SESSION_NAME: 'TMUX_SESSION_NAME',
349
+ /** PTY session name used for agent identity and heartbeat tracking */
350
+ CREWLY_SESSION_NAME: 'CREWLY_SESSION_NAME',
351
351
  /** Crewly role identifier */
352
352
  CREWLY_ROLE: 'CREWLY_ROLE',
353
353
  /** API server port */
@@ -768,6 +768,44 @@ export const ORCHESTRATOR_RESTART_CONSTANTS = {
768
768
  RESTART_DELAY_MS: 5000,
769
769
  } as const;
770
770
 
771
+ // ========================= ORCHESTRATOR HEARTBEAT MONITOR =========================
772
+
773
+ /**
774
+ * Orchestrator heartbeat monitoring configuration.
775
+ * Used by OrchestratorHeartbeatMonitorService to detect unresponsive orchestrators,
776
+ * send proactive heartbeat requests, and trigger auto-restart with resume.
777
+ */
778
+ export const ORCHESTRATOR_HEARTBEAT_CONSTANTS = {
779
+ /** Interval between heartbeat checks in milliseconds (30 seconds) */
780
+ CHECK_INTERVAL_MS: 30_000,
781
+ /** Time without API activity before sending a heartbeat request to orchestrator (5 minutes) */
782
+ HEARTBEAT_REQUEST_THRESHOLD_MS: 300_000,
783
+ /** Time after heartbeat request before triggering auto-restart (1 minute) */
784
+ RESTART_THRESHOLD_MS: 60_000,
785
+ /** Message sent to orchestrator PTY to request a heartbeat */
786
+ HEARTBEAT_REQUEST_MESSAGE: 'Please run your heartbeat skill now: bash config/skills/orchestrator/heartbeat/execute.sh',
787
+ /** Grace period after server start before monitoring begins (30 seconds) */
788
+ STARTUP_GRACE_PERIOD_MS: 30_000,
789
+ } as const;
790
+
791
+ /**
792
+ * Agent heartbeat monitor configuration.
793
+ * Used by AgentHeartbeatMonitorService to detect crashed/unresponsive agents
794
+ * and auto-restart them with task re-delivery.
795
+ */
796
+ export const AGENT_HEARTBEAT_MONITOR_CONSTANTS = {
797
+ /** Interval between heartbeat checks in milliseconds (30 seconds) */
798
+ CHECK_INTERVAL_MS: 30_000,
799
+ /** Time without activity (both PTY and API) before checking process liveness (5 minutes) */
800
+ HEARTBEAT_REQUEST_THRESHOLD_MS: 300_000,
801
+ /** Grace period after server start before monitoring begins (1 minute) */
802
+ STARTUP_GRACE_PERIOD_MS: 60_000,
803
+ /** Maximum restarts per cooldown window per agent */
804
+ MAX_RESTARTS_PER_WINDOW: 3,
805
+ /** Cooldown window for restart tracking (1 hour) */
806
+ COOLDOWN_WINDOW_MS: 3_600_000,
807
+ } as const;
808
+
771
809
  // ========================= AGENT SUSPEND SYSTEM =========================
772
810
 
773
811
  /**
@@ -19,7 +19,8 @@ api_call() {
19
19
  local url="${CREWLY_API_URL}/api${endpoint}"
20
20
  local args=(-s -w '\n%{http_code}' -X "$method" -H "Content-Type: application/json")
21
21
  # Include agent session identity header for heartbeat tracking
22
- [ -n "$TMUX_SESSION_NAME" ] && args+=(-H "X-Agent-Session: $TMUX_SESSION_NAME")
22
+ # Use ${VAR:-} pattern to avoid 'unbound variable' error under set -u (nounset)
23
+ [ -n "${CREWLY_SESSION_NAME:-}" ] && args+=(-H "X-Agent-Session: $CREWLY_SESSION_NAME")
23
24
  [ -n "$body" ] && args+=(-d "$body")
24
25
 
25
26
  local response
@@ -0,0 +1,151 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # Desktop Automation (Computer Use) Skill
4
+ # Controls macOS desktop: screenshots, mouse, keyboard
5
+ # Uses native macOS APIs — no external dependencies required
6
+ # =============================================================================
7
+ set -euo pipefail
8
+
9
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
+ source "${SCRIPT_DIR}/../_common/lib.sh"
11
+
12
+ INPUT="${1:-}"
13
+ [ -z "$INPUT" ] && error_exit "Usage: execute.sh '{\"action\":\"screenshot|move|click|type\", ...}'"
14
+
15
+ ACTION=$(echo "$INPUT" | jq -r '.action // empty')
16
+ require_param "action" "$ACTION"
17
+
18
+ # -----------------------------------------------------------------------------
19
+ # screenshot — capture the screen to a file
20
+ # -----------------------------------------------------------------------------
21
+ do_screenshot() {
22
+ local output
23
+ output=$(echo "$INPUT" | jq -r '.output // "/tmp/screenshot.png"')
24
+
25
+ # Ensure output directory exists
26
+ mkdir -p "$(dirname "$output")"
27
+
28
+ screencapture -x "$output" 2>/dev/null
29
+
30
+ if [ -f "$output" ]; then
31
+ local size
32
+ size=$(stat -f%z "$output" 2>/dev/null || stat -c%s "$output" 2>/dev/null || echo "unknown")
33
+ echo "{\"success\":true,\"action\":\"screenshot\",\"file\":\"$output\",\"size\":$size}"
34
+ else
35
+ error_exit "Screenshot failed — file not created"
36
+ fi
37
+ }
38
+
39
+ # -----------------------------------------------------------------------------
40
+ # move — move mouse cursor to (x, y)
41
+ # -----------------------------------------------------------------------------
42
+ do_move() {
43
+ local x y
44
+ x=$(echo "$INPUT" | jq -r '.x // empty')
45
+ y=$(echo "$INPUT" | jq -r '.y // empty')
46
+ require_param "x" "$x"
47
+ require_param "y" "$y"
48
+
49
+ osascript -l JavaScript -e "
50
+ ObjC.import('CoreGraphics');
51
+ var point = $.CGPointMake($x, $y);
52
+ var event = $.CGEventCreateMouseEvent(null, $.kCGEventMouseMoved, point, 0);
53
+ $.CGEventPost($.kCGHIDEventTap, event);
54
+ 'moved';
55
+ " >/dev/null 2>&1
56
+
57
+ echo "{\"success\":true,\"action\":\"move\",\"x\":$x,\"y\":$y}"
58
+ }
59
+
60
+ # -----------------------------------------------------------------------------
61
+ # click — click at (x, y) with optional button type
62
+ # -----------------------------------------------------------------------------
63
+ do_click() {
64
+ local x y button
65
+ x=$(echo "$INPUT" | jq -r '.x // empty')
66
+ y=$(echo "$INPUT" | jq -r '.y // empty')
67
+ button=$(echo "$INPUT" | jq -r '.button // "left"')
68
+ require_param "x" "$x"
69
+ require_param "y" "$y"
70
+
71
+ case "$button" in
72
+ left)
73
+ osascript -l JavaScript -e "
74
+ ObjC.import('CoreGraphics');
75
+ var point = $.CGPointMake($x, $y);
76
+ var move = $.CGEventCreateMouseEvent(null, $.kCGEventMouseMoved, point, 0);
77
+ $.CGEventPost($.kCGHIDEventTap, move);
78
+ var down = $.CGEventCreateMouseEvent(null, $.kCGEventLeftMouseDown, point, 0);
79
+ $.CGEventPost($.kCGHIDEventTap, down);
80
+ var up = $.CGEventCreateMouseEvent(null, $.kCGEventLeftMouseUp, point, 0);
81
+ $.CGEventPost($.kCGHIDEventTap, up);
82
+ 'clicked';
83
+ " >/dev/null 2>&1
84
+ ;;
85
+ right)
86
+ osascript -l JavaScript -e "
87
+ ObjC.import('CoreGraphics');
88
+ var point = $.CGPointMake($x, $y);
89
+ var move = $.CGEventCreateMouseEvent(null, $.kCGEventMouseMoved, point, 0);
90
+ $.CGEventPost($.kCGHIDEventTap, move);
91
+ var down = $.CGEventCreateMouseEvent(null, $.kCGEventRightMouseDown, point, $.kCGMouseButtonRight);
92
+ $.CGEventPost($.kCGHIDEventTap, down);
93
+ var up = $.CGEventCreateMouseEvent(null, $.kCGEventRightMouseUp, point, $.kCGMouseButtonRight);
94
+ $.CGEventPost($.kCGHIDEventTap, up);
95
+ 'clicked';
96
+ " >/dev/null 2>&1
97
+ ;;
98
+ double)
99
+ osascript -l JavaScript -e "
100
+ ObjC.import('CoreGraphics');
101
+ var point = $.CGPointMake($x, $y);
102
+ var move = $.CGEventCreateMouseEvent(null, $.kCGEventMouseMoved, point, 0);
103
+ $.CGEventPost($.kCGHIDEventTap, move);
104
+ var down1 = $.CGEventCreateMouseEvent(null, $.kCGEventLeftMouseDown, point, 0);
105
+ $.CGEventSetIntegerValueField(down1, $.kCGMouseEventClickState, 1);
106
+ $.CGEventPost($.kCGHIDEventTap, down1);
107
+ var up1 = $.CGEventCreateMouseEvent(null, $.kCGEventLeftMouseUp, point, 0);
108
+ $.CGEventSetIntegerValueField(up1, $.kCGMouseEventClickState, 1);
109
+ $.CGEventPost($.kCGHIDEventTap, up1);
110
+ delay(0.05);
111
+ var down2 = $.CGEventCreateMouseEvent(null, $.kCGEventLeftMouseDown, point, 0);
112
+ $.CGEventSetIntegerValueField(down2, $.kCGMouseEventClickState, 2);
113
+ $.CGEventPost($.kCGHIDEventTap, down2);
114
+ var up2 = $.CGEventCreateMouseEvent(null, $.kCGEventLeftMouseUp, point, 0);
115
+ $.CGEventSetIntegerValueField(up2, $.kCGMouseEventClickState, 2);
116
+ $.CGEventPost($.kCGHIDEventTap, up2);
117
+ 'double-clicked';
118
+ " >/dev/null 2>&1
119
+ ;;
120
+ *)
121
+ error_exit "Unknown button type: $button (use left, right, or double)"
122
+ ;;
123
+ esac
124
+
125
+ echo "{\"success\":true,\"action\":\"click\",\"x\":$x,\"y\":$y,\"button\":\"$button\"}"
126
+ }
127
+
128
+ # -----------------------------------------------------------------------------
129
+ # type — simulate keyboard input
130
+ # -----------------------------------------------------------------------------
131
+ do_type() {
132
+ local text
133
+ text=$(echo "$INPUT" | jq -r '.text // empty')
134
+ require_param "text" "$text"
135
+
136
+ osascript -e "tell application \"System Events\" to keystroke \"$text\"" 2>/dev/null
137
+
138
+ local char_count=${#text}
139
+ echo "{\"success\":true,\"action\":\"type\",\"characters\":$char_count}"
140
+ }
141
+
142
+ # -----------------------------------------------------------------------------
143
+ # Dispatch
144
+ # -----------------------------------------------------------------------------
145
+ case "$ACTION" in
146
+ screenshot) do_screenshot ;;
147
+ move) do_move ;;
148
+ click) do_click ;;
149
+ type) do_type ;;
150
+ *) error_exit "Unknown action: $ACTION (use screenshot, move, click, or type)" ;;
151
+ esac
@@ -0,0 +1,60 @@
1
+ # Desktop Automation (Computer Use)
2
+
3
+ Control the macOS desktop by taking screenshots, moving/clicking the mouse, and typing text.
4
+
5
+ ## Actions
6
+
7
+ ### Screenshot
8
+ Capture the screen (or a region) to an image file.
9
+
10
+ ```bash
11
+ bash execute.sh '{"action":"screenshot"}'
12
+ bash execute.sh '{"action":"screenshot","output":"/tmp/my-screenshot.png"}'
13
+ ```
14
+
15
+ ### Move Mouse
16
+ Move the mouse cursor to a specific coordinate.
17
+
18
+ ```bash
19
+ bash execute.sh '{"action":"move","x":500,"y":300}'
20
+ ```
21
+
22
+ ### Click
23
+ Click at a specific coordinate. Supports left click, right click, and double click.
24
+
25
+ ```bash
26
+ bash execute.sh '{"action":"click","x":500,"y":300}'
27
+ bash execute.sh '{"action":"click","x":500,"y":300,"button":"right"}'
28
+ bash execute.sh '{"action":"click","x":500,"y":300,"button":"double"}'
29
+ ```
30
+
31
+ ### Type Text
32
+ Type a string of text using simulated keystrokes.
33
+
34
+ ```bash
35
+ bash execute.sh '{"action":"type","text":"Hello, world!"}'
36
+ ```
37
+
38
+ ## Parameters
39
+
40
+ | Parameter | Required | Default | Description |
41
+ |-----------|----------|---------|-------------|
42
+ | `action` | Yes | - | One of: `screenshot`, `move`, `click`, `type` |
43
+ | `x` | For move/click | - | X coordinate (pixels from left) |
44
+ | `y` | For move/click | - | Y coordinate (pixels from top) |
45
+ | `button` | No | `left` | Click button: `left`, `right`, or `double` |
46
+ | `text` | For type | - | Text string to type |
47
+ | `output` | No | `/tmp/screenshot.png` | Screenshot output file path |
48
+
49
+ ## Requirements
50
+
51
+ - macOS (uses native `screencapture` and CoreGraphics)
52
+ - Accessibility permissions must be granted to the terminal app (System Settings > Privacy & Security > Accessibility)
53
+
54
+ ## Typical Workflow
55
+
56
+ 1. Take a screenshot to see the current screen state
57
+ 2. Identify the coordinates of the target element
58
+ 3. Move the mouse and/or click on the target
59
+ 4. Optionally type text into a focused field
60
+ 5. Take another screenshot to verify the result
@@ -0,0 +1,22 @@
1
+ {
2
+ "id": "computer-use",
3
+ "name": "Desktop Automation (Computer Use)",
4
+ "description": "Control the macOS desktop: take screenshots, move/click the mouse, and type text. Uses native macOS APIs (screencapture, CoreGraphics via JXA, System Events) with zero external dependencies.",
5
+ "category": "automation",
6
+ "skillType": "claude-skill",
7
+ "promptFile": "instructions.md",
8
+ "execution": {
9
+ "type": "script",
10
+ "script": {
11
+ "file": "execute.sh",
12
+ "interpreter": "bash",
13
+ "timeoutMs": 30000
14
+ }
15
+ },
16
+ "triggers": ["screenshot", "click", "mouse", "type text", "desktop automation", "computer use"],
17
+ "tags": ["automation", "desktop", "screenshot", "mouse", "keyboard", "macos"],
18
+ "assignableRoles": ["generalist", "designer", "qa"],
19
+ "version": "1.0.0",
20
+ "createdAt": "2026-02-20T00:00:00.000Z",
21
+ "updatedAt": "2026-02-20T00:00:00.000Z"
22
+ }
@@ -0,0 +1,22 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # Agent Heartbeat Skill - Lightweight health check and heartbeat update
4
+ #
5
+ # Calls the /health endpoint to update the agent heartbeat via the
6
+ # X-Agent-Session middleware header (set automatically by lib.sh).
7
+ # =============================================================================
8
+
9
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
+ source "$SCRIPT_DIR/../../_common/lib.sh"
11
+
12
+ # Single API call updates heartbeat via the X-Agent-Session middleware
13
+ health_response=$(api_call GET "/health" 2>/dev/null) || health_response='{"error":"unavailable"}'
14
+
15
+ cat <<EOF
16
+ {
17
+ "status": "ok",
18
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
19
+ "session": "${CREWLY_SESSION_NAME:-unknown}",
20
+ "health": $health_response
21
+ }
22
+ EOF
@@ -0,0 +1,23 @@
1
+ # Agent Heartbeat Skill
2
+
3
+ Perform a lightweight health check to confirm that you are responsive.
4
+
5
+ ## When to Use
6
+
7
+ Run this skill when the system asks you to perform a heartbeat check. This updates your heartbeat timestamp so the monitoring system knows you are alive and responsive.
8
+
9
+ ## Usage
10
+
11
+ ```bash
12
+ bash config/skills/agent/heartbeat/execute.sh
13
+ ```
14
+
15
+ No parameters required.
16
+
17
+ ## Output
18
+
19
+ Returns a JSON object with:
20
+ - `status`: "ok" if the health endpoint responded
21
+ - `timestamp`: UTC timestamp of the check
22
+ - `session`: your session name
23
+ - `health`: response from the health endpoint
@@ -0,0 +1,20 @@
1
+ {
2
+ "id": "agent-heartbeat",
3
+ "name": "Heartbeat",
4
+ "description": "Perform a lightweight health check to confirm the agent is responsive. Updates the heartbeat timestamp via the API middleware.",
5
+ "category": "monitoring",
6
+ "skillType": "claude-skill",
7
+ "promptFile": "instructions.md",
8
+ "execution": {
9
+ "type": "script",
10
+ "script": {
11
+ "file": "execute.sh",
12
+ "interpreter": "bash",
13
+ "timeoutMs": 15000
14
+ }
15
+ },
16
+ "assignableRoles": ["developer", "qa", "devops", "pm", "tpm"],
17
+ "triggers": ["heartbeat", "health check"],
18
+ "tags": ["monitoring", "heartbeat", "health"],
19
+ "version": "1.0.0"
20
+ }
@@ -16,7 +16,7 @@ require_param "minutes" "$MINUTES"
16
16
  require_param "message" "$MESSAGE"
17
17
 
18
18
  # Default target to the caller's own session (orchestrator sends reminders to itself)
19
- TARGET_SESSION="${TARGET:-${TMUX_SESSION_NAME:-crewly-orc}}"
19
+ TARGET_SESSION="${TARGET:-${CREWLY_SESSION_NAME:-crewly-orc}}"
20
20
 
21
21
  # API expects: targetSession, minutes, message
22
22
  # When recurring, also send isRecurring, intervalMinutes, and optional maxOccurrences
@@ -105,7 +105,7 @@ trap cleanup EXIT
105
105
 
106
106
  # Convert markdown to PDF using weasyprint
107
107
  if ! "$PYTHON" -c "
108
- import sys, markdown, weasyprint
108
+ import sys, os, re, markdown, weasyprint
109
109
 
110
110
  md_file = sys.argv[1]
111
111
  pdf_file = sys.argv[2]
@@ -113,28 +113,51 @@ pdf_file = sys.argv[2]
113
113
  with open(md_file, 'r', encoding='utf-8') as f:
114
114
  md_content = f.read()
115
115
 
116
+ # Resolve the base directory for relative image paths
117
+ base_dir = os.path.dirname(os.path.abspath(md_file))
118
+
119
+ # Convert local absolute image paths to file:// URLs so weasyprint can load them
120
+ def fix_image_paths(content):
121
+ # Match markdown image syntax: ![alt](path)
122
+ def replace_path(match):
123
+ alt = match.group(1)
124
+ path = match.group(2)
125
+ if os.path.isabs(path) and os.path.exists(path):
126
+ return '![' + alt + '](file://' + path + ')'
127
+ elif not path.startswith(('http://', 'https://', 'file://')) and os.path.exists(os.path.join(base_dir, path)):
128
+ full = os.path.join(base_dir, path)
129
+ return '![' + alt + '](file://' + full + ')'
130
+ return match.group(0)
131
+ return re.sub(r'!\[([^\]]*)\]\(([^)]+)\)', replace_path, content)
132
+
133
+ md_content = fix_image_paths(md_content)
134
+
116
135
  html = markdown.markdown(md_content, extensions=['tables', 'fenced_code', 'codehilite', 'toc', 'meta'])
117
136
 
118
137
  styled_html = '''<!DOCTYPE html>
119
138
  <html><head><meta charset=\"utf-8\">
120
139
  <style>
140
+ @page { size: A4; margin: 20mm 15mm; }
121
141
  body { font-family: -apple-system, \"Noto Sans SC\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif;
122
- max-width: 800px; margin: 40px auto; padding: 0 20px; line-height: 1.8;
142
+ max-width: 800px; margin: 0 auto; padding: 0; line-height: 1.8;
123
143
  font-size: 14px; color: #333; }
124
144
  h1 { font-size: 24px; border-bottom: 2px solid #333; padding-bottom: 8px; }
125
145
  h2 { font-size: 20px; border-bottom: 1px solid #ddd; padding-bottom: 6px; margin-top: 28px; }
126
146
  h3 { font-size: 16px; margin-top: 20px; }
127
- table { border-collapse: collapse; width: 100%; margin: 16px 0; }
128
- th, td { border: 1px solid #ddd; padding: 8px 12px; text-align: left; }
147
+ img { max-width: 100%; height: auto; display: block; margin: 16px auto;
148
+ border: 1px solid #e0e0e0; border-radius: 4px; }
149
+ table { border-collapse: collapse; width: 100%; margin: 16px 0; font-size: 12px; }
150
+ th, td { border: 1px solid #ddd; padding: 6px 10px; text-align: left; }
129
151
  th { background-color: #f5f5f5; font-weight: 600; }
130
152
  code { background: #f4f4f4; padding: 2px 6px; border-radius: 3px; font-size: 13px; }
131
153
  pre { background: #f4f4f4; padding: 16px; border-radius: 6px; overflow-x: auto; }
132
154
  blockquote { border-left: 4px solid #ddd; margin: 16px 0; padding: 8px 16px; color: #666; }
133
155
  ul, ol { padding-left: 24px; }
134
156
  li { margin: 4px 0; }
157
+ hr { border: none; border-top: 1px solid #ddd; margin: 24px 0; }
135
158
  </style></head><body>''' + html + '</body></html>'
136
159
 
137
- weasyprint.HTML(string=styled_html).write_pdf(pdf_file)
160
+ weasyprint.HTML(string=styled_html, base_url='file://' + base_dir + '/').write_pdf(pdf_file)
138
161
  " "$MD_FILE" "$PDF_FILE" 2>/tmp/weasyprint-err-$$.log; then
139
162
  WP_ERR="$(cat /tmp/weasyprint-err-$$.log 2>/dev/null || echo 'unknown error')"
140
163
  error_exit "weasyprint conversion failed: ${WP_ERR}"
@@ -1,6 +1,16 @@
1
1
  # Send PDF to Slack
2
2
 
3
- Converts a markdown file to PDF using `weasyprint` (Python) and uploads it to a Slack channel via the `/api/slack/upload-file` endpoint.
3
+ Converts a markdown file to a styled PDF and uploads it to a Slack channel via the `/api/slack/upload-file` endpoint. Supports embedding local images referenced in the markdown.
4
+
5
+ ## Image Support
6
+
7
+ The skill automatically handles local images referenced in markdown:
8
+
9
+ - **Absolute paths**: `![alt](/Users/me/images/photo.png)` — converted to `file://` URLs
10
+ - **Relative paths**: `![alt](./images/photo.png)` — resolved relative to the markdown file's directory
11
+ - **Remote URLs**: `![alt](https://example.com/photo.png)` — passed through as-is
12
+
13
+ Images are rendered at full width (max 100% of page) with auto height scaling, centered with a subtle border.
4
14
 
5
15
  ## Prerequisites
6
16
 
@@ -10,7 +20,12 @@ Converts a markdown file to PDF using `weasyprint` (Python) and uploads it to a
10
20
  ## Usage
11
21
 
12
22
  ```bash
13
- bash config/skills/orchestrator/send-pdf-to-slack/execute.sh --channel C0123ABC --file /path/to/document.md --title "Weekly Report"
23
+ bash config/skills/orchestrator/send-pdf-to-slack/execute.sh \
24
+ --channel C0123ABC \
25
+ --file /path/to/document.md \
26
+ --title "Weekly Report" \
27
+ --text "Here is the report" \
28
+ --thread 1707123456.789000
14
29
  ```
15
30
 
16
31
  ## Parameters
@@ -23,19 +38,43 @@ bash config/skills/orchestrator/send-pdf-to-slack/execute.sh --channel C0123ABC
23
38
  | `--text`, `-t` | No | Initial comment to include with the upload |
24
39
  | `--thread`, `-r` | No | Slack thread timestamp for threaded upload |
25
40
 
26
- ## Examples
41
+ ## Example: Daily Report with Infographics
27
42
 
28
- ```bash
29
- # Basic upload
30
- bash execute.sh --channel C0123ABC --file report.md
43
+ Create a markdown file with embedded images:
44
+
45
+ ```markdown
46
+ # AI Daily Report - 2026-02-21
47
+
48
+ ## 1. Paper Title
49
+ Summary of the paper.
31
50
 
32
- # Upload with title and comment
33
- bash execute.sh --channel C0123ABC --file report.md --title "Q4 Report" --text "Here is the quarterly report"
51
+ ![Paper Infographic](/path/to/output/infographic-1.png)
34
52
 
35
- # Upload in a thread
36
- bash execute.sh --channel C0123ABC --file notes.md --thread 1707123456.789000
53
+ ## 2. Another Paper
54
+ Summary here.
55
+
56
+ ![Another Infographic](/path/to/output/infographic-2.png)
37
57
  ```
38
58
 
59
+ Then send it:
60
+
61
+ ```bash
62
+ bash config/skills/orchestrator/send-pdf-to-slack/execute.sh \
63
+ --channel D0AC7NF5N7L \
64
+ --file /path/to/daily-report.md \
65
+ --title "AI Daily Report 2026-02-21" \
66
+ --text "Today's AI report with infographics" \
67
+ --thread 1771651155.079579
68
+ ```
69
+
70
+ ## PDF Styling
71
+
72
+ - A4 page size with 20mm/15mm margins
73
+ - System fonts with CJK support (PingFang SC, Noto Sans SC)
74
+ - Images: max-width 100%, centered, with subtle border
75
+ - Tables: full width, bordered cells
76
+ - Code blocks: gray background with monospace font
77
+
39
78
  ## Output
40
79
 
41
80
  JSON response from the upload API with `fileId` on success. Also emits a `[NOTIFY]` block for chat service integration.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "orc-send-pdf-to-slack",
3
3
  "name": "Send PDF to Slack",
4
- "description": "Convert a markdown file to PDF and upload it to a Slack channel.",
4
+ "description": "Convert a markdown file to a styled PDF (with embedded local images) and upload it to a Slack channel. Supports markdown image syntax ![alt](/path/to/image.png) — local absolute and relative image paths are automatically resolved and embedded in the PDF.",
5
5
  "category": "communication",
6
6
  "skillType": "claude-skill",
7
7
  "promptFile": "instructions.md",
@@ -10,9 +10,9 @@ INPUT="${1:-}"
10
10
  EVENT_TYPE=$(echo "$INPUT" | jq -r '.eventType // empty')
11
11
  require_param "eventType" "$EVENT_TYPE"
12
12
 
13
- # Inject subscriberSession from env (set by Crewly on each tmux session)
13
+ # Inject subscriberSession from env (set by Crewly on each PTY session)
14
14
  # The API requires subscriberSession and filter but the agent doesn't need to provide them
15
- SUBSCRIBER_SESSION="${TMUX_SESSION_NAME:-crewly-orc}"
15
+ SUBSCRIBER_SESSION="${CREWLY_SESSION_NAME:-crewly-orc}"
16
16
  BODY=$(echo "$INPUT" | jq --arg ss "$SUBSCRIBER_SESSION" \
17
17
  '. + {subscriberSession: $ss} | if .filter == null then .filter = {} else . end')
18
18
 
@@ -160,6 +160,20 @@ export declare const VERSION_CHECK_CONSTANTS: {
160
160
  readonly CACHE_TTL_MS: number;
161
161
  readonly REQUEST_TIMEOUT_MS: 5000;
162
162
  };
163
+ export declare const AGENT_HEARTBEAT_MONITOR_CONSTANTS: {
164
+ readonly CHECK_INTERVAL_MS: 30000;
165
+ readonly HEARTBEAT_REQUEST_THRESHOLD_MS: 300000;
166
+ readonly STARTUP_GRACE_PERIOD_MS: 60000;
167
+ readonly MAX_RESTARTS_PER_WINDOW: 3;
168
+ readonly COOLDOWN_WINDOW_MS: 3600000;
169
+ };
170
+ export declare const ORCHESTRATOR_HEARTBEAT_CONSTANTS: {
171
+ readonly CHECK_INTERVAL_MS: 30000;
172
+ readonly HEARTBEAT_REQUEST_THRESHOLD_MS: 300000;
173
+ readonly RESTART_THRESHOLD_MS: 60000;
174
+ readonly HEARTBEAT_REQUEST_MESSAGE: "Please run your heartbeat skill now: bash config/skills/orchestrator/heartbeat/execute.sh";
175
+ readonly STARTUP_GRACE_PERIOD_MS: 30000;
176
+ };
163
177
  export declare const ORCHESTRATOR_SESSION_NAME = "crewly-orc";
164
178
  export declare const ORCHESTRATOR_ROLE = "orchestrator";
165
179
  export declare const ORCHESTRATOR_WINDOW_NAME = "Crewly Orchestrator";
@@ -229,8 +243,8 @@ export declare const CREWLY_CONSTANTS: {
229
243
  };
230
244
  };
231
245
  export declare const ENV_CONSTANTS: {
232
- /** Session name (legacy: kept for compatibility with older agents) */
233
- readonly TMUX_SESSION_NAME: "TMUX_SESSION_NAME";
246
+ /** PTY session name used for agent identity and heartbeat tracking */
247
+ readonly CREWLY_SESSION_NAME: "CREWLY_SESSION_NAME";
234
248
  readonly CREWLY_ROLE: "CREWLY_ROLE";
235
249
  /** Base URL for the Crewly backend API (used by orchestrator bash skills) */
236
250
  readonly CREWLY_API_URL: "CREWLY_API_URL";
@@ -553,6 +567,14 @@ export declare const RUNTIME_EXIT_CONSTANTS: {
553
567
  * normal runtime initialization output.
554
568
  */
555
569
  readonly STARTUP_GRACE_PERIOD_MS: 0;
570
+ /**
571
+ * Grace period for API activity before confirming a runtime exit (ms).
572
+ * If the agent made an API call within this window, the exit detection
573
+ * is treated as a false positive and skipped. This prevents false
574
+ * restarts when agents are actively calling skills/APIs but happen to
575
+ * produce PTY output that matches exit patterns.
576
+ */
577
+ readonly API_ACTIVITY_GRACE_PERIOD_MS: 120000;
556
578
  };
557
579
  /**
558
580
  * Constants for sub-agent message queue.
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../backend/src/constants.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../backend/src/constants.ts"],"names":[],"mappings":"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"}