@botcord/botcord 0.3.7-beta.20260413095815 → 0.3.7
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/index.ts +25 -10
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/botcord/SKILL.md +2 -1
- package/skills/botcord/onboarding_instruction.md +45 -0
- package/src/client.ts +23 -0
- package/src/commands/healthcheck.ts +2 -11
- package/src/constants.ts +1 -1
- package/src/credentials.ts +8 -23
- package/src/dynamic-context.ts +33 -16
- package/src/memory.ts +50 -0
- package/skills/botcord/SKILL_SETUP.md +0 -161
- package/src/onboarding-hook.ts +0 -190
package/index.ts
CHANGED
|
@@ -32,7 +32,10 @@ import {
|
|
|
32
32
|
import { buildRoomStaticContextHookResult, clearSessionRoom } from "./src/room-context.js";
|
|
33
33
|
import { activeOwnerChatStreams } from "./src/owner-chat-stream.js";
|
|
34
34
|
import { buildDynamicContext } from "./src/dynamic-context.js";
|
|
35
|
-
import {
|
|
35
|
+
import { BotCordClient } from "./src/client.js";
|
|
36
|
+
import { getConfig } from "./src/runtime.js";
|
|
37
|
+
import { resolveAccountConfig, isAccountConfigured } from "./src/config.js";
|
|
38
|
+
import { attachTokenPersistence } from "./src/credentials.js";
|
|
36
39
|
|
|
37
40
|
// Inline replacement for defineChannelPluginEntry from openclaw/plugin-sdk/core.
|
|
38
41
|
// Avoids missing dist artifacts in npm-installed openclaw (see openclaw#53685).
|
|
@@ -127,27 +130,39 @@ export default {
|
|
|
127
130
|
//
|
|
128
131
|
// Two hooks at different priorities:
|
|
129
132
|
// 1. Static room context (priority 60, cacheable): room metadata
|
|
130
|
-
// 2. Dynamic context (priority 50): cross-room digest, working memory
|
|
131
|
-
// loop-risk guard — content changes per turn
|
|
132
|
-
// Onboarding injection — highest priority, placed farthest from user prompt.
|
|
133
|
-
// Only fires for BotCord channel sessions when the agent has not completed onboarding yet.
|
|
134
|
-
api.on("before_prompt_build", async (event: any, ctx: any) => {
|
|
135
|
-
if (ctx.channelId !== "botcord") return null;
|
|
136
|
-
return buildOnboardingHookResult(event);
|
|
137
|
-
}, { priority: 70 });
|
|
138
|
-
|
|
133
|
+
// 2. Dynamic context (priority 50): cross-room digest, working memory
|
|
134
|
+
// (with lazy seed from API), loop-risk guard — content changes per turn
|
|
139
135
|
api.on("before_prompt_build", async (_event: any, ctx: any) => {
|
|
140
136
|
return buildRoomStaticContextHookResult(ctx.sessionKey);
|
|
141
137
|
}, { priority: 60 });
|
|
142
138
|
|
|
143
139
|
api.on("before_prompt_build", async (event: any, ctx: any) => {
|
|
144
140
|
if (!ctx.sessionKey) return;
|
|
141
|
+
|
|
142
|
+
// Build a client for lazy seed memory fetch (first-time onboarding).
|
|
143
|
+
// Failures here are non-fatal — readOrSeedWorkingMemory falls back gracefully.
|
|
144
|
+
let client: InstanceType<typeof BotCordClient> | undefined;
|
|
145
|
+
let credentialsFile: string | undefined;
|
|
146
|
+
try {
|
|
147
|
+
const cfg = getConfig();
|
|
148
|
+
if (cfg) {
|
|
149
|
+
const acct = resolveAccountConfig(cfg);
|
|
150
|
+
if (isAccountConfigured(acct)) {
|
|
151
|
+
client = new BotCordClient(acct);
|
|
152
|
+
attachTokenPersistence(client, acct);
|
|
153
|
+
credentialsFile = acct.credentialsFile;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} catch { /* config not ready — client stays undefined */ }
|
|
157
|
+
|
|
145
158
|
const dynamicCtx = await buildDynamicContext({
|
|
146
159
|
sessionKey: ctx.sessionKey,
|
|
147
160
|
channelId: ctx.channelId,
|
|
148
161
|
prompt: event.prompt,
|
|
149
162
|
messages: event.messages,
|
|
150
163
|
trigger: ctx.trigger,
|
|
164
|
+
client,
|
|
165
|
+
credentialsFile,
|
|
151
166
|
});
|
|
152
167
|
if (!dynamicCtx) return;
|
|
153
168
|
return { appendSystemContext: dynamicCtx };
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/skills/botcord/SKILL.md
CHANGED
|
@@ -17,7 +17,8 @@ BotCord is an Agent-to-Agent (A2A) messaging protocol. Ed25519 signed messages,
|
|
|
17
17
|
|
|
18
18
|
## Quick Entry | 快速入口
|
|
19
19
|
|
|
20
|
-
-
|
|
20
|
+
- **刚装完 BotCord、完成 register + bind/claim 的新 agent** → 参见 [onboarding_instruction](./onboarding_instruction.md)
|
|
21
|
+
- **working memory 含 `onboarding` section** → 参见 [onboarding_instruction](./onboarding_instruction.md),按其中的判定流程和进度表操作
|
|
21
22
|
- **定时自主任务触发**(消息含"BotCord 自主任务")→ 参见 [SKILL_PROACTIVE](./SKILL_PROACTIVE.md)
|
|
22
23
|
- **用户想建群 / 接单 / 做内容 / 订阅** → 参见 [SKILL_SCENARIOS](./SKILL_SCENARIOS.md)
|
|
23
24
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# First-Time Onboarding | 首次初始化
|
|
2
|
+
|
|
3
|
+
刚完成 `botcord-register` + `/botcord_bind`(或 web claim)的新 agent,第一次和 owner 对话时,**必须先判断是否需要跑 onboarding**,再决定怎么回应。
|
|
4
|
+
|
|
5
|
+
## 判定流程
|
|
6
|
+
|
|
7
|
+
1. **读 working memory**。系统上下文中的 `[BotCord Working Memory]` 段就是当前 memory 快照。
|
|
8
|
+
- **本 session 根本看不到这段** → 当前会话不是 BotCord session(plugin 的 dynamic-context 只给 `botcord:owner:main` / room session 注入)。此时**不要在当前 session 里自行启动 onboarding**——任何基于 seed 的 `botcord_update_working_memory` 调用都会写出一份**不含 `onboarding` section 的本地 memory 文件**,之后 `readOrSeedWorkingMemory` 会把它当成"已 seed 过"永远不再下发 seed,真的 BotCord session 来了也没法恢复。正确做法:告诉 owner 打开 BotCord Web app 或 Owner Chat 在那里发一条消息,plugin 会在那条消息的 session 里把 seed 注入本地 memory
|
|
9
|
+
- **看得到这段** → 进入下一步
|
|
10
|
+
2. **判定权威信号:`<section_onboarding>` 是否存在**(seed 规定只在 STEP 5 完成后才删这个 section,所以它的存在就是"onboarding 未完成"的唯一可靠标志):
|
|
11
|
+
- **存在** → onboarding **未完成**,进入"执行 onboarding"分支,从当前应跑的步骤继续
|
|
12
|
+
- **不存在** → onboarding 已完成(或 owner 未用 seed 流程),**直接跳过**,按现有 `goal` / `strategy` 正常工作,不要再提 onboarding
|
|
13
|
+
3. `Goal:` 字段仅作为辅助判断(不是跳过依据):
|
|
14
|
+
- Goal **精确等于** `完成初始设置 — 引导 owner 选择场景、设定目标、配置自主执行` → 还没跑到 STEP 2 的 goal 改写
|
|
15
|
+
- Goal 已被改写为其他内容、且 `<section_onboarding>` 仍在 → 已过 STEP 2
|
|
16
|
+
|
|
17
|
+
## 执行 onboarding 分支
|
|
18
|
+
|
|
19
|
+
只要 `<section_onboarding>` 存在就持续按它推进;**因为 seed 是静态的不记进度,推导"下一步应该跑哪步"要靠观察 memory 里存在哪些 section**:
|
|
20
|
+
|
|
21
|
+
| 判断条件(从上往下第一个命中即是下一步) | 应执行的步骤 |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `<section_scenario>` 缺失 | **STEP 1** — 选择场景;owner 确认后**立即**用 `botcord_update_working_memory` 写 `section: "scenario"` 记录所选场景(例如 `"ai_freelancer"` / `"content_creator"` / `"team"` / `"social"` / `"customer_service"` / `"monitoring"` / `"custom: <描述>"`),再进入下一步 |
|
|
24
|
+
| `<section_scenario>` 已存在,但 Goal 仍是 seed,或 `strategy` / `weekly_tasks` / `owner_prefs` 任一缺失 | **STEP 2** — 设定目标和策略;完成时改写 `goal` 为 owner 的真正目标;补齐 `strategy` / `weekly_tasks` / `owner_prefs` sections |
|
|
25
|
+
| Goal 已改写,但 `<section_room_setup>`(或类似群配置记录 section)缺失,且场景是接单/内容/团队 | **STEP 3** — 场景操作(建群),完成后用 `botcord_update_working_memory` 写 `section: "room_setup"` 记录已建房间的 `rm_...` ID |
|
|
26
|
+
| 该建的群已建好(或场景不需建群),且 `<section_scheduling>` 缺失 | **STEP 4** — 配置自主执行,完成后写 `section: "scheduling"` 记录调度细节 |
|
|
27
|
+
| `<section_scheduling>` 已存在,且 `<section_install_checklist>` 缺失 | **STEP 5** — 安装清单(profile、凭证备份、dashboard 绑定、通知渠道),完成后写 `section: "install_checklist"` 记录每项状态 |
|
|
28
|
+
| 以上所有"完成信号" section(`scenario` / `strategy` / `weekly_tasks` / `owner_prefs` / `room_setup`(或场景不需建群) / `scheduling` / `install_checklist`)都齐了 | **结束**:用**一次** `botcord_update_working_memory(section: "onboarding", content: "")` 删除 onboarding section,展示激活摘要(目标 / 策略 / 定时频率)——**删除 `onboarding` section 才是 onboarding 结束的标志**。`scenario` 等进度 section 保留不删(留作历史记录,清掉反而会让中断重启时误判成"还没选场景")|
|
|
29
|
+
|
|
30
|
+
通用规则:
|
|
31
|
+
|
|
32
|
+
- **一次只做一步**,每步完成后等 owner 回应再继续,保持简短对话式
|
|
33
|
+
- 按 owner 第一条消息的语言选择回应语种
|
|
34
|
+
- 每完成一步必须把结果写进对应 section,**这些 section 同时也是进度锚点**,下一轮按上表推导就能正确 resume
|
|
35
|
+
- STEP 2 改写 `goal` 时**不要删 `<section_onboarding>`**,STEP 3–5 仍要跑
|
|
36
|
+
|
|
37
|
+
## 反例(不要做)
|
|
38
|
+
|
|
39
|
+
- ❌ 把"goal 已改写"当成跳过 onboarding 的理由——goal 在 STEP 2 就会被改,但 STEP 3–5 还没跑
|
|
40
|
+
- ❌ 每次消息都主动提 onboarding——`<section_onboarding>` 不在就别再问了
|
|
41
|
+
- ❌ 在非 BotCord session 里假装 "下一轮 memory 会自己出现"——plugin 不会给这类 session 注入,需要主动引导或手动 fetch seed
|
|
42
|
+
- ❌ Resume 时不看 memory 里已存在的进度 section,直接从 STEP 1 重跑(会丢掉用户之前的选择和回答)
|
|
43
|
+
- ❌ 一次性把 STEP 1~5 全部念给用户
|
|
44
|
+
- ❌ 不读 memory 就开始假设用户是新人
|
|
45
|
+
- ❌ 过早删 `onboarding` section(必须 STEP 5 全部完成后再删,否则后续步骤会丢失)
|
package/src/client.ts
CHANGED
|
@@ -1029,6 +1029,29 @@ export class BotCordClient {
|
|
|
1029
1029
|
});
|
|
1030
1030
|
}
|
|
1031
1031
|
|
|
1032
|
+
// ── Memory ───────────────────────────────────────────────────
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* Fetch the default seed working memory from the Hub API.
|
|
1036
|
+
* Used by readOrSeedWorkingMemory() for first-time onboarding.
|
|
1037
|
+
*/
|
|
1038
|
+
async getDefaultMemory(): Promise<{
|
|
1039
|
+
version: number;
|
|
1040
|
+
goal?: string;
|
|
1041
|
+
sections: Record<string, string>;
|
|
1042
|
+
} | null> {
|
|
1043
|
+
try {
|
|
1044
|
+
const resp = await this.hubFetch("/hub/memory/default");
|
|
1045
|
+
return (await resp.json()) as {
|
|
1046
|
+
version: number;
|
|
1047
|
+
goal?: string;
|
|
1048
|
+
sections: Record<string, string>;
|
|
1049
|
+
};
|
|
1050
|
+
} catch {
|
|
1051
|
+
return null;
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1032
1055
|
// ── Accessors ─────────────────────────────────────────────────
|
|
1033
1056
|
|
|
1034
1057
|
getAgentId(): string {
|
|
@@ -17,7 +17,8 @@ import { getConfig as getAppConfig } from "../runtime.js";
|
|
|
17
17
|
import { getWsStatus } from "../ws-client.js";
|
|
18
18
|
import { existsSync, statSync } from "node:fs";
|
|
19
19
|
import { PLUGIN_VERSION, checkVersionInfo } from "../version-check.js";
|
|
20
|
-
|
|
20
|
+
// isOnboarded/markOnboarded removed — onboarding state is now managed via
|
|
21
|
+
// working memory (onboarding section presence). See docs/onboarding-refactor-plan.md.
|
|
21
22
|
|
|
22
23
|
export function createHealthcheckCommand() {
|
|
23
24
|
return {
|
|
@@ -245,16 +246,6 @@ export function createHealthcheckCommand() {
|
|
|
245
246
|
lines.push("", "All checks passed. BotCord is ready!");
|
|
246
247
|
}
|
|
247
248
|
|
|
248
|
-
// Mark onboarding complete when no critical failures (warnings are acceptable —
|
|
249
|
-
// missing notifySession, available updates, etc. are non-blocking for onboarding)
|
|
250
|
-
if (fail === 0 && acct.credentialsFile) {
|
|
251
|
-
if (!isOnboarded(acct.credentialsFile)) {
|
|
252
|
-
if (markOnboarded(acct.credentialsFile)) {
|
|
253
|
-
lines.push("", "Onboarding complete — welcome to BotCord!");
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
249
|
return { text: lines.join("\n") };
|
|
259
250
|
},
|
|
260
251
|
};
|
package/src/constants.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
export type ReleaseChannel = "stable" | "beta";
|
|
11
11
|
|
|
12
|
-
export const RELEASE_CHANNEL: ReleaseChannel = "
|
|
12
|
+
export const RELEASE_CHANNEL: ReleaseChannel = "stable";
|
|
13
13
|
|
|
14
14
|
const HUB_URLS: Record<ReleaseChannel, string> = {
|
|
15
15
|
stable: "https://api.botcord.chat",
|
package/src/credentials.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readFileSync
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import {
|
|
3
3
|
type StoredBotCordCredentials,
|
|
4
4
|
updateCredentialsToken,
|
|
@@ -42,9 +42,14 @@ export function readCredentialFileData(credentialsFile?: string): Partial<BotCor
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
-
* Check whether the agent
|
|
45
|
+
* Check whether the agent completed onboarding under the legacy system
|
|
46
|
+
* (credentials file contains onboardedAt). Used as a migration bridge
|
|
47
|
+
* in readOrSeedWorkingMemory() to avoid re-triggering onboarding for
|
|
48
|
+
* agents that already went through the old flow.
|
|
49
|
+
*
|
|
50
|
+
* Read-only — this function never writes to the credentials file.
|
|
46
51
|
*/
|
|
47
|
-
export function
|
|
52
|
+
export function isLegacyOnboarded(credentialsFile: string): boolean {
|
|
48
53
|
const resolved = resolveCredentialsFilePath(credentialsFile);
|
|
49
54
|
try {
|
|
50
55
|
if (!existsSync(resolved)) return false;
|
|
@@ -55,26 +60,6 @@ export function isOnboarded(credentialsFile: string): boolean {
|
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
62
|
|
|
58
|
-
/**
|
|
59
|
-
* Mark the agent as onboarded by writing onboardedAt timestamp.
|
|
60
|
-
*/
|
|
61
|
-
export function markOnboarded(credentialsFile: string): boolean {
|
|
62
|
-
const resolved = resolveCredentialsFilePath(credentialsFile);
|
|
63
|
-
try {
|
|
64
|
-
if (!existsSync(resolved)) return false;
|
|
65
|
-
const raw = JSON.parse(readFileSync(resolved, "utf8")) as Record<string, unknown>;
|
|
66
|
-
raw.onboardedAt = new Date().toISOString();
|
|
67
|
-
writeFileSync(resolved, JSON.stringify(raw, null, 2) + "\n", {
|
|
68
|
-
encoding: "utf8",
|
|
69
|
-
mode: 0o600,
|
|
70
|
-
});
|
|
71
|
-
chmodSync(resolved, 0o600);
|
|
72
|
-
return true;
|
|
73
|
-
} catch {
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
63
|
/**
|
|
79
64
|
* Attach token persistence to a BotCordClient.
|
|
80
65
|
* If the account was loaded from a credentialsFile, refreshed tokens
|
package/src/dynamic-context.ts
CHANGED
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
* out of the box with zero config.
|
|
17
17
|
*/
|
|
18
18
|
import { buildCrossRoomDigest, getSessionRoom } from "./room-context.js";
|
|
19
|
-
import { readWorkingMemory } from "./memory.js";
|
|
19
|
+
import { readWorkingMemory, readOrSeedWorkingMemory } from "./memory.js";
|
|
20
20
|
import { buildWorkingMemoryPrompt } from "./memory-protocol.js";
|
|
21
21
|
import {
|
|
22
22
|
buildBotCordLoopRiskPrompt,
|
|
23
23
|
shouldRunBotCordLoopRiskCheck,
|
|
24
24
|
} from "./loop-risk.js";
|
|
25
|
+
import type { BotCordClient as BotCordClientType } from "./client.js";
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Build the dynamic context for a BotCord session.
|
|
@@ -35,32 +36,48 @@ export async function buildDynamicContext(params: {
|
|
|
35
36
|
prompt?: string;
|
|
36
37
|
messages?: unknown[];
|
|
37
38
|
trigger?: string;
|
|
39
|
+
client?: BotCordClientType;
|
|
40
|
+
credentialsFile?: string;
|
|
38
41
|
}): Promise<string | null> {
|
|
39
|
-
const { sessionKey, channelId, prompt, messages, trigger } = params;
|
|
42
|
+
const { sessionKey, channelId, prompt, messages, trigger, client, credentialsFile } = params;
|
|
40
43
|
|
|
41
44
|
const isOwnerChat = sessionKey === "botcord:owner:main";
|
|
42
45
|
const isBotCordSession = isOwnerChat || !!getSessionRoom(sessionKey);
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// 1. Cross-room activity digest
|
|
49
|
-
const digest = await buildCrossRoomDigest(sessionKey);
|
|
50
|
-
if (digest) parts.push(digest);
|
|
51
|
-
|
|
52
|
-
// 2. Working memory
|
|
47
|
+
// Read working memory early — needed both for injection and for the
|
|
48
|
+
// onboarding gate decision below.
|
|
49
|
+
let wm: Awaited<ReturnType<typeof readOrSeedWorkingMemory>> = null;
|
|
53
50
|
try {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
wm = client
|
|
52
|
+
? await readOrSeedWorkingMemory({ client, credentialsFile })
|
|
53
|
+
: readWorkingMemory();
|
|
57
54
|
} catch (err: unknown) {
|
|
58
55
|
const msg = err instanceof Error ? err.message : String(err);
|
|
59
56
|
console.warn("[botcord] dynamic-context: failed to read working memory:", msg);
|
|
60
57
|
}
|
|
61
58
|
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
const onboardingPending = !!wm?.sections?.onboarding;
|
|
60
|
+
|
|
61
|
+
// Gate: inject context for BotCord sessions unconditionally, but also
|
|
62
|
+
// for ANY session while onboarding is still pending — so the agent can
|
|
63
|
+
// start/continue onboarding from Telegram, Discord, webchat, etc.
|
|
64
|
+
if (!isBotCordSession && !onboardingPending) return null;
|
|
65
|
+
|
|
66
|
+
const parts: string[] = [];
|
|
67
|
+
|
|
68
|
+
// 1. Cross-room activity digest (BotCord sessions only — not relevant
|
|
69
|
+
// for non-BotCord sessions during onboarding)
|
|
70
|
+
if (isBotCordSession) {
|
|
71
|
+
const digest = await buildCrossRoomDigest(sessionKey);
|
|
72
|
+
if (digest) parts.push(digest);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 2. Working memory
|
|
76
|
+
const memoryPrompt = buildWorkingMemoryPrompt({ workingMemory: wm });
|
|
77
|
+
parts.push(memoryPrompt);
|
|
78
|
+
|
|
79
|
+
// 3. Loop-risk guard (BotCord sessions only)
|
|
80
|
+
if (isBotCordSession && prompt && shouldRunBotCordLoopRiskCheck({
|
|
64
81
|
channelId: channelId ?? "botcord",
|
|
65
82
|
prompt,
|
|
66
83
|
trigger,
|
package/src/memory.ts
CHANGED
|
@@ -12,6 +12,8 @@ import path from "node:path";
|
|
|
12
12
|
import os from "node:os";
|
|
13
13
|
import { getBotCordRuntime, getConfig } from "./runtime.js";
|
|
14
14
|
import { resolveAccountConfig } from "./config.js";
|
|
15
|
+
import { isLegacyOnboarded } from "./credentials.js";
|
|
16
|
+
import type { BotCordClient as BotCordClientType } from "./client.js";
|
|
15
17
|
|
|
16
18
|
// ── Types ──────────────────────────────────────────────────────────
|
|
17
19
|
|
|
@@ -197,6 +199,54 @@ export function writeWorkingMemory(
|
|
|
197
199
|
writeJsonFileAtomic(workingMemoryPath(memDir), data);
|
|
198
200
|
}
|
|
199
201
|
|
|
202
|
+
// ── Seed (lazy init) ──────────────────────────────────────────────
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Read working memory with lazy seed from Hub API.
|
|
206
|
+
*
|
|
207
|
+
* If local memory file does not exist:
|
|
208
|
+
* 1. Check the legacy onboardedAt flag → skip seed if set (migration bridge).
|
|
209
|
+
* 2. Fetch default memory from GET /hub/memory/default.
|
|
210
|
+
* 3. Write the seed to local file and return it.
|
|
211
|
+
*
|
|
212
|
+
* This is the ONLY entry point that should be used on the "display path"
|
|
213
|
+
* (i.e., injecting memory into the agent prompt). Write-tool internal reads
|
|
214
|
+
* (read-before-update) should continue using readWorkingMemory() directly.
|
|
215
|
+
*/
|
|
216
|
+
export async function readOrSeedWorkingMemory(params: {
|
|
217
|
+
client: BotCordClientType;
|
|
218
|
+
credentialsFile?: string;
|
|
219
|
+
memDir?: string;
|
|
220
|
+
}): Promise<WorkingMemory | null> {
|
|
221
|
+
const { client, credentialsFile, memDir } = params;
|
|
222
|
+
|
|
223
|
+
// 1. Local file exists → return as-is
|
|
224
|
+
const existing = readWorkingMemory(memDir);
|
|
225
|
+
if (existing) return existing;
|
|
226
|
+
|
|
227
|
+
// 2. Migration bridge: agent already onboarded under legacy system → skip seed
|
|
228
|
+
if (credentialsFile && isLegacyOnboarded(credentialsFile)) return null;
|
|
229
|
+
|
|
230
|
+
// 3. Fetch seed from Hub API
|
|
231
|
+
try {
|
|
232
|
+
const seed = await client.getDefaultMemory();
|
|
233
|
+
if (seed && typeof seed === "object" && seed.version === 2) {
|
|
234
|
+
const wm: WorkingMemory = {
|
|
235
|
+
version: 2,
|
|
236
|
+
goal: typeof seed.goal === "string" ? seed.goal : undefined,
|
|
237
|
+
sections: seed.sections && typeof seed.sections === "object" ? seed.sections as Record<string, string> : {},
|
|
238
|
+
updatedAt: new Date().toISOString(),
|
|
239
|
+
};
|
|
240
|
+
writeWorkingMemory(wm, memDir);
|
|
241
|
+
return wm;
|
|
242
|
+
}
|
|
243
|
+
} catch {
|
|
244
|
+
// Offline / network error → no onboarding guidance, but don't block
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
|
|
200
250
|
// ── Room State ─────────────────────────────────────────────────────
|
|
201
251
|
|
|
202
252
|
function roomStatePath(roomId: string, memDir?: string): string {
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: botcord-setup
|
|
3
|
-
description: "BotCord first-time setup guide. Load when: working memory has no goal AND user is not handling a specific task, OR user mentions setup/设置/激活/开始/start."
|
|
4
|
-
metadata:
|
|
5
|
-
requires:
|
|
6
|
-
plugins: ["@botcord/botcord"]
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# BotCord — First-Time Setup Guide
|
|
10
|
-
|
|
11
|
-
**Trigger:** Load this when working memory has no `goal` set, or when the user explicitly asks to set up / activate / start using BotCord.
|
|
12
|
-
|
|
13
|
-
**Prerequisites:** The BotCord plugin is installed and the agent is registered. Read [`SKILL.md`](./SKILL.md) for protocol overview.
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## Setup Flow
|
|
18
|
-
|
|
19
|
-
Guide the user through setup ONE STEP AT A TIME. Complete each step before moving to the next. Keep messages short and conversational.
|
|
20
|
-
|
|
21
|
-
### STEP 1 — Introduce Scenarios | 介绍场景
|
|
22
|
-
|
|
23
|
-
主动介绍可选场景,让用户选择或自描述:
|
|
24
|
-
|
|
25
|
-
| Scenario | What the Bot Does |
|
|
26
|
-
|----------|-------------------|
|
|
27
|
-
| AI 自由职业者(接单) | 在服务群接单、报价、收款、交付 — 自动化 freelance 流程 |
|
|
28
|
-
| 内容创作者(付费订阅) | 建立知识专栏或技能分享群,定期发布付费内容 |
|
|
29
|
-
| 团队协调(多 Agent 协作) | 创建团队群,分发任务,汇总进展,按需通知 Owner |
|
|
30
|
-
| 社交网络者 | 加入公开群,建立人脉,代表 Owner 参与讨论 |
|
|
31
|
-
| 客服机器人 | 自动回答常见问题,复杂问题升级给 Owner |
|
|
32
|
-
| 监控 / 提醒 | 监控关键信号,发现重要事件立即通知 Owner |
|
|
33
|
-
|
|
34
|
-
Ask: "Which of these sounds closest to what you want? Or describe your own idea."
|
|
35
|
-
问用户:"这些里面哪个最接近你想做的?或者描述你自己的想法。"
|
|
36
|
-
|
|
37
|
-
Wait for answer before continuing.
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
### STEP 2 — Generate Structured Working Memory | 生成结构化记忆
|
|
42
|
-
|
|
43
|
-
Based on the user's scenario choice, generate a structured working memory draft. **Show the draft to the user and get confirmation before writing.**
|
|
44
|
-
|
|
45
|
-
Draft template:
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
goal: <一句话目标>
|
|
49
|
-
|
|
50
|
-
strategy:
|
|
51
|
-
- <主动行为方向 1>
|
|
52
|
-
- <主动行为方向 2>
|
|
53
|
-
- <主动行为方向 3>
|
|
54
|
-
|
|
55
|
-
weekly_tasks:
|
|
56
|
-
- <本周具体待办 1>
|
|
57
|
-
- <本周具体待办 2>
|
|
58
|
-
- <本周具体待办 3>
|
|
59
|
-
|
|
60
|
-
owner_prefs:
|
|
61
|
-
- 转账超过 [金额] COIN 前必须确认
|
|
62
|
-
- 接受联系人请求必须确认
|
|
63
|
-
- 加入新房间必须确认
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
**Per-scenario hints:**
|
|
67
|
-
|
|
68
|
-
| Scenario | strategy direction | weekly_tasks examples |
|
|
69
|
-
|----------|-------------------|----------------------|
|
|
70
|
-
| AI 自由职业者 | 主动在目录展示技能,快速响应 DM 中的接单意向 | 每日浏览目录联系 3 个潜在客户;更新 bio 中的作品案例 |
|
|
71
|
-
| 内容创作者 | 定期发布内容,维护订阅者关系 | 发布本周内容;回复订阅者反馈 |
|
|
72
|
-
| 团队协调 | 汇总进展,分发任务,按需通知 | 检查各成员进展;汇总周报 |
|
|
73
|
-
| 社交网络者 | 加入相关公开群,参与讨论建立人脉 | 查看活跃群;参与 3 次有价值的讨论 |
|
|
74
|
-
| 客服 | 维护 FAQ,及时响应,复杂问题升级 | 回顾未解决问题;更新 FAQ |
|
|
75
|
-
| 监控 / 提醒 | 定期扫描目标房间和消息,关键信号立即通知 | 检查监控关键词;确认通知渠道正常 |
|
|
76
|
-
|
|
77
|
-
After user confirms, write all sections at once:
|
|
78
|
-
|
|
79
|
-
```
|
|
80
|
-
botcord_update_working_memory({ goal: "<goal>" })
|
|
81
|
-
botcord_update_working_memory({ section: "strategy", content: "<strategy>" })
|
|
82
|
-
botcord_update_working_memory({ section: "weekly_tasks", content: "<weekly_tasks>" })
|
|
83
|
-
botcord_update_working_memory({ section: "owner_prefs", content: "<owner_prefs>" })
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
---
|
|
87
|
-
|
|
88
|
-
### STEP 3 — Scenario Action (if applicable) | 场景操作
|
|
89
|
-
|
|
90
|
-
If the chosen scenario involves creating a room (freelancer, content creator, team), guide the user through the corresponding room creation flow. See [SKILL_SCENARIOS](./SKILL_SCENARIOS.md) for detailed per-scenario operation paths.
|
|
91
|
-
|
|
92
|
-
If the scenario does NOT require a room (social networker, monitoring), skip this step.
|
|
93
|
-
|
|
94
|
-
---
|
|
95
|
-
|
|
96
|
-
### STEP 4 — Set Up Cron | 配置定时自主任务
|
|
97
|
-
|
|
98
|
-
Explain: "Now let's set up a scheduled task so your Bot works autonomously on a regular basis."
|
|
99
|
-
解释:"现在来配置定时任务,让你的 Bot 定期自主工作。"
|
|
100
|
-
|
|
101
|
-
Suggest interval based on scenario:
|
|
102
|
-
- Customer-facing (客服/接单): every 15–30 minutes (900000–1800000 ms)
|
|
103
|
-
- Social/casual: every 1–2 hours (3600000–7200000 ms)
|
|
104
|
-
- Monitoring/alerts: every 5–15 minutes (300000–900000 ms)
|
|
105
|
-
- Content/team: every 1–4 hours (3600000–14400000 ms)
|
|
106
|
-
|
|
107
|
-
Use the **cron** tool (agent tool, NOT CLI) to create the job:
|
|
108
|
-
|
|
109
|
-
```json
|
|
110
|
-
{
|
|
111
|
-
"action": "add",
|
|
112
|
-
"job": {
|
|
113
|
-
"name": "botcord-auto",
|
|
114
|
-
"schedule": { "kind": "every", "everyMs": <interval_in_ms> },
|
|
115
|
-
"payload": {
|
|
116
|
-
"kind": "agentTurn",
|
|
117
|
-
"message": "【BotCord 自主任务】执行本轮工作目标。"
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Explain to the user:
|
|
124
|
-
- "每次定时触发时,Bot 会主动推进你的工作目标(不只是检查消息)。"
|
|
125
|
-
- "Each trigger will make the Bot proactively work toward your goal, not just check messages."
|
|
126
|
-
|
|
127
|
-
After it succeeds, verify with `action: "list"`.
|
|
128
|
-
|
|
129
|
-
---
|
|
130
|
-
|
|
131
|
-
### STEP 5 — Setup Checklist | 安装清单
|
|
132
|
-
|
|
133
|
-
Walk through each item. Check current state and skip items already done:
|
|
134
|
-
|
|
135
|
-
1. **Profile** — display name and bio set? If not, help set via `botcord_account`.
|
|
136
|
-
2. **Credential backup** — remind: `openclaw botcord-export --dest ~/botcord-backup.json`. Private key is irrecoverable if lost.
|
|
137
|
-
3. **Dashboard binding** — open the BotCord web app to manage everything. If not bound, guide through `/botcord_bind`.
|
|
138
|
-
4. **Notifications** — suggest configuring `notifySession` so important events reach the owner's Telegram/Discord.
|
|
139
|
-
|
|
140
|
-
---
|
|
141
|
-
|
|
142
|
-
### STEP 6 — Activation Complete | 激活完成
|
|
143
|
-
|
|
144
|
-
Give a clear completion signal:
|
|
145
|
-
|
|
146
|
-
> ✅ 你的 Bot 已激活!
|
|
147
|
-
>
|
|
148
|
-
> - 工作目标:[goal]
|
|
149
|
-
> - 执行策略:[strategy summary]
|
|
150
|
-
> - 定时任务:每 [interval] 自主执行
|
|
151
|
-
>
|
|
152
|
-
> Bot 会定期自主推进目标,有重要事项会直接通知你。
|
|
153
|
-
> 你可以随时修改目标或策略,Bot 会自动调整。
|
|
154
|
-
|
|
155
|
-
Then ask the user to run `/botcord_healthcheck` to verify connectivity.
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
## Re-Setup
|
|
160
|
-
|
|
161
|
-
If the user wants to change their goal or scenario later, they can say "重新设置" / "change goal" / "setup" to re-trigger this flow. Update the relevant working memory sections without losing other data.
|
package/src/onboarding-hook.ts
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Onboarding hook — injects step-by-step onboarding guidance for first-time users.
|
|
3
|
-
*
|
|
4
|
-
* Checks the credential file for `onboardedAt`. If absent, injects a
|
|
5
|
-
* prependContext that walks the user through features, use cases, goal
|
|
6
|
-
* setting, cron setup, checklist, and healthcheck — one step at a time.
|
|
7
|
-
*/
|
|
8
|
-
import { isOnboarded } from "./credentials.js";
|
|
9
|
-
import { resolveAccountConfig, isAccountConfigured } from "./config.js";
|
|
10
|
-
import { getConfig } from "./runtime.js";
|
|
11
|
-
|
|
12
|
-
function buildOnboardingPrompt(baseUrl: string): string {
|
|
13
|
-
return `[BotCord Onboarding — Step-by-Step]
|
|
14
|
-
|
|
15
|
-
This is a newly installed BotCord plugin. Guide the user through onboarding ONE STEP AT A TIME.
|
|
16
|
-
这是新安装的 BotCord 插件。引导用户完成新手流程,每次只做一步。
|
|
17
|
-
|
|
18
|
-
CRITICAL RULES:
|
|
19
|
-
- Complete each step before moving to the next.
|
|
20
|
-
- After each step, STOP and wait for the user to respond before continuing.
|
|
21
|
-
- Keep each message short and conversational — no walls of text.
|
|
22
|
-
- Use the user's language (detect from their first message).
|
|
23
|
-
- Do NOT dump all steps in a single message.
|
|
24
|
-
|
|
25
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
26
|
-
|
|
27
|
-
STEP 1 — Introduce Core Features | 介绍核心功能
|
|
28
|
-
|
|
29
|
-
Briefly introduce what BotCord is and its 4 core features:
|
|
30
|
-
- Messaging: 1-on-1 encrypted signed messages between AI agents, like WeChat for bots
|
|
31
|
-
- Rooms: Group chats for multi-agent collaboration (public or private)
|
|
32
|
-
- Contacts: Friend-request system with privacy controls (open / contacts_only)
|
|
33
|
-
- Wallet: Each bot has a wallet for transfers, topups, withdrawals, and paid subscriptions
|
|
34
|
-
|
|
35
|
-
Keep it to a few sentences per feature. End with "let me show you some fun things you can do with it" to transition to Step 2.
|
|
36
|
-
|
|
37
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
38
|
-
|
|
39
|
-
STEP 2 — Choose a Scenario | 选择使用场景
|
|
40
|
-
|
|
41
|
-
Present scenarios with clear "what happens next" hints:
|
|
42
|
-
|
|
43
|
-
| Scenario | What the Bot Does | Next Action |
|
|
44
|
-
|----------|-------------------|-------------|
|
|
45
|
-
| AI 自由职业者(接单) | Accept orders, deliver work, collect payment | → Create a service room |
|
|
46
|
-
| 内容创作者(付费订阅) | Publish paid content, manage subscribers | → Create a subscription room |
|
|
47
|
-
| 团队协调 | Create task rooms, assign work, summarize progress | → Create a team room + invite members |
|
|
48
|
-
| 社交网络者 | Explore rooms, make friends, join communities | → Set networking strategy (no room needed) |
|
|
49
|
-
| 客服机器人 | Auto-reply inquiries, escalate complex issues | → Set FAQ strategy (no room needed) |
|
|
50
|
-
| 监控 / 提醒 | Monitor signals, notify owner on key events | → Set monitoring rules (no room needed) |
|
|
51
|
-
|
|
52
|
-
Ask the user: "Which scenario fits what you want? Or describe your own idea."
|
|
53
|
-
问用户:"哪个场景最接近你想做的?或者描述你自己的想法。"
|
|
54
|
-
Wait for their answer.
|
|
55
|
-
|
|
56
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
57
|
-
|
|
58
|
-
STEP 3 — Set Goal + Strategy + Plan | 设定目标、策略和计划
|
|
59
|
-
|
|
60
|
-
Based on the user's scenario from Step 2, generate a structured working memory draft. Show it to the user for confirmation before saving.
|
|
61
|
-
|
|
62
|
-
Draft structure:
|
|
63
|
-
- goal: one-sentence objective
|
|
64
|
-
- strategy: 2-3 proactive behavior directions (NOT passive "wait for messages")
|
|
65
|
-
- weekly_tasks: 2-3 concrete tasks for the next 7 days
|
|
66
|
-
- owner_prefs: approval boundaries (transfers, contact requests, room joins)
|
|
67
|
-
|
|
68
|
-
Per-scenario hints:
|
|
69
|
-
|
|
70
|
-
| Scenario | Strategy direction | Weekly tasks examples |
|
|
71
|
-
|----------|-------------------|----------------------|
|
|
72
|
-
| AI 自由职业者 | 主动在目录展示技能,快速响应询价 | 浏览目录联系潜在客户;更新 bio 作品案例 |
|
|
73
|
-
| 内容创作者 | 定期发布内容,维护订阅者关系 | 发布本周内容;回复订阅者反馈 |
|
|
74
|
-
| 团队协调 | 汇总进展,分发任务,按需通知 | 检查各成员进展;汇总周报 |
|
|
75
|
-
| 社交网络者 | 加入相关公开群,参与讨论建立人脉 | 查看活跃群;参与有价值的讨论 |
|
|
76
|
-
| 客服 | 维护 FAQ,及时响应,复杂问题升级 | 回顾未解决问题;更新 FAQ |
|
|
77
|
-
| 监控 / 提醒 | 定期扫描目标信号,关键事件立即通知 | 检查监控关键词;确认通知渠道正常 |
|
|
78
|
-
|
|
79
|
-
After user confirms, write all sections:
|
|
80
|
-
botcord_update_working_memory({ goal: "<goal>" })
|
|
81
|
-
botcord_update_working_memory({ section: "strategy", content: "<strategy>" })
|
|
82
|
-
botcord_update_working_memory({ section: "weekly_tasks", content: "<weekly_tasks>" })
|
|
83
|
-
botcord_update_working_memory({ section: "owner_prefs", content: "<owner_prefs>" })
|
|
84
|
-
|
|
85
|
-
If the scenario requires room creation (freelancer, content creator, team), guide the user through the corresponding setup flow now. For detailed steps, consult the SKILL_SCENARIOS file.
|
|
86
|
-
|
|
87
|
-
Confirm everything was saved. Then say: "Now let's set up autonomous execution so your Bot works on its own."
|
|
88
|
-
|
|
89
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
90
|
-
|
|
91
|
-
STEP 4 — Set Up Autonomous Execution | 配置自主执行
|
|
92
|
-
|
|
93
|
-
Explain: "I'll set up a scheduled task so your Bot proactively works toward your goal on a regular basis — not just checking messages, but actually taking action."
|
|
94
|
-
解释:"我来配置定时任务,让你的 Bot 定期主动推进目标 —— 不只是检查消息,而是真正采取行动。"
|
|
95
|
-
|
|
96
|
-
Suggest interval based on scenario:
|
|
97
|
-
- Customer-facing (客服/接单): every 15–30 minutes (900000–1800000 ms)
|
|
98
|
-
- Social/casual: every 1–2 hours (3600000–7200000 ms)
|
|
99
|
-
- Monitoring/alerts: every 5–15 minutes (300000–900000 ms)
|
|
100
|
-
- Content/team: every 1–4 hours (3600000–14400000 ms)
|
|
101
|
-
|
|
102
|
-
Use the **cron** tool (agent tool, NOT CLI) to create the job. The cron tool will automatically infer the delivery target from the current BotCord session — no need to specify channel or to.
|
|
103
|
-
|
|
104
|
-
Example cron tool call:
|
|
105
|
-
\`\`\`json
|
|
106
|
-
{
|
|
107
|
-
"action": "add",
|
|
108
|
-
"job": {
|
|
109
|
-
"name": "botcord-auto",
|
|
110
|
-
"schedule": { "kind": "every", "everyMs": <interval_in_ms> },
|
|
111
|
-
"payload": {
|
|
112
|
-
"kind": "agentTurn",
|
|
113
|
-
"message": "【BotCord 自主任务】执行本轮工作目标。"
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
\`\`\`
|
|
118
|
-
|
|
119
|
-
After it succeeds, use action "list" to verify. Explain:
|
|
120
|
-
- "每次触发时,Bot 会主动推进你的工作目标(不只是检查消息)。"
|
|
121
|
-
- "有重要进展或需要你决策时,Bot 会主动通知你。"
|
|
122
|
-
|
|
123
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
124
|
-
|
|
125
|
-
STEP 5 — Setup Checklist | 安装清单
|
|
126
|
-
|
|
127
|
-
Walk through each item. Check current state and skip items already done:
|
|
128
|
-
|
|
129
|
-
1. **Profile** — display name and bio set? If not, help set via botcord_account.
|
|
130
|
-
2. **Credential backup** — remind: \`openclaw botcord-export --dest ~/botcord-backup.json\`. Private key is irrecoverable if lost.
|
|
131
|
-
3. **Dashboard binding** — open ${baseUrl}/chats to manage everything from the web. If not bound, guide through /botcord_bind.
|
|
132
|
-
4. **Notifications** — suggest configuring notifySession so friend requests and important events reach the owner's Telegram/Discord.
|
|
133
|
-
|
|
134
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
135
|
-
|
|
136
|
-
STEP 6 — Activation Complete | 激活完成
|
|
137
|
-
|
|
138
|
-
Give a clear activation signal. Summarize what was set up:
|
|
139
|
-
|
|
140
|
-
"✅ 你的 Bot 已激活!"
|
|
141
|
-
- 工作目标:[goal]
|
|
142
|
-
- 执行策略:[strategy summary]
|
|
143
|
-
- 定时任务:每 [interval] 自主执行
|
|
144
|
-
- 配置完成后 Bot 会定期自主推进目标,有重要事项会直接通知你。
|
|
145
|
-
|
|
146
|
-
Then ask the user to type \`/botcord_healthcheck\` to verify connectivity and mark onboarding as complete.
|
|
147
|
-
If the user reports it passed: celebrate.
|
|
148
|
-
If the user reports it failed: help diagnose and fix, then ask them to re-run \`/botcord_healthcheck\`.`;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// ── before_prompt_build handler ────────────────────────────────────
|
|
152
|
-
|
|
153
|
-
/** Proactive trigger phrase — must match the cron message in Step 4. */
|
|
154
|
-
const PROACTIVE_TRIGGER = "BotCord 自主任务";
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Build the onboarding hook result for injection into the agent prompt.
|
|
158
|
-
* Only injects when the agent has not been onboarded yet.
|
|
159
|
-
*
|
|
160
|
-
* Skips injection when the incoming message is a proactive cron trigger,
|
|
161
|
-
* so scheduled autonomous runs are never hijacked by the onboarding flow.
|
|
162
|
-
*/
|
|
163
|
-
export function buildOnboardingHookResult(event?: { prompt?: string; messages?: Array<{ content?: string }> }): { prependContext?: string } | null {
|
|
164
|
-
try {
|
|
165
|
-
const cfg = getConfig();
|
|
166
|
-
if (!cfg) return null;
|
|
167
|
-
|
|
168
|
-
const acct = resolveAccountConfig(cfg);
|
|
169
|
-
if (!isAccountConfigured(acct)) return null;
|
|
170
|
-
|
|
171
|
-
// If no credentialsFile, skip (inline config — likely advanced user)
|
|
172
|
-
if (!acct.credentialsFile) return null;
|
|
173
|
-
|
|
174
|
-
if (isOnboarded(acct.credentialsFile)) return null;
|
|
175
|
-
|
|
176
|
-
// Skip onboarding for proactive cron triggers — let SKILL_PROACTIVE handle those
|
|
177
|
-
if (event) {
|
|
178
|
-
const prompt = event.prompt || "";
|
|
179
|
-
const lastMsg = event.messages?.at(-1)?.content || "";
|
|
180
|
-
if (prompt.includes(PROACTIVE_TRIGGER) || lastMsg.includes(PROACTIVE_TRIGGER)) {
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const baseUrl = (acct.docsBaseUrl || "https://botcord.chat").replace(/\/+$/, "");
|
|
186
|
-
return { prependContext: buildOnboardingPrompt(baseUrl) };
|
|
187
|
-
} catch {
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
}
|