@rubytech/taskmaster 1.0.110 → 1.0.111

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.
@@ -6,7 +6,7 @@
6
6
  <title>Taskmaster Control</title>
7
7
  <meta name="color-scheme" content="dark light" />
8
8
  <link rel="icon" type="image/png" href="./favicon.png" />
9
- <script type="module" crossorigin src="./assets/index-D4TpiIHx.js"></script>
9
+ <script type="module" crossorigin src="./assets/index-Cp_azZBu.js"></script>
10
10
  <link rel="stylesheet" crossorigin href="./assets/index-BM3zZtpB.css">
11
11
  </head>
12
12
  <body>
@@ -2,8 +2,9 @@
2
2
  * Deliver OTP verification codes via WhatsApp.
3
3
  */
4
4
  import { sendMessageWhatsApp } from "../../web/outbound.js";
5
- export async function deliverOtp(phone, code) {
5
+ export async function deliverOtp(phone, code, accountId) {
6
6
  await sendMessageWhatsApp(phone, `Your verification code is: ${code}`, {
7
7
  verbose: false,
8
+ accountId,
8
9
  });
9
10
  }
@@ -26,6 +26,7 @@ import path from "node:path";
26
26
  import { resolveAgentWorkspaceDir, resolveSessionAgentId } from "../agents/agent-scope.js";
27
27
  import { resolveEffectiveMessagesConfig, resolveIdentityName } from "../agents/identity.js";
28
28
  import { dispatchInboundMessage } from "../auto-reply/dispatch.js";
29
+ import { resolveAgentBoundAccountId } from "../routing/bindings.js";
29
30
  import { createReplyDispatcher } from "../auto-reply/reply/reply-dispatcher.js";
30
31
  import { extractShortModelName, } from "../auto-reply/reply/response-prefix-template.js";
31
32
  import { loadConfig } from "../config/config.js";
@@ -202,7 +203,7 @@ async function handleSession(req, res, accountId, cfg, maxBodyBytes) {
202
203
  // ---------------------------------------------------------------------------
203
204
  // Route: POST /otp/request
204
205
  // ---------------------------------------------------------------------------
205
- async function handleOtpRequest(req, res, _accountId, cfg, maxBodyBytes) {
206
+ async function handleOtpRequest(req, res, accountId, cfg, maxBodyBytes) {
206
207
  if (req.method !== "POST") {
207
208
  sendMethodNotAllowed(res);
208
209
  return;
@@ -228,8 +229,12 @@ async function handleOtpRequest(req, res, _accountId, cfg, maxBodyBytes) {
228
229
  });
229
230
  return;
230
231
  }
232
+ // Resolve the WhatsApp account bound to this account's public agent so the
233
+ // OTP code is sent from the correct number (not the first active account).
234
+ const agentId = resolvePublicAgentId(cfg, accountId);
235
+ const whatsappAccountId = resolveAgentBoundAccountId(cfg, agentId, "whatsapp") ?? undefined;
231
236
  try {
232
- await deliverOtp(phone, result.code);
237
+ await deliverOtp(phone, result.code, whatsappAccountId);
233
238
  }
234
239
  catch {
235
240
  sendUnavailable(res, "failed to send verification code — is WhatsApp connected?");
@@ -2,6 +2,7 @@
2
2
  * RPC handlers for public chat: OTP verification and session resolution.
3
3
  */
4
4
  import { loadConfig } from "../../config/config.js";
5
+ import { resolveAgentBoundAccountId } from "../../routing/bindings.js";
5
6
  import { ErrorCodes, errorShape } from "../protocol/index.js";
6
7
  import { requestOtp, verifyOtp } from "../public-chat/otp.js";
7
8
  import { deliverOtp } from "../public-chat/deliver-otp.js";
@@ -29,6 +30,7 @@ export const publicChatHandlers = {
29
30
  */
30
31
  "public.otp.request": async ({ params, respond, context }) => {
31
32
  const phone = typeof params.phone === "string" ? normalizePhone(params.phone.trim()) : "";
33
+ const accountId = validateAccountId(params.accountId);
32
34
  if (!phone || !isValidPhone(phone)) {
33
35
  respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "invalid phone number"));
34
36
  return;
@@ -43,8 +45,14 @@ export const publicChatHandlers = {
43
45
  respond(false, { retryAfterMs: result.retryAfterMs }, errorShape(ErrorCodes.INVALID_REQUEST, "rate limited — try again shortly"));
44
46
  return;
45
47
  }
48
+ // Resolve the WhatsApp account bound to this account's public agent so the
49
+ // OTP code is sent from the correct number (not the first active account).
50
+ const agentId = accountId ? resolvePublicAgentId(cfg, accountId) : undefined;
51
+ const whatsappAccountId = agentId
52
+ ? (resolveAgentBoundAccountId(cfg, agentId, "whatsapp") ?? undefined)
53
+ : undefined;
46
54
  try {
47
- await deliverOtp(phone, result.code);
55
+ await deliverOtp(phone, result.code, whatsappAccountId);
48
56
  }
49
57
  catch (err) {
50
58
  context.logGateway.warn(`public-chat OTP delivery failed: ${String(err)}`);
@@ -885,25 +885,17 @@ export class MemoryIndexManager {
885
885
  throw err;
886
886
  }
887
887
  }
888
- async swapIndexFiles(targetPath, tempPath) {
889
- const backupPath = `${targetPath}.backup-${randomUUID()}`;
890
- await this.moveIndexFiles(targetPath, backupPath);
891
- try {
892
- await this.moveIndexFiles(tempPath, targetPath);
893
- }
894
- catch (err) {
895
- await this.moveIndexFiles(backupPath, targetPath);
896
- throw err;
897
- }
898
- await this.removeIndexFiles(backupPath);
888
+ async removeIndexFiles(basePath) {
889
+ const suffixes = ["", "-wal", "-shm"];
890
+ await Promise.all(suffixes.map((suffix) => fs.rm(`${basePath}${suffix}`, { force: true })));
899
891
  }
900
- async moveIndexFiles(sourceBase, targetBase) {
892
+ moveIndexFilesSync(sourceBase, targetBase) {
901
893
  const suffixes = ["", "-wal", "-shm"];
902
894
  for (const suffix of suffixes) {
903
895
  const source = `${sourceBase}${suffix}`;
904
896
  const target = `${targetBase}${suffix}`;
905
897
  try {
906
- await fs.rename(source, target);
898
+ fsSync.renameSync(source, target);
907
899
  }
908
900
  catch (err) {
909
901
  if (err.code !== "ENOENT") {
@@ -912,9 +904,23 @@ export class MemoryIndexManager {
912
904
  }
913
905
  }
914
906
  }
915
- async removeIndexFiles(basePath) {
916
- const suffixes = ["", "-wal", "-shm"];
917
- await Promise.all(suffixes.map((suffix) => fs.rm(`${basePath}${suffix}`, { force: true })));
907
+ /**
908
+ * Swap index files synchronously. This must be sync to avoid an async yield
909
+ * between closing the old database and opening the new one — any await in
910
+ * that window lets concurrent search/read/write operations hit a closed
911
+ * database handle, causing "attempt to write a readonly database" errors.
912
+ */
913
+ swapIndexFilesSync(targetPath, tempPath) {
914
+ const backupPath = `${targetPath}.backup-${randomUUID()}`;
915
+ this.moveIndexFilesSync(targetPath, backupPath);
916
+ try {
917
+ this.moveIndexFilesSync(tempPath, targetPath);
918
+ }
919
+ catch (err) {
920
+ this.moveIndexFilesSync(backupPath, targetPath);
921
+ throw err;
922
+ }
923
+ this.removeIndexFilesSync(backupPath);
918
924
  }
919
925
  removeIndexFilesSync(basePath) {
920
926
  const suffixes = ["", "-wal", "-shm"];
@@ -1590,7 +1596,10 @@ export class MemoryIndexManager {
1590
1596
  this.db.close();
1591
1597
  originalDb.close();
1592
1598
  originalDbClosed = true;
1593
- await this.swapIndexFiles(dbPath, tempDbPath);
1599
+ // Sync swap: close → rename → reopen must be atomic from the event
1600
+ // loop's perspective. An async swap here would yield control and let
1601
+ // concurrent search/read/write hit the closed this.db handle.
1602
+ this.swapIndexFilesSync(dbPath, tempDbPath);
1594
1603
  this.db = this.openDatabaseAtPath(dbPath);
1595
1604
  this.vectorReady = null;
1596
1605
  this.vector.available = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/taskmaster",
3
- "version": "1.0.110",
3
+ "version": "1.0.111",
4
4
  "description": "AI-powered business assistant for small businesses",
5
5
  "publishConfig": {
6
6
  "access": "public"