crewly 1.20.202 → 1.20.203

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 (153) hide show
  1. package/dist/backend/backend/src/constants.d.ts +98 -0
  2. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  3. package/dist/backend/backend/src/constants.js +98 -0
  4. package/dist/backend/backend/src/constants.js.map +1 -1
  5. package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
  6. package/dist/backend/backend/src/controllers/chat/chat.controller.js +10 -1
  7. package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
  8. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts.map +1 -1
  9. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js +28 -1
  10. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js.map +1 -1
  11. package/dist/backend/backend/src/controllers/session/session.controller.d.ts.map +1 -1
  12. package/dist/backend/backend/src/controllers/session/session.controller.js +7 -0
  13. package/dist/backend/backend/src/controllers/session/session.controller.js.map +1 -1
  14. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts.map +1 -1
  15. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +4 -1
  16. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
  17. package/dist/backend/backend/src/index.d.ts +28 -0
  18. package/dist/backend/backend/src/index.d.ts.map +1 -1
  19. package/dist/backend/backend/src/index.js +132 -34
  20. package/dist/backend/backend/src/index.js.map +1 -1
  21. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  22. package/dist/backend/backend/src/services/agent/agent-registration.service.js +7 -0
  23. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  24. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts.map +1 -1
  25. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js +11 -0
  26. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js.map +1 -1
  27. package/dist/backend/backend/src/services/agent/fresh-task-conversation.service.d.ts +22 -0
  28. package/dist/backend/backend/src/services/agent/fresh-task-conversation.service.d.ts.map +1 -1
  29. package/dist/backend/backend/src/services/agent/fresh-task-conversation.service.js +39 -3
  30. package/dist/backend/backend/src/services/agent/fresh-task-conversation.service.js.map +1 -1
  31. package/dist/backend/backend/src/services/agent/in-process-turn-failure.service.d.ts +140 -0
  32. package/dist/backend/backend/src/services/agent/in-process-turn-failure.service.d.ts.map +1 -0
  33. package/dist/backend/backend/src/services/agent/in-process-turn-failure.service.js +267 -0
  34. package/dist/backend/backend/src/services/agent/in-process-turn-failure.service.js.map +1 -0
  35. package/dist/backend/backend/src/services/decisions/decision.service.d.ts +17 -0
  36. package/dist/backend/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  37. package/dist/backend/backend/src/services/decisions/decision.service.js +27 -2
  38. package/dist/backend/backend/src/services/decisions/decision.service.js.map +1 -1
  39. package/dist/backend/backend/src/services/decisions/decision.wiring.d.ts +2 -0
  40. package/dist/backend/backend/src/services/decisions/decision.wiring.d.ts.map +1 -1
  41. package/dist/backend/backend/src/services/decisions/decision.wiring.js +1 -0
  42. package/dist/backend/backend/src/services/decisions/decision.wiring.js.map +1 -1
  43. package/dist/backend/backend/src/services/messaging/drain-queued-delivery.d.ts +59 -0
  44. package/dist/backend/backend/src/services/messaging/drain-queued-delivery.d.ts.map +1 -0
  45. package/dist/backend/backend/src/services/messaging/drain-queued-delivery.js +70 -0
  46. package/dist/backend/backend/src/services/messaging/drain-queued-delivery.js.map +1 -0
  47. package/dist/backend/backend/src/services/messaging/owner-message-watchdog.service.d.ts +46 -1
  48. package/dist/backend/backend/src/services/messaging/owner-message-watchdog.service.d.ts.map +1 -1
  49. package/dist/backend/backend/src/services/messaging/owner-message-watchdog.service.js +109 -0
  50. package/dist/backend/backend/src/services/messaging/owner-message-watchdog.service.js.map +1 -1
  51. package/dist/backend/backend/src/services/messaging/owner-message-watchdog.wiring.d.ts +2 -0
  52. package/dist/backend/backend/src/services/messaging/owner-message-watchdog.wiring.d.ts.map +1 -1
  53. package/dist/backend/backend/src/services/messaging/owner-message-watchdog.wiring.js +6 -0
  54. package/dist/backend/backend/src/services/messaging/owner-message-watchdog.wiring.js.map +1 -1
  55. package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.d.ts +30 -1
  56. package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.d.ts.map +1 -1
  57. package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.js +28 -3
  58. package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.js.map +1 -1
  59. package/dist/backend/backend/src/services/monitoring/liveness-monitor.service.d.ts +142 -0
  60. package/dist/backend/backend/src/services/monitoring/liveness-monitor.service.d.ts.map +1 -0
  61. package/dist/backend/backend/src/services/monitoring/liveness-monitor.service.js +231 -0
  62. package/dist/backend/backend/src/services/monitoring/liveness-monitor.service.js.map +1 -0
  63. package/dist/backend/backend/src/services/open-items/open-items.service.d.ts +21 -0
  64. package/dist/backend/backend/src/services/open-items/open-items.service.d.ts.map +1 -1
  65. package/dist/backend/backend/src/services/open-items/open-items.service.js +72 -3
  66. package/dist/backend/backend/src/services/open-items/open-items.service.js.map +1 -1
  67. package/dist/backend/backend/src/services/orc/orc-chat-owner-mirror.d.ts +106 -0
  68. package/dist/backend/backend/src/services/orc/orc-chat-owner-mirror.d.ts.map +1 -0
  69. package/dist/backend/backend/src/services/orc/orc-chat-owner-mirror.js +199 -0
  70. package/dist/backend/backend/src/services/orc/orc-chat-owner-mirror.js.map +1 -0
  71. package/dist/backend/backend/src/services/slack/slack-agent-dm.service.d.ts +11 -2
  72. package/dist/backend/backend/src/services/slack/slack-agent-dm.service.d.ts.map +1 -1
  73. package/dist/backend/backend/src/services/slack/slack-agent-dm.service.js +22 -1
  74. package/dist/backend/backend/src/services/slack/slack-agent-dm.service.js.map +1 -1
  75. package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
  76. package/dist/backend/backend/src/services/slack/slack-initializer.js +30 -2
  77. package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
  78. package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.d.ts +8 -2
  79. package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.d.ts.map +1 -1
  80. package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.js +7 -4
  81. package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.js.map +1 -1
  82. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +41 -0
  83. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  84. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +394 -231
  85. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  86. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.d.ts +21 -5
  87. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.d.ts.map +1 -1
  88. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.js +21 -8
  89. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.js.map +1 -1
  90. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +1 -0
  91. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  92. package/dist/backend/backend/src/services/task-pool/task-pool.service.js +8 -3
  93. package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  94. package/dist/backend/build-info.json +2 -2
  95. package/dist/cli/backend/src/constants.d.ts +98 -0
  96. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  97. package/dist/cli/backend/src/constants.js +98 -0
  98. package/dist/cli/backend/src/constants.js.map +1 -1
  99. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  100. package/dist/cli/backend/src/services/agent/agent-registration.service.js +7 -0
  101. package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
  102. package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts.map +1 -1
  103. package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js +11 -0
  104. package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js.map +1 -1
  105. package/dist/cli/backend/src/services/agent/fresh-task-conversation.service.d.ts +22 -0
  106. package/dist/cli/backend/src/services/agent/fresh-task-conversation.service.d.ts.map +1 -1
  107. package/dist/cli/backend/src/services/agent/fresh-task-conversation.service.js +39 -3
  108. package/dist/cli/backend/src/services/agent/fresh-task-conversation.service.js.map +1 -1
  109. package/dist/cli/backend/src/services/agent/in-process-turn-failure.service.d.ts +140 -0
  110. package/dist/cli/backend/src/services/agent/in-process-turn-failure.service.d.ts.map +1 -0
  111. package/dist/cli/backend/src/services/agent/in-process-turn-failure.service.js +267 -0
  112. package/dist/cli/backend/src/services/agent/in-process-turn-failure.service.js.map +1 -0
  113. package/dist/cli/backend/src/services/decisions/decision.service.d.ts +17 -0
  114. package/dist/cli/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  115. package/dist/cli/backend/src/services/decisions/decision.service.js +27 -2
  116. package/dist/cli/backend/src/services/decisions/decision.service.js.map +1 -1
  117. package/dist/cli/backend/src/services/messaging/owner-message-watchdog.service.d.ts +46 -1
  118. package/dist/cli/backend/src/services/messaging/owner-message-watchdog.service.d.ts.map +1 -1
  119. package/dist/cli/backend/src/services/messaging/owner-message-watchdog.service.js +109 -0
  120. package/dist/cli/backend/src/services/messaging/owner-message-watchdog.service.js.map +1 -1
  121. package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.d.ts +30 -1
  122. package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.d.ts.map +1 -1
  123. package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.js +28 -3
  124. package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.js.map +1 -1
  125. package/dist/cli/backend/src/services/open-items/open-items.service.d.ts +21 -0
  126. package/dist/cli/backend/src/services/open-items/open-items.service.d.ts.map +1 -1
  127. package/dist/cli/backend/src/services/open-items/open-items.service.js +72 -3
  128. package/dist/cli/backend/src/services/open-items/open-items.service.js.map +1 -1
  129. package/dist/cli/backend/src/services/runtime-fallback/reset-time.d.ts +66 -0
  130. package/dist/cli/backend/src/services/runtime-fallback/reset-time.d.ts.map +1 -0
  131. package/dist/cli/backend/src/services/runtime-fallback/reset-time.js +211 -0
  132. package/dist/cli/backend/src/services/runtime-fallback/reset-time.js.map +1 -0
  133. package/dist/cli/backend/src/services/runtime-fallback/usage-limit-rules.d.ts +113 -0
  134. package/dist/cli/backend/src/services/runtime-fallback/usage-limit-rules.d.ts.map +1 -0
  135. package/dist/cli/backend/src/services/runtime-fallback/usage-limit-rules.js +257 -0
  136. package/dist/cli/backend/src/services/runtime-fallback/usage-limit-rules.js.map +1 -0
  137. package/dist/cli/backend/src/services/slack/slack-agent-dm.service.d.ts +11 -2
  138. package/dist/cli/backend/src/services/slack/slack-agent-dm.service.d.ts.map +1 -1
  139. package/dist/cli/backend/src/services/slack/slack-agent-dm.service.js +22 -1
  140. package/dist/cli/backend/src/services/slack/slack-agent-dm.service.js.map +1 -1
  141. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +41 -0
  142. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  143. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +394 -231
  144. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  145. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.d.ts +21 -5
  146. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.d.ts.map +1 -1
  147. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.js +21 -8
  148. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.js.map +1 -1
  149. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +1 -0
  150. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  151. package/dist/cli/backend/src/services/task-pool/task-pool.service.js +8 -3
  152. package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  153. package/package.json +1 -1
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Liveness monitor (crewly#1015 §12).
3
+ *
4
+ * On 2026-10-01 the backend on the Mac was silent from 09:50 to 14:39 ET:
5
+ * no shutdown line, no crash line, no alert. Every owner message in that
6
+ * window waited up to 4 h 46 min and nobody knew why.
7
+ *
8
+ * The monitor writes `<CREWLY_HOME>/liveness.json` every TICK_MS and finds
9
+ * two kinds of gap:
10
+ *
11
+ * - **stall**: two of its ticks more than GAP_ALERT_MS apart on the
12
+ * MONOTONIC clock — the event loop was blocked or the process was
13
+ * stopped. A gap only the wall clock shows is the computer sleeping
14
+ * (macOS's monotonic clock does not advance in sleep): logged, no DM —
15
+ * every lid-close would otherwise alarm the owner;
16
+ * - **crash**: the previous process ended through an uncaught exception or
17
+ * an unhandled rejection (recorded on its way out), however quickly it
18
+ * came back;
19
+ * - **unclean stop**: at boot, the previous process's last tick is more than
20
+ * GAP_ALERT_MS old and it never recorded a clean shutdown.
21
+ *
22
+ * Either is logged as an error and the owner is told once by Slack DM,
23
+ * retried every tick while Slack is not up yet (for at most
24
+ * ALERT_RETRY_MAX_MS). An alert while the machine is still down has to come
25
+ * from Cloud (heartbeats stop) — not something a stopped process can send.
26
+ *
27
+ * @module services/monitoring/liveness-monitor.service
28
+ * @see specs/2026-10-03-harness-drop-gaps.md §12
29
+ */
30
+ /** What `liveness.json` holds. */
31
+ export interface LivenessRecord {
32
+ /** Last tick of the process (epoch ms) */
33
+ lastAliveAt: number;
34
+ /** The process that wrote it */
35
+ pid: number;
36
+ /** When that process started (epoch ms) */
37
+ startedAt: number;
38
+ /** Set when it shut down on purpose (epoch ms) */
39
+ cleanShutdownAt?: number;
40
+ /** Set when it went down through a crash handler: when, and why */
41
+ crash?: {
42
+ at: number;
43
+ reason: string;
44
+ };
45
+ }
46
+ /** A gap to tell the owner about. */
47
+ export interface LivenessGap {
48
+ kind: 'stalled' | 'stopped' | 'crashed';
49
+ /** Crash reason (`crashed`) */
50
+ reason?: string;
51
+ /** Last sign of life before the gap (epoch ms) */
52
+ from: number;
53
+ /** First sign of life after it (epoch ms) */
54
+ to: number;
55
+ }
56
+ /** Injected behaviour. */
57
+ export interface LivenessMonitorDeps {
58
+ /** Path of `liveness.json` */
59
+ storePath: string;
60
+ /** DM the owner; true when delivered */
61
+ notifyOwner: (text: string) => Promise<boolean>;
62
+ /** This machine's name ("Mac", "steamfun-ops") */
63
+ machineName: () => string;
64
+ /** Clock (tests) */
65
+ now?: () => number;
66
+ /** Process id (tests) */
67
+ pid?: number;
68
+ /** Time zone for the times in the alert (tests); machine-local by default */
69
+ timeZone?: string;
70
+ /**
71
+ * Monotonic clock in ms (default `performance.now()`); on macOS it does
72
+ * not advance while the computer sleeps.
73
+ */
74
+ monoNow?: () => number;
75
+ }
76
+ /**
77
+ * The owner's alert for a gap, in plain words.
78
+ *
79
+ * @param gap - The gap
80
+ * @param machine - Machine name
81
+ * @param timeZone - Time zone for the times (default machine-local)
82
+ * @returns Alert text
83
+ */
84
+ export declare function livenessAlertText(gap: LivenessGap, machine: string, timeZone?: string): string;
85
+ /**
86
+ * "4 h 49 min" / "12 min".
87
+ *
88
+ * @param ms - Duration
89
+ * @returns Text
90
+ */
91
+ export declare function formatDuration(ms: number): string;
92
+ /**
93
+ * Watches for gaps in this process's life and tells the owner about them.
94
+ */
95
+ export declare class LivenessMonitorService {
96
+ private readonly deps;
97
+ private readonly logger;
98
+ private timer;
99
+ private lastTickAt;
100
+ private lastTickMono;
101
+ private readonly startedAt;
102
+ private pending;
103
+ private sending;
104
+ /**
105
+ * @param deps - Injected behaviour
106
+ */
107
+ constructor(deps: LivenessMonitorDeps);
108
+ private now;
109
+ private mono;
110
+ private get pid();
111
+ /**
112
+ * Check how the previous process ended, write the first record and start
113
+ * ticking (unref'd: never keeps the process alive).
114
+ */
115
+ start(): void;
116
+ /** Stop ticking. */
117
+ stop(): void;
118
+ /**
119
+ * One tick: detect a stall since the last tick, record that the process
120
+ * is alive, and (re)try a pending owner alert.
121
+ */
122
+ tick(): void;
123
+ /**
124
+ * Record that this process is shutting down on purpose, so the next boot
125
+ * does not report an unclean stop.
126
+ */
127
+ markCleanShutdown(): void;
128
+ /**
129
+ * Record that this process is going down through a crash handler, so the
130
+ * next boot tells the owner (crewly#1015 review B1).
131
+ *
132
+ * @param reason - `uncaughtException` / `unhandledRejection` (and the message)
133
+ */
134
+ markCrash(reason: string): void;
135
+ /** @returns The gap waiting to be told to the owner (tests / debugging) */
136
+ get pendingGap(): LivenessGap | null;
137
+ private raise;
138
+ private sendPending;
139
+ private read;
140
+ private write;
141
+ }
142
+ //# sourceMappingURL=liveness-monitor.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"liveness-monitor.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/monitoring/liveness-monitor.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAQH,kCAAkC;AAClC,MAAM,WAAW,cAAc;IAC9B,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,KAAK,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CACvC;AAED,qCAAqC;AACrC,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IACxC,+BAA+B;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,EAAE,EAAE,MAAM,CAAC;CACX;AAED,0BAA0B;AAC1B,MAAM,WAAW,mBAAmB;IACnC,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAChD,kDAAkD;IAClD,WAAW,EAAE,MAAM,MAAM,CAAC;IAC1B,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,yBAAyB;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAa9F;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAMjD;AAED;;GAEG;AACH,qBAAa,sBAAsB;IAYtB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,OAAO,CAA2D;IAC1E,OAAO,CAAC,OAAO,CAAS;IAExB;;OAEG;gBAC0B,IAAI,EAAE,mBAAmB;IAKtD,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,IAAI;IAIZ,OAAO,KAAK,GAAG,GAEd;IAED;;;OAGG;IACH,KAAK,IAAI,IAAI;IAmBb,oBAAoB;IACpB,IAAI,IAAI,IAAI;IAKZ;;;OAGG;IACH,IAAI,IAAI,IAAI;IAqBZ;;;OAGG;IACH,iBAAiB,IAAI,IAAI;IAMzB;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAM/B,2EAA2E;IAC3E,IAAI,UAAU,IAAI,WAAW,GAAG,IAAI,CAEnC;IAED,OAAO,CAAC,KAAK;YAaC,WAAW;IAqBzB,OAAO,CAAC,IAAI;IAYZ,OAAO,CAAC,KAAK;CAQb"}
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Liveness monitor (crewly#1015 §12).
3
+ *
4
+ * On 2026-10-01 the backend on the Mac was silent from 09:50 to 14:39 ET:
5
+ * no shutdown line, no crash line, no alert. Every owner message in that
6
+ * window waited up to 4 h 46 min and nobody knew why.
7
+ *
8
+ * The monitor writes `<CREWLY_HOME>/liveness.json` every TICK_MS and finds
9
+ * two kinds of gap:
10
+ *
11
+ * - **stall**: two of its ticks more than GAP_ALERT_MS apart on the
12
+ * MONOTONIC clock — the event loop was blocked or the process was
13
+ * stopped. A gap only the wall clock shows is the computer sleeping
14
+ * (macOS's monotonic clock does not advance in sleep): logged, no DM —
15
+ * every lid-close would otherwise alarm the owner;
16
+ * - **crash**: the previous process ended through an uncaught exception or
17
+ * an unhandled rejection (recorded on its way out), however quickly it
18
+ * came back;
19
+ * - **unclean stop**: at boot, the previous process's last tick is more than
20
+ * GAP_ALERT_MS old and it never recorded a clean shutdown.
21
+ *
22
+ * Either is logged as an error and the owner is told once by Slack DM,
23
+ * retried every tick while Slack is not up yet (for at most
24
+ * ALERT_RETRY_MAX_MS). An alert while the machine is still down has to come
25
+ * from Cloud (heartbeats stop) — not something a stopped process can send.
26
+ *
27
+ * @module services/monitoring/liveness-monitor.service
28
+ * @see specs/2026-10-03-harness-drop-gaps.md §12
29
+ */
30
+ import { existsSync, mkdirSync, readFileSync } from 'fs';
31
+ import * as path from 'path';
32
+ import { LIVENESS_MONITOR_CONSTANTS as C } from '../../constants.js';
33
+ import { LoggerService } from '../core/logger.service.js';
34
+ import { atomicWriteFileSync } from '../../utils/file-io.utils.js';
35
+ /**
36
+ * The owner's alert for a gap, in plain words.
37
+ *
38
+ * @param gap - The gap
39
+ * @param machine - Machine name
40
+ * @param timeZone - Time zone for the times (default machine-local)
41
+ * @returns Alert text
42
+ */
43
+ export function livenessAlertText(gap, machine, timeZone) {
44
+ const fmt = (ms) => new Date(ms).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit', ...(timeZone ? { timeZone } : {}) });
45
+ const dur = formatDuration(gap.to - gap.from);
46
+ const tail = ' Messages sent in that time were delayed; agents are picking them up now.';
47
+ if (gap.kind === 'stalled') {
48
+ return `⚠️ Crewly on ${machine} was stuck from ${fmt(gap.from)} to ${fmt(gap.to)} (${dur}) and did not handle anything in that time.${tail}`;
49
+ }
50
+ if (gap.kind === 'crashed') {
51
+ const why = gap.reason ? ` (${gap.reason})` : '';
52
+ return `⚠️ Crewly on ${machine} crashed at ${fmt(gap.from)}${why} and was back at ${fmt(gap.to)}.${tail}`;
53
+ }
54
+ return `⚠️ Crewly on ${machine} stopped without shutting down at ${fmt(gap.from)} and was back at ${fmt(gap.to)} (${dur} offline).${tail}`;
55
+ }
56
+ /**
57
+ * "4 h 49 min" / "12 min".
58
+ *
59
+ * @param ms - Duration
60
+ * @returns Text
61
+ */
62
+ export function formatDuration(ms) {
63
+ const minutes = Math.max(1, Math.round(ms / 60000));
64
+ const h = Math.floor(minutes / 60);
65
+ const m = minutes % 60;
66
+ if (h === 0)
67
+ return `${m} min`;
68
+ return m === 0 ? `${h} h` : `${h} h ${m} min`;
69
+ }
70
+ /**
71
+ * Watches for gaps in this process's life and tells the owner about them.
72
+ */
73
+ export class LivenessMonitorService {
74
+ deps;
75
+ logger;
76
+ timer = null;
77
+ lastTickAt = null;
78
+ lastTickMono = null;
79
+ startedAt;
80
+ pending = null;
81
+ sending = false;
82
+ /**
83
+ * @param deps - Injected behaviour
84
+ */
85
+ constructor(deps) {
86
+ this.deps = deps;
87
+ this.logger = LoggerService.getInstance().createComponentLogger('LivenessMonitor');
88
+ this.startedAt = this.now();
89
+ }
90
+ now() {
91
+ return this.deps.now ? this.deps.now() : Date.now();
92
+ }
93
+ mono() {
94
+ return this.deps.monoNow ? this.deps.monoNow() : performance.now();
95
+ }
96
+ get pid() {
97
+ return this.deps.pid ?? process.pid;
98
+ }
99
+ /**
100
+ * Check how the previous process ended, write the first record and start
101
+ * ticking (unref'd: never keeps the process alive).
102
+ */
103
+ start() {
104
+ const previous = this.read();
105
+ const now = this.now();
106
+ if (previous && previous.pid !== this.pid && previous.crash) {
107
+ this.raise({ kind: 'crashed', from: previous.crash.at, to: now, reason: previous.crash.reason });
108
+ }
109
+ else if (previous &&
110
+ previous.pid !== this.pid &&
111
+ !(previous.cleanShutdownAt !== undefined && previous.cleanShutdownAt >= previous.lastAliveAt) &&
112
+ now - previous.lastAliveAt > C.GAP_ALERT_MS) {
113
+ this.raise({ kind: 'stopped', from: previous.lastAliveAt, to: now });
114
+ }
115
+ this.tick();
116
+ if (this.timer)
117
+ return;
118
+ this.timer = setInterval(() => this.tick(), C.TICK_MS);
119
+ this.timer.unref?.();
120
+ }
121
+ /** Stop ticking. */
122
+ stop() {
123
+ if (this.timer)
124
+ clearInterval(this.timer);
125
+ this.timer = null;
126
+ }
127
+ /**
128
+ * One tick: detect a stall since the last tick, record that the process
129
+ * is alive, and (re)try a pending owner alert.
130
+ */
131
+ tick() {
132
+ const now = this.now();
133
+ const mono = this.mono();
134
+ if (this.lastTickAt !== null && this.lastTickMono !== null && now - this.lastTickAt > C.GAP_ALERT_MS) {
135
+ if (mono - this.lastTickMono > C.GAP_ALERT_MS) {
136
+ this.raise({ kind: 'stalled', from: this.lastTickAt, to: now });
137
+ }
138
+ else {
139
+ // Wall clock jumped, monotonic did not: the computer slept. Not an outage of Crewly's making.
140
+ this.logger.info('The computer was asleep — no ticks for a while (not alerting)', {
141
+ from: new Date(this.lastTickAt).toISOString(),
142
+ to: new Date(now).toISOString(),
143
+ minutes: Math.round((now - this.lastTickAt) / 60000),
144
+ });
145
+ }
146
+ }
147
+ this.lastTickAt = now;
148
+ this.lastTickMono = mono;
149
+ this.write({ lastAliveAt: now, pid: this.pid, startedAt: this.startedAt });
150
+ void this.sendPending();
151
+ }
152
+ /**
153
+ * Record that this process is shutting down on purpose, so the next boot
154
+ * does not report an unclean stop.
155
+ */
156
+ markCleanShutdown() {
157
+ const now = this.now();
158
+ this.write({ lastAliveAt: now, pid: this.pid, startedAt: this.startedAt, cleanShutdownAt: now });
159
+ this.stop();
160
+ }
161
+ /**
162
+ * Record that this process is going down through a crash handler, so the
163
+ * next boot tells the owner (crewly#1015 review B1).
164
+ *
165
+ * @param reason - `uncaughtException` / `unhandledRejection` (and the message)
166
+ */
167
+ markCrash(reason) {
168
+ const now = this.now();
169
+ this.write({ lastAliveAt: now, pid: this.pid, startedAt: this.startedAt, crash: { at: now, reason: reason.slice(0, 160) } });
170
+ this.stop();
171
+ }
172
+ /** @returns The gap waiting to be told to the owner (tests / debugging) */
173
+ get pendingGap() {
174
+ return this.pending?.gap ?? null;
175
+ }
176
+ raise(gap) {
177
+ this.logger.error(gap.kind === 'stalled'
178
+ ? 'Crewly was stuck for a while (not asleep: the monotonic clock advanced) — telling the owner'
179
+ : gap.kind === 'crashed'
180
+ ? 'The previous Crewly process crashed — telling the owner'
181
+ : 'The previous Crewly process stopped without a clean shutdown — telling the owner', { kind: gap.kind, from: new Date(gap.from).toISOString(), to: new Date(gap.to).toISOString(), minutes: Math.round((gap.to - gap.from) / 60000) });
182
+ // A newer gap replaces one not yet told: the owner hears the latest.
183
+ this.pending = { gap, firstTriedAt: this.now() };
184
+ }
185
+ async sendPending() {
186
+ if (!this.pending || this.sending)
187
+ return;
188
+ const pending = this.pending;
189
+ if (this.now() - pending.firstTriedAt > C.ALERT_RETRY_MAX_MS) {
190
+ this.logger.warn('Could not tell the owner about a Crewly outage (Slack never came up) — giving up', { kind: pending.gap.kind });
191
+ this.pending = null;
192
+ return;
193
+ }
194
+ this.sending = true;
195
+ try {
196
+ const text = livenessAlertText(pending.gap, this.deps.machineName(), this.deps.timeZone);
197
+ const sent = await this.deps.notifyOwner(text).catch(() => false);
198
+ if (sent && this.pending === pending) {
199
+ this.pending = null;
200
+ this.logger.info('Owner told about a Crewly outage', { kind: pending.gap.kind });
201
+ }
202
+ }
203
+ finally {
204
+ this.sending = false;
205
+ }
206
+ }
207
+ read() {
208
+ try {
209
+ if (!existsSync(this.deps.storePath))
210
+ return null;
211
+ const raw = JSON.parse(readFileSync(this.deps.storePath, 'utf8'));
212
+ if (typeof raw.lastAliveAt !== 'number' || typeof raw.pid !== 'number')
213
+ return null;
214
+ return raw;
215
+ }
216
+ catch (err) {
217
+ this.logger.warn('Could not read the liveness record', { error: err instanceof Error ? err.message : String(err) });
218
+ return null;
219
+ }
220
+ }
221
+ write(record) {
222
+ try {
223
+ mkdirSync(path.dirname(this.deps.storePath), { recursive: true });
224
+ atomicWriteFileSync(this.deps.storePath, JSON.stringify(record));
225
+ }
226
+ catch (err) {
227
+ this.logger.debug('Could not write the liveness record', { error: err instanceof Error ? err.message : String(err) });
228
+ }
229
+ }
230
+ }
231
+ //# sourceMappingURL=liveness-monitor.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"liveness-monitor.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/monitoring/liveness-monitor.service.ts"],"names":[],"mappings":"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"}
@@ -488,6 +488,27 @@ export declare class OpenItemsService {
488
488
  expired: number;
489
489
  closed: number;
490
490
  }>;
491
+ /**
492
+ * Promises this sweep closed without delivery are told to the owner once,
493
+ * in one note per request:
494
+ * - the follow-up WorkItem was cancelled by ANOTHER agent (an agent's
495
+ * cleanup, a bulk script) — the cancel API records who
496
+ * (`metadata.cancelledBy`);
497
+ * - nothing happened for 7 days and the owner was never told it was late.
498
+ *
499
+ * The promising agent cancelling its own follow-up, owner skips,
500
+ * superseded promises and cancelled tickets were closed on purpose and
501
+ * stay quiet. Before this, ~60 tracked promises were cancelled in one
502
+ * cleanup and Owen's two article links were never sent (crewly#1015 §10).
503
+ *
504
+ * @param request - Request
505
+ * @param previous - Its items before this sweep
506
+ * @param items - Its items after this sweep (same order)
507
+ * @param pool - Every WorkItem
508
+ * @param now - Clock
509
+ * @returns The items, with `ownerNotifiedAt` set on the ones the owner was told about
510
+ */
511
+ private tellOwnerAboutDroppedPromises;
491
512
  /**
492
513
  * One item's sweep step.
493
514
  */
@@ -1 +1 @@
1
- {"version":3,"file":"open-items.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/open-items/open-items.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"open-items.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/open-items/open-items.service.ts"],"names":[],"mappings":"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"}
@@ -30,7 +30,7 @@
30
30
  *
31
31
  * @module services/open-items/open-items.service
32
32
  */
33
- import { OPEN_ITEMS_CONSTANTS, REPLY_ROUTING_CONSTANTS } from '../../constants.js';
33
+ import { OPEN_ITEMS_CONSTANTS, ORCHESTRATOR_SESSION_NAME, REPLY_ROUTING_CONSTANTS } from '../../constants.js';
34
34
  import { isInterim } from '../slack/slack-typing-placeholder.service.js';
35
35
  import { LoggerService } from '../core/logger.service.js';
36
36
  import { ACTIVE_OPEN_ITEM_STATUSES } from '../../types/v2/open-item.types.js';
@@ -1078,9 +1078,10 @@ export class OpenItemsService {
1078
1078
  for (const request of tracked) {
1079
1079
  try {
1080
1080
  const before = JSON.stringify(request.openItems);
1081
- const items = [];
1081
+ let items = [];
1082
1082
  for (const item of request.openItems ?? [])
1083
1083
  items.push(await this.sweepItem(request, item, now, pool, counts));
1084
+ items = await this.tellOwnerAboutDroppedPromises(request, request.openItems ?? [], items, pool, now);
1084
1085
  const changed = JSON.stringify(items) !== before;
1085
1086
  const allClosed = !items.some((i) => ACTIVE_OPEN_ITEM_STATUSES.has(i.status));
1086
1087
  if (changed || (request.status === 'awaiting_followup' && allClosed))
@@ -1096,6 +1097,74 @@ export class OpenItemsService {
1096
1097
  return counts;
1097
1098
  });
1098
1099
  }
1100
+ /**
1101
+ * Promises this sweep closed without delivery are told to the owner once,
1102
+ * in one note per request:
1103
+ * - the follow-up WorkItem was cancelled by ANOTHER agent (an agent's
1104
+ * cleanup, a bulk script) — the cancel API records who
1105
+ * (`metadata.cancelledBy`);
1106
+ * - nothing happened for 7 days and the owner was never told it was late.
1107
+ *
1108
+ * The promising agent cancelling its own follow-up, owner skips,
1109
+ * superseded promises and cancelled tickets were closed on purpose and
1110
+ * stay quiet. Before this, ~60 tracked promises were cancelled in one
1111
+ * cleanup and Owen's two article links were never sent (crewly#1015 §10).
1112
+ *
1113
+ * @param request - Request
1114
+ * @param previous - Its items before this sweep
1115
+ * @param items - Its items after this sweep (same order)
1116
+ * @param pool - Every WorkItem
1117
+ * @param now - Clock
1118
+ * @returns The items, with `ownerNotifiedAt` set on the ones the owner was told about
1119
+ */
1120
+ async tellOwnerAboutDroppedPromises(request, previous, items, pool, now) {
1121
+ const cancelledByOther = (item) => {
1122
+ if (item.status !== 'cancelled' || !item.workItemId)
1123
+ return null;
1124
+ const fu = pool.find((w) => w.id === item.workItemId);
1125
+ if (!fu || fu.status !== 'cancelled')
1126
+ return null;
1127
+ const by = (fu.metadata ?? {})[OPEN_ITEMS_CONSTANTS.CANCELLED_BY_METADATA_KEY];
1128
+ // The orchestrator cancels follow-ups when the owner asks it to: the
1129
+ // owner knows (crewly#1015 review).
1130
+ if (by === ORCHESTRATOR_SESSION_NAME)
1131
+ return null;
1132
+ return typeof by === 'string' && by && by !== item.agent ? by : null;
1133
+ };
1134
+ const dropped = items.filter((item, i) => {
1135
+ const was = previous[i];
1136
+ if (!was || was.id !== item.id || item.type !== 'commitment')
1137
+ return false;
1138
+ if (!ACTIVE_OPEN_ITEM_STATUSES.has(was.status))
1139
+ return false;
1140
+ const expiredUntold = item.status === 'expired' && !was.ownerNotifiedAt;
1141
+ return expiredUntold || cancelledByOther(item) !== null;
1142
+ });
1143
+ if (dropped.length === 0)
1144
+ return items;
1145
+ const lines = [];
1146
+ for (const item of dropped) {
1147
+ const name = (await this.deps.displayName?.(item.agent).catch(() => undefined)) ?? item.agent;
1148
+ const by = cancelledByOther(item);
1149
+ const byName = by ? ((await this.deps.displayName?.(by).catch(() => undefined)) ?? by) : '';
1150
+ const reason = item.closedReason?.includes(': ') ? short(item.closedReason.slice(item.closedReason.indexOf(': ') + 2), 100) : '';
1151
+ const why = item.status === 'expired'
1152
+ ? 'nothing happened on it for 7 days'
1153
+ : `${byName} cancelled its follow-up${reason ? ` ("${reason}")` : ''}`;
1154
+ lines.push(`${name}'s promise "${short(item.text, 160)}" was closed without being delivered: ${why}. If you still want it, ask ${name} again.`);
1155
+ }
1156
+ const ok = await this.deps.postOwnerNote(request, lines.join('\n')).catch(() => false);
1157
+ this.logger.warn('Promises closed undelivered — owner told in the thread', {
1158
+ tkt: ticketLabel(request),
1159
+ items: dropped.map((i) => i.id),
1160
+ posted: ok,
1161
+ });
1162
+ if (!ok)
1163
+ return items;
1164
+ const nowIso = now.toISOString();
1165
+ const told = new Set(dropped.map((i) => i.id));
1166
+ return items.map((i) => (told.has(i.id) ? { ...i, ownerNotifiedAt: nowIso } : i));
1167
+ }
1099
1168
  /**
1100
1169
  * One item's sweep step.
1101
1170
  */
@@ -1153,7 +1222,7 @@ export class OpenItemsService {
1153
1222
  ...item,
1154
1223
  status: delivered ? 'delivered' : 'cancelled',
1155
1224
  closedAt: nowIso,
1156
- closedReason: `follow-up ${followUp.id.slice(0, 8)} is ${followUp.status}`,
1225
+ closedReason: `follow-up ${followUp.id.slice(0, 8)} is ${followUp.status}${!delivered && followUp.cancelReason ? `: ${short(followUp.cancelReason, 160)}` : ''}`,
1157
1226
  };
1158
1227
  }
1159
1228
  // Commitment: link child work started after the promise.