@rubytech/taskmaster 1.0.109 → 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.
- package/dist/agents/tool-images.js +1 -1
- package/dist/agents/tools/message-tool.js +29 -32
- package/dist/auto-reply/reply/agent-runner-execution.js +1 -1
- package/dist/auto-reply/reply/get-reply-inline-actions.js +4 -1
- package/dist/auto-reply/reply/get-reply-run.js +4 -1
- package/dist/build-info.json +3 -3
- package/dist/control-ui/assets/{index-D4TpiIHx.js → index-Cp_azZBu.js} +2 -2
- package/dist/control-ui/assets/index-Cp_azZBu.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/gateway/public-chat/deliver-otp.js +2 -1
- package/dist/gateway/public-chat-api.js +7 -2
- package/dist/gateway/server-methods/public-chat.js +9 -1
- package/dist/memory/manager.js +26 -17
- package/package.json +1 -1
- package/taskmaster-docs/USER-GUIDE.md +10 -8
- package/dist/control-ui/assets/index-D4TpiIHx.js.map +0 -1
|
@@ -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-
|
|
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,
|
|
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)}`);
|
package/dist/memory/manager.js
CHANGED
|
@@ -885,25 +885,17 @@ export class MemoryIndexManager {
|
|
|
885
885
|
throw err;
|
|
886
886
|
}
|
|
887
887
|
}
|
|
888
|
-
async
|
|
889
|
-
const
|
|
890
|
-
await
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
916
|
-
|
|
917
|
-
|
|
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
|
-
|
|
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
|
@@ -641,19 +641,21 @@ Each account can have its own branding — a logo and colour scheme that appears
|
|
|
641
641
|
|
|
642
642
|
| Setting | What it does |
|
|
643
643
|
|---------|-------------|
|
|
644
|
-
| **Logo** | Your business logo, shown on public chat pages
|
|
645
|
-
| **Accent colour** | The colour used for buttons, links, and interactive elements
|
|
646
|
-
| **Background tint** | A
|
|
644
|
+
| **Logo** | Your business logo, shown in the navigation header and on public chat pages |
|
|
645
|
+
| **Accent colour** | The colour used for buttons, links, and interactive elements. Button text automatically switches to dark or light for readability. |
|
|
646
|
+
| **Background tint** | A colour wash blended into page backgrounds, cards, chat bubbles, and input fields |
|
|
647
647
|
|
|
648
648
|
### How to set up branding
|
|
649
649
|
|
|
650
650
|
1. Go to the **Setup** page
|
|
651
651
|
2. Find the **Branding** row in the status dashboard
|
|
652
652
|
3. Click **Edit** — a modal opens with your branding options
|
|
653
|
-
4. **Upload a logo** — click Upload and select an image (PNG, JPEG, SVG, or WebP, max 2 MB)
|
|
654
|
-
5. **Choose your accent colour** — click the colour picker
|
|
655
|
-
6. **Choose your background tint** — click the colour picker
|
|
656
|
-
7.
|
|
653
|
+
4. **Upload a logo** — click Upload and select an image (PNG, JPEG, SVG, or WebP, max 2 MB). The logo appears in the navigation header and on public chat pages.
|
|
654
|
+
5. **Choose your accent colour** — click the colour picker, or type a hex code (e.g. `#7C8C72`) directly into the text field and press Enter
|
|
655
|
+
6. **Choose your background tint** — click the colour picker or type a hex code to add a colour wash across page backgrounds, cards, chat bubbles, and input fields (optional)
|
|
656
|
+
7. Close the modal with the **×** button — changes are applied immediately
|
|
657
|
+
|
|
658
|
+
To clear an individual colour without resetting everything, click the **Clear** button next to that colour.
|
|
657
659
|
|
|
658
660
|
To see what each setting does, click the **(i)** button on the Branding row.
|
|
659
661
|
|
|
@@ -663,7 +665,7 @@ Branding is set per account. If you have multiple accounts, switch to each accou
|
|
|
663
665
|
|
|
664
666
|
### Resetting to defaults
|
|
665
667
|
|
|
666
|
-
To remove all custom branding and return to the default colours, open the Branding modal and click **Reset to defaults**. To remove just the logo, click **Remove** next to the logo preview.
|
|
668
|
+
To remove all custom branding and return to the default colours, open the Branding modal and click **Reset to defaults**. To remove just one colour, click **Clear** next to it. To remove just the logo, click **Remove** next to the logo preview.
|
|
667
669
|
|
|
668
670
|
### Where branding appears
|
|
669
671
|
|