@ozaiya/openclaw-channel 0.10.8 → 0.10.11

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.
@@ -0,0 +1,37 @@
1
+ import type { OzaiyaMessageContent } from "./types.js";
2
+ export type ActionCardStatus = "pending" | "approved" | "rejected" | "expired";
3
+ export interface ActionCardContent {
4
+ action: string;
5
+ actionId: string;
6
+ status: ActionCardStatus;
7
+ params: Record<string, unknown>;
8
+ label?: string;
9
+ }
10
+ export interface ConfirmationDeps {
11
+ sendEncrypted: (groupId: string, content: OzaiyaMessageContent) => Promise<{
12
+ message: {
13
+ id: string;
14
+ };
15
+ }>;
16
+ editEncrypted: (groupId: string, messageId: string, content: OzaiyaMessageContent) => Promise<void>;
17
+ }
18
+ /**
19
+ * Send a confirmation card and block until the user approves, rejects, or timeout.
20
+ *
21
+ * @returns `true` if approved, `false` if rejected or timed out.
22
+ */
23
+ export declare function requestConfirmation(deps: ConfirmationDeps, groupId: string, action: string, params: Record<string, unknown>, label?: string): Promise<boolean>;
24
+ /**
25
+ * Check if a callback data string is an action confirmation callback.
26
+ * Returns parsed info or null.
27
+ */
28
+ export declare function parseActionCallback(callbackData: string): {
29
+ actionId: string;
30
+ approved: boolean;
31
+ } | null;
32
+ /**
33
+ * Resolve a pending confirmation. Called when the user taps Approve/Reject.
34
+ *
35
+ * @returns `true` if the confirmation was found and resolved, `false` otherwise.
36
+ */
37
+ export declare function resolveConfirmation(deps: ConfirmationDeps, actionId: string, approved: boolean): Promise<boolean>;
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Action confirmation cards — human-in-the-loop approval for bot tool execution.
3
+ *
4
+ * When a bot tool (phone call, DM, group creation) is invoked, instead of executing
5
+ * immediately, we send a confirmation card to the chat. The user taps Approve/Reject
6
+ * via inline keyboard buttons. The tool's execute() blocks on a Promise that resolves
7
+ * when the user responds or a 5-minute timeout occurs.
8
+ *
9
+ * Callback data uses the `oz_ac:` prefix to distinguish from regular inline keyboard
10
+ * callbacks processed by the agent.
11
+ */
12
+ import { randomBytes } from "node:crypto";
13
+ // ─── Constants ───────────────────────────────────────────────────────────────
14
+ const ACTION_CALLBACK_PREFIX = "oz_ac:";
15
+ const CONFIRMATION_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
16
+ // ─── State ───────────────────────────────────────────────────────────────────
17
+ const pendingConfirmations = new Map();
18
+ // ─── Action label mapping (server-side fallback) ─────────────────────────────
19
+ const ACTION_LABELS = {
20
+ make_phone_call: "Make Phone Call",
21
+ start_in_app_call: "Start Call",
22
+ message_user: "Send Direct Message",
23
+ create_group: "Create Group",
24
+ };
25
+ // ─── Public API ──────────────────────────────────────────────────────────────
26
+ /**
27
+ * Send a confirmation card and block until the user approves, rejects, or timeout.
28
+ *
29
+ * @returns `true` if approved, `false` if rejected or timed out.
30
+ */
31
+ export async function requestConfirmation(deps, groupId, action, params, label) {
32
+ const actionId = randomBytes(8).toString("hex");
33
+ const displayLabel = label ?? ACTION_LABELS[action] ?? action;
34
+ const actionCard = {
35
+ action,
36
+ actionId,
37
+ status: "pending",
38
+ params,
39
+ label: displayLabel,
40
+ };
41
+ const content = {
42
+ text: `⏳ ${displayLabel}`,
43
+ actionCard,
44
+ inlineKeyboard: {
45
+ rows: [[
46
+ { text: "✅ Approve", callbackData: `${ACTION_CALLBACK_PREFIX}approve:${actionId}` },
47
+ { text: "❌ Reject", callbackData: `${ACTION_CALLBACK_PREFIX}reject:${actionId}` },
48
+ ]],
49
+ },
50
+ };
51
+ const result = await deps.sendEncrypted(groupId, content);
52
+ return new Promise((resolve) => {
53
+ const timer = setTimeout(() => {
54
+ const entry = pendingConfirmations.get(actionId);
55
+ if (entry) {
56
+ pendingConfirmations.delete(actionId);
57
+ // Update card to show expired status
58
+ void updateCardStatus(deps, groupId, result.message.id, actionCard, "expired");
59
+ resolve(false);
60
+ }
61
+ }, CONFIRMATION_TIMEOUT_MS);
62
+ pendingConfirmations.set(actionId, {
63
+ messageId: result.message.id,
64
+ groupId,
65
+ resolve,
66
+ timer,
67
+ });
68
+ });
69
+ }
70
+ /**
71
+ * Check if a callback data string is an action confirmation callback.
72
+ * Returns parsed info or null.
73
+ */
74
+ export function parseActionCallback(callbackData) {
75
+ if (!callbackData.startsWith(ACTION_CALLBACK_PREFIX))
76
+ return null;
77
+ const rest = callbackData.slice(ACTION_CALLBACK_PREFIX.length);
78
+ const colonIdx = rest.indexOf(":");
79
+ if (colonIdx < 0)
80
+ return null;
81
+ const verb = rest.slice(0, colonIdx);
82
+ const actionId = rest.slice(colonIdx + 1);
83
+ if (!actionId)
84
+ return null;
85
+ if (verb === "approve")
86
+ return { actionId, approved: true };
87
+ if (verb === "reject")
88
+ return { actionId, approved: false };
89
+ return null;
90
+ }
91
+ /**
92
+ * Resolve a pending confirmation. Called when the user taps Approve/Reject.
93
+ *
94
+ * @returns `true` if the confirmation was found and resolved, `false` otherwise.
95
+ */
96
+ export async function resolveConfirmation(deps, actionId, approved) {
97
+ const entry = pendingConfirmations.get(actionId);
98
+ if (!entry)
99
+ return false;
100
+ clearTimeout(entry.timer);
101
+ pendingConfirmations.delete(actionId);
102
+ // Update card to show final status and remove keyboard
103
+ const status = approved ? "approved" : "rejected";
104
+ await updateCardStatus(deps, entry.groupId, entry.messageId, undefined, status);
105
+ entry.resolve(approved);
106
+ return true;
107
+ }
108
+ // ─── Internal ────────────────────────────────────────────────────────────────
109
+ async function updateCardStatus(deps, groupId, messageId, existingCard, newStatus) {
110
+ try {
111
+ const statusEmoji = newStatus === "approved" ? "✅" : newStatus === "rejected" ? "❌" : "⏰";
112
+ const statusLabel = newStatus === "approved" ? "Approved" : newStatus === "rejected" ? "Rejected" : "Expired";
113
+ const label = existingCard?.label ?? "";
114
+ const updatedCard = existingCard
115
+ ? { ...existingCard, status: newStatus }
116
+ : { action: "", actionId: "", status: newStatus, params: {} };
117
+ const updatedContent = {
118
+ text: `${statusEmoji} ${label} — ${statusLabel}`,
119
+ actionCard: updatedCard,
120
+ // No inlineKeyboard — removes the buttons
121
+ };
122
+ await deps.editEncrypted(groupId, messageId, updatedContent);
123
+ }
124
+ catch {
125
+ // Best-effort edit — don't let failure break the confirmation flow
126
+ }
127
+ }
128
+ //# sourceMappingURL=actionConfirmation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actionConfirmation.js","sourceRoot":"","sources":["../../src/actionConfirmation.ts"],"names":[],"mappings":"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"}