bopodev-api 0.1.24 → 0.1.26

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.
@@ -1,4 +1,4 @@
1
- import type { TemplateApplyResponse, TemplateManifest } from "bopodev-contracts";
1
+ import { AGENT_ROLE_LABELS, AgentRoleKeySchema, type TemplateApplyResponse, type TemplateManifest } from "bopodev-contracts";
2
2
  import type { BopoDb } from "bopodev-db";
3
3
  import {
4
4
  createAgent,
@@ -59,7 +59,9 @@ export async function applyTemplateManifest(
59
59
  const createdAgent = await createAgent(db, {
60
60
  companyId: input.companyId,
61
61
  managerAgentId: managerId,
62
- role: agent.role,
62
+ role: resolveAgentRoleText(agent.role, agent.roleKey, agent.title),
63
+ roleKey: normalizeRoleKey(agent.roleKey),
64
+ title: normalizeTitle(agent.title),
63
65
  name: agent.name,
64
66
  providerType: agent.providerType,
65
67
  heartbeatCron: agent.heartbeatCron,
@@ -136,3 +138,36 @@ export async function applyTemplateManifest(
136
138
  warnings: preview.warnings
137
139
  };
138
140
  }
141
+
142
+ function normalizeRoleKey(input: string | null | undefined) {
143
+ const normalized = input?.trim().toLowerCase();
144
+ if (!normalized) {
145
+ return null;
146
+ }
147
+ return AgentRoleKeySchema.safeParse(normalized).success ? normalized : null;
148
+ }
149
+
150
+ function normalizeTitle(input: string | null | undefined) {
151
+ const normalized = input?.trim();
152
+ return normalized ? normalized : null;
153
+ }
154
+
155
+ function resolveAgentRoleText(
156
+ legacyRole: string | undefined,
157
+ roleKeyInput: string | undefined,
158
+ titleInput: string | null | undefined
159
+ ) {
160
+ const normalizedLegacy = legacyRole?.trim();
161
+ if (normalizedLegacy) {
162
+ return normalizedLegacy;
163
+ }
164
+ const normalizedTitle = normalizeTitle(titleInput);
165
+ if (normalizedTitle) {
166
+ return normalizedTitle;
167
+ }
168
+ const roleKey = normalizeRoleKey(roleKeyInput);
169
+ if (roleKey) {
170
+ return AGENT_ROLE_LABELS[roleKey as keyof typeof AGENT_ROLE_LABELS];
171
+ }
172
+ return AGENT_ROLE_LABELS.general;
173
+ }
@@ -1,23 +1,61 @@
1
1
  import type { BopoDb } from "bopodev-db";
2
2
  import type { RealtimeHub } from "../realtime/hub";
3
3
  import { runHeartbeatSweep } from "../services/heartbeat-service";
4
+ import { runHeartbeatQueueSweep } from "../services/heartbeat-queue-service";
5
+ import { runIssueCommentDispatchSweep } from "../services/comment-recipient-dispatch-service";
4
6
 
5
7
  export function createHeartbeatScheduler(db: BopoDb, companyId: string, realtimeHub?: RealtimeHub) {
6
- const intervalMs = Number(process.env.BOPO_HEARTBEAT_SWEEP_MS ?? 60_000);
7
- let running = false;
8
- const timer = setInterval(() => {
9
- if (running) {
8
+ const heartbeatIntervalMs = Number(process.env.BOPO_HEARTBEAT_SWEEP_MS ?? 60_000);
9
+ const queueIntervalMs = Number(process.env.BOPO_HEARTBEAT_QUEUE_SWEEP_MS ?? 2_000);
10
+ const commentDispatchIntervalMs = Number(process.env.BOPO_COMMENT_DISPATCH_SWEEP_MS ?? 3_000);
11
+ let heartbeatRunning = false;
12
+ let queueRunning = false;
13
+ let commentDispatchRunning = false;
14
+ const heartbeatTimer = setInterval(() => {
15
+ if (heartbeatRunning) {
10
16
  return;
11
17
  }
12
- running = true;
18
+ heartbeatRunning = true;
13
19
  void runHeartbeatSweep(db, companyId, { realtimeHub })
14
20
  .catch((error) => {
15
21
  // eslint-disable-next-line no-console
16
22
  console.error("[scheduler] heartbeat sweep failed", error);
17
23
  })
18
24
  .finally(() => {
19
- running = false;
25
+ heartbeatRunning = false;
20
26
  });
21
- }, intervalMs);
22
- return () => clearInterval(timer);
27
+ }, heartbeatIntervalMs);
28
+ const queueTimer = setInterval(() => {
29
+ if (queueRunning) {
30
+ return;
31
+ }
32
+ queueRunning = true;
33
+ void runHeartbeatQueueSweep(db, companyId, { realtimeHub })
34
+ .catch((error) => {
35
+ // eslint-disable-next-line no-console
36
+ console.error("[scheduler] queue sweep failed", error);
37
+ })
38
+ .finally(() => {
39
+ queueRunning = false;
40
+ });
41
+ }, queueIntervalMs);
42
+ const commentDispatchTimer = setInterval(() => {
43
+ if (commentDispatchRunning) {
44
+ return;
45
+ }
46
+ commentDispatchRunning = true;
47
+ void runIssueCommentDispatchSweep(db, companyId, { realtimeHub })
48
+ .catch((error) => {
49
+ // eslint-disable-next-line no-console
50
+ console.error("[scheduler] comment dispatch sweep failed", error);
51
+ })
52
+ .finally(() => {
53
+ commentDispatchRunning = false;
54
+ });
55
+ }, commentDispatchIntervalMs);
56
+ return () => {
57
+ clearInterval(heartbeatTimer);
58
+ clearInterval(queueTimer);
59
+ clearInterval(commentDispatchTimer);
60
+ };
23
61
  }