@botcord/daemon 0.2.69 → 0.2.71

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/src/provision.ts CHANGED
@@ -400,6 +400,16 @@ export function createProvisioner(opts: ProvisionerOptions): (
400
400
  );
401
401
  }
402
402
 
403
+ case "gateway_send": {
404
+ const v = validateGatewayParams(frame.params, {
405
+ required: ["agentId", "gatewayId", "conversationId", "text"],
406
+ });
407
+ if (!v.ok) return v.ack;
408
+ return gatewayControl.handleSend(
409
+ v.params as unknown as Parameters<typeof gatewayControl.handleSend>[0],
410
+ );
411
+ }
412
+
403
413
  case "list_agent_files": {
404
414
  const params = (frame.params ?? {}) as unknown as ListAgentFilesParams;
405
415
  if (!params.agentId) {
@@ -467,6 +477,10 @@ interface WakeAgentParams {
467
477
  runId?: string;
468
478
  schedule_id?: string;
469
479
  scheduleId?: string;
480
+ scheduled_for?: string;
481
+ scheduledFor?: string;
482
+ dispatched_at?: string;
483
+ dispatchedAt?: string;
470
484
  dedupe_key?: string;
471
485
  dedupeKey?: string;
472
486
  }
@@ -504,6 +518,8 @@ async function handleWakeAgent(gateway: Gateway, raw: unknown): Promise<AckBody>
504
518
 
505
519
  const runId = params.run_id || params.runId || `wake-${Date.now()}`;
506
520
  const scheduleId = params.schedule_id || params.scheduleId;
521
+ const scheduledFor = params.scheduled_for || params.scheduledFor;
522
+ const dispatchedAt = params.dispatched_at || params.dispatchedAt;
507
523
  const dedupeKey = params.dedupe_key || params.dedupeKey;
508
524
  const conversationId = `rm_schedule_${agentId}`;
509
525
  const msg: GatewayInboundMessage = {
@@ -525,6 +541,8 @@ async function handleWakeAgent(gateway: Gateway, raw: unknown): Promise<AckBody>
525
541
  raw: {
526
542
  source_type: "botcord_schedule",
527
543
  schedule_id: scheduleId,
544
+ scheduled_for: scheduledFor,
545
+ dispatched_at: dispatchedAt,
528
546
  run_id: runId,
529
547
  dedupe_key: dedupeKey,
530
548
  },
package/src/turn-text.ts CHANGED
@@ -39,6 +39,10 @@ const GROUP_HINT =
39
39
  "When a message matches an active monitoring rule, automation goal, working-memory task, " +
40
40
  "keyword, sender rule, or owner-approved workflow, perform the required action even if " +
41
41
  "you do not reply to the group.\n\n" +
42
+ "Before replying NO_REPLY in a non-owner group room, consider whether this message could " +
43
+ "match a memory-backed monitoring rule, automation goal, pending task, keyword, sender rule, " +
44
+ "or owner-approved workflow. If needed, use the botcord_memory skill to retrieve or update " +
45
+ "working memory.\n\n" +
42
46
  'If no group reply and no background action is needed, reply exactly "NO_REPLY".]';
43
47
  const DIRECT_HINT =
44
48
  '[If the conversation has naturally concluded or no response is needed, ' +
@@ -120,6 +124,41 @@ interface RoomContextRaw {
120
124
  my_can_send?: unknown;
121
125
  }
122
126
 
127
+ interface ScheduleRaw {
128
+ source_type?: unknown;
129
+ schedule_id?: unknown;
130
+ scheduled_for?: unknown;
131
+ dispatched_at?: unknown;
132
+ run_id?: unknown;
133
+ }
134
+
135
+ function formatScheduleContext(raw: unknown): string[] {
136
+ const r = raw && typeof raw === "object" ? (raw as ScheduleRaw) : {};
137
+ if (r.source_type !== "botcord_schedule") return [];
138
+
139
+ const fields: string[] = [];
140
+ if (typeof r.schedule_id === "string" && r.schedule_id) {
141
+ fields.push(`schedule_id: ${sanitizeSenderName(r.schedule_id)}`);
142
+ }
143
+ if (typeof r.scheduled_for === "string" && r.scheduled_for) {
144
+ fields.push(`scheduled_for: ${sanitizeSenderName(r.scheduled_for)}`);
145
+ }
146
+ if (typeof r.dispatched_at === "string" && r.dispatched_at) {
147
+ fields.push(`dispatched_at: ${sanitizeSenderName(r.dispatched_at)}`);
148
+ }
149
+ if (typeof r.run_id === "string" && r.run_id) {
150
+ fields.push(`run_id: ${sanitizeSenderName(r.run_id)}`);
151
+ }
152
+
153
+ return fields.length > 0
154
+ ? [
155
+ "[BotCord Schedule]",
156
+ "This turn was triggered by a proactive schedule.",
157
+ fields.join(" | "),
158
+ ]
159
+ : ["[BotCord Schedule]", "This turn was triggered by a proactive schedule."];
160
+ }
161
+
123
162
  /**
124
163
  * Read the `raw.batch` array emitted by the BotCord channel when inbox
125
164
  * drain groups multiple messages for the same `(room, topic)`. Returns the
@@ -256,6 +295,7 @@ export function composeBotCordUserTurn(msg: GatewayInboundMessage): string {
256
295
 
257
296
  const lines: string[] = [
258
297
  headerFields.join(" | "),
298
+ ...formatScheduleContext(msg.raw),
259
299
  ...(isGroup ? formatRoomContext(msg.raw, { id: conversation.id, title: roomTitle }) : []),
260
300
  `<${tag} sender="${sanitizedSenderLabel}" sender_kind="${senderKindAttr}">`,
261
301
  trimmed,