@rubytech/create-maxy-code 0.1.243 → 0.1.246
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/package.json +1 -1
- package/payload/platform/docs/superpowers/plans/2026-06-04-public-agent-knowledge-delivery.md +230 -0
- package/payload/platform/neo4j/schema.cypher +0 -5
- package/payload/platform/plugins/admin/mcp/dist/index.js +3 -4
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +19 -22
- package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +38 -72
- package/payload/platform/plugins/cloudflare/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/cloudflare/PLUGIN.md +10 -7
- package/payload/platform/plugins/cloudflare/references/api.md +166 -0
- package/payload/platform/plugins/cloudflare/references/d1-data-capture.md +157 -0
- package/payload/platform/plugins/cloudflare/references/dashboard-guide.md +6 -6
- package/payload/platform/plugins/cloudflare/references/hosting-sites.md +66 -0
- package/payload/platform/plugins/cloudflare/references/manual-setup.md +5 -3
- package/payload/platform/plugins/cloudflare/skills/cloudflare/SKILL.md +72 -0
- package/payload/platform/plugins/docs/references/cloudflare.md +4 -4
- package/payload/platform/plugins/docs/references/deployment.md +1 -1
- package/payload/platform/plugins/docs/references/internals.md +3 -6
- package/payload/platform/plugins/memory/PLUGIN.md +2 -2
- package/payload/platform/scripts/setup-account.sh +16 -8
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +1 -32
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts +12 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +7 -33
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +150 -132
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +2 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js +39 -6
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
- package/payload/platform/templates/agents/public/IDENTITY.md +9 -62
- package/payload/platform/templates/specialists/agents/personal-assistant.md +2 -2
- package/payload/server/{chunk-ZY6W3UA2.js → chunk-JMEX5NRX.js} +1 -22
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/server.js +7 -21
- package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +0 -55
- package/payload/platform/templates/agents/public/SOUL.md +0 -19
- package/payload/platform/templates/agents/public/config.json +0 -9
|
@@ -3627,13 +3627,11 @@ function estimateTokens(text) {
|
|
|
3627
3627
|
}
|
|
3628
3628
|
function resolveAgentConfig(accountDir, agentName) {
|
|
3629
3629
|
let model = null;
|
|
3630
|
-
let plugins = null;
|
|
3631
3630
|
let status = null;
|
|
3632
3631
|
let displayName = null;
|
|
3633
3632
|
let image = null;
|
|
3634
3633
|
let imageShape = null;
|
|
3635
3634
|
let showAgentName = false;
|
|
3636
|
-
let liveMemory = false;
|
|
3637
3635
|
let knowledgeKeywords = null;
|
|
3638
3636
|
let accessMode = "open";
|
|
3639
3637
|
const MAX_KNOWLEDGE_KEYWORDS = 5;
|
|
@@ -3647,7 +3645,6 @@ function resolveAgentConfig(accountDir, agentName) {
|
|
|
3647
3645
|
parsed = {};
|
|
3648
3646
|
}
|
|
3649
3647
|
model = typeof parsed.model === "string" ? parsed.model : null;
|
|
3650
|
-
plugins = Array.isArray(parsed.plugins) ? parsed.plugins : null;
|
|
3651
3648
|
status = typeof parsed.status === "string" ? parsed.status : null;
|
|
3652
3649
|
displayName = typeof parsed.displayName === "string" ? parsed.displayName : null;
|
|
3653
3650
|
image = typeof parsed.image === "string" ? parsed.image : null;
|
|
@@ -3665,22 +3662,6 @@ function resolveAgentConfig(accountDir, agentName) {
|
|
|
3665
3662
|
if (typeof parsed.accessMode === "string" && ["gated", "paid"].includes(parsed.accessMode)) {
|
|
3666
3663
|
accessMode = parsed.accessMode;
|
|
3667
3664
|
}
|
|
3668
|
-
if (typeof parsed.liveMemory === "boolean") {
|
|
3669
|
-
liveMemory = parsed.liveMemory;
|
|
3670
|
-
} else if (typeof parsed.liveMemory === "string") {
|
|
3671
|
-
const lower = parsed.liveMemory.toLowerCase();
|
|
3672
|
-
if (lower === "true") {
|
|
3673
|
-
liveMemory = true;
|
|
3674
|
-
console.warn(`[agent-config] ${agentName}: liveMemory is string "true" \u2014 coercing to boolean. Fix the config to use a boolean value.`);
|
|
3675
|
-
} else if (lower === "false") {
|
|
3676
|
-
liveMemory = false;
|
|
3677
|
-
console.warn(`[agent-config] ${agentName}: liveMemory is string "false" \u2014 coercing to boolean. Fix the config to use a boolean value.`);
|
|
3678
|
-
} else {
|
|
3679
|
-
throw new Error(`[agent-config] ${agentName}: liveMemory has invalid string value "${parsed.liveMemory}" \u2014 expected boolean or "true"/"false"`);
|
|
3680
|
-
}
|
|
3681
|
-
} else if (parsed.liveMemory !== void 0 && parsed.liveMemory !== null) {
|
|
3682
|
-
throw new Error(`[agent-config] ${agentName}: liveMemory has invalid type ${typeof parsed.liveMemory} \u2014 expected boolean or "true"/"false"`);
|
|
3683
|
-
}
|
|
3684
3665
|
if (Array.isArray(parsed.knowledgeKeywords) && parsed.knowledgeKeywords.length > 0) {
|
|
3685
3666
|
const filtered = parsed.knowledgeKeywords.filter((k) => typeof k === "string" && k.trim()).map((k) => k.replace(/,/g, "").trim().toLowerCase()).filter(Boolean);
|
|
3686
3667
|
if (filtered.length > MAX_KNOWLEDGE_KEYWORDS) {
|
|
@@ -3725,7 +3706,7 @@ function resolveAgentConfig(accountDir, agentName) {
|
|
|
3725
3706
|
total: identityTokens + soulTokens + knowledgeTokens
|
|
3726
3707
|
};
|
|
3727
3708
|
}
|
|
3728
|
-
return { model,
|
|
3709
|
+
return { model, status, displayName, image, imageShape, showAgentName, knowledge, knowledgeBaked, knowledgeKeywords, budget, accessMode };
|
|
3729
3710
|
}
|
|
3730
3711
|
function getDefaultAccountId() {
|
|
3731
3712
|
return resolveAccount()?.accountId ?? null;
|
|
@@ -5347,7 +5328,6 @@ async function projectAgent(accountId, accountDir, slug) {
|
|
|
5347
5328
|
SET a.displayName = $displayName,
|
|
5348
5329
|
a.status = $status,
|
|
5349
5330
|
a.model = $model,
|
|
5350
|
-
a.liveMemory = $liveMemory,
|
|
5351
5331
|
a.knowledgeKeywords = $knowledgeKeywords,
|
|
5352
5332
|
a.role = 'agent',
|
|
5353
5333
|
a.updatedAt = datetime()
|
|
@@ -5358,7 +5338,6 @@ async function projectAgent(accountId, accountDir, slug) {
|
|
|
5358
5338
|
displayName: config.displayName ?? slug,
|
|
5359
5339
|
status: config.status ?? "unknown",
|
|
5360
5340
|
model: config.model ?? "",
|
|
5361
|
-
liveMemory: config.liveMemory ?? false,
|
|
5362
5341
|
knowledgeKeywords: config.knowledgeKeywords ?? []
|
|
5363
5342
|
}
|
|
5364
5343
|
);
|
package/payload/server/server.js
CHANGED
|
@@ -91,7 +91,7 @@ import {
|
|
|
91
91
|
vncLog,
|
|
92
92
|
walkPremiumBundles,
|
|
93
93
|
writeAdminUserAndPerson
|
|
94
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-JMEX5NRX.js";
|
|
95
95
|
import {
|
|
96
96
|
__commonJS,
|
|
97
97
|
__toESM
|
|
@@ -4169,7 +4169,6 @@ async function managerSpawn(opts) {
|
|
|
4169
4169
|
channel: opts.channel,
|
|
4170
4170
|
accountId: opts.accountId,
|
|
4171
4171
|
agentSlug: opts.agentSlug,
|
|
4172
|
-
liveMemory: opts.liveMemory,
|
|
4173
4172
|
attachmentDir: opts.attachmentDir,
|
|
4174
4173
|
aboutOwner: opts.aboutOwner,
|
|
4175
4174
|
dormantPlugins: opts.dormantPlugins,
|
|
@@ -4490,15 +4489,15 @@ function publicIdleMs() {
|
|
|
4490
4489
|
return Number(process.env.CHANNEL_PTY_IDLE_MS ?? String(30 * 6e4));
|
|
4491
4490
|
}
|
|
4492
4491
|
var index = /* @__PURE__ */ new Map();
|
|
4493
|
-
function indexKey(accountId, agentSlug, senderId,
|
|
4492
|
+
function indexKey(accountId, agentSlug, senderId, sliceToken = "") {
|
|
4494
4493
|
const slice = sliceToken && sliceToken.length > 0 ? sliceToken : "open";
|
|
4495
|
-
return `${accountId}:${agentSlug}:${
|
|
4494
|
+
return `${accountId}:${agentSlug}:${slice}:${senderId}`;
|
|
4496
4495
|
}
|
|
4497
4496
|
async function ensureEntry(input) {
|
|
4498
4497
|
startReaper();
|
|
4499
4498
|
const sliceTokenValue = input.sliceToken ?? "";
|
|
4500
4499
|
const personIdValue = input.personId ?? null;
|
|
4501
|
-
const key = indexKey(input.accountId, input.agentSlug, input.senderId,
|
|
4500
|
+
const key = indexKey(input.accountId, input.agentSlug, input.senderId, sliceTokenValue);
|
|
4502
4501
|
const existing = index.get(key);
|
|
4503
4502
|
const tag = tagFor(input.channel);
|
|
4504
4503
|
if (existing) {
|
|
@@ -4521,7 +4520,6 @@ async function ensureEntry(input) {
|
|
|
4521
4520
|
channel: input.channel,
|
|
4522
4521
|
accountId: input.accountId,
|
|
4523
4522
|
agentSlug: input.agentSlug,
|
|
4524
|
-
liveMemory: input.liveMemory,
|
|
4525
4523
|
attachmentDir,
|
|
4526
4524
|
sliceToken: sliceTokenValue.length > 0 ? sliceTokenValue : void 0,
|
|
4527
4525
|
personId: personIdValue ?? void 0
|
|
@@ -4547,7 +4545,6 @@ async function ensureEntry(input) {
|
|
|
4547
4545
|
pendingTurnText: "",
|
|
4548
4546
|
followerAbort: null,
|
|
4549
4547
|
followerRunning: false,
|
|
4550
|
-
liveMemory: input.liveMemory,
|
|
4551
4548
|
sliceToken: sliceTokenValue,
|
|
4552
4549
|
personId: personIdValue
|
|
4553
4550
|
};
|
|
@@ -4586,14 +4583,13 @@ async function writeInput(entry, text) {
|
|
|
4586
4583
|
dispatchFor: "exit"
|
|
4587
4584
|
});
|
|
4588
4585
|
}
|
|
4589
|
-
index.delete(indexKey(entry.accountId, entry.agentSlug, entry.senderId, entry.
|
|
4586
|
+
index.delete(indexKey(entry.accountId, entry.agentSlug, entry.senderId, entry.sliceToken));
|
|
4590
4587
|
const respawned = await ensureEntry({
|
|
4591
4588
|
accountId: entry.accountId,
|
|
4592
4589
|
senderId: entry.senderId,
|
|
4593
4590
|
role: entry.role,
|
|
4594
4591
|
channel: entry.channel,
|
|
4595
4592
|
agentSlug: entry.agentSlug,
|
|
4596
|
-
liveMemory: entry.liveMemory,
|
|
4597
4593
|
sliceToken: entry.sliceToken.length > 0 ? entry.sliceToken : void 0,
|
|
4598
4594
|
personId: entry.personId
|
|
4599
4595
|
});
|
|
@@ -4633,7 +4629,6 @@ async function dispatchOnce(input) {
|
|
|
4633
4629
|
role: input.role,
|
|
4634
4630
|
channel: input.channel,
|
|
4635
4631
|
agentSlug: input.agentSlug,
|
|
4636
|
-
liveMemory: input.liveMemory,
|
|
4637
4632
|
sliceToken: input.sliceToken,
|
|
4638
4633
|
personId: input.personId
|
|
4639
4634
|
});
|
|
@@ -4835,11 +4830,10 @@ app2.post("/", async (c) => {
|
|
|
4835
4830
|
}
|
|
4836
4831
|
attachmentCount = stored.length;
|
|
4837
4832
|
if (stored.length > 0) {
|
|
4838
|
-
const
|
|
4833
|
+
const names = stored.map((s) => s.name).join(", ");
|
|
4839
4834
|
uploadNote = `
|
|
4840
4835
|
|
|
4841
|
-
[The user uploaded ${stored.length} file(s).
|
|
4842
|
-
${lines}]`;
|
|
4836
|
+
[The user uploaded ${stored.length} file(s): ${names}. You cannot open uploaded files in this session \u2014 ask the visitor to describe or paste the relevant content.]`;
|
|
4843
4837
|
}
|
|
4844
4838
|
} else {
|
|
4845
4839
|
requestFormat = "json";
|
|
@@ -4884,11 +4878,9 @@ ${lines}]`;
|
|
|
4884
4878
|
if (!agentSlug) {
|
|
4885
4879
|
return c.json({ error: "No default agent configured" }, 500);
|
|
4886
4880
|
}
|
|
4887
|
-
let liveMemory = false;
|
|
4888
4881
|
let accessMode = "open";
|
|
4889
4882
|
try {
|
|
4890
4883
|
const config = resolveAgentConfig(account.accountDir, agentSlug);
|
|
4891
|
-
liveMemory = config.liveMemory;
|
|
4892
4884
|
accessMode = config.accessMode;
|
|
4893
4885
|
} catch (err) {
|
|
4894
4886
|
const m = err instanceof Error ? err.message : String(err);
|
|
@@ -4929,7 +4921,6 @@ ${lines}]`;
|
|
|
4929
4921
|
role: "public",
|
|
4930
4922
|
channel: "webchat",
|
|
4931
4923
|
agentSlug,
|
|
4932
|
-
liveMemory,
|
|
4933
4924
|
sliceToken,
|
|
4934
4925
|
personId,
|
|
4935
4926
|
text: fullMessage,
|
|
@@ -14532,7 +14523,6 @@ async function dispatchToClaude(input) {
|
|
|
14532
14523
|
role: input.role,
|
|
14533
14524
|
channel: "whatsapp",
|
|
14534
14525
|
agentSlug: input.agentSlug,
|
|
14535
|
-
liveMemory: input.liveMemory,
|
|
14536
14526
|
text: input.text,
|
|
14537
14527
|
timeoutMs: whatsappTurnTimeoutMs()
|
|
14538
14528
|
});
|
|
@@ -15657,10 +15647,8 @@ init({
|
|
|
15657
15647
|
if (process.env.WHATSAPP_PTY_BRIDGE_ENABLED === "true" && msg.text && !msg.isOwnerMirror) {
|
|
15658
15648
|
try {
|
|
15659
15649
|
let agentSlugForBridge;
|
|
15660
|
-
let liveMemoryForBridge;
|
|
15661
15650
|
if (msg.agentType === "admin") {
|
|
15662
15651
|
agentSlugForBridge = "admin";
|
|
15663
|
-
liveMemoryForBridge = true;
|
|
15664
15652
|
} else {
|
|
15665
15653
|
const resolved = bootAccount ? resolvePublicAgent(bootAccount.accountDir, { accountId: msg.accountId, groupJid: msg.groupJid }) : null;
|
|
15666
15654
|
if (!resolved) {
|
|
@@ -15668,7 +15656,6 @@ init({
|
|
|
15668
15656
|
return;
|
|
15669
15657
|
}
|
|
15670
15658
|
agentSlugForBridge = resolved.slug;
|
|
15671
|
-
liveMemoryForBridge = bootAccount ? resolveAgentConfig(bootAccount.accountDir, resolved.slug).liveMemory : false;
|
|
15672
15659
|
}
|
|
15673
15660
|
startReaper2();
|
|
15674
15661
|
await dispatchToClaude({
|
|
@@ -15676,7 +15663,6 @@ init({
|
|
|
15676
15663
|
senderId: msg.senderPhone,
|
|
15677
15664
|
role: msg.agentType,
|
|
15678
15665
|
agentSlug: agentSlugForBridge,
|
|
15679
|
-
liveMemory: liveMemoryForBridge,
|
|
15680
15666
|
text: msg.text,
|
|
15681
15667
|
reply: msg.reply
|
|
15682
15668
|
});
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: setup-tunnel
|
|
3
|
-
description: Cloudflare Tunnel setup, diagnosis, and reset — the agent drives cloudflared directly via Bash, following the manual runbook step by step, and proves success with an external HTTP 200.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Cloudflare operations
|
|
7
|
-
|
|
8
|
-
Every Cloudflare task — setup, diagnosis, reset, DNS edit — is driven by the agent invoking `cloudflared` directly via the Bash tool, following the numbered steps in `references/manual-setup.md`. There are no Cloudflare MCP tools; the plugin registers none. There is no shell-script wrapper, no state machine, no orchestrator. The PTY surface streams `cloudflared` stdout and stderr verbatim into chat, and the operator's own browser handles the OAuth click. The agent's job is to pick the right step from the runbook, collect the inputs it needs, run the command in Bash, relay the literal output, and verify the outcome with an external `curl`.
|
|
9
|
-
|
|
10
|
-
## Outcome contract — what "done" means
|
|
11
|
-
|
|
12
|
-
A Cloudflare setup is done when, and only when, `curl -I https://<hostname>` issued from outside the local network returns `HTTP/2 200` (or `HTTP/1.1 200 OK`) and the response is surfaced verbatim in chat. No state file, no `result=ok` phase line, no "service is active" claim substitutes for the live HTTP response. If the curl returns anything other than 200, the setup is not done — diagnose with `cloudflared tunnel info <tunnelId>` and `systemctl --user status ${BRAND}-cloudflared.service` and recover per `references/manual-setup.md`.
|
|
13
|
-
|
|
14
|
-
## Inputs
|
|
15
|
-
|
|
16
|
-
Four inputs come from the operator in chat: the admin FQDN, optional public FQDN, optional apex FQDN, and the admin password. `BRAND` is derived from disk — never hardcoded — with `BRAND=$(jq -r .hostname ~/.<configDir>/brand.json)`. The same install can host different brands on different ports; `brand.json.hostname` is the only authoritative source.
|
|
17
|
-
|
|
18
|
-
## Execution
|
|
19
|
-
|
|
20
|
-
The agent reads `references/manual-setup.md` and executes its steps with Bash. Every `cloudflared` invocation's stdout and stderr are relayed into chat verbatim — the operator sees the same output the agent does. The OAuth URL printed by `cloudflared tunnel login` is linkified by the native PTY; the operator clicks it in their own browser. The agent does not spawn a browser, automate the Cloudflare consent page, or drive the dashboard via Playwright or Chrome DevTools.
|
|
21
|
-
|
|
22
|
-
Mutations the agent performs from the runbook (each one only after reading the relevant step):
|
|
23
|
-
|
|
24
|
-
- `cloudflared tunnel login` — emits the OAuth URL; cert lands at `~/.cloudflared/cert.pem` after the operator authorises.
|
|
25
|
-
- `cloudflared tunnel --origincert <cert> create <name>` — produces `<tunnelId>.json` credentials.
|
|
26
|
-
- Write `~/.${BRAND}/cloudflared/config.yml` (ingress block per the runbook).
|
|
27
|
-
- `cloudflared tunnel --origincert <cert> route dns <tunnelId> <hostname>` — one call per non-apex hostname.
|
|
28
|
-
- For non-admin hostnames not starting with `public.`, append to `~/.${BRAND}/alias-domains.json` so `isPublicHost()` treats them as public.
|
|
29
|
-
- Install + start the `${BRAND}-cloudflared.service` user unit (`systemctl --user daemon-reload && systemctl --user enable --now ${BRAND}-cloudflared.service`).
|
|
30
|
-
|
|
31
|
-
After the service is up, the agent runs `curl -I https://<admin-hostname>` and pastes the response. The setup-done claim only fires when a `200` line appears in that response.
|
|
32
|
-
|
|
33
|
-
## Apex hostnames
|
|
34
|
-
|
|
35
|
-
The CLI cannot route apex records (e.g. `maxy.chat`). When an apex is supplied, the agent quotes the `ACTION REQUIRED` block from `references/manual-setup.md` verbatim — the operator edits the apex CNAME in the Cloudflare dashboard. The Cloudflare API and SDK are banned ([[feedback_cf_api_total_eradication]]); dashboard click is the only path.
|
|
36
|
-
|
|
37
|
-
## Reset
|
|
38
|
-
|
|
39
|
-
When local Cloudflare state is corrupt or the operator wants to start from a known-good cert, follow `references/reset-guide.md`. The agent issues the relevant `cloudflared tunnel delete` and `rm -rf ~/.${BRAND}/cloudflared/` commands in Bash, then re-runs the setup steps above.
|
|
40
|
-
|
|
41
|
-
## Dashboard guidance
|
|
42
|
-
|
|
43
|
-
For operations only the Cloudflare dashboard can do (sign in, switch accounts, add a site, edit an apex CNAME, verify zone nameservers, delete stale CNAMEs, create URL Rewrite Rules), quote the relevant click-path from `references/dashboard-guide.md` verbatim. The operator follows it in their browser. The agent does not drive the dashboard programmatically.
|
|
44
|
-
|
|
45
|
-
## Tool discipline — binding
|
|
46
|
-
|
|
47
|
-
When the operator's request touches Cloudflare, the agent's permitted actions are:
|
|
48
|
-
|
|
49
|
-
- Invoke `cloudflared` via Bash, following the numbered steps in `references/manual-setup.md`.
|
|
50
|
-
- Write `config.yml` and `alias-domains.json` per the runbook.
|
|
51
|
-
- Install and start the brand's cloudflared user service.
|
|
52
|
-
- Quote `references/manual-setup.md`, `references/reset-guide.md`, or `references/dashboard-guide.md` verbatim when guidance is needed.
|
|
53
|
-
- Verify reachability via `curl -I https://<hostname>` and surface the response.
|
|
54
|
-
|
|
55
|
-
The agent does not call any Cloudflare API or SDK. The agent does not drive the dashboard via Playwright or Chrome DevTools. The agent does not synthesise `cloudflared` flag combinations from web search or training — every command comes from the runbook. When a step fails, the agent reports the exact `cloudflared` output, names the recovery step from `references/reset-guide.md`, and stops. Improvisation — "let me try a different flag" or "let me check the dashboard myself" — is the behaviour this rule exists to prevent.
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# Soul
|
|
2
|
-
|
|
3
|
-
## Personality
|
|
4
|
-
|
|
5
|
-
Helpful, precise, warm without being effusive. Speaks for a real business to a real visitor. Not a marketing voice, not a chatbot, not a simulated friend. Treats the visitor's time as the most valuable thing in the conversation.
|
|
6
|
-
|
|
7
|
-
Comfortable saying "I don't know" when the answer isn't in the knowledge provided. Honest about what is on offer and what isn't. Never invents detail to fill a gap.
|
|
8
|
-
|
|
9
|
-
## Tone
|
|
10
|
-
|
|
11
|
-
Plain language. British English. Plain hyphens, straight quotes, three periods. No emoji. No filler ("just", "simply", "let me", "I'll check"). No narration of internal process ("searching", "looking that up").
|
|
12
|
-
|
|
13
|
-
Short answers when the question is short. Longer answers when the visitor genuinely needs the detail: a comparison, a process, a decision. Never padded.
|
|
14
|
-
|
|
15
|
-
Direct without being abrupt. The visitor asked for help; help them, and stop when help is delivered.
|
|
16
|
-
|
|
17
|
-
## Greeting
|
|
18
|
-
|
|
19
|
-
Open by naming the business and what is on offer. Make it clear in the same breath that the visitor is talking to AI. Natural, not bolted on. Invite the question the visitor came to ask.
|