botschat 0.1.19 → 0.1.20

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.
Files changed (56) hide show
  1. package/migrations/0013_agents_table.sql +29 -0
  2. package/migrations/0014_agent_sessions.sql +19 -0
  3. package/migrations/0015_message_traces.sql +27 -0
  4. package/migrations/0016_multi_agent_channels_messages.sql +9 -0
  5. package/migrations/0017_rename_cron_job_id.sql +2 -0
  6. package/package.json +1 -1
  7. package/packages/api/src/do/connection-do.ts +382 -186
  8. package/packages/api/src/index.ts +67 -50
  9. package/packages/api/src/protocol-v2.ts +154 -0
  10. package/packages/api/src/routes/agents-v2.ts +192 -0
  11. package/packages/api/src/routes/agents.ts +3 -3
  12. package/packages/api/src/routes/auth.ts +0 -1
  13. package/packages/api/src/routes/channels.ts +11 -11
  14. package/packages/api/src/routes/history-v2.ts +221 -0
  15. package/packages/api/src/routes/migrate-v2.ts +110 -0
  16. package/packages/api/src/routes/sessions.ts +5 -5
  17. package/packages/api/src/routes/tasks.ts +33 -33
  18. package/packages/plugin/dist/src/channel.d.ts +10 -0
  19. package/packages/plugin/dist/src/channel.d.ts.map +1 -1
  20. package/packages/plugin/dist/src/channel.js +10 -51
  21. package/packages/plugin/dist/src/channel.js.map +1 -1
  22. package/packages/plugin/dist/src/types.d.ts +13 -0
  23. package/packages/plugin/dist/src/types.d.ts.map +1 -1
  24. package/packages/plugin/package.json +2 -18
  25. package/packages/web/dist/assets/index-BARPtt0v.css +1 -0
  26. package/packages/web/dist/assets/index-Bf-XL3te.js +2 -0
  27. package/packages/web/dist/assets/{index-C_GamcQc.js → index-CYQMu_-c.js} +1 -1
  28. package/packages/web/dist/assets/index-CYlvfpX9.js +1519 -0
  29. package/packages/web/dist/assets/index-CxcpA4Qo.js +1 -0
  30. package/packages/web/dist/assets/{index-MyoWvQAH.js → index-DYCO-ry1.js} +1 -1
  31. package/packages/web/dist/assets/index-QebPVqwj.js +2 -0
  32. package/packages/web/dist/assets/{index.esm-BpQAwtdR.js → index.esm-CvOpngZM.js} +1 -1
  33. package/packages/web/dist/assets/{web-cnzjgNfD.js → web-1cdhq2RW.js} +1 -1
  34. package/packages/web/dist/assets/{web-BbTzVNLt.js → web-D3LMODYp.js} +1 -1
  35. package/packages/web/dist/index.html +2 -2
  36. package/packages/web/src/App.tsx +56 -9
  37. package/packages/web/src/api.ts +61 -5
  38. package/packages/web/src/components/AgentSettings.tsx +328 -0
  39. package/packages/web/src/components/ChatWindow.tsx +9 -9
  40. package/packages/web/src/components/CronDetail.tsx +1 -1
  41. package/packages/web/src/components/LoginPage.tsx +1 -59
  42. package/packages/web/src/components/MessageContent.tsx +2 -17
  43. package/packages/web/src/components/SessionTabs.tsx +1 -1
  44. package/packages/web/src/components/Sidebar.tsx +3 -1
  45. package/packages/web/src/hooks/useIMEComposition.ts +9 -14
  46. package/packages/web/src/store.ts +39 -7
  47. package/packages/web/src/ws.ts +1 -0
  48. package/scripts/dev.sh +53 -0
  49. package/scripts/mock-openclaw-v2.mjs +486 -0
  50. package/packages/api/src/routes/demo.ts +0 -156
  51. package/packages/web/dist/assets/index-BtPyCBCl.css +0 -1
  52. package/packages/web/dist/assets/index-BtpsFe4Z.js +0 -2
  53. package/packages/web/dist/assets/index-CQbIYr6_.js +0 -2
  54. package/packages/web/dist/assets/index-LiBjPMg2.js +0 -1
  55. package/packages/web/dist/assets/index-STIPTMK8.js +0 -1516
  56. package/packages/web/src/components/ImageLightbox.tsx +0 -96
