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,99 @@
1
+ /**
2
+ * Drive mode wire shapes, machine half (mirrors crewly-services
3
+ * `auth/src/drive/drive.contract.ts`). Pure.
4
+ *
5
+ * Cloud → machine: relay `talk_message` with `{v:1, kind:'drive', op,
6
+ * sessionId, id?, instanceId}` — ids only. The machine then fetches, with its
7
+ * own Cloud token:
8
+ * GET /api/cloud/talk/session/:sid/machine/deliveries/:id?instanceId= → the owner's words
9
+ * GET /api/cloud/talk/session/:sid/machine/state?instanceId= → its conversations (after `end`)
10
+ * GET /api/cloud/talk/session/:sid/machine/recalls/:id?instanceId= → what to recall
11
+ * POST /api/cloud/talk/session/:sid/machine/recalls/:id {instanceId, messages}
12
+ * (`op:'warm'` → `machine/state` also lists the agents to keep warm, with `warmUntil`)
13
+ * Machine → Cloud (an agent's `reply --drive`):
14
+ * POST /api/cloud/talk/session/:sid/replies {instanceId, conversationId, agentSession, text, interim?, ack?, recap?, nextStep?}
15
+ * Machine → Cloud (its status snapshot, specs/2026-10-09-drive-mode-v3.md):
16
+ * PUT /api/cloud/instances/:instanceId/briefing {snapshot}
17
+ *
18
+ * @module services/drive/drive-cloud.contract
19
+ */
20
+ /** What the owner can talk to. */
21
+ export type DriveTargetKind = 'agent' | 'team' | 'channel';
22
+ /** A Drive mode push. */
23
+ export interface DriveRelayData {
24
+ v: 1;
25
+ kind: 'drive';
26
+ op: 'deliver' | 'end' | 'recall' | 'warm';
27
+ sessionId: string;
28
+ id?: string;
29
+ instanceId: string;
30
+ }
31
+ /** The owner's words, as Cloud hands them to the machine. */
32
+ export interface DriveDeliveryFetch {
33
+ id: string;
34
+ sessionId: string;
35
+ conversationId: string;
36
+ target: {
37
+ kind: DriveTargetKind;
38
+ name: string;
39
+ agentSession: string;
40
+ team?: string;
41
+ slackChannelId?: string;
42
+ members?: string[];
43
+ };
44
+ text: string;
45
+ }
46
+ /** A recall request. */
47
+ export interface DriveRecallFetch {
48
+ agentSessions: string[];
49
+ hint?: string;
50
+ slackChannelId?: string;
51
+ }
52
+ /** The machine's part of a session. */
53
+ export interface DriveStateFetch {
54
+ ended: boolean;
55
+ conversations: Array<{
56
+ conversationId: string;
57
+ agentSession: string;
58
+ }>;
59
+ /** Agents to keep warm while the session runs (v3) */
60
+ warm: string[];
61
+ /** Until when (ms); 0 when nothing is warm */
62
+ warmUntil: number;
63
+ }
64
+ /**
65
+ * A Drive mode relay payload, or null (any other `talk_message`).
66
+ *
67
+ * @param payload - Relay payload
68
+ * @returns Data or null
69
+ */
70
+ export declare function parseDriveRelayData(payload: unknown): DriveRelayData | null;
71
+ /**
72
+ * Whether a relay payload is Drive mode's (so the Talk handler leaves it alone).
73
+ *
74
+ * @param payload - Relay payload
75
+ * @returns True for `kind: 'drive'`
76
+ */
77
+ export declare function isDrivePayload(payload: unknown): boolean;
78
+ /**
79
+ * Cloud's delivery answer, or null.
80
+ *
81
+ * @param data - `data` of the response
82
+ * @returns Delivery or null
83
+ */
84
+ export declare function parseDeliveryFetch(data: unknown): DriveDeliveryFetch | null;
85
+ /**
86
+ * Cloud's recall request, or null.
87
+ *
88
+ * @param data - `data` of the response
89
+ * @returns Request or null
90
+ */
91
+ export declare function parseRecallFetch(data: unknown): DriveRecallFetch | null;
92
+ /**
93
+ * Cloud's session state for this machine, or null.
94
+ *
95
+ * @param data - `data` of the response
96
+ * @returns State or null
97
+ */
98
+ export declare function parseStateFetch(data: unknown): DriveStateFetch | null;
99
+ //# sourceMappingURL=drive-cloud.contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drive-cloud.contract.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/drive/drive-cloud.contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,kCAAkC;AAClC,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAE3D,yBAAyB;AACzB,MAAM,WAAW,cAAc;IAC7B,CAAC,EAAE,CAAC,CAAC;IACL,IAAI,EAAE,OAAO,CAAC;IACd,EAAE,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,6DAA6D;AAC7D,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAClI,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAwB;AACxB,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,uCAAuC;AACvC,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,KAAK,CAAC;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvE,sDAAsD;IACtD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAU3E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAExD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,kBAAkB,GAAG,IAAI,CA0B3E;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAKvE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAUrE"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Drive mode wire shapes, machine half (mirrors crewly-services
3
+ * `auth/src/drive/drive.contract.ts`). Pure.
4
+ *
5
+ * Cloud → machine: relay `talk_message` with `{v:1, kind:'drive', op,
6
+ * sessionId, id?, instanceId}` — ids only. The machine then fetches, with its
7
+ * own Cloud token:
8
+ * GET /api/cloud/talk/session/:sid/machine/deliveries/:id?instanceId= → the owner's words
9
+ * GET /api/cloud/talk/session/:sid/machine/state?instanceId= → its conversations (after `end`)
10
+ * GET /api/cloud/talk/session/:sid/machine/recalls/:id?instanceId= → what to recall
11
+ * POST /api/cloud/talk/session/:sid/machine/recalls/:id {instanceId, messages}
12
+ * (`op:'warm'` → `machine/state` also lists the agents to keep warm, with `warmUntil`)
13
+ * Machine → Cloud (an agent's `reply --drive`):
14
+ * POST /api/cloud/talk/session/:sid/replies {instanceId, conversationId, agentSession, text, interim?, ack?, recap?, nextStep?}
15
+ * Machine → Cloud (its status snapshot, specs/2026-10-09-drive-mode-v3.md):
16
+ * PUT /api/cloud/instances/:instanceId/briefing {snapshot}
17
+ *
18
+ * @module services/drive/drive-cloud.contract
19
+ */
20
+ import { DRIVE_CONSTANTS } from '../../constants.js';
21
+ const obj = (v) => (v && typeof v === 'object' && !Array.isArray(v) ? v : null);
22
+ const str = (v) => (typeof v === 'string' && v.trim() ? v : null);
23
+ /**
24
+ * A Drive mode relay payload, or null (any other `talk_message`).
25
+ *
26
+ * @param payload - Relay payload
27
+ * @returns Data or null
28
+ */
29
+ export function parseDriveRelayData(payload) {
30
+ const p = obj(payload);
31
+ if (!p || p['kind'] !== DRIVE_CONSTANTS.RELAY_KIND || p['v'] !== 1)
32
+ return null;
33
+ const op = p['op'];
34
+ const sessionId = str(p['sessionId']);
35
+ const instanceId = str(p['instanceId']);
36
+ if ((op !== 'deliver' && op !== 'end' && op !== 'recall' && op !== 'warm') || !sessionId || !DRIVE_CONSTANTS.SESSION_ID_PATTERN.test(sessionId) || !instanceId)
37
+ return null;
38
+ const id = str(p['id']);
39
+ if ((op === 'deliver' || op === 'recall') && !id)
40
+ return null;
41
+ return { v: 1, kind: 'drive', op, sessionId, instanceId, ...(id ? { id } : {}) };
42
+ }
43
+ /**
44
+ * Whether a relay payload is Drive mode's (so the Talk handler leaves it alone).
45
+ *
46
+ * @param payload - Relay payload
47
+ * @returns True for `kind: 'drive'`
48
+ */
49
+ export function isDrivePayload(payload) {
50
+ return obj(payload)?.['kind'] === DRIVE_CONSTANTS.RELAY_KIND;
51
+ }
52
+ /**
53
+ * Cloud's delivery answer, or null.
54
+ *
55
+ * @param data - `data` of the response
56
+ * @returns Delivery or null
57
+ */
58
+ export function parseDeliveryFetch(data) {
59
+ const d = obj(data);
60
+ const t = obj(d?.['target']);
61
+ if (!d || !t)
62
+ return null;
63
+ const kind = t['kind'];
64
+ const id = str(d['id']);
65
+ const sessionId = str(d['sessionId']);
66
+ const conversationId = str(d['conversationId']);
67
+ const text = str(d['text']);
68
+ const agentSession = str(t['agentSession']);
69
+ if ((kind !== 'agent' && kind !== 'team' && kind !== 'channel') || !id || !sessionId || !conversationId || !text || !agentSession)
70
+ return null;
71
+ const members = Array.isArray(t['members']) ? t['members'].filter((m) => typeof m === 'string') : undefined;
72
+ return {
73
+ id,
74
+ sessionId,
75
+ conversationId,
76
+ text,
77
+ target: {
78
+ kind,
79
+ name: str(t['name']) ?? agentSession,
80
+ agentSession,
81
+ ...(str(t['team']) ? { team: t['team'] } : {}),
82
+ ...(str(t['slackChannelId']) ? { slackChannelId: t['slackChannelId'] } : {}),
83
+ ...(members ? { members } : {}),
84
+ },
85
+ };
86
+ }
87
+ /**
88
+ * Cloud's recall request, or null.
89
+ *
90
+ * @param data - `data` of the response
91
+ * @returns Request or null
92
+ */
93
+ export function parseRecallFetch(data) {
94
+ const d = obj(data);
95
+ const sessions = Array.isArray(d?.['agentSessions']) ? (d?.['agentSessions']).filter((s) => typeof s === 'string' && !!s) : [];
96
+ if (!d || sessions.length === 0)
97
+ return null;
98
+ return { agentSessions: sessions, ...(str(d['hint']) ? { hint: d['hint'] } : {}), ...(str(d['slackChannelId']) ? { slackChannelId: d['slackChannelId'] } : {}) };
99
+ }
100
+ /**
101
+ * Cloud's session state for this machine, or null.
102
+ *
103
+ * @param data - `data` of the response
104
+ * @returns State or null
105
+ */
106
+ export function parseStateFetch(data) {
107
+ const d = obj(data);
108
+ if (!d || !Array.isArray(d['conversations']))
109
+ return null;
110
+ const conversations = d['conversations']
111
+ .map(obj)
112
+ .filter((c) => !!c && !!str(c['conversationId']) && !!str(c['agentSession']))
113
+ .map((c) => ({ conversationId: c['conversationId'], agentSession: c['agentSession'] }));
114
+ const warm = Array.isArray(d['warm']) ? d['warm'].filter((s) => typeof s === 'string' && !!s.trim()).slice(0, DRIVE_CONSTANTS.MAX_WARM) : [];
115
+ const until = typeof d['warmUntil'] === 'string' ? Date.parse(d['warmUntil']) : NaN;
116
+ return { ended: d['ended'] === true, conversations, warm, warmUntil: warm.length && Number.isFinite(until) ? until : 0 };
117
+ }
118
+ //# sourceMappingURL=drive-cloud.contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drive-cloud.contract.js","sourceRoot":"","sources":["../../../../../../backend/src/services/drive/drive-cloud.contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * This machine's Drive mode conversations (specs/2026-10-08-drive-mode.md
3
+ * §7): where each one is recorded (the agent's DM, or the room's thread), who
4
+ * answers it, its turns (for the recap) and whether it is closed — in
5
+ * `<crewlyHome>/drive-conversations.json`, so a restart mid-drive still
6
+ * routes the agents' answers and recaps. Cloud keeps the session itself.
7
+ *
8
+ * @module services/drive/drive-conversation.store
9
+ */
10
+ import type { DriveTargetKind } from './drive-cloud.contract.js';
11
+ /** One turn. */
12
+ export interface DriveLocalTurn {
13
+ from: 'owner' | 'agent';
14
+ name: string;
15
+ text: string;
16
+ /** ISO */
17
+ at: string;
18
+ interim?: boolean;
19
+ }
20
+ /** One Drive mode conversation on this machine. */
21
+ export interface DriveLocalConversation {
22
+ sessionId: string;
23
+ conversationId: string;
24
+ kind: DriveTargetKind;
25
+ /** "Ella", "CE", "#ce" */
26
+ targetName: string;
27
+ /** Who answers (the agent, the lead) */
28
+ agentSession: string;
29
+ /** Others who may answer (team / room members) */
30
+ members?: string[];
31
+ /** chat-v2 channel it is recorded in */
32
+ channelId: string;
33
+ /** Thread root (room conversations) */
34
+ threadId?: string;
35
+ /** ISO */
36
+ startedAt: string;
37
+ turns: DriveLocalTurn[];
38
+ /** ISO — the recap was asked for */
39
+ recapAskedAt?: string;
40
+ /** ISO — the recap was posted; closed */
41
+ closedAt?: string;
42
+ }
43
+ /**
44
+ * The key of a conversation.
45
+ *
46
+ * @param sessionId - Session
47
+ * @param conversationId - Conversation
48
+ * @returns `<sessionId>:<conversationId>`
49
+ */
50
+ export declare function localKey(sessionId: string, conversationId: string): string;
51
+ /** Read / change local Drive mode conversations. Writes are serialised. */
52
+ export declare class DriveConversationStore {
53
+ private readonly filePath?;
54
+ private chain;
55
+ /**
56
+ * @param filePath - File; default `<crewlyHome>/drive-conversations.json` (resolved per call)
57
+ */
58
+ constructor(filePath?: string | undefined);
59
+ /** @returns Absolute path */
60
+ getFilePath(): string;
61
+ /** @returns Every conversation (empty when missing or unreadable) */
62
+ list(): Promise<DriveLocalConversation[]>;
63
+ /**
64
+ * One conversation.
65
+ *
66
+ * @param sessionId - Session
67
+ * @param conversationId - Conversation
68
+ * @returns It, or null
69
+ */
70
+ get(sessionId: string, conversationId: string): Promise<DriveLocalConversation | null>;
71
+ /**
72
+ * Change one conversation; `fn` gets it (or null) and returns the new one,
73
+ * or null to leave it. Conversations older than
74
+ * {@link DRIVE_CONSTANTS.KEEP_MS} are dropped on the way.
75
+ *
76
+ * @param sessionId - Session
77
+ * @param conversationId - Conversation
78
+ * @param fn - Updater
79
+ * @param nowMs - Clock
80
+ * @returns The new conversation (or null)
81
+ */
82
+ update(sessionId: string, conversationId: string, fn: (cur: DriveLocalConversation | null) => DriveLocalConversation | null, nowMs?: number): Promise<DriveLocalConversation | null>;
83
+ private read;
84
+ private write;
85
+ }
86
+ //# sourceMappingURL=drive-conversation.store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drive-conversation.store.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/drive/drive-conversation.store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEjE,gBAAgB;AAChB,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU;IACV,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,mDAAmD;AACnD,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,eAAe,CAAC;IACtB,0BAA0B;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU;IACV,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAQD;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAE1E;AAED,2EAA2E;AAC3E,qBAAa,sBAAsB;IAMrB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IALtC,OAAO,CAAC,KAAK,CAAuC;IAEpD;;OAEG;gBAC0B,QAAQ,CAAC,EAAE,MAAM,YAAA;IAE9C,6BAA6B;IAC7B,WAAW,IAAI,MAAM;IAIrB,qEAAqE;IAC/D,IAAI,IAAI,OAAO,CAAC,sBAAsB,EAAE,CAAC;IAK/C;;;;;;OAMG;IACG,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC;IAK5F;;;;;;;;;;OAUG;IACH,MAAM,CACJ,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,EAAE,EAAE,CAAC,GAAG,EAAE,sBAAsB,GAAG,IAAI,KAAK,sBAAsB,GAAG,IAAI,EACzE,KAAK,GAAE,MAAmB,GACzB,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC;YAiB3B,IAAI;YAUJ,KAAK;CAOpB"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * This machine's Drive mode conversations (specs/2026-10-08-drive-mode.md
3
+ * §7): where each one is recorded (the agent's DM, or the room's thread), who
4
+ * answers it, its turns (for the recap) and whether it is closed — in
5
+ * `<crewlyHome>/drive-conversations.json`, so a restart mid-drive still
6
+ * routes the agents' answers and recaps. Cloud keeps the session itself.
7
+ *
8
+ * @module services/drive/drive-conversation.store
9
+ */
10
+ import * as fs from 'fs';
11
+ import * as path from 'path';
12
+ import { DRIVE_CONSTANTS } from '../../constants.js';
13
+ import { getCrewlyHomePath } from '../core/crewly-home.utils.js';
14
+ /**
15
+ * The key of a conversation.
16
+ *
17
+ * @param sessionId - Session
18
+ * @param conversationId - Conversation
19
+ * @returns `<sessionId>:<conversationId>`
20
+ */
21
+ export function localKey(sessionId, conversationId) {
22
+ return `${sessionId}:${conversationId}`;
23
+ }
24
+ /** Read / change local Drive mode conversations. Writes are serialised. */
25
+ export class DriveConversationStore {
26
+ filePath;
27
+ chain = Promise.resolve();
28
+ /**
29
+ * @param filePath - File; default `<crewlyHome>/drive-conversations.json` (resolved per call)
30
+ */
31
+ constructor(filePath) {
32
+ this.filePath = filePath;
33
+ }
34
+ /** @returns Absolute path */
35
+ getFilePath() {
36
+ return this.filePath ?? path.join(getCrewlyHomePath(), DRIVE_CONSTANTS.STATE_FILENAME);
37
+ }
38
+ /** @returns Every conversation (empty when missing or unreadable) */
39
+ async list() {
40
+ await this.chain;
41
+ return Object.values((await this.read()).conversations);
42
+ }
43
+ /**
44
+ * One conversation.
45
+ *
46
+ * @param sessionId - Session
47
+ * @param conversationId - Conversation
48
+ * @returns It, or null
49
+ */
50
+ async get(sessionId, conversationId) {
51
+ await this.chain;
52
+ return (await this.read()).conversations[localKey(sessionId, conversationId)] ?? null;
53
+ }
54
+ /**
55
+ * Change one conversation; `fn` gets it (or null) and returns the new one,
56
+ * or null to leave it. Conversations older than
57
+ * {@link DRIVE_CONSTANTS.KEEP_MS} are dropped on the way.
58
+ *
59
+ * @param sessionId - Session
60
+ * @param conversationId - Conversation
61
+ * @param fn - Updater
62
+ * @param nowMs - Clock
63
+ * @returns The new conversation (or null)
64
+ */
65
+ update(sessionId, conversationId, fn, nowMs = Date.now()) {
66
+ const run = this.chain.then(async () => {
67
+ const state = await this.read();
68
+ const key = localKey(sessionId, conversationId);
69
+ const cur = state.conversations[key] ? structuredClone(state.conversations[key]) : null;
70
+ const next = fn(cur);
71
+ for (const [k, c] of Object.entries(state.conversations)) {
72
+ if (nowMs - Date.parse(c.startedAt) > DRIVE_CONSTANTS.KEEP_MS)
73
+ delete state.conversations[k];
74
+ }
75
+ if (next)
76
+ state.conversations[key] = next;
77
+ await this.write(state);
78
+ return next;
79
+ });
80
+ this.chain = run.catch(() => undefined);
81
+ return run;
82
+ }
83
+ async read() {
84
+ try {
85
+ const raw = JSON.parse(await fs.promises.readFile(this.getFilePath(), 'utf8'));
86
+ if (raw && raw.version === 1 && raw.conversations && typeof raw.conversations === 'object')
87
+ return { version: 1, conversations: raw.conversations };
88
+ }
89
+ catch {
90
+ // missing or unreadable: start empty
91
+ }
92
+ return { version: 1, conversations: {} };
93
+ }
94
+ async write(state) {
95
+ const file = this.getFilePath();
96
+ await fs.promises.mkdir(path.dirname(file), { recursive: true });
97
+ const tmp = `${file}.${process.pid}.tmp`;
98
+ await fs.promises.writeFile(tmp, JSON.stringify(state, null, 2), { mode: 0o600 });
99
+ await fs.promises.rename(tmp, file);
100
+ }
101
+ }
102
+ //# sourceMappingURL=drive-conversation.store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drive-conversation.store.js","sourceRoot":"","sources":["../../../../../../backend/src/services/drive/drive-conversation.store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Drive mode keep-warm (specs/2026-10-09-drive-mode-v3.md §5): while the
3
+ * owner is in a Drive mode session, the agents he names and the team leads
4
+ * he talks to stay running, so the next thing he says to them is answered in
5
+ * seconds instead of after a cold start.
6
+ *
7
+ * Crewly Cloud tells this machine which of its agents to keep warm (relay
8
+ * push `op:'warm'`, the list read back from `machine/state` with this
9
+ * machine's token) and repeats it every few minutes while the phone is
10
+ * connected. Each list holds until its `until` time (or the session's end),
11
+ * so a phone that drops never pins agents for long.
12
+ *
13
+ * Consulted by every path that stops an idle agent or frees a running slot
14
+ * (idle stop, memory-pressure stop, slot freeing, reconciler wake eviction)
15
+ * and by the start gate, which lets a warm agent's start go ahead of
16
+ * ordinary starts.
17
+ *
18
+ * @module services/drive/drive-keep-warm
19
+ */
20
+ /** Which agents Drive mode keeps running right now. */
21
+ export declare class DriveKeepWarm {
22
+ private readonly now;
23
+ private readonly sessions;
24
+ /**
25
+ * @param now - Clock (ms)
26
+ */
27
+ constructor(now?: () => number);
28
+ /**
29
+ * Replace a session's warm list.
30
+ *
31
+ * @param driveSessionId - Drive mode session
32
+ * @param agents - Agent sessions to keep warm
33
+ * @param untilMs - Until when (ms)
34
+ * @returns The agents that were not warm before (to pre-start)
35
+ */
36
+ set(driveSessionId: string, agents: readonly string[], untilMs: number): string[];
37
+ /**
38
+ * The session ended: its agents are no longer kept warm.
39
+ *
40
+ * @param driveSessionId - Drive mode session
41
+ */
42
+ end(driveSessionId: string): void;
43
+ /**
44
+ * Whether an agent is kept warm by any live session.
45
+ *
46
+ * @param agentSession - Agent session name
47
+ * @returns True while a session's list holds it
48
+ */
49
+ isWarm(agentSession: string): boolean;
50
+ /**
51
+ * Every agent kept warm now (expired lists are dropped).
52
+ *
53
+ * @returns Agent sessions
54
+ */
55
+ warmAgents(): string[];
56
+ }
57
+ /** @returns The process-wide keep-warm registry */
58
+ export declare function getDriveKeepWarm(): DriveKeepWarm;
59
+ /**
60
+ * Replace the registry (tests).
61
+ *
62
+ * @param registry - Registry
63
+ */
64
+ export declare function setDriveKeepWarm(registry: DriveKeepWarm): void;
65
+ /**
66
+ * Whether Drive mode keeps an agent running right now. Never throws.
67
+ *
68
+ * @param agentSession - Agent session name
69
+ * @returns True while the owner's Drive mode session keeps it warm
70
+ */
71
+ export declare function isDriveWarm(agentSession: string): boolean;
72
+ //# sourceMappingURL=drive-keep-warm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drive-keep-warm.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/drive/drive-keep-warm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,uDAAuD;AACvD,qBAAa,aAAa;IAMZ,OAAO,CAAC,QAAQ,CAAC,GAAG;IALhC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8B;IAEvD;;OAEG;gBAC0B,GAAG,GAAE,MAAM,MAAiB;IAEzD;;;;;;;OAOG;IACH,GAAG,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAOjF;;;;OAIG;IACH,GAAG,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAIjC;;;;;OAKG;IACH,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAIrC;;;;OAIG;IACH,UAAU,IAAI,MAAM,EAAE;CAYvB;AAID,mDAAmD;AACnD,wBAAgB,gBAAgB,IAAI,aAAa,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAE9D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAMzD"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Drive mode keep-warm (specs/2026-10-09-drive-mode-v3.md §5): while the
3
+ * owner is in a Drive mode session, the agents he names and the team leads
4
+ * he talks to stay running, so the next thing he says to them is answered in
5
+ * seconds instead of after a cold start.
6
+ *
7
+ * Crewly Cloud tells this machine which of its agents to keep warm (relay
8
+ * push `op:'warm'`, the list read back from `machine/state` with this
9
+ * machine's token) and repeats it every few minutes while the phone is
10
+ * connected. Each list holds until its `until` time (or the session's end),
11
+ * so a phone that drops never pins agents for long.
12
+ *
13
+ * Consulted by every path that stops an idle agent or frees a running slot
14
+ * (idle stop, memory-pressure stop, slot freeing, reconciler wake eviction)
15
+ * and by the start gate, which lets a warm agent's start go ahead of
16
+ * ordinary starts.
17
+ *
18
+ * @module services/drive/drive-keep-warm
19
+ */
20
+ /** Which agents Drive mode keeps running right now. */
21
+ export class DriveKeepWarm {
22
+ now;
23
+ sessions = new Map();
24
+ /**
25
+ * @param now - Clock (ms)
26
+ */
27
+ constructor(now = Date.now) {
28
+ this.now = now;
29
+ }
30
+ /**
31
+ * Replace a session's warm list.
32
+ *
33
+ * @param driveSessionId - Drive mode session
34
+ * @param agents - Agent sessions to keep warm
35
+ * @param untilMs - Until when (ms)
36
+ * @returns The agents that were not warm before (to pre-start)
37
+ */
38
+ set(driveSessionId, agents, untilMs) {
39
+ const before = new Set(this.warmAgents());
40
+ if (agents.length === 0 || untilMs <= this.now())
41
+ this.sessions.delete(driveSessionId);
42
+ else
43
+ this.sessions.set(driveSessionId, { agents: new Set(agents), until: untilMs });
44
+ return agents.filter((a) => !before.has(a));
45
+ }
46
+ /**
47
+ * The session ended: its agents are no longer kept warm.
48
+ *
49
+ * @param driveSessionId - Drive mode session
50
+ */
51
+ end(driveSessionId) {
52
+ this.sessions.delete(driveSessionId);
53
+ }
54
+ /**
55
+ * Whether an agent is kept warm by any live session.
56
+ *
57
+ * @param agentSession - Agent session name
58
+ * @returns True while a session's list holds it
59
+ */
60
+ isWarm(agentSession) {
61
+ return this.warmAgents().includes(agentSession);
62
+ }
63
+ /**
64
+ * Every agent kept warm now (expired lists are dropped).
65
+ *
66
+ * @returns Agent sessions
67
+ */
68
+ warmAgents() {
69
+ const now = this.now();
70
+ const out = new Set();
71
+ for (const [id, s] of this.sessions) {
72
+ if (s.until <= now) {
73
+ this.sessions.delete(id);
74
+ continue;
75
+ }
76
+ for (const a of s.agents)
77
+ out.add(a);
78
+ }
79
+ return [...out];
80
+ }
81
+ }
82
+ let instance = new DriveKeepWarm();
83
+ /** @returns The process-wide keep-warm registry */
84
+ export function getDriveKeepWarm() {
85
+ return instance;
86
+ }
87
+ /**
88
+ * Replace the registry (tests).
89
+ *
90
+ * @param registry - Registry
91
+ */
92
+ export function setDriveKeepWarm(registry) {
93
+ instance = registry;
94
+ }
95
+ /**
96
+ * Whether Drive mode keeps an agent running right now. Never throws.
97
+ *
98
+ * @param agentSession - Agent session name
99
+ * @returns True while the owner's Drive mode session keeps it warm
100
+ */
101
+ export function isDriveWarm(agentSession) {
102
+ try {
103
+ return instance.isWarm(agentSession);
104
+ }
105
+ catch {
106
+ return false;
107
+ }
108
+ }
109
+ //# sourceMappingURL=drive-keep-warm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drive-keep-warm.js","sourceRoot":"","sources":["../../../../../../backend/src/services/drive/drive-keep-warm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,uDAAuD;AACvD,MAAM,OAAO,aAAa;IAMK;IALZ,QAAQ,GAAG,IAAI,GAAG,EAAmB,CAAC;IAEvD;;OAEG;IACH,YAA6B,MAAoB,IAAI,CAAC,GAAG;QAA5B,QAAG,GAAH,GAAG,CAAyB;IAAG,CAAC;IAE7D;;;;;;;OAOG;IACH,GAAG,CAAC,cAAsB,EAAE,MAAyB,EAAE,OAAe;QACpE,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QAC1C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE;YAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;;YAClF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QACpF,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED;;;;OAIG;IACH,GAAG,CAAC,cAAsB;QACxB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IACvC,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,YAAoB;QACzB,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAClD,CAAC;IAED;;;;OAIG;IACH,UAAU;QACR,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;QAC9B,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,IAAI,CAAC,CAAC,KAAK,IAAI,GAAG,EAAE,CAAC;gBACnB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACzB,SAAS;YACX,CAAC;YACD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM;gBAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;IAClB,CAAC;CACF;AAED,IAAI,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;AAEnC,mDAAmD;AACnD,MAAM,UAAU,gBAAgB;IAC9B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAuB;IACtD,QAAQ,GAAG,QAAQ,CAAC;AACtB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,YAAoB;IAC9C,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}