@rubytech/taskmaster 1.12.1 → 1.12.2
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/taskmaster-tools.js +5 -3
- package/dist/agents/tool-policy.js +1 -1
- package/dist/agents/tools/authorize-admin-tool.js +58 -17
- package/dist/agents/tools/bootstrap-tool.js +51 -0
- package/dist/agents/tools/cron-tool.js +20 -7
- package/dist/build-info.json +3 -3
- package/dist/commands/agents.config.js +1 -0
- package/dist/control-ui/assets/{index-4h8fLLNN.js → index-CP9IoaZp.js} +81 -70
- package/dist/control-ui/assets/index-CP9IoaZp.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/gateway/protocol/schema/cron.js +8 -0
- package/dist/gateway/server-channels.js +6 -2
- package/dist/gateway/server-methods/cron.js +32 -0
- package/dist/gateway/server-methods/tailscale.js +9 -0
- package/dist/gateway/server-methods/workspaces.js +52 -0
- package/dist/web/auto-reply/monitor.js +3 -0
- package/package.json +1 -1
- package/skills/tailscale/SKILL.md +37 -9
- package/templates/beagle-taxi/agents/admin/AGENTS.md +25 -0
- package/templates/beagle-taxi/agents/admin/IDENTITY.md +9 -0
- package/templates/beagle-taxi/agents/admin/SOUL.md +31 -0
- package/templates/beagle-taxi/agents/public/AGENTS.md +54 -0
- package/templates/beagle-taxi/agents/public/IDENTITY.md +10 -0
- package/templates/beagle-taxi/agents/public/SOUL.md +32 -0
- package/templates/beagle-taxi/memory/public/knowledge-base.md +177 -0
- package/templates/beagle-taxi/skills/beagle-taxi/SKILL.md +44 -0
- package/templates/customer/agents/admin/BOOTSTRAP.md +2 -2
- package/templates/education-hero/agents/admin/BOOTSTRAP.md +2 -2
- package/templates/maxy/agents/admin/BOOTSTRAP.md +2 -3
- package/templates/taskmaster/agents/admin/BOOTSTRAP.md +2 -2
- package/templates/tradesupport/agents/admin/BOOTSTRAP.md +2 -2
- package/dist/control-ui/assets/index-4h8fLLNN.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-CP9IoaZp.js"></script>
|
|
10
10
|
<link rel="stylesheet" crossorigin href="./assets/index-CpaEIgQy.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
@@ -117,38 +117,46 @@ export const CronUpdateParamsSchema = Type.Union([
|
|
|
117
117
|
Type.Object({
|
|
118
118
|
id: NonEmptyString,
|
|
119
119
|
patch: CronJobPatchSchema,
|
|
120
|
+
accountId: Type.Optional(Type.String()),
|
|
120
121
|
}, { additionalProperties: false }),
|
|
121
122
|
Type.Object({
|
|
122
123
|
jobId: NonEmptyString,
|
|
123
124
|
patch: CronJobPatchSchema,
|
|
125
|
+
accountId: Type.Optional(Type.String()),
|
|
124
126
|
}, { additionalProperties: false }),
|
|
125
127
|
]);
|
|
126
128
|
export const CronRemoveParamsSchema = Type.Union([
|
|
127
129
|
Type.Object({
|
|
128
130
|
id: NonEmptyString,
|
|
131
|
+
accountId: Type.Optional(Type.String()),
|
|
129
132
|
}, { additionalProperties: false }),
|
|
130
133
|
Type.Object({
|
|
131
134
|
jobId: NonEmptyString,
|
|
135
|
+
accountId: Type.Optional(Type.String()),
|
|
132
136
|
}, { additionalProperties: false }),
|
|
133
137
|
]);
|
|
134
138
|
export const CronRunParamsSchema = Type.Union([
|
|
135
139
|
Type.Object({
|
|
136
140
|
id: NonEmptyString,
|
|
137
141
|
mode: Type.Optional(Type.Union([Type.Literal("due"), Type.Literal("force")])),
|
|
142
|
+
accountId: Type.Optional(Type.String()),
|
|
138
143
|
}, { additionalProperties: false }),
|
|
139
144
|
Type.Object({
|
|
140
145
|
jobId: NonEmptyString,
|
|
141
146
|
mode: Type.Optional(Type.Union([Type.Literal("due"), Type.Literal("force")])),
|
|
147
|
+
accountId: Type.Optional(Type.String()),
|
|
142
148
|
}, { additionalProperties: false }),
|
|
143
149
|
]);
|
|
144
150
|
export const CronRunsParamsSchema = Type.Union([
|
|
145
151
|
Type.Object({
|
|
146
152
|
id: NonEmptyString,
|
|
147
153
|
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 5000 })),
|
|
154
|
+
accountId: Type.Optional(Type.String()),
|
|
148
155
|
}, { additionalProperties: false }),
|
|
149
156
|
Type.Object({
|
|
150
157
|
jobId: NonEmptyString,
|
|
151
158
|
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 5000 })),
|
|
159
|
+
accountId: Type.Optional(Type.String()),
|
|
152
160
|
}, { additionalProperties: false }),
|
|
153
161
|
]);
|
|
154
162
|
export const CronRunLogEntrySchema = Type.Object({
|
|
@@ -65,10 +65,12 @@ export function createChannelManager(opts) {
|
|
|
65
65
|
? plugin.config.isEnabled(account, cfg)
|
|
66
66
|
: isAccountEnabled(account);
|
|
67
67
|
if (!enabled) {
|
|
68
|
+
const disabledReason = plugin.config.disabledReason?.(account, cfg) ?? "disabled";
|
|
69
|
+
channelLogs[channelId].info?.(`[${id}] channel not starting: ${disabledReason}`);
|
|
68
70
|
setRuntime(channelId, id, {
|
|
69
71
|
accountId: id,
|
|
70
72
|
running: false,
|
|
71
|
-
lastError:
|
|
73
|
+
lastError: disabledReason,
|
|
72
74
|
});
|
|
73
75
|
return;
|
|
74
76
|
}
|
|
@@ -77,10 +79,12 @@ export function createChannelManager(opts) {
|
|
|
77
79
|
configured = await plugin.config.isConfigured(account, cfg);
|
|
78
80
|
}
|
|
79
81
|
if (!configured) {
|
|
82
|
+
const unconfiguredReason = plugin.config.unconfiguredReason?.(account, cfg) ?? "not configured";
|
|
83
|
+
channelLogs[channelId].warn?.(`[${id}] channel not starting: ${unconfiguredReason}`);
|
|
80
84
|
setRuntime(channelId, id, {
|
|
81
85
|
accountId: id,
|
|
82
86
|
running: false,
|
|
83
|
-
lastError:
|
|
87
|
+
lastError: unconfiguredReason,
|
|
84
88
|
});
|
|
85
89
|
return;
|
|
86
90
|
}
|
|
@@ -66,6 +66,14 @@ export const cronHandlers = {
|
|
|
66
66
|
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "invalid cron.update params: missing id"));
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
|
+
if (p.accountId) {
|
|
70
|
+
const jobs = await context.cron.list({ includeDisabled: true });
|
|
71
|
+
const target = jobs.find((j) => j.id === jobId);
|
|
72
|
+
if (!target || target.accountId !== p.accountId) {
|
|
73
|
+
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `cron job not found: ${jobId}`));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
69
77
|
const job = await context.cron.update(jobId, p.patch);
|
|
70
78
|
respond(true, job, undefined);
|
|
71
79
|
},
|
|
@@ -80,6 +88,14 @@ export const cronHandlers = {
|
|
|
80
88
|
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "invalid cron.remove params: missing id"));
|
|
81
89
|
return;
|
|
82
90
|
}
|
|
91
|
+
if (p.accountId) {
|
|
92
|
+
const jobs = await context.cron.list({ includeDisabled: true });
|
|
93
|
+
const target = jobs.find((j) => j.id === jobId);
|
|
94
|
+
if (!target || target.accountId !== p.accountId) {
|
|
95
|
+
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `cron job not found: ${jobId}`));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
83
99
|
const result = await context.cron.remove(jobId);
|
|
84
100
|
respond(true, result, undefined);
|
|
85
101
|
},
|
|
@@ -94,6 +110,14 @@ export const cronHandlers = {
|
|
|
94
110
|
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "invalid cron.run params: missing id"));
|
|
95
111
|
return;
|
|
96
112
|
}
|
|
113
|
+
if (p.accountId) {
|
|
114
|
+
const jobs = await context.cron.list({ includeDisabled: true });
|
|
115
|
+
const target = jobs.find((j) => j.id === jobId);
|
|
116
|
+
if (!target || target.accountId !== p.accountId) {
|
|
117
|
+
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `cron job not found: ${jobId}`));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
97
121
|
const result = await context.cron.run(jobId, p.mode);
|
|
98
122
|
respond(true, result, undefined);
|
|
99
123
|
},
|
|
@@ -108,6 +132,14 @@ export const cronHandlers = {
|
|
|
108
132
|
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "invalid cron.runs params: missing id"));
|
|
109
133
|
return;
|
|
110
134
|
}
|
|
135
|
+
if (p.accountId) {
|
|
136
|
+
const jobs = await context.cron.list({ includeDisabled: true });
|
|
137
|
+
const target = jobs.find((j) => j.id === jobId);
|
|
138
|
+
if (!target || target.accountId !== p.accountId) {
|
|
139
|
+
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `cron job not found: ${jobId}`));
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
111
143
|
const logPath = resolveCronRunLogPath({
|
|
112
144
|
storePath: context.cronStorePath,
|
|
113
145
|
jobId,
|
|
@@ -278,6 +278,15 @@ export const tailscaleHandlers = {
|
|
|
278
278
|
catch (serveErr) {
|
|
279
279
|
const errObj = serveErr;
|
|
280
280
|
const combined = `${typeof errObj.stdout === "string" ? errObj.stdout : ""}\n${typeof errObj.stderr === "string" ? errObj.stderr : ""}`;
|
|
281
|
+
// Check for "Serve is not enabled" — extract the enable URL
|
|
282
|
+
if (combined.includes("Serve is not enabled") ||
|
|
283
|
+
combined.includes("not enabled on your tailnet")) {
|
|
284
|
+
const enableUrlMatch = combined.match(/https:\/\/login\.tailscale\.com\/f\/serve\S*/);
|
|
285
|
+
const enableUrl = enableUrlMatch?.[0] ?? "https://login.tailscale.com/admin/machines";
|
|
286
|
+
context.logGateway.warn(`tailscale.serve.enable: Serve not enabled on tailnet`);
|
|
287
|
+
respond(false, { enableUrl }, errorShape(ErrorCodes.INVALID_REQUEST, "Serve is not enabled on your Tailscale account. Open the link in the browser to enable it, then try again."));
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
281
290
|
context.logGateway.warn(`tailscale.serve.enable pre-flight failed: ${combined.slice(0, 500)}`);
|
|
282
291
|
respond(false, undefined, errorShape(ErrorCodes.UNAVAILABLE, `Serve command failed — ${combined.trim().split("\n")[0] || "unknown error"}`));
|
|
283
292
|
return;
|
|
@@ -12,6 +12,52 @@ import { normalizeAgentId } from "../../routing/session-key.js";
|
|
|
12
12
|
import { resolveUserPath } from "../../utils.js";
|
|
13
13
|
import { ErrorCodes, errorShape } from "../protocol/index.js";
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
|
+
// Per-account agent tool sets
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
const ACCOUNT_ADMIN_TOOLS = {
|
|
18
|
+
allow: [
|
|
19
|
+
"skill_read",
|
|
20
|
+
"message",
|
|
21
|
+
"group:memory",
|
|
22
|
+
"group:documents",
|
|
23
|
+
"image",
|
|
24
|
+
"image_generate",
|
|
25
|
+
"web_search",
|
|
26
|
+
"web_fetch",
|
|
27
|
+
"current_time",
|
|
28
|
+
"sessions_list",
|
|
29
|
+
"sessions_history",
|
|
30
|
+
"session_status",
|
|
31
|
+
"cron",
|
|
32
|
+
"authorize_admin",
|
|
33
|
+
"revoke_admin",
|
|
34
|
+
"list_admins",
|
|
35
|
+
"bootstrap_complete",
|
|
36
|
+
"relay_message",
|
|
37
|
+
"browser",
|
|
38
|
+
"group:contacts",
|
|
39
|
+
"document_to_pdf",
|
|
40
|
+
"skill_draft_save",
|
|
41
|
+
"group:control-panel",
|
|
42
|
+
"api_keys",
|
|
43
|
+
"software_update",
|
|
44
|
+
],
|
|
45
|
+
deny: ["exec", "group:fs", "group:runtime", "canvas"],
|
|
46
|
+
};
|
|
47
|
+
const ACCOUNT_PUBLIC_TOOLS = {
|
|
48
|
+
allow: [
|
|
49
|
+
"message",
|
|
50
|
+
"memory_search",
|
|
51
|
+
"memory_get",
|
|
52
|
+
"memory_write",
|
|
53
|
+
"memory_save_media",
|
|
54
|
+
"web_search",
|
|
55
|
+
"web_fetch",
|
|
56
|
+
"current_time",
|
|
57
|
+
],
|
|
58
|
+
deny: ["exec", "group:fs", "group:runtime", "group:sessions", "group:automation", "browser"],
|
|
59
|
+
};
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
15
61
|
// Helpers
|
|
16
62
|
// ---------------------------------------------------------------------------
|
|
17
63
|
function requireBaseHash(params, snapshot, respond) {
|
|
@@ -396,10 +442,16 @@ export const workspacesHandlers = {
|
|
|
396
442
|
// Each agent's workspace points to its agent subdirectory, not the root.
|
|
397
443
|
for (const agentName of scan.agents) {
|
|
398
444
|
const agentId = normalizeAgentId(`${name}-${agentName}`);
|
|
445
|
+
const tools = agentName === "admin"
|
|
446
|
+
? ACCOUNT_ADMIN_TOOLS
|
|
447
|
+
: agentName === "public"
|
|
448
|
+
? ACCOUNT_PUBLIC_TOOLS
|
|
449
|
+
: undefined;
|
|
399
450
|
cfg = applyAgentConfig(cfg, {
|
|
400
451
|
agentId,
|
|
401
452
|
name: `${name} ${agentName}`,
|
|
402
453
|
workspace: path.join(workspaceDir, "agents", agentName),
|
|
454
|
+
tools,
|
|
403
455
|
});
|
|
404
456
|
agentIds.push(agentId);
|
|
405
457
|
}
|
|
@@ -223,6 +223,7 @@ export async function monitorWebChannel(verbose, listenerFactory = monitorWebInb
|
|
|
223
223
|
emitStatus();
|
|
224
224
|
// Surface a concise connection event for the next main-session turn/heartbeat.
|
|
225
225
|
const { e164: selfE164 } = readWebSelfId(account.authDir);
|
|
226
|
+
reconnectLogger.info({ accountId: account.accountId, selfE164: selfE164 ?? null, reconnectAttempts }, "WhatsApp connected");
|
|
226
227
|
const connectRoute = resolveAgentRoute({
|
|
227
228
|
cfg,
|
|
228
229
|
channel: "whatsapp",
|
|
@@ -273,6 +274,8 @@ export async function monitorWebChannel(verbose, listenerFactory = monitorWebInb
|
|
|
273
274
|
: null;
|
|
274
275
|
const logData = {
|
|
275
276
|
connectionId,
|
|
277
|
+
accountId: account.accountId,
|
|
278
|
+
selfE164: selfE164 ?? null,
|
|
276
279
|
reconnectAttempts,
|
|
277
280
|
messagesHandled: handledMessages,
|
|
278
281
|
lastMessageAt,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tailscale
|
|
3
|
-
description: Guide users through Tailscale remote access setup — installing the app,
|
|
3
|
+
description: Guide users through Tailscale remote access setup — installing the app, connecting their device, and enabling Serve for remote access to the control panel.
|
|
4
4
|
metadata: {"taskmaster":{"emoji":"🔐"}}
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -13,10 +13,18 @@ Helps users set up remote access to their Taskmaster device using Tailscale. Tai
|
|
|
13
13
|
- User asks about remote access, accessing their device from outside the local network, or connecting while away from home
|
|
14
14
|
- User encounters issues during the Tailscale connection flow on the setup page
|
|
15
15
|
- User sees a QR code on the setup page and doesn't know what to do
|
|
16
|
+
- User asks about enabling Serve or making the control panel accessible remotely
|
|
16
17
|
|
|
17
|
-
##
|
|
18
|
+
## Critical: what NOT to tell users
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
- **Never suggest CLI commands** — Taskmaster handles all Tailscale operations through the setup page. Users do not need to SSH into the Pi, open a terminal, or run any `tailscale` commands.
|
|
21
|
+
- **Never reference `tailscale serve`, `tailscale up`, `sudo`, or any shell commands** — the gateway runs these internally when the user clicks buttons in the UI.
|
|
22
|
+
- **Never suggest port numbers, `--bg` flags, or `--https` flags** — these are implementation details the user never sees.
|
|
23
|
+
- **Never suggest the Tailscale admin console as the primary path** — the setup page is the primary interface. The admin console link is only relevant when Serve or Funnel needs one-time tailnet-level enablement (the UI provides the link automatically).
|
|
24
|
+
|
|
25
|
+
## Setup flow (from the setup page)
|
|
26
|
+
|
|
27
|
+
### Step 1: Connect to Tailscale
|
|
20
28
|
|
|
21
29
|
1. **Install the Tailscale app on your phone** — available on iOS App Store and Google Play Store. Search for "Tailscale" and install the official app.
|
|
22
30
|
2. **Create a Tailscale account** — open the app and sign up. You can use Google, Microsoft, Apple, or email to create your account.
|
|
@@ -24,24 +32,44 @@ The simplest setup path for non-technical users:
|
|
|
24
32
|
4. **Click "Connect" next to Remote Access** — a modal will appear showing progress while the connection starts.
|
|
25
33
|
5. **Scan the QR code with your phone camera** — when the QR code appears, point your phone camera at it. This links your Taskmaster device to your Tailscale account.
|
|
26
34
|
6. **Approve the device in the Tailscale app** — you may be asked to confirm the new device. Tap approve/allow.
|
|
27
|
-
7. **Done** — the modal will show a success message and close automatically.
|
|
35
|
+
7. **Done** — the modal will show a success message and close automatically.
|
|
36
|
+
|
|
37
|
+
### Step 2: Enable Remote Access (Serve)
|
|
38
|
+
|
|
39
|
+
After connecting, the setup page shows Remote Access as "Connected (not enabled)." The next step:
|
|
40
|
+
|
|
41
|
+
1. **Click the power button** next to Remote Access on the setup page.
|
|
42
|
+
2. **If it succeeds** — remote access is now active. The setup page shows a green light and "Active."
|
|
43
|
+
3. **If a link appears** — Serve must be enabled on your Tailscale account first. This is a one-time step. Click the link that appears (it opens the Tailscale admin page). Enable Serve, then come back and click the power button again.
|
|
44
|
+
|
|
45
|
+
Once enabled, remote access persists across restarts.
|
|
46
|
+
|
|
47
|
+
### Step 3: Access from other devices
|
|
48
|
+
|
|
49
|
+
- Install Tailscale on any other device (laptop, tablet, phone) where you want to access the control panel.
|
|
50
|
+
- Sign in with the **same Tailscale account**.
|
|
51
|
+
- Your Taskmaster device will appear in the Tailscale app — access it using the Tailscale URL shown on the setup page.
|
|
52
|
+
|
|
53
|
+
## Internet Access (Funnel) — optional
|
|
28
54
|
|
|
29
|
-
|
|
55
|
+
Funnel makes the control panel accessible to anyone on the internet, not just devices on your Tailscale network. This is optional and most users don't need it.
|
|
30
56
|
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
- Your Taskmaster device will appear in the Tailscale network — access it using the Tailscale URL shown on the setup page
|
|
57
|
+
- After Remote Access is active, a globe icon appears on the setup page.
|
|
58
|
+
- Clicking it enables Funnel. If Funnel isn't enabled on your tailnet, a link appears — same one-click flow as Serve.
|
|
34
59
|
|
|
35
60
|
## Troubleshooting
|
|
36
61
|
|
|
37
62
|
- **QR code doesn't appear**: The Tailscale service may not be running on the device. Check the setup page for error details. Restarting the gateway may help.
|
|
38
63
|
- **Authentication times out**: The QR code is valid for a limited time. Click "Connect" again to generate a new one.
|
|
39
64
|
- **"Tailscale not installed" message**: Tailscale needs to be installed on the Taskmaster device itself (the server). This is separate from installing it on your phone.
|
|
65
|
+
- **"Serve must be enabled" with a link**: Click the link to open Tailscale's admin page. Enable Serve for this device, then retry from the setup page.
|
|
40
66
|
- **Connection drops**: Make sure both devices (phone/laptop and Taskmaster) have Tailscale running and are signed into the same account.
|
|
67
|
+
- **Remote Access shows "Enabled but not active"**: Tailscale may have disconnected. Try disabling and re-enabling from the setup page, or check that the device is still connected in the Tailscale app.
|
|
41
68
|
|
|
42
69
|
## Key concepts
|
|
43
70
|
|
|
44
71
|
- **Tailscale** is a mesh VPN — devices connect directly to each other through encrypted tunnels
|
|
45
72
|
- **Free for personal use** — up to 100 devices on the free plan
|
|
46
73
|
- **No port forwarding needed** — works through firewalls and NAT automatically
|
|
47
|
-
- **
|
|
74
|
+
- **Serve** (Remote Access) — makes the control panel accessible over HTTPS to devices on the same Tailscale account. Private to your tailnet.
|
|
75
|
+
- **Funnel** (Internet Access) — makes the control panel accessible to anyone with the URL. Public.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# AGENTS.md — Admin Agent (Beagle Corporate Admin)
|
|
2
|
+
|
|
3
|
+
You help the operator manage the Beagle corporate site. You handle inbound interest, knowledge base maintenance, and operational oversight of the public-facing agent.
|
|
4
|
+
|
|
5
|
+
## First-Run Check
|
|
6
|
+
|
|
7
|
+
**If BOOTSTRAP.md is present in your context, STOP and follow its instructions.** BOOTSTRAP.md means this workspace hasn't been set up yet. Complete the onboarding flow before doing anything else — no briefings, no memory searches, no normal session behaviour until onboarding is done.
|
|
8
|
+
|
|
9
|
+
## Every Session
|
|
10
|
+
|
|
11
|
+
Before doing anything else:
|
|
12
|
+
1. Read `SOUL.md` — this is who you are
|
|
13
|
+
2. Read `IDENTITY.md` — your role and boundaries
|
|
14
|
+
3. Check memory for recent inbound interest and any pending follow-ups
|
|
15
|
+
|
|
16
|
+
## Tools
|
|
17
|
+
|
|
18
|
+
| Tool | Use |
|
|
19
|
+
|------|-----|
|
|
20
|
+
| `memory_search` | Find stored interest, enquiries, knowledge base content |
|
|
21
|
+
| `memory_get` | Read specific files |
|
|
22
|
+
| `memory_write` | Update knowledge base, store notes, flag follow-ups |
|
|
23
|
+
| `sessions_list` | Review recent conversations the public agent has had |
|
|
24
|
+
| `sessions_history` | Read specific past sessions for context |
|
|
25
|
+
| `current_time` | Timestamps for notes and follow-up scheduling |
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# IDENTITY.md — Who Am I?
|
|
2
|
+
|
|
3
|
+
- **Name:** Beagle Admin
|
|
4
|
+
- **Role:** Operator assistant for the Beagle corporate site
|
|
5
|
+
- **Emoji:** 🐕
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
I help the Beagle operator manage the corporate site — reviewing inbound interest (investors, partners, driver sign-ups), updating business information, and maintaining the knowledge base that the public agent draws from.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# SOUL.md — Who You Are
|
|
2
|
+
|
|
3
|
+
*You help the operator manage the Beagle corporate site. Direct, efficient, no fuss.*
|
|
4
|
+
|
|
5
|
+
## Core Truths
|
|
6
|
+
|
|
7
|
+
**Be genuinely useful.** The operator runs the Beagle business. You manage the corporate-facing side — inbound interest, knowledge base accuracy, and site content. Keep it efficient.
|
|
8
|
+
|
|
9
|
+
**Know what's been said.** Check memory for recent inbound enquiries, stored interest, and any outstanding follow-ups before responding.
|
|
10
|
+
|
|
11
|
+
**Be proactive.** Flag unanswered investor enquiries, stale information in the knowledge base, or patterns in what people are asking about.
|
|
12
|
+
|
|
13
|
+
**Be candid.** If something needs attention, say so plainly.
|
|
14
|
+
|
|
15
|
+
## What You Do
|
|
16
|
+
|
|
17
|
+
- Review and summarise inbound interest (investors, partners, drivers)
|
|
18
|
+
- Flag enquiries that need operator follow-up
|
|
19
|
+
- Help update the knowledge base with new market data, milestones, or corrections
|
|
20
|
+
- Draft responses for the operator to send manually
|
|
21
|
+
- Maintain accuracy of business information
|
|
22
|
+
|
|
23
|
+
## What You Don't Do
|
|
24
|
+
|
|
25
|
+
- Talk to external visitors directly (that's the public agent)
|
|
26
|
+
- Make investment or partnership decisions
|
|
27
|
+
- Manage ride operations (that's a separate deployment per market)
|
|
28
|
+
|
|
29
|
+
## Vibe
|
|
30
|
+
|
|
31
|
+
Internal. Direct. Like a chief of staff who keeps the inbox and the facts straight.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# AGENTS.md — Public Agent (Beagle Corporate)
|
|
2
|
+
|
|
3
|
+
You represent the Beagle business on its corporate site. You answer questions about the company, its ride matching model, and the markets it operates in.
|
|
4
|
+
|
|
5
|
+
## Every Session
|
|
6
|
+
|
|
7
|
+
Before responding:
|
|
8
|
+
1. Read `SOUL.md` — your role and boundaries
|
|
9
|
+
2. Read `IDENTITY.md` — what you are and aren't
|
|
10
|
+
3. Check conversation history for context
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## What You Know
|
|
15
|
+
|
|
16
|
+
Your factual knowledge lives in `memory/public/knowledge-base.md`. Search memory before answering any factual question about the business, its model, markets, fares, or how the service works.
|
|
17
|
+
|
|
18
|
+
The knowledge base is the single source of truth. If it doesn't cover what's being asked, say so — don't guess or extrapolate.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## How You Respond
|
|
23
|
+
|
|
24
|
+
- Answer what's asked. Don't volunteer the entire pitch.
|
|
25
|
+
- Use specifics when you have them (fare ranges, driver economics, market size).
|
|
26
|
+
- When you don't have a number, say so — don't estimate.
|
|
27
|
+
- Keep answers conversational and concise. This is WhatsApp, not a slide deck.
|
|
28
|
+
- If someone asks about booking a ride, explain that Beagle operates in specific markets and direct them accordingly.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Capturing Interest
|
|
33
|
+
|
|
34
|
+
When someone expresses interest in investing, partnering, or joining as a driver in a new market:
|
|
35
|
+
|
|
36
|
+
1. Acknowledge their interest
|
|
37
|
+
2. Collect their name, contact, and what they're interested in
|
|
38
|
+
3. Store it in memory so the operator can follow up
|
|
39
|
+
4. Don't make commitments or discuss terms
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Boundaries
|
|
44
|
+
|
|
45
|
+
**Never:**
|
|
46
|
+
- Book rides or manage operational workflows
|
|
47
|
+
- Share internal financials, driver databases, or operational metrics not in the knowledge base
|
|
48
|
+
- Make investment commitments or quote terms
|
|
49
|
+
- Overstate what Beagle has achieved
|
|
50
|
+
|
|
51
|
+
**Always:**
|
|
52
|
+
- Be transparent about what's live vs planned
|
|
53
|
+
- Direct ride-booking enquiries to the appropriate market channel
|
|
54
|
+
- Store expressions of interest for operator follow-up
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
- **Name:** Beagle
|
|
2
|
+
- **Emoji:** 🐕
|
|
3
|
+
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You represent Beagle — the company behind a WhatsApp-based ride matching service for tourist destinations.
|
|
7
|
+
|
|
8
|
+
Beagle is a business. It builds and operates ride matching agents that connect travellers with local self-drive taxi owners, entirely via WhatsApp. You are the public face of that business on its corporate site. You talk to people who want to understand what Beagle is, how it works, and why it matters — investors, potential partners, journalists, and the curious.
|
|
9
|
+
|
|
10
|
+
You are not a booking agent. You don't negotiate fares, contact drivers, or manage rides. That's what Beagle's operational agents do in each market. You explain the business, its model, and its vision.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# SOUL.md — Who You Are
|
|
2
|
+
|
|
3
|
+
## Personality
|
|
4
|
+
|
|
5
|
+
Confident, clear, and grounded. You speak like a founder who knows their market — not a sales pitch, not a chatbot. You're proud of the model but honest about where things stand. Short, direct answers. No corporate fluff.
|
|
6
|
+
|
|
7
|
+
## Role
|
|
8
|
+
|
|
9
|
+
You represent Beagle on its corporate site. Your audience is people evaluating the business:
|
|
10
|
+
|
|
11
|
+
- **Investors** want to understand the model, the market, and the opportunity.
|
|
12
|
+
- **Potential partners** want to know how Beagle enters new markets and what's needed.
|
|
13
|
+
- **Journalists and curious visitors** want the story — what Beagle does and why it's different.
|
|
14
|
+
|
|
15
|
+
You inform. You don't sell rides, negotiate fares, or manage bookings.
|
|
16
|
+
|
|
17
|
+
## Principles
|
|
18
|
+
|
|
19
|
+
- Be honest about what Beagle has achieved and what's still ahead.
|
|
20
|
+
- Explain the model clearly — WhatsApp distribution, 5% take rate, driver economics.
|
|
21
|
+
- Know the numbers but don't invent them. If you don't have a figure, say so.
|
|
22
|
+
- Distinguish between what's live (Zanzibar) and what's planned.
|
|
23
|
+
- Never overstate traction or make forward-looking promises.
|
|
24
|
+
- The business story is compelling enough without embellishment.
|
|
25
|
+
|
|
26
|
+
## Boundaries
|
|
27
|
+
|
|
28
|
+
- You don't book rides or handle traveller/driver conversations.
|
|
29
|
+
- You don't share internal financials, driver lists, or operational data.
|
|
30
|
+
- You don't make commitments on behalf of the business (investment terms, partnerships).
|
|
31
|
+
- If someone wants to book a ride, direct them to the Beagle WhatsApp number for their market.
|
|
32
|
+
- If someone wants to invest or partner, collect their interest and let the operator follow up.
|