@@ -0,0 +1,221 @@
1
+ import { Hono } from "hono";
2
+ import type { Env } from "../env.js";
3
+
4
+ /**
5
+ * History Query API v2 — supports verbose-level filtering, agent filtering,
6
+ * trace type filtering, and keyword search across messages + message_traces.
7
+ *
8
+ * GET /api/v2/messages/query?sessionKey=...&verboseLevel=1&limit=50
9
+ */
10
+ const historyV2 = new Hono<{ Bindings: Env; Variables: { userId: string } }>();
11
+
12
+ historyV2.get("/query", async (c) => {
13
+ const userId = c.get("userId");
14
+ const sessionKey = c.req.query("sessionKey");
15
+ if (!sessionKey) return c.json({ error: "sessionKey required" }, 400);
16
+
17
+ const verboseLevel = Math.min(Number(c.req.query("verboseLevel") ?? 1), 3);
18
+ const limit = Math.min(Number(c.req.query("limit") ?? 50), 200);
19
+ const senderFilter = c.req.query("senderFilter"); // "user" | "agent" | undefined
20
+ const agentIdFilter = c.req.query("agentIdFilter");
21
+ const traceTypeFilter = c.req.query("traceTypeFilter");
22
+ const keyword = c.req.query("keyword");
23
+ const beforeMessageId = c.req.query("beforeMessageId");
24
+
25
+ // Build lv1 query (messages table)
26
+ const conditions: string[] = ["m.session_key = ?", "m.user_id = ?"];
27
+ const params: unknown[] = [sessionKey, userId];
28
+
29
+ if (senderFilter === "user" || senderFilter === "agent") {
30
+ conditions.push("m.sender = ?");
31
+ params.push(senderFilter);
32
+ }
33
+ if (agentIdFilter) {
34
+ conditions.push("(m.sender_agent_id = ? OR m.target_agent_id = ?)");
35
+ params.push(agentIdFilter, agentIdFilter);
36
+ }
37
+ if (keyword) {
38
+ conditions.push("m.text LIKE ?");
39
+ params.push(`%${keyword}%`);
40
+ }
41
+ if (beforeMessageId) {
42
+ conditions.push("m.created_at < (SELECT created_at FROM messages WHERE id = ?)");
43
+ params.push(beforeMessageId);
44
+ }
45
+
46
+ params.push(limit);
47
+
48
+ const { results: messages } = await c.env.DB.prepare(
49
+ `SELECT m.id, m.sender, m.sender_agent_id, m.target_agent_id, m.text, m.media_url, m.encrypted, m.created_at
50
+ FROM messages m
51
+ WHERE ${conditions.join(" AND ")}
52
+ ORDER BY m.created_at DESC
53
+ LIMIT ?`,
54
+ )
55
+ .bind(...params)
56
+ .all<{
57
+ id: string;
58
+ sender: string;
59
+ sender_agent_id: string | null;
60
+ target_agent_id: string | null;
61
+ text: string;
62
+ media_url: string | null;
63
+ encrypted: number;
64
+ created_at: number;
65
+ }>();
66
+
67
+ // Resolve agent names
68
+ const agentIds = new Set<string>();
69
+ for (const m of messages ?? []) {
70
+ if (m.sender_agent_id) agentIds.add(m.sender_agent_id);
71
+ if (m.target_agent_id) agentIds.add(m.target_agent_id);
72
+ }
73
+
74
+ const agentNames: Record<string, string> = {};
75
+ if (agentIds.size > 0) {
76
+ const placeholders = [...agentIds].map(() => "?").join(",");
77
+ const { results: agents } = await c.env.DB.prepare(
78
+ `SELECT id, name FROM agents WHERE id IN (${placeholders})`,
79
+ )
80
+ .bind(...agentIds)
81
+ .all<{ id: string; name: string }>();
82
+ for (const a of agents ?? []) {
83
+ agentNames[a.id] = a.name;
84
+ }
85
+ }
86
+
87
+ // Build response
88
+ type MessageResponse = {
89
+ id: string;
90
+ sender: string;
91
+ senderAgentId?: string;
92
+ senderAgentName?: string;
93
+ targetAgentId?: string;
94
+ text: string;
95
+ mediaUrl?: string;
96
+ encrypted: boolean;
97
+ timestamp: number;
98
+ traces?: Array<{
99
+ verboseLevel: number;
100
+ traceType: string;
101
+ content: string;
102
+ metadata?: Record<string, unknown>;
103
+ }>;
104
+ };
105
+
106
+ const result: MessageResponse[] = [];
107
+ const messageIds = (messages ?? []).map((m) => m.id);
108
+
109
+ // Fetch traces if verboseLevel >= 2
110
+ let tracesByMessageId: Record<string, Array<{ verbose_level: number; trace_type: string; content: string; metadata_json: string }>> = {};
111
+
112
+ if (verboseLevel >= 2 && messageIds.length > 0) {
113
+ const traceConds: string[] = ["message_id IN (" + messageIds.map(() => "?").join(",") + ")"];
114
+ const traceParams: unknown[] = [...messageIds];
115
+
116
+ traceConds.push("verbose_level <= ?");
117
+ traceParams.push(verboseLevel);
118
+
119
+ if (traceTypeFilter) {
120
+ traceConds.push("trace_type = ?");
121
+ traceParams.push(traceTypeFilter);
122
+ }
123
+ if (agentIdFilter) {
124
+ traceConds.push("agent_id = ?");
125
+ traceParams.push(agentIdFilter);
126
+ }
127
+
128
+ const { results: traces } = await c.env.DB.prepare(
129
+ `SELECT message_id, verbose_level, trace_type, content, metadata_json
130
+ FROM message_traces
131
+ WHERE ${traceConds.join(" AND ")}
132
+ ORDER BY created_at ASC`,
133
+ )
134
+ .bind(...traceParams)
135
+ .all<{ message_id: string; verbose_level: number; trace_type: string; content: string; metadata_json: string }>();
136
+
137
+ for (const t of traces ?? []) {
138
+ if (!tracesByMessageId[t.message_id]) tracesByMessageId[t.message_id] = [];
139
+ tracesByMessageId[t.message_id].push(t);
140
+ }
141
+ }
142
+
143
+ for (const m of messages ?? []) {
144
+ const msg: MessageResponse = {
145
+ id: m.id,
146
+ sender: m.sender,
147
+ senderAgentId: m.sender_agent_id ?? undefined,
148
+ senderAgentName: m.sender_agent_id ? agentNames[m.sender_agent_id] : undefined,
149
+ targetAgentId: m.target_agent_id ?? undefined,
150
+ text: typeof m.text === "string" ? m.text : "",
151
+ mediaUrl: m.media_url ?? undefined,
152
+ encrypted: !!m.encrypted,
153
+ timestamp: m.created_at,
154
+ };
155
+
156
+ if (verboseLevel >= 2 && tracesByMessageId[m.id]) {
157
+ msg.traces = tracesByMessageId[m.id].map((t) => ({
158
+ verboseLevel: t.verbose_level,
159
+ traceType: t.trace_type,
160
+ content: typeof t.content === "string" ? t.content : "",
161
+ metadata: t.metadata_json ? JSON.parse(t.metadata_json) : undefined,
162
+ }));
163
+ }
164
+
165
+ result.push(msg);
166
+ }
167
+
168
+ // Return in chronological order (query was DESC for LIMIT, reverse for output)
169
+ result.reverse();
170
+
171
+ return c.json({ messages: result, hasMore: (messages?.length ?? 0) === limit });
172
+ });
173
+
174
+ /** GET /api/v2/messages/traces/:messageId — fetch traces for a specific message */
175
+ historyV2.get("/traces/:messageId", async (c) => {
176
+ const userId = c.get("userId");
177
+ const messageId = c.req.param("messageId");
178
+ const verboseLevel = Math.min(Number(c.req.query("verboseLevel") ?? 3), 3);
179
+
180
+ // Verify message belongs to user
181
+ const msg = await c.env.DB.prepare(
182
+ "SELECT id FROM messages WHERE id = ? AND user_id = ?",
183
+ )
184
+ .bind(messageId, userId)
185
+ .first();
186
+
187
+ if (!msg) return c.json({ error: "Message not found" }, 404);
188
+
189
+ const { results: traces } = await c.env.DB.prepare(
190
+ `SELECT id, verbose_level, trace_type, content, metadata_json, agent_id, encrypted, created_at
191
+ FROM message_traces
192
+ WHERE message_id = ? AND verbose_level <= ?
193
+ ORDER BY created_at ASC`,
194
+ )
195
+ .bind(messageId, verboseLevel)
196
+ .all<{
197
+ id: string;
198
+ verbose_level: number;
199
+ trace_type: string;
200
+ content: string;
201
+ metadata_json: string;
202
+ agent_id: string;
203
+ encrypted: number;
204
+ created_at: number;
205
+ }>();
206
+
207
+ return c.json({
208
+ traces: (traces ?? []).map((t) => ({
209
+ id: t.id,
210
+ verboseLevel: t.verbose_level,
211
+ traceType: t.trace_type,
212
+ content: typeof t.content === "string" ? t.content : "",
213
+ metadata: t.metadata_json ? JSON.parse(t.metadata_json) : undefined,
214
+ agentId: t.agent_id,
215
+ encrypted: !!t.encrypted,
216
+ timestamp: t.created_at,
217
+ })),
218
+ });
219
+ });
220
+
221
+ export { historyV2 };
@@ -0,0 +1,110 @@
1
+ import { Hono } from "hono";
2
+ import type { Env } from "../env.js";
3
+ import { generateId } from "../utils/id.js";
4
+
5
+ /**
6
+ * One-time migration endpoint: converts legacy pairing_tokens into agents,
7
+ * backfills channels.default_agent_id, and sets messages.sender_agent_id.
8
+ *
9
+ * POST /api/v2/migrate — idempotent, safe to run multiple times.
10
+ * Only available in development mode.
11
+ */
12
+ const migrateV2 = new Hono<{ Bindings: Env; Variables: { userId: string } }>();
13
+
14
+ migrateV2.post("/", async (c) => {
15
+ if (c.env.ENVIRONMENT !== "development") {
16
+ return c.json({ error: "Migration endpoint only available in development" }, 403);
17
+ }
18
+
19
+ const userId = c.get("userId");
20
+ const stats = { agentsCreated: 0, channelsUpdated: 0, messagesUpdated: 0, skipped: 0 };
21
+
22
+ // Step 1: Migrate pairing_tokens → agents (skip if agent with same token already exists)
23
+ const { results: tokens } = await c.env.DB.prepare(
24
+ "SELECT id, token, label, last_connected_at, last_ip, connection_count FROM pairing_tokens WHERE user_id = ? AND revoked_at IS NULL",
25
+ )
26
+ .bind(userId)
27
+ .all<{
28
+ id: string;
29
+ token: string;
30
+ label: string | null;
31
+ last_connected_at: number | null;
32
+ last_ip: string | null;
33
+ connection_count: number;
34
+ }>();
35
+
36
+ let defaultAgentId: string | null = null;
37
+
38
+ for (const pt of tokens ?? []) {
39
+ const existing = await c.env.DB.prepare(
40
+ "SELECT id FROM agents WHERE pairing_token = ?",
41
+ )
42
+ .bind(pt.token)
43
+ .first<{ id: string }>();
44
+
45
+ if (existing) {
46
+ if (!defaultAgentId) defaultAgentId = existing.id;
47
+ stats.skipped++;
48
+ continue;
49
+ }
50
+
51
+ const agentId = generateId("agt_");
52
+ await c.env.DB.prepare(
53
+ `INSERT INTO agents (id, user_id, name, type, role, pairing_token, capabilities,
54
+ status, last_connected_at, last_ip, connection_count)
55
+ VALUES (?, ?, ?, 'openclaw', 'general', ?, '["chat","streaming","cron","a2ui","media","code_edit","delegate"]',
56
+ 'disconnected', ?, ?, ?)`,
57
+ )
58
+ .bind(
59
+ agentId,
60
+ userId,
61
+ pt.label ?? "OpenClaw",
62
+ pt.token,
63
+ pt.last_connected_at,
64
+ pt.last_ip,
65
+ pt.connection_count ?? 0,
66
+ )
67
+ .run();
68
+
69
+ if (!defaultAgentId) defaultAgentId = agentId;
70
+ stats.agentsCreated++;
71
+ }
72
+
73
+ // If no pairing tokens exist but user has no agents, skip backfill
74
+ if (!defaultAgentId) {
75
+ const anyAgent = await c.env.DB.prepare(
76
+ "SELECT id FROM agents WHERE user_id = ? LIMIT 1",
77
+ )
78
+ .bind(userId)
79
+ .first<{ id: string }>();
80
+ defaultAgentId = anyAgent?.id ?? null;
81
+ }
82
+
83
+ // Step 2: Backfill channels.default_agent_id (set to first agent if null)
84
+ if (defaultAgentId) {
85
+ const { meta } = await c.env.DB.prepare(
86
+ "UPDATE channels SET default_agent_id = ?, updated_at = unixepoch() WHERE user_id = ? AND default_agent_id IS NULL",
87
+ )
88
+ .bind(defaultAgentId, userId)
89
+ .run();
90
+ stats.channelsUpdated = meta.changes ?? 0;
91
+ }
92
+
93
+ // Step 3: Backfill messages.sender_agent_id for agent messages (set to default agent)
94
+ if (defaultAgentId) {
95
+ const { meta } = await c.env.DB.prepare(
96
+ "UPDATE messages SET sender_agent_id = ? WHERE user_id = ? AND sender = 'agent' AND sender_agent_id IS NULL",
97
+ )
98
+ .bind(defaultAgentId, userId)
99
+ .run();
100
+ stats.messagesUpdated = meta.changes ?? 0;
101
+ }
102
+
103
+ return c.json({
104
+ ok: true,
105
+ defaultAgentId,
106
+ stats,
107
+ });
108
+ });
109
+
110
+ export { migrateV2 };
@@ -11,10 +11,10 @@ sessions.get("/", async (c) => {
11
11
 
12
12
  // Verify channel ownership
13
13
  const channel = await c.env.DB.prepare(
14
- "SELECT id, openclaw_agent_id FROM channels WHERE id = ? AND user_id = ?",
14
+ "SELECT id, provider_agent_id FROM channels WHERE id = ? AND user_id = ?",
15
15
  )
16
16
  .bind(channelId, userId)
17
- .first<{ id: string; openclaw_agent_id: string }>();
17
+ .first<{ id: string; provider_agent_id: string }>();
18
18
 
19
19
  if (!channel) return c.json({ error: "Channel not found" }, 404);
20
20
 
@@ -48,10 +48,10 @@ sessions.post("/", async (c) => {
48
48
 
49
49
  // Verify channel ownership
50
50
  const channel = await c.env.DB.prepare(
51
- "SELECT id, openclaw_agent_id FROM channels WHERE id = ? AND user_id = ?",
51
+ "SELECT id, provider_agent_id FROM channels WHERE id = ? AND user_id = ?",
52
52
  )
53
53
  .bind(channelId, userId)
54
- .first<{ id: string; openclaw_agent_id: string }>();
54
+ .first<{ id: string; provider_agent_id: string }>();
55
55
 
56
56
  if (!channel) return c.json({ error: "Channel not found" }, 404);
57
57
 
@@ -67,7 +67,7 @@ sessions.post("/", async (c) => {
67
67
 
68
68
  const sessionName = name?.trim() || `Session ${count + 1}`;
69
69
  const id = generateId("ses_");
70
- const sessionKey = `agent:${channel.openclaw_agent_id}:botschat:${userId}:ses:${id}`;
70
+ const sessionKey = `agent:${channel.provider_agent_id}:botschat:${userId}:ses:${id}`;
71
71
 
72
72
  await c.env.DB.prepare(
73
73
  "INSERT INTO sessions (id, channel_id, user_id, name, session_key) VALUES (?, ?, ?, ?, ?)",
@@ -10,7 +10,7 @@ const tasks = new Hono<{ Bindings: Env; Variables: { userId: string } }>();
10
10
  async function pushScheduleToOpenClaw(
11
11
  env: Env,
12
12
  userId: string,
13
- task: { taskId: string; name?: string; openclawCronJobId: string; agentId: string; schedule: string; instructions: string; enabled: boolean; model?: string },
13
+ task: { taskId: string; name?: string; providerJobId: string; agentId: string; schedule: string; instructions: string; enabled: boolean; model?: string },
14
14
  ): Promise<void> {
15
15
  try {
16
16
  const doId = env.CONNECTION_DO.idFromName(userId);
@@ -23,7 +23,7 @@ async function pushScheduleToOpenClaw(
23
23
  type: "task.schedule",
24
24
  taskId: task.taskId,
25
25
  name: task.name,
26
- cronJobId: task.openclawCronJobId,
26
+ cronJobId: task.providerJobId,
27
27
  agentId: task.agentId,
28
28
  schedule: task.schedule,
29
29
  instructions: task.instructions,
@@ -70,24 +70,24 @@ tasks.get("/", async (c) => {
70
70
 
71
71
  // Verify channel ownership
72
72
  const channel = await c.env.DB.prepare(
73
- "SELECT id, openclaw_agent_id FROM channels WHERE id = ? AND user_id = ?",
73
+ "SELECT id, provider_agent_id FROM channels WHERE id = ? AND user_id = ?",
74
74
  )
75
75
  .bind(channelId, userId)
76
- .first<{ id: string; openclaw_agent_id: string }>();
76
+ .first<{ id: string; provider_agent_id: string }>();
77
77
 
78
78
  if (!channel) return c.json({ error: "Channel not found" }, 404);
79
79
 
80
80
  // Note: schedule, instructions, model are NOT stored in D1.
81
81
  // They belong to OpenClaw and are delivered to the frontend via WebSocket task.scan.result.
82
82
  const { results } = await c.env.DB.prepare(
83
- "SELECT id, name, kind, openclaw_cron_job_id, session_key, enabled, created_at, updated_at FROM tasks WHERE channel_id = ? ORDER BY kind ASC, created_at ASC",
83
+ "SELECT id, name, kind, provider_job_id, session_key, enabled, created_at, updated_at FROM tasks WHERE channel_id = ? ORDER BY kind ASC, created_at ASC",
84
84
  )
85
85
  .bind(channelId)
86
86
  .all<{
87
87
  id: string;
88
88
  name: string;
89
89
  kind: string;
90
- openclaw_cron_job_id: string | null;
90
+ provider_job_id: string | null;
91
91
  session_key: string | null;
92
92
  enabled: number;
93
93
  created_at: number;
@@ -99,7 +99,7 @@ tasks.get("/", async (c) => {
99
99
  id: r.id,
100
100
  name: r.name,
101
101
  kind: r.kind,
102
- openclawCronJobId: r.openclaw_cron_job_id,
102
+ providerJobId: r.provider_job_id,
103
103
  sessionKey: r.session_key,
104
104
  enabled: !!r.enabled,
105
105
  createdAt: r.created_at,
@@ -115,10 +115,10 @@ tasks.post("/", async (c) => {
115
115
 
116
116
  // Verify channel ownership
117
117
  const channel = await c.env.DB.prepare(
118
- "SELECT id, openclaw_agent_id FROM channels WHERE id = ? AND user_id = ?",
118
+ "SELECT id, provider_agent_id FROM channels WHERE id = ? AND user_id = ?",
119
119
  )
120
120
  .bind(channelId, userId)
121
- .first<{ id: string; openclaw_agent_id: string }>();
121
+ .first<{ id: string; provider_agent_id: string }>();
122
122
 
123
123
  if (!channel) return c.json({ error: "Channel not found" }, 404);
124
124
 
@@ -137,7 +137,7 @@ tasks.post("/", async (c) => {
137
137
  }
138
138
 
139
139
  const id = generateId("tsk_");
140
- const agentId = channel.openclaw_agent_id;
140
+ const agentId = channel.provider_agent_id;
141
141
 
142
142
  // Build session key based on task kind
143
143
  let sessionKey: string;
@@ -153,10 +153,10 @@ tasks.post("/", async (c) => {
153
153
  }
154
154
 
155
155
  // D1 only stores basic task metadata — schedule/instructions/model belong to OpenClaw.
156
- // openclawCronJobId is initially null for new background tasks; it will be set
156
+ // providerJobId is initially null for new background tasks; it will be set
157
157
  // when the plugin creates the cron job and sends back a task.schedule.ack.
158
158
  await c.env.DB.prepare(
159
- "INSERT INTO tasks (id, channel_id, name, kind, openclaw_cron_job_id, session_key) VALUES (?, ?, ?, ?, ?, ?)",
159
+ "INSERT INTO tasks (id, channel_id, name, kind, provider_job_id, session_key) VALUES (?, ?, ?, ?, ?, ?)",
160
160
  )
161
161
  .bind(id, channelId, name.trim(), kind, null, sessionKey)
162
162
  .run();
@@ -166,7 +166,7 @@ tasks.post("/", async (c) => {
166
166
  await pushScheduleToOpenClaw(c.env, userId, {
167
167
  taskId: id,
168
168
  name: name.trim(),
169
- openclawCronJobId: "",
169
+ providerJobId: "",
170
170
  agentId,
171
171
  schedule: schedule.trim(),
172
172
  instructions: instructions?.trim() ?? "",
@@ -179,7 +179,7 @@ tasks.post("/", async (c) => {
179
179
  id,
180
180
  name: name.trim(),
181
181
  kind,
182
- openclawCronJobId: null,
182
+ providerJobId: null,
183
183
  sessionKey,
184
184
  enabled: true,
185
185
  },
@@ -195,21 +195,21 @@ tasks.patch("/:taskId", async (c) => {
195
195
 
196
196
  // Verify ownership and get task+channel info
197
197
  const channel = await c.env.DB.prepare(
198
- "SELECT id, openclaw_agent_id FROM channels WHERE id = ? AND user_id = ?",
198
+ "SELECT id, provider_agent_id FROM channels WHERE id = ? AND user_id = ?",
199
199
  )
200
200
  .bind(channelId, userId)
201
- .first<{ id: string; openclaw_agent_id: string }>();
201
+ .first<{ id: string; provider_agent_id: string }>();
202
202
 
203
203
  if (!channel) return c.json({ error: "Channel not found" }, 404);
204
204
 
205
205
  const existingTask = await c.env.DB.prepare(
206
- "SELECT id, kind, openclaw_cron_job_id, enabled FROM tasks WHERE id = ? AND channel_id = ?",
206
+ "SELECT id, kind, provider_job_id, enabled FROM tasks WHERE id = ? AND channel_id = ?",
207
207
  )
208
208
  .bind(taskId, channelId)
209
209
  .first<{
210
210
  id: string;
211
211
  kind: string;
212
- openclaw_cron_job_id: string | null;
212
+ provider_job_id: string | null;
213
213
  enabled: number;
214
214
  }>();
215
215
 
@@ -250,13 +250,13 @@ tasks.patch("/:taskId", async (c) => {
250
250
  // Push OpenClaw-owned fields directly to OpenClaw for background tasks.
251
251
  // The client must send ALL OpenClaw fields (schedule, instructions, enabled)
252
252
  // together since they are not stored in D1.
253
- if (existingTask.kind === "background" && existingTask.openclaw_cron_job_id) {
253
+ if (existingTask.kind === "background" && existingTask.provider_job_id) {
254
254
  const needsPush = body.schedule !== undefined || body.instructions !== undefined || body.enabled !== undefined || body.model !== undefined;
255
255
  if (needsPush) {
256
256
  await pushScheduleToOpenClaw(c.env, userId, {
257
257
  taskId: taskId,
258
- openclawCronJobId: existingTask.openclaw_cron_job_id,
259
- agentId: channel.openclaw_agent_id,
258
+ providerJobId: existingTask.provider_job_id,
259
+ agentId: channel.provider_agent_id,
260
260
  schedule: body.schedule ?? "",
261
261
  instructions: body.instructions ?? "",
262
262
  enabled: body.enabled ?? !!existingTask.enabled,
@@ -276,26 +276,26 @@ tasks.post("/:taskId/run", async (c) => {
276
276
 
277
277
  // Verify ownership
278
278
  const channel = await c.env.DB.prepare(
279
- "SELECT id, openclaw_agent_id FROM channels WHERE id = ? AND user_id = ?",
279
+ "SELECT id, provider_agent_id FROM channels WHERE id = ? AND user_id = ?",
280
280
  )
281
281
  .bind(channelId, userId)
282
- .first<{ id: string; openclaw_agent_id: string }>();
282
+ .first<{ id: string; provider_agent_id: string }>();
283
283
 
284
284
  if (!channel) return c.json({ error: "Channel not found" }, 404);
285
285
 
286
286
  const task = await c.env.DB.prepare(
287
- "SELECT id, kind, openclaw_cron_job_id FROM tasks WHERE id = ? AND channel_id = ?",
287
+ "SELECT id, kind, provider_job_id FROM tasks WHERE id = ? AND channel_id = ?",
288
288
  )
289
289
  .bind(taskId, channelId)
290
290
  .first<{
291
291
  id: string;
292
292
  kind: string;
293
- openclaw_cron_job_id: string | null;
293
+ provider_job_id: string | null;
294
294
  }>();
295
295
 
296
296
  if (!task) return c.json({ error: "Task not found" }, 404);
297
297
  if (task.kind !== "background") return c.json({ error: "Only background tasks can be triggered" }, 400);
298
- if (!task.openclaw_cron_job_id) return c.json({ error: "Task has no associated cron job" }, 400);
298
+ if (!task.provider_job_id) return c.json({ error: "Task has no associated cron job" }, 400);
299
299
 
300
300
  // Send a task.run message to OpenClaw via ConnectionDO.
301
301
  // Instructions and model are not included — the plugin reads them
@@ -309,8 +309,8 @@ tasks.post("/:taskId/run", async (c) => {
309
309
  headers: { "Content-Type": "application/json" },
310
310
  body: JSON.stringify({
311
311
  type: "task.run",
312
- cronJobId: task.openclaw_cron_job_id,
313
- agentId: channel.openclaw_agent_id,
312
+ cronJobId: task.provider_job_id,
313
+ agentId: channel.provider_agent_id,
314
314
  }),
315
315
  }),
316
316
  );
@@ -343,10 +343,10 @@ tasks.delete("/:taskId", async (c) => {
343
343
 
344
344
  // Get task to check if it's background (need to delete CronJob)
345
345
  const task = await c.env.DB.prepare(
346
- "SELECT kind, openclaw_cron_job_id FROM tasks WHERE id = ? AND channel_id = ?",
346
+ "SELECT kind, provider_job_id FROM tasks WHERE id = ? AND channel_id = ?",
347
347
  )
348
348
  .bind(taskId, channelId)
349
- .first<{ kind: string; openclaw_cron_job_id: string | null }>();
349
+ .first<{ kind: string; provider_job_id: string | null }>();
350
350
 
351
351
  await c.env.DB.prepare(
352
352
  "DELETE FROM tasks WHERE id = ? AND channel_id = ?",
@@ -358,15 +358,15 @@ tasks.delete("/:taskId", async (c) => {
358
358
  await c.env.DB.prepare("DELETE FROM jobs WHERE task_id = ?").bind(taskId).run();
359
359
 
360
360
  // Push delete to OpenClaw for background tasks
361
- if (task?.kind === "background" && task.openclaw_cron_job_id) {
361
+ if (task?.kind === "background" && task.provider_job_id) {
362
362
  // Record the deletion so task.scan won't re-create the task
363
363
  await c.env.DB.prepare(
364
364
  "INSERT OR IGNORE INTO deleted_cron_jobs (cron_job_id, user_id) VALUES (?, ?)",
365
365
  )
366
- .bind(task.openclaw_cron_job_id, userId)
366
+ .bind(task.provider_job_id, userId)
367
367
  .run();
368
368
 
369
- await pushDeleteToOpenClaw(c.env, userId, task.openclaw_cron_job_id);
369
+ await pushDeleteToOpenClaw(c.env, userId, task.provider_job_id);
370
370
  }
371
371
 
372
372
  return c.json({ ok: true });
@@ -30,6 +30,7 @@ export declare const botschatPlugin: {
30
30
  };
31
31
  readonly reload: {
32
32
  readonly configPrefixes: readonly ["channels.botschat"];
33
+ readonly noopPrefixes: readonly ["plugins.installs.botschat"];
33
34
  };
34
35
  readonly config: {
35
36
  readonly listAccountIds: (cfg: unknown) => string[];
@@ -56,6 +57,15 @@ export declare const botschatPlugin: {
56
57
  };
57
58
  readonly outbound: {
58
59
  readonly deliveryMode: "direct";
60
+ readonly resolveTarget: ({ to }: {
61
+ to?: string;
62
+ allowFrom?: string[];
63
+ accountId?: string | null;
64
+ mode?: string;
65
+ }) => {
66
+ ok: true;
67
+ to: string;
68
+ };
59
69
  readonly sendText: (ctx: {
60
70
  to: string;
61
71
  text: string;
@@ -1 +1 @@
1
- {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../src/channel.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../src/channel.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAuC,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAoMrD,eAAO,MAAM,cAAc;;;;;;;;;;;;;4BAeqB,MAAM,EAAE;;;;;;;;;;;;wCAc/B,MAAM,eAAe,MAAM;;;;;;;;uCAgB1B,OAAO;uCACP,OAAO,cAAc,MAAM,GAAG,IAAI;yCAEhC,OAAO;kEACkB;YAAE,GAAG,EAAE,OAAO,CAAC;YAAC,SAAS,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,OAAO,CAAA;SAAE;qDAElE;YAAE,GAAG,EAAE,OAAO,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE;yCAE/C,uBAAuB;sCAC1B,uBAAuB;4CACjB,uBAAuB;;;;;;;;;;yCAY1B;YAAE,EAAE,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE;;;;iCAMjF;YACpB,EAAE,EAAE,MAAM,CAAC;YACX,IAAI,EAAE,MAAM,CAAC;YACb,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;YAClC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;SAC3B;;;;;;;kCA2CsB;YACrB,EAAE,EAAE,MAAM,CAAC;YACX,IAAI,EAAE,MAAM,CAAC;YACb,QAAQ,CAAC,EAAE,MAAM,CAAC;YAClB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;SAC3B;;;;;;;;;qCAiGyB;YACxB,GAAG,EAAE,OAAO,CAAC;YACb,SAAS,EAAE,MAAM,CAAC;YAClB,OAAO,EAAE,uBAAuB,CAAC;YACjC,OAAO,EAAE,OAAO,CAAC;YACjB,WAAW,EAAE,WAAW,CAAC;YACzB,GAAG,CAAC,EAAE;gBAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;gBAAC,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;gBAAC,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;aAAE,CAAC;YAC3F,SAAS,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACzC,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;SACjD;oCAoEwB;YACvB,SAAS,EAAE,MAAM,CAAC;YAClB,SAAS,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACzC,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;SACjD;;;;gEAiB8C;YAC7C,OAAO,EAAE;gBAAE,EAAE,CAAC,EAAE,MAAM,CAAC;gBAAC,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;gBAAC,SAAS,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;YAChF,aAAa,CAAC,EAAE;gBAAE,KAAK,EAAE,OAAO,CAAA;aAAE,CAAC;SACpC;;;;;uBAD0B,OAAO;;;;;;8CAWL,MAAM;;;yCAIX;YAAE,OAAO,EAAE,uBAAuB,CAAA;SAAE;;uBAEzC,MAAM,EAAE;;;;;;;sDAQU;YACnC,GAAG,EAAE,OAAO,CAAC;YACb,SAAS,EAAE,MAAM,CAAC;YAClB,KAAK,EAAE;gBAAE,GAAG,CAAC,EAAE,MAAM,CAAC;gBAAC,KAAK,CAAC,EAAE,MAAM,CAAC;gBAAC,IAAI,CAAC,EAAE,MAAM,CAAC;gBAAC,MAAM,CAAC,EAAE,OAAO,CAAA;aAAE,CAAC;SAC1E;;;;;;;;;;;;4CAiB0B;YACzB,GAAG,EAAE,OAAO,CAAC;YACb,SAAS,EAAE,MAAM,CAAC;YAClB,KAAK,EAAE;gBAAE,GAAG,CAAC,EAAE,MAAM,CAAC;gBAAC,KAAK,CAAC,EAAE,MAAM,CAAC;gBAAC,MAAM,CAAC,EAAE,OAAO,CAAA;aAAE,CAAC;SAC3D;;;;;;;;;;;8DAiB4C;YAC3C,OAAO,EAAE,uBAAuB,CAAC;YACjC,GAAG,EAAE,OAAO,CAAC;YACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;SACnC;;;;;;;;;;;;;;iDAc+B,KAAK,CAAC;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,SAAS,CAAC,EAAE,OAAO,CAAC;YAAC,UAAU,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;qBAE/F,MAAM;uBAAa,MAAM;kBAAQ,MAAM;qBAAW,MAAM;;;CAerF,CAAC"}