crewly 1.20.278 → 1.20.280

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 (138) hide show
  1. package/config/skills/agent/core/reply/SKILL.md +3 -0
  2. package/config/skills/agent/core/reply/execute.sh +23 -1
  3. package/config/skills/agent/core/reply/execute.test.sh +11 -0
  4. package/dist/backend/backend/src/constants.d.ts +115 -0
  5. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  6. package/dist/backend/backend/src/constants.js +115 -0
  7. package/dist/backend/backend/src/constants.js.map +1 -1
  8. package/dist/backend/backend/src/controllers/chat/agent-reply.controller.d.ts +10 -0
  9. package/dist/backend/backend/src/controllers/chat/agent-reply.controller.d.ts.map +1 -1
  10. package/dist/backend/backend/src/controllers/chat/agent-reply.controller.js +25 -0
  11. package/dist/backend/backend/src/controllers/chat/agent-reply.controller.js.map +1 -1
  12. package/dist/backend/backend/src/index.d.ts.map +1 -1
  13. package/dist/backend/backend/src/index.js +207 -3
  14. package/dist/backend/backend/src/index.js.map +1 -1
  15. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  16. package/dist/backend/backend/src/services/agent/agent-registration.service.js +4 -2
  17. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  18. package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts.map +1 -1
  19. package/dist/backend/backend/src/services/agent/idle-detection.service.js +7 -0
  20. package/dist/backend/backend/src/services/agent/idle-detection.service.js.map +1 -1
  21. package/dist/backend/backend/src/services/agent/resource-mode.service.d.ts.map +1 -1
  22. package/dist/backend/backend/src/services/agent/resource-mode.service.js +4 -1
  23. package/dist/backend/backend/src/services/agent/resource-mode.service.js.map +1 -1
  24. package/dist/backend/backend/src/services/briefing/briefing-cards.d.ts +99 -0
  25. package/dist/backend/backend/src/services/briefing/briefing-cards.d.ts.map +1 -0
  26. package/dist/backend/backend/src/services/briefing/briefing-cards.js +156 -0
  27. package/dist/backend/backend/src/services/briefing/briefing-cards.js.map +1 -0
  28. package/dist/backend/backend/src/services/briefing/briefing.service.d.ts +8 -0
  29. package/dist/backend/backend/src/services/briefing/briefing.service.d.ts.map +1 -1
  30. package/dist/backend/backend/src/services/briefing/briefing.service.js +31 -9
  31. package/dist/backend/backend/src/services/briefing/briefing.service.js.map +1 -1
  32. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts +33 -1
  33. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
  34. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js +33 -0
  35. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
  36. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts +21 -0
  37. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts.map +1 -1
  38. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js +37 -0
  39. package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js.map +1 -1
  40. package/dist/backend/backend/src/services/cloud/cloud-talk-inbound.service.d.ts.map +1 -1
  41. package/dist/backend/backend/src/services/cloud/cloud-talk-inbound.service.js +4 -0
  42. package/dist/backend/backend/src/services/cloud/cloud-talk-inbound.service.js.map +1 -1
  43. package/dist/backend/backend/src/services/drive/drive-agent.service.d.ts +244 -0
  44. package/dist/backend/backend/src/services/drive/drive-agent.service.d.ts.map +1 -0
  45. package/dist/backend/backend/src/services/drive/drive-agent.service.js +444 -0
  46. package/dist/backend/backend/src/services/drive/drive-agent.service.js.map +1 -0
  47. package/dist/backend/backend/src/services/drive/drive-briefing-snapshot.d.ts +172 -0
  48. package/dist/backend/backend/src/services/drive/drive-briefing-snapshot.d.ts.map +1 -0
  49. package/dist/backend/backend/src/services/drive/drive-briefing-snapshot.js +301 -0
  50. package/dist/backend/backend/src/services/drive/drive-briefing-snapshot.js.map +1 -0
  51. package/dist/backend/backend/src/services/drive/drive-briefing-sync.service.d.ts +112 -0
  52. package/dist/backend/backend/src/services/drive/drive-briefing-sync.service.d.ts.map +1 -0
  53. package/dist/backend/backend/src/services/drive/drive-briefing-sync.service.js +247 -0
  54. package/dist/backend/backend/src/services/drive/drive-briefing-sync.service.js.map +1 -0
  55. package/dist/backend/backend/src/services/drive/drive-briefing.contract.d.ts +88 -0
  56. package/dist/backend/backend/src/services/drive/drive-briefing.contract.d.ts.map +1 -0
  57. package/dist/backend/backend/src/services/drive/drive-briefing.contract.js +18 -0
  58. package/dist/backend/backend/src/services/drive/drive-briefing.contract.js.map +1 -0
  59. package/dist/backend/backend/src/services/drive/drive-briefing.wiring.d.ts +73 -0
  60. package/dist/backend/backend/src/services/drive/drive-briefing.wiring.d.ts.map +1 -0
  61. package/dist/backend/backend/src/services/drive/drive-briefing.wiring.js +131 -0
  62. package/dist/backend/backend/src/services/drive/drive-briefing.wiring.js.map +1 -0
  63. package/dist/backend/backend/src/services/drive/drive-cloud.contract.d.ts +99 -0
  64. package/dist/backend/backend/src/services/drive/drive-cloud.contract.d.ts.map +1 -0
  65. package/dist/backend/backend/src/services/drive/drive-cloud.contract.js +118 -0
  66. package/dist/backend/backend/src/services/drive/drive-cloud.contract.js.map +1 -0
  67. package/dist/backend/backend/src/services/drive/drive-conversation.store.d.ts +86 -0
  68. package/dist/backend/backend/src/services/drive/drive-conversation.store.d.ts.map +1 -0
  69. package/dist/backend/backend/src/services/drive/drive-conversation.store.js +102 -0
  70. package/dist/backend/backend/src/services/drive/drive-conversation.store.js.map +1 -0
  71. package/dist/backend/backend/src/services/drive/drive-keep-warm.d.ts +72 -0
  72. package/dist/backend/backend/src/services/drive/drive-keep-warm.d.ts.map +1 -0
  73. package/dist/backend/backend/src/services/drive/drive-keep-warm.js +109 -0
  74. package/dist/backend/backend/src/services/drive/drive-keep-warm.js.map +1 -0
  75. package/dist/backend/backend/src/services/drive/drive-recall.d.ts +65 -0
  76. package/dist/backend/backend/src/services/drive/drive-recall.d.ts.map +1 -0
  77. package/dist/backend/backend/src/services/drive/drive-recall.js +91 -0
  78. package/dist/backend/backend/src/services/drive/drive-recall.js.map +1 -0
  79. package/dist/backend/backend/src/services/drive/drive-row.utils.d.ts +19 -0
  80. package/dist/backend/backend/src/services/drive/drive-row.utils.d.ts.map +1 -0
  81. package/dist/backend/backend/src/services/drive/drive-row.utils.js +20 -0
  82. package/dist/backend/backend/src/services/drive/drive-row.utils.js.map +1 -0
  83. package/dist/backend/backend/src/services/messaging/owner-hook-message.d.ts.map +1 -1
  84. package/dist/backend/backend/src/services/messaging/owner-hook-message.js +11 -5
  85. package/dist/backend/backend/src/services/messaging/owner-hook-message.js.map +1 -1
  86. package/dist/backend/backend/src/services/open-items/open-items.service.d.ts.map +1 -1
  87. package/dist/backend/backend/src/services/open-items/open-items.service.js +14 -2
  88. package/dist/backend/backend/src/services/open-items/open-items.service.js.map +1 -1
  89. package/dist/backend/backend/src/services/slack/slack-agent-dm.service.d.ts.map +1 -1
  90. package/dist/backend/backend/src/services/slack/slack-agent-dm.service.js +4 -0
  91. package/dist/backend/backend/src/services/slack/slack-agent-dm.service.js.map +1 -1
  92. package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
  93. package/dist/backend/backend/src/services/slack/slack-initializer.js +2 -1
  94. package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
  95. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  96. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +7 -1
  97. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  98. package/dist/backend/backend/src/services/talk/talk-live-token.service.d.ts +23 -7
  99. package/dist/backend/backend/src/services/talk/talk-live-token.service.d.ts.map +1 -1
  100. package/dist/backend/backend/src/services/talk/talk-live-token.service.js +78 -40
  101. package/dist/backend/backend/src/services/talk/talk-live-token.service.js.map +1 -1
  102. package/dist/backend/build-info.json +2 -2
  103. package/dist/cli/backend/src/constants.d.ts +115 -0
  104. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  105. package/dist/cli/backend/src/constants.js +115 -0
  106. package/dist/cli/backend/src/constants.js.map +1 -1
  107. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  108. package/dist/cli/backend/src/services/agent/agent-registration.service.js +4 -2
  109. package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
  110. package/dist/cli/backend/src/services/agent/resource-mode.service.d.ts.map +1 -1
  111. package/dist/cli/backend/src/services/agent/resource-mode.service.js +4 -1
  112. package/dist/cli/backend/src/services/agent/resource-mode.service.js.map +1 -1
  113. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts +33 -1
  114. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
  115. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js +33 -0
  116. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
  117. package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.d.ts +21 -0
  118. package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.d.ts.map +1 -1
  119. package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.js +37 -0
  120. package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.js.map +1 -1
  121. package/dist/cli/backend/src/services/drive/drive-keep-warm.d.ts +72 -0
  122. package/dist/cli/backend/src/services/drive/drive-keep-warm.d.ts.map +1 -0
  123. package/dist/cli/backend/src/services/drive/drive-keep-warm.js +109 -0
  124. package/dist/cli/backend/src/services/drive/drive-keep-warm.js.map +1 -0
  125. package/dist/cli/backend/src/services/drive/drive-row.utils.d.ts +19 -0
  126. package/dist/cli/backend/src/services/drive/drive-row.utils.d.ts.map +1 -0
  127. package/dist/cli/backend/src/services/drive/drive-row.utils.js +20 -0
  128. package/dist/cli/backend/src/services/drive/drive-row.utils.js.map +1 -0
  129. package/dist/cli/backend/src/services/open-items/open-items.service.d.ts.map +1 -1
  130. package/dist/cli/backend/src/services/open-items/open-items.service.js +14 -2
  131. package/dist/cli/backend/src/services/open-items/open-items.service.js.map +1 -1
  132. package/dist/cli/backend/src/services/slack/slack-agent-dm.service.d.ts.map +1 -1
  133. package/dist/cli/backend/src/services/slack/slack-agent-dm.service.js +4 -0
  134. package/dist/cli/backend/src/services/slack/slack-agent-dm.service.js.map +1 -1
  135. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  136. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +7 -1
  137. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  138. package/package.json +1 -1
@@ -0,0 +1,444 @@
1
+ /**
2
+ * DriveAgentService — the machine side of Drive mode
3
+ * (specs/2026-10-08-drive-mode.md §7). Crewly Cloud hosts the session (the
4
+ * voice, the targets, the inbox); this machine does what only it can:
5
+ *
6
+ * - `deliver` (push `op:'deliver'`): fetch the owner's words from Cloud with
7
+ * this machine's token, record them as the owner's voice turn in the
8
+ * agent's DM (a team → its lead's DM; a channel → a thread in its room)
9
+ * and deliver them, with a note: the owner is listening, answer with
10
+ * `reply --drive <session>`, not in Slack;
11
+ * - `agentReply` (`reply --drive`): record the answer in that conversation
12
+ * (kept off Slack) and send it to Cloud for the phone;
13
+ * - `noteChatTurn`: a plain `reply` in a Drive conversation still reaches
14
+ * the phone;
15
+ * - `recall` (push `op:'recall'`): read back an agent's recent messages to
16
+ * the owner;
17
+ * - `end` (push `op:'end'`): ask each agent with an open conversation for
18
+ * ONE recap; the recap (`reply --drive … --recap`) is posted in the
19
+ * owner's DM with the agent, or in the channel, and closes the
20
+ * conversation: owner-message tracking stops, and the open-items tracker
21
+ * takes it as delivered unless it names a next step.
22
+ * - `warm` (push `op:'warm'`, v3): read which agents to keep warm from
23
+ * Cloud, record them in the keep-warm registry (idle stop and slot freeing
24
+ * leave them alone) and pre-start the ones that are stopped;
25
+ * - two-phase answers (v3): the delivered words ask for `reply --drive
26
+ * --ack` within seconds, then the full result.
27
+ *
28
+ * Nothing the owner or the agents say is logged.
29
+ *
30
+ * @module services/drive/drive-agent.service
31
+ */
32
+ import { DRIVE_CONSTANTS } from '../../constants.js';
33
+ import { LoggerService } from '../core/logger.service.js';
34
+ import { clip, speakable } from '../briefing/briefing.utils.js';
35
+ import { isDriveModeRow } from './drive-row.utils.js';
36
+ import { messagesToOwner, pickRecall } from './drive-recall.js';
37
+ import { parseDeliveryFetch, parseDriveRelayData, parseRecallFetch, parseStateFetch, } from './drive-cloud.contract.js';
38
+ import { getDriveKeepWarm } from './drive-keep-warm.js';
39
+ const C = DRIVE_CONSTANTS;
40
+ /** A recap that says nothing is left to do (the conversation is fully closed). */
41
+ const NOTHING_PENDING = /nothing (is )?(pending|left|open)|no next step|next:\s*(none|nothing)\b|没有待办|无待办|没有后续|无后续|不需要后续|没有了/i;
42
+ /** A Drive mode failure with an HTTP status (for the agent's `reply`). */
43
+ export class DriveAgentError extends Error {
44
+ status;
45
+ constructor(status, message) {
46
+ super(message);
47
+ this.status = status;
48
+ this.name = 'DriveAgentError';
49
+ }
50
+ }
51
+ let active = false;
52
+ /**
53
+ * Capabilities to advertise to Cloud (heartbeat and uploads).
54
+ *
55
+ * @returns `['drive_message']` while the handler runs, else `[]`
56
+ */
57
+ export function driveCapabilities() {
58
+ return active ? [C.CAPABILITY] : [];
59
+ }
60
+ /** The machine side of Drive mode — see module docs. */
61
+ export class DriveAgentService {
62
+ deps;
63
+ logger;
64
+ now;
65
+ fetchImpl;
66
+ inFlight = new Set();
67
+ listener = (msg) => {
68
+ void this.handle(msg);
69
+ };
70
+ started = false;
71
+ /**
72
+ * @param deps - Collaborators
73
+ */
74
+ constructor(deps) {
75
+ this.deps = deps;
76
+ this.logger = deps.logger ?? LoggerService.getInstance().createComponentLogger('DriveAgent');
77
+ this.now = deps.now ?? (() => new Date());
78
+ this.fetchImpl = deps.fetchImpl ?? ((input, init) => fetch(input, init));
79
+ }
80
+ /** Subscribe to relay messages and advertise the capability. Idempotent. */
81
+ start() {
82
+ if (this.started)
83
+ return;
84
+ this.started = true;
85
+ this.deps.source?.on('message', this.listener);
86
+ active = true;
87
+ }
88
+ /** Unsubscribe. */
89
+ stop() {
90
+ if (!this.started)
91
+ return;
92
+ this.started = false;
93
+ this.deps.source?.off('message', this.listener);
94
+ active = false;
95
+ }
96
+ /**
97
+ * One relay message; anything but a Drive mode push is ignored. Never throws.
98
+ *
99
+ * @param msg - Relay message
100
+ * @returns What was done
101
+ */
102
+ async handle(msg) {
103
+ if (msg?.type !== C.RELAY_MESSAGE_TYPE)
104
+ return 'ignored';
105
+ const data = parseDriveRelayData(msg.payload);
106
+ if (!data)
107
+ return 'ignored';
108
+ const key = `${data.op}:${data.sessionId}:${data.id ?? ''}`;
109
+ if (this.inFlight.has(key))
110
+ return 'duplicate';
111
+ this.inFlight.add(key);
112
+ try {
113
+ const { instanceId } = await this.deps.identity();
114
+ if (data.instanceId !== instanceId)
115
+ return 'ignored';
116
+ if (data.op === 'deliver')
117
+ await this.deliver(data, instanceId);
118
+ else if (data.op === 'recall')
119
+ await this.recall(data, instanceId);
120
+ else if (data.op === 'warm')
121
+ await this.warm(data, instanceId);
122
+ else
123
+ await this.end(data, instanceId);
124
+ return 'done';
125
+ }
126
+ catch (error) {
127
+ this.logger.warn('Drive mode push failed', { op: data.op, sessionId: data.sessionId, error: error instanceof Error ? error.message : String(error) });
128
+ return 'failed';
129
+ }
130
+ finally {
131
+ this.inFlight.delete(key);
132
+ }
133
+ }
134
+ // ---------------------------------------------------------------------------
135
+ // Owner → agent
136
+ // ---------------------------------------------------------------------------
137
+ async deliver(data, instanceId) {
138
+ const raw = await this.cloudGet(`/${data.sessionId}/machine/deliveries/${encodeURIComponent(data.id)}?instanceId=${encodeURIComponent(instanceId)}`);
139
+ const d = parseDeliveryFetch(raw);
140
+ if (!d)
141
+ throw new Error('Cloud sent a malformed delivery');
142
+ await this.deliverFetched(d);
143
+ }
144
+ /**
145
+ * Deliver the owner's words (already fetched from Cloud).
146
+ *
147
+ * @param d - Delivery
148
+ * @returns The local conversation
149
+ */
150
+ async deliverFetched(d) {
151
+ if (this.deps.agentExists && !(await this.deps.agentExists(d.target.agentSession))) {
152
+ throw new Error(`No agent ${d.target.agentSession} on this machine`);
153
+ }
154
+ const existing = await this.deps.store.get(d.sessionId, d.conversationId);
155
+ const where = await this.deps.deliverOwnerTurn({
156
+ kind: d.target.kind,
157
+ agentSession: d.target.agentSession,
158
+ ...(d.target.slackChannelId ? { slackChannelId: d.target.slackChannelId } : {}),
159
+ ...(existing ? { channelId: existing.channelId, ...(existing.threadId ? { threadId: existing.threadId } : {}) } : {}),
160
+ text: ownerTurnText(d.sessionId, d.target.kind, d.target.name, d.text),
161
+ });
162
+ const at = this.now().toISOString();
163
+ const conv = await this.deps.store.update(d.sessionId, d.conversationId, (cur) => {
164
+ const c = cur ?? {
165
+ sessionId: d.sessionId,
166
+ conversationId: d.conversationId,
167
+ kind: d.target.kind,
168
+ targetName: d.target.name,
169
+ agentSession: d.target.agentSession,
170
+ ...(d.target.members ? { members: d.target.members } : {}),
171
+ channelId: where.channelId,
172
+ ...(where.threadId ? { threadId: where.threadId } : {}),
173
+ startedAt: at,
174
+ turns: [],
175
+ };
176
+ if (!c.threadId && where.threadId)
177
+ c.threadId = where.threadId;
178
+ c.turns.push({ from: 'owner', name: 'owner', text: d.text, at });
179
+ return c;
180
+ }, this.now().getTime());
181
+ this.logger.info('Drive mode: owner words delivered', { sessionId: d.sessionId, conversationId: d.conversationId, kind: d.target.kind, agent: d.target.agentSession });
182
+ return conv;
183
+ }
184
+ // ---------------------------------------------------------------------------
185
+ // Agent → owner
186
+ // ---------------------------------------------------------------------------
187
+ /**
188
+ * An agent's answer for the owner's phone (`reply --drive <session>`), or
189
+ * its recap (`--recap`).
190
+ *
191
+ * @param agentSession - Who answers
192
+ * @param sessionId - Drive mode session
193
+ * @param input - `{ text, interim, recap }`
194
+ * @returns Which conversation, and whether it closed
195
+ * @throws DriveAgentError 400 / 404 (no such conversation) / 502 (Cloud unreachable)
196
+ */
197
+ async agentReply(agentSession, sessionId, input) {
198
+ if (!C.SESSION_ID_PATTERN.test(sessionId))
199
+ throw new DriveAgentError(400, 'That is not a Drive mode session id (drv_…).');
200
+ const text = (input.text ?? '').trim();
201
+ if (!text)
202
+ throw new DriveAgentError(400, 'Reply text is required.');
203
+ if (text.length > C.TEXT_MAX_CHARS)
204
+ throw new DriveAgentError(400, `Too long for Drive mode (max ${C.TEXT_MAX_CHARS} characters) — say it short; details go in a normal reply.`);
205
+ const conv = pickConversation(await this.deps.store.list(), sessionId, agentSession, input.recap);
206
+ if (!conv)
207
+ throw new DriveAgentError(404, 'You have no open Drive mode conversation in that session. Answer the usual way (reply).');
208
+ if (input.recap)
209
+ return this.recap(conv, agentSession, text);
210
+ const interim = input.interim || input.ack === true;
211
+ await this.deps.recordAgentTurn({ agentSession, channelId: conv.channelId, ...(conv.threadId ? { threadId: conv.threadId } : {}), text, interim, sessionId });
212
+ await this.forward(conv, agentSession, text, { interim, ...(input.ack ? { ack: true } : {}) });
213
+ return { conversationId: conv.conversationId, closed: false };
214
+ }
215
+ /**
216
+ * Fallback: an agent answered a Drive mode conversation with a plain
217
+ * `reply` instead of `reply --drive`. Its words still reach the phone. Call
218
+ * for every chat-v2 message. Never throws.
219
+ *
220
+ * @param dto - The chat message
221
+ */
222
+ async noteChatTurn(dto) {
223
+ try {
224
+ if (dto.senderType !== 'agent' || isDriveModeRow(dto) || !dto.content.trim())
225
+ return;
226
+ const open = (await this.deps.store.list()).filter((c) => !c.closedAt && c.channelId === dto.channelId && (!c.threadId || dto.threadId === c.threadId));
227
+ for (const c of open.reverse()) {
228
+ if (dto.senderId !== c.agentSession && !(c.members ?? []).includes(dto.senderId))
229
+ continue;
230
+ const lastOwner = [...c.turns].reverse().find((t) => t.from === 'owner');
231
+ const lastAgent = [...c.turns].reverse().find((t) => t.from === 'agent' && !t.interim);
232
+ if (!lastOwner || (lastAgent && Date.parse(lastAgent.at) >= Date.parse(lastOwner.at)))
233
+ continue;
234
+ if (dto.createdAt < Date.parse(lastOwner.at))
235
+ continue;
236
+ await this.forward(c, dto.senderId, dto.content, { interim: dto.metadata?.interim === true });
237
+ this.logger.info('Drive mode: picked up a plain reply', { sessionId: c.sessionId, conversationId: c.conversationId });
238
+ return;
239
+ }
240
+ }
241
+ catch (err) {
242
+ this.logger.warn('Drive mode: plain reply not forwarded', { error: err instanceof Error ? err.message : String(err) });
243
+ }
244
+ }
245
+ async recap(conv, agentSession, text) {
246
+ const nextStep = !NOTHING_PENDING.test(text);
247
+ const posted = await this.deps.postRecap({ conversation: conv, agentSession, text, nextStep });
248
+ await this.forward(conv, agentSession, text, { recap: true, nextStep }).catch((err) => {
249
+ // The recap is posted where it belongs; Cloud learning of it is best effort.
250
+ this.logger.warn('Drive mode: recap not reported to Cloud', { error: err instanceof Error ? err.message : String(err) });
251
+ });
252
+ await this.deps.store.update(conv.sessionId, conv.conversationId, (cur) => (cur ? { ...cur, closedAt: this.now().toISOString() } : null), this.now().getTime());
253
+ this.deps.closeTracking?.(conv.agentSession, conv.channelId);
254
+ this.logger.info('Drive mode recap posted; conversation closed', { sessionId: conv.sessionId, conversationId: conv.conversationId, where: posted.where, nextStep });
255
+ return { conversationId: conv.conversationId, closed: true };
256
+ }
257
+ /** Send an answer to Cloud and keep the local transcript. */
258
+ async forward(conv, agentSession, text, opts) {
259
+ const { instanceId } = await this.deps.identity();
260
+ await this.cloudPost(`/${conv.sessionId}/replies`, {
261
+ instanceId,
262
+ conversationId: conv.conversationId,
263
+ agentSession,
264
+ text: text.slice(0, C.TEXT_MAX_CHARS),
265
+ ...(opts.interim ? { interim: true } : {}),
266
+ ...(opts.ack ? { ack: true } : {}),
267
+ ...(opts.recap ? { recap: true, nextStep: !!opts.nextStep } : {}),
268
+ });
269
+ if (opts.recap)
270
+ return;
271
+ const at = this.now().toISOString();
272
+ await this.deps.store.update(conv.sessionId, conv.conversationId, (cur) => (cur ? { ...cur, turns: [...cur.turns, { from: 'agent', name: agentSession, text: clip(speakable(text), 600), at, ...(opts.interim ? { interim: true } : {}) }] } : null), this.now().getTime());
273
+ }
274
+ // ---------------------------------------------------------------------------
275
+ // Recall / end
276
+ // ---------------------------------------------------------------------------
277
+ async recall(data, instanceId) {
278
+ const path = `/${data.sessionId}/machine/recalls/${encodeURIComponent(data.id)}`;
279
+ const req = parseRecallFetch(await this.cloudGet(`${path}?instanceId=${encodeURIComponent(instanceId)}`));
280
+ if (!req)
281
+ throw new Error('Cloud sent a malformed recall');
282
+ const feed = await this.deps.ownerFeed(this.now().getTime() - C.RECALL_WINDOW_MS);
283
+ const messages = pickRecall(messagesToOwner(feed.messages, feed.ownerTurns, req.agentSessions), req.hint);
284
+ await this.cloudPost(path, { instanceId, messages });
285
+ }
286
+ /**
287
+ * Keep the agents Cloud names warm while the session runs, and start the
288
+ * ones that are stopped now (warm agents go ahead of ordinary starts).
289
+ */
290
+ async warm(data, instanceId) {
291
+ const state = parseStateFetch(await this.cloudGet(`/${data.sessionId}/machine/state?instanceId=${encodeURIComponent(instanceId)}`));
292
+ if (!state)
293
+ throw new Error('Cloud sent a malformed session state');
294
+ const registry = this.deps.keepWarm ?? getDriveKeepWarm();
295
+ if (state.ended) {
296
+ registry.end(data.sessionId);
297
+ return;
298
+ }
299
+ const fresh = registry.set(data.sessionId, state.warm, state.warmUntil);
300
+ if (fresh.length)
301
+ this.logger.info('Drive mode: keeping agents warm', { sessionId: data.sessionId, agents: fresh });
302
+ for (const agent of fresh)
303
+ await this.deps.prestart?.(agent).catch(() => undefined);
304
+ }
305
+ async end(data, instanceId) {
306
+ const state = parseStateFetch(await this.cloudGet(`/${data.sessionId}/machine/state?instanceId=${encodeURIComponent(instanceId)}`));
307
+ if (!state || !state.ended)
308
+ return;
309
+ (this.deps.keepWarm ?? getDriveKeepWarm()).end(data.sessionId);
310
+ const wanted = new Set(state.conversations.map((c) => c.conversationId));
311
+ const local = (await this.deps.store.list()).filter((c) => c.sessionId === data.sessionId && wanted.has(c.conversationId) && !c.closedAt && !c.recapAskedAt);
312
+ for (const c of local) {
313
+ const ok = await this.deps.notifyAgent(c.agentSession, recapRequest(c)).catch(() => false);
314
+ if (!ok) {
315
+ this.logger.warn('Drive mode: could not ask for a recap', { sessionId: c.sessionId, conversationId: c.conversationId, agent: c.agentSession });
316
+ continue;
317
+ }
318
+ await this.deps.store.update(c.sessionId, c.conversationId, (cur) => (cur ? { ...cur, recapAskedAt: this.now().toISOString() } : null), this.now().getTime());
319
+ }
320
+ }
321
+ // ---------------------------------------------------------------------------
322
+ // Cloud
323
+ // ---------------------------------------------------------------------------
324
+ async cloudGet(path) {
325
+ return this.cloudCall('GET', path);
326
+ }
327
+ async cloudPost(path, body) {
328
+ return this.cloudCall('POST', path, body);
329
+ }
330
+ async cloudCall(method, path, body, refreshed = false) {
331
+ const base = this.deps.cloud.getCloudUrl();
332
+ const token = this.deps.cloud.getToken();
333
+ if (!base || !token)
334
+ throw new DriveAgentError(503, 'This machine is not signed in to Crewly Cloud.');
335
+ let res;
336
+ try {
337
+ res = await this.fetchImpl(`${base.replace(/\/+$/, '')}${C.CLOUD_BASE_PATH}${path}`, {
338
+ method,
339
+ headers: { Authorization: `Bearer ${token}`, ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}) },
340
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
341
+ signal: AbortSignal.timeout(C.REQUEST_TIMEOUT_MS),
342
+ });
343
+ }
344
+ catch {
345
+ throw new DriveAgentError(502, 'Could not reach Crewly Cloud — try again in a moment.');
346
+ }
347
+ if (res.status === 401 && !refreshed && this.deps.cloud.tryRefreshToken && (await this.deps.cloud.tryRefreshToken())) {
348
+ return this.cloudCall(method, path, body, true);
349
+ }
350
+ let json = {};
351
+ try {
352
+ json = (await res.json());
353
+ }
354
+ catch {
355
+ json = {};
356
+ }
357
+ if (!res.ok || json.success === false) {
358
+ const why = typeof json.error === 'string' ? json.error : `Crewly Cloud answered ${res.status}`;
359
+ throw new DriveAgentError(res.status >= 500 ? 502 : res.status, why);
360
+ }
361
+ return json.data;
362
+ }
363
+ }
364
+ // ---------------------------------------------------------------------------
365
+ // Pure helpers
366
+ // ---------------------------------------------------------------------------
367
+ /**
368
+ * The owner's words as delivered: the Drive mode note, then the words.
369
+ *
370
+ * @param sessionId - Session
371
+ * @param kind - Target kind
372
+ * @param name - Target name
373
+ * @param text - The owner's words
374
+ * @returns Message text
375
+ */
376
+ export function ownerTurnText(sessionId, kind, name, text) {
377
+ const forTeam = kind === 'team' ? ` This is for the ${name} team: answer for the team, or hand it on.` : '';
378
+ return (`[Drive mode · session ${sessionId}] The owner is driving and LISTENING on the phone, not reading.${forTeam}\n` +
379
+ `1. Within seconds, before any other work: reply --drive ${sessionId} --ack "<one short sentence: got it and how long, or the direct answer>".\n` +
380
+ `2. If it needs work, do it, then: reply --drive ${sessionId} "<the result>" — conclusion first, at most 3 short spoken sentences; ` +
381
+ 'if he must decide, give 2–3 options. No URLs, tables, code or ids; details go in your end-of-session recap. Not in Slack. ' +
382
+ 'If the ack already answered it, you are done.\n\n' +
383
+ text);
384
+ }
385
+ /**
386
+ * The harness note asking an agent for its recap.
387
+ *
388
+ * @param c - Conversation
389
+ * @returns Note text
390
+ */
391
+ export function recapRequest(c) {
392
+ const where = c.kind === 'channel' ? c.targetName : c.kind === 'team' ? `your DM with the owner (for the ${c.targetName} team)` : 'your DM with the owner';
393
+ const transcript = c.turns
394
+ .slice(-12)
395
+ .map((t) => `${t.from === 'owner' ? 'Owner' : t.name}: ${clip(t.text, 400)}`)
396
+ .join('\n');
397
+ return [
398
+ `[Drive mode ended · session ${c.sessionId}] The owner ended the Drive mode conversation with you. Post ONE recap now, for reading:`,
399
+ `reply --drive ${c.sessionId} --recap "Drive mode recap — you said …; I did / answered …; next: … (or: nothing pending / waiting on X)"`,
400
+ `It is posted in ${where}. After it the conversation is closed: no follow-ups or nudges unless the recap names a next step.`,
401
+ '',
402
+ 'The conversation:',
403
+ transcript,
404
+ ].join('\n');
405
+ }
406
+ /**
407
+ * The conversation an agent's reply belongs to: one it answers or is a
408
+ * member of; a recap goes to one whose recap was asked for; otherwise the one
409
+ * waiting for an answer, newest first.
410
+ *
411
+ * @param all - Local conversations
412
+ * @param sessionId - Session
413
+ * @param agentSession - Who replies
414
+ * @param recap - It is the recap
415
+ * @returns Conversation or undefined
416
+ */
417
+ export function pickConversation(all, sessionId, agentSession, recap) {
418
+ const mine = all.filter((c) => c.sessionId === sessionId && !c.closedAt && (c.agentSession === agentSession || (c.members ?? []).includes(agentSession)));
419
+ const waiting = (c) => {
420
+ const lastOwner = [...c.turns].reverse().find((t) => t.from === 'owner');
421
+ const lastAgent = [...c.turns].reverse().find((t) => t.from === 'agent' && !t.interim);
422
+ return !!lastOwner && (!lastAgent || Date.parse(lastAgent.at) < Date.parse(lastOwner.at));
423
+ };
424
+ const pref = recap ? mine.filter((c) => c.recapAskedAt) : mine.filter(waiting);
425
+ const pool = pref.length > 0 ? pref : mine;
426
+ const own = pool.filter((c) => c.agentSession === agentSession);
427
+ const list = own.length > 0 ? own : pool;
428
+ const lastAt = (c) => Date.parse(c.turns[c.turns.length - 1]?.at ?? c.startedAt);
429
+ return [...list].sort((a, b) => lastAt(b) - lastAt(a))[0];
430
+ }
431
+ let instance = null;
432
+ /** @returns The running service, or null */
433
+ export function getDriveAgentService() {
434
+ return instance;
435
+ }
436
+ /**
437
+ * Install the service (boot, tests).
438
+ *
439
+ * @param service - Service or null
440
+ */
441
+ export function setDriveAgentService(service) {
442
+ instance = service;
443
+ }
444
+ //# sourceMappingURL=drive-agent.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drive-agent.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/drive/drive-agent.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Build the Drive mode status snapshot (specs/2026-10-09-drive-mode-v3.md §1)
3
+ * from structured data this machine already has. Pure, no LLM calls: the
4
+ * text in it is what agents already wrote — ticket titles and log lines,
5
+ * work item `output.summary`, decision card summaries, their own messages to
6
+ * the owner.
7
+ *
8
+ * - teams: lead, members, counts (open / in progress / review / blocked /
9
+ * done today);
10
+ * - agents: state (working / idle / starting / stopped), what each is on
11
+ * and since when (its running work item, else its ticket in progress),
12
+ * its last few messages to the owner;
13
+ * - items: project tickets and work items not linked to a ticket, in order
14
+ * of attention, done / cancelled ones only from the last 24 h;
15
+ * - waiting: the briefing queue's live owner items (cards, questions,
16
+ * reviews — stale and duplicate ones are already dropped there).
17
+ *
18
+ * Every text field is made speakable, passed through the secret redactor
19
+ * and clipped; the whole snapshot is kept under
20
+ * {@link DRIVE_BRIEFING_CONSTANTS.MAX_BYTES}.
21
+ *
22
+ * @module services/drive/drive-briefing-snapshot
23
+ */
24
+ import type { OwnerTurnMark } from '../briefing/briefing-cards.js';
25
+ import { type RecallFeedMessage } from './drive-recall.js';
26
+ import type { BriefingAgentState, BriefingItemStatus, BriefingSnapshot } from './drive-briefing.contract.js';
27
+ /** An agent on this machine. */
28
+ export interface SnapshotAgentSource {
29
+ agentSession: string;
30
+ name: string;
31
+ /** Team name */
32
+ team?: string;
33
+ role?: string;
34
+ state: BriefingAgentState;
35
+ }
36
+ /** A team on this machine. */
37
+ export interface SnapshotTeamSource {
38
+ id: string;
39
+ name: string;
40
+ /** Lead's session */
41
+ lead?: string;
42
+ /** Members' sessions */
43
+ members: string[];
44
+ }
45
+ /** A project ticket (the fields used). */
46
+ export interface SnapshotTicketSource {
47
+ /** Project name */
48
+ project: string;
49
+ id: string;
50
+ title: string;
51
+ status: string;
52
+ labels: string[];
53
+ /** Agent session or a person's name */
54
+ assignee: string | null;
55
+ /** Team id */
56
+ team: string | null;
57
+ updatedAt: string;
58
+ workItemId: string | null;
59
+ log: string[];
60
+ }
61
+ /** A task-pool work item (the fields used). */
62
+ export interface SnapshotWorkItemSource {
63
+ id: string;
64
+ title: string;
65
+ status: string;
66
+ target?: string;
67
+ createdAt: string;
68
+ startedAt?: string;
69
+ completedAt?: string;
70
+ statusChangedAt?: string;
71
+ output?: Record<string, unknown>;
72
+ blockedReason?: string;
73
+ error?: string;
74
+ metadata?: Record<string, unknown>;
75
+ }
76
+ /** A live owner item from the briefing queue (the fields used). */
77
+ export interface SnapshotWaitingSource {
78
+ id: string;
79
+ kind: 'decision' | 'question' | 'review';
80
+ agentSession: string;
81
+ agentName: string;
82
+ teamName?: string;
83
+ summary: string;
84
+ since: string;
85
+ urgency: 'high' | 'normal' | 'low';
86
+ answerTarget: {
87
+ kind: string;
88
+ decisionId?: string;
89
+ tkt?: string;
90
+ };
91
+ }
92
+ /** Everything the snapshot is built from. */
93
+ export interface SnapshotSources {
94
+ now: Date;
95
+ agents: SnapshotAgentSource[];
96
+ teams: SnapshotTeamSource[];
97
+ tickets: SnapshotTicketSource[];
98
+ workItems: SnapshotWorkItemSource[];
99
+ waiting: SnapshotWaitingSource[];
100
+ ownerFeed: {
101
+ messages: RecallFeedMessage[];
102
+ ownerTurns: OwnerTurnMark[];
103
+ };
104
+ }
105
+ /**
106
+ * Speakable, redacted, clipped text (empty → '').
107
+ *
108
+ * @param raw - Agent-written text
109
+ * @param max - Longest length
110
+ * @returns Safe text
111
+ */
112
+ export declare function safeText(raw: unknown, max: number): string;
113
+ /**
114
+ * An agent's state from its team member record.
115
+ *
116
+ * @param agentStatus - Connection status
117
+ * @param workingStatus - Activity
118
+ * @returns State
119
+ */
120
+ export declare function agentStateOf(agentStatus: string | undefined, workingStatus: string | undefined): BriefingAgentState;
121
+ /**
122
+ * A project ticket's status for the voice (`blocked` label wins).
123
+ *
124
+ * @param status - Ticket status
125
+ * @param labels - Ticket labels
126
+ * @returns Normalised status
127
+ */
128
+ export declare function ticketStatus(status: string, labels: readonly string[]): BriefingItemStatus;
129
+ /**
130
+ * A work item's status for the voice.
131
+ *
132
+ * @param status - WorkItem status
133
+ * @returns Normalised status
134
+ */
135
+ export declare function workItemStatus(status: string): BriefingItemStatus;
136
+ /**
137
+ * The words of a ticket log line (`<iso> · <actor> · <message>` → message).
138
+ *
139
+ * @param line - Log line
140
+ * @returns The message
141
+ */
142
+ export declare function logMessage(line: string): string;
143
+ /**
144
+ * Build the snapshot.
145
+ *
146
+ * @param src - Sources
147
+ * @returns Snapshot, under the size cap
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * const snapshot = buildBriefingSnapshot({ now: new Date(), agents, teams, tickets, workItems, waiting, ownerFeed });
152
+ * ```
153
+ */
154
+ export declare function buildBriefingSnapshot(src: SnapshotSources): BriefingSnapshot;
155
+ /**
156
+ * A spoken reference for a waiting item: the card id (`D-7`), the ticket
157
+ * (`TKT-5`), or a short question id.
158
+ *
159
+ * @param w - Waiting item
160
+ * @returns Reference
161
+ */
162
+ export declare function waitingRef(w: Pick<SnapshotWaitingSource, 'id' | 'kind' | 'answerTarget'>): string;
163
+ /**
164
+ * Keep the snapshot under `maxBytes`: drop finished work first, then older
165
+ * messages to the owner, then the least urgent items and waiting entries.
166
+ *
167
+ * @param s - Snapshot
168
+ * @param maxBytes - Size cap (JSON)
169
+ * @returns A snapshot within the cap
170
+ */
171
+ export declare function capSnapshot(s: BriefingSnapshot, maxBytes: number): BriefingSnapshot;
172
+ //# sourceMappingURL=drive-briefing-snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drive-briefing-snapshot.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/drive/drive-briefing-snapshot.ts"],"names":[],"mappings":"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"}