@openclaw/discord 2026.7.2-beta.2 → 2026.7.2-beta.3
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/dist/action-runtime-api.js +1 -1
- package/dist/{activities-api-D0D_87n3.js → activities-api-DS1L7KRY.js} +15 -42
- package/dist/activities-api.js +1 -1
- package/dist/{allow-list-B7qOH5xu.js → allow-list-DzKrtfah.js} +18 -7
- package/dist/{api-B88T4rFN.js → api-C43z8vUm.js} +3 -1
- package/dist/api.js +25 -16
- package/dist/{approval-handler.runtime-B7E1-x5q.js → approval-handler.runtime-BbkfPoGc.js} +5 -6
- package/dist/{conversation-identity-BPcbhCAH.js → approval-message-safety-DrLlgIPz.js} +48 -2
- package/dist/{audit-CjpnY1yb.js → audit-BC6bgPMX.js} +3 -3
- package/dist/{channel-BDfKr14H.js → channel-5MfaC2IE.js} +18 -18
- package/dist/{channel-actions-BQct-o4T.js → channel-actions-DicWTbYH.js} +2 -2
- package/dist/{channel-actions.runtime-CC4QwYVO.js → channel-actions.runtime-D9Qadn-l.js} +8 -8
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.setup-BS4gd8sc.js → channel.setup-CJXfFclP.js} +3 -3
- package/dist/{chunk-CHpY_lsO.js → chunk-CWzmeWf_.js} +9 -49
- package/dist/{config-schema-CoDck1GW.js → config-schema-BxrgfSMD.js} +4 -0
- package/dist/{directory-config-G1QwBIdi.js → directory-config-CmukHSms.js} +1 -1
- package/dist/directory-contract-api.js +1 -1
- package/dist/{directory-live-nfvekMil.js → directory-live-BYlCRO__.js} +3 -3
- package/dist/{doctor-Bq4pJAQk.js → doctor-Ci7USQAW.js} +1 -1
- package/dist/{handle-action.guild-admin-LYY75_Oa.js → handle-action.guild-admin-Bsk2YBPs.js} +2 -2
- package/dist/index.js +2 -2
- package/dist/{manager.runtime-w6VVYQMl.js → manager.runtime-Cc8vS-M7.js} +98 -145
- package/dist/message-handler-CWJ-mQvH.js +590 -0
- package/dist/{message-handler.preflight-DnAH04Ss.js → message-handler.preflight-DK46_qbS.js} +16 -15
- package/dist/{message-handler.process-ClgKz8I5.js → message-handler.process-BK0mjFJH.js} +37 -61
- package/dist/{message-utils-BU3gc01v.js → message-utils-Cfe6CZ6N.js} +1 -1
- package/dist/{outbound-adapter-ByoXsR-V.js → outbound-adapter-B2_oa-zV.js} +36 -5
- package/dist/{outbound-components-7wAolLuS.js → outbound-components-BGhk6HI3.js} +3 -3
- package/dist/{outbound-session-route-D4QqePZN.js → outbound-session-route-M2bEY9tI.js} +36 -6
- package/dist/{pluralkit-r4wiI2yy.js → pluralkit-CT0VRsZd.js} +1 -1
- package/dist/{probe-WKeTYyJX.js → probe-C3SCKyYU.js} +1 -1
- package/dist/{probe.runtime-CRY-qUke.js → probe.runtime-D55QqC9u.js} +1 -1
- package/dist/{provider-PRlF_lZm.js → provider-HW0G-2l6.js} +868 -794
- package/dist/{provider-session.runtime-BZPUZRjn.js → provider-session.runtime-cCfGMsF_.js} +3 -3
- package/dist/provider.runtime-BWCRsVoT.js +2 -0
- package/dist/{resolve-allowlist-common-QN7VMZaC.js → resolve-allowlist-common-CJlqw_nC.js} +2 -2
- package/dist/{resolve-channels-BO9NUPlS.js → resolve-channels-9KcG3mLg.js} +4 -4
- package/dist/{resolve-users-CU3oG5-m.js → resolve-users-GCGf3oV0.js} +3 -3
- package/dist/{runtime-CCf5nkV3.js → runtime-CD5MRZkD.js} +1 -2
- package/dist/{runtime-BsefAQ9q.js → runtime-Z2qkKakn.js} +9 -8
- package/dist/runtime-api.actions.js +2 -2
- package/dist/runtime-api.js +23 -22
- package/dist/runtime-api.lookup.js +5 -5
- package/dist/runtime-api.monitor-C3EDYJqk.js +6 -0
- package/dist/runtime-api.monitor.js +8 -8
- package/dist/runtime-api.send.js +6 -5
- package/dist/runtime-api.threads.js +3 -3
- package/dist/{secret-config-contract-BDNrRWvd.js → secret-config-contract-B1akrlyY.js} +1 -0
- package/dist/secret-contract-api.js +1 -1
- package/dist/security-audit-contract-api.js +1 -1
- package/dist/{security-audit.runtime-K0nkzjzJ.js → security-audit.runtime-DEEywczv.js} +1 -1
- package/dist/{send-JJzVo_sK.js → send-CSRxNNgq.js} +7 -86
- package/dist/{send.components-CCRW54gK.js → send.components-DLqzOEPJ.js} +5 -4
- package/dist/{send.outbound-Cvulb-k_.js → send.outbound-C1SfX20w.js} +4 -3
- package/dist/send.reactions-Cj4ugX3F.js +85 -0
- package/dist/send.receipt-CuwRrpFz.js +48 -0
- package/dist/{send.shared-XuOTO1dW.js → send.shared-Dber6X57.js} +2 -2
- package/dist/{sender-identity-C62lBe18.js → sender-identity-mG_zsdox.js} +1 -1
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{shared-Bngr0I-v.js → shared-BEhmAdMY.js} +5 -5
- package/dist/{subagent-hooks-mSxRv4AE.js → subagent-hooks-Wrk61DwY.js} +2 -2
- package/dist/subagent-hooks-api.js +10 -1
- package/dist/subagent-progress-B2fpHe3m.js +662 -0
- package/dist/{system-events-BANBRAe3.js → system-events-AhfcKRRb.js} +2 -2
- package/dist/{target-resolver-BaEmTxeK.js → target-resolver-vqLA450R.js} +2 -2
- package/dist/targets-BjEMIe8t.js +3 -0
- package/dist/{thread-bindings-4MV-u8vU.js → thread-bindings-CJQoXuWY.js} +4 -4
- package/dist/{thread-bindings.discord-api-DXfXsHGZ.js → thread-bindings.discord-api-mNCWlgvM.js} +4 -4
- package/dist/{thread-bindings.manager-DJ4cO5Eu.js → thread-bindings.manager-D1z8mK4b.js} +3 -3
- package/dist/{timeouts-D5lWili0.js → timeouts-B9cy6RvQ.js} +1 -6
- package/dist/{transcripts-source-TDZcMriu.js → transcripts-source-DjDbt14x.js} +1 -1
- package/dist/transcripts-source-api.js +1 -1
- package/dist/{typing-CKd2NMWG.js → typing-wxtm1NlF.js} +2 -2
- package/npm-shrinkwrap.json +3 -3
- package/openclaw.plugin.json +10 -0
- package/package.json +4 -4
- package/dist/approval-message-safety-C-8BL6Kv.js +0 -47
- package/dist/message-handler-DExaghWK.js +0 -331
- package/dist/provider.runtime-BX8GcCYx.js +0 -2
- package/dist/runtime-api.monitor-DCS_ETft.js +0 -6
- package/dist/targets-fjJt3PYG.js +0 -3
- package/dist/{security-audit-aLbWmUjl.js → security-audit-DtT0SIns.js} +1 -1
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
import { s as resolveDiscordAccount } from "./accounts-CMd4nzLB.js";
|
|
2
|
+
import { a as sendTypingDiscord, i as removeReactionDiscord, n as reactMessageDiscord } from "./send.reactions-Cj4ugX3F.js";
|
|
3
|
+
import { normalizeOptionalLowercaseString, normalizeOptionalStringifiedId } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
4
|
+
import { DEFAULT_EMOJIS } from "openclaw/plugin-sdk/channel-feedback";
|
|
5
|
+
//#region extensions/discord/src/subagent-progress-config.ts
|
|
6
|
+
const RUNNING_EMOJIS = [
|
|
7
|
+
"1️⃣",
|
|
8
|
+
"2️⃣",
|
|
9
|
+
"3️⃣",
|
|
10
|
+
"4️⃣",
|
|
11
|
+
"5️⃣",
|
|
12
|
+
"6️⃣",
|
|
13
|
+
"7️⃣",
|
|
14
|
+
"8️⃣",
|
|
15
|
+
"9️⃣",
|
|
16
|
+
"🔟"
|
|
17
|
+
];
|
|
18
|
+
function channelIdFromTarget(target) {
|
|
19
|
+
const trimmed = target?.trim();
|
|
20
|
+
if (!trimmed) return;
|
|
21
|
+
if (trimmed.startsWith("channel:")) return trimmed.slice(8).trim() || void 0;
|
|
22
|
+
return /^\d+$/u.test(trimmed) ? trimmed : void 0;
|
|
23
|
+
}
|
|
24
|
+
function resolveDiscordProgressTarget(requester) {
|
|
25
|
+
if (normalizeOptionalLowercaseString(requester?.channel) !== "discord") return;
|
|
26
|
+
const channelId = normalizeOptionalStringifiedId(requester?.channelId) ?? channelIdFromTarget(requester?.to);
|
|
27
|
+
const messageId = normalizeOptionalStringifiedId(requester?.messageId);
|
|
28
|
+
if (!channelId || !messageId) return;
|
|
29
|
+
return {
|
|
30
|
+
channelId,
|
|
31
|
+
messageId
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function reservedReactionEmojis(config, ackReaction) {
|
|
35
|
+
const reserved = new Set(Object.values(DEFAULT_EMOJIS));
|
|
36
|
+
for (const emoji of Object.values(config.messages?.statusReactions?.emojis ?? {})) if (emoji?.trim()) reserved.add(emoji.trim());
|
|
37
|
+
for (const emoji of [config.messages?.ackReaction, ackReaction]) if (emoji?.trim()) reserved.add(emoji.trim());
|
|
38
|
+
for (const agent of config.agents?.list ?? []) {
|
|
39
|
+
const emoji = agent.identity?.emoji?.trim();
|
|
40
|
+
if (emoji) reserved.add(emoji);
|
|
41
|
+
}
|
|
42
|
+
return reserved;
|
|
43
|
+
}
|
|
44
|
+
function reactionsAreAvailable(config, ackReaction) {
|
|
45
|
+
const reserved = reservedReactionEmojis(config, ackReaction);
|
|
46
|
+
return !RUNNING_EMOJIS.some((emoji) => reserved.has(emoji)) && !reserved.has("🔴");
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region extensions/discord/src/subagent-progress-state.ts
|
|
50
|
+
const PROGRESS_STORE_TTL_MS = 10080 * 6e4;
|
|
51
|
+
const MAX_TRACKED_RUNS = 4096;
|
|
52
|
+
const TERMINAL_TOMBSTONE_TTL_MS = 60 * 6e4;
|
|
53
|
+
function persistedTerminalOutcome(persisted) {
|
|
54
|
+
return persisted?.status === "cleanup" ? persisted.outcome : void 0;
|
|
55
|
+
}
|
|
56
|
+
let progressStores = /* @__PURE__ */ new WeakMap();
|
|
57
|
+
const trackerQueues = /* @__PURE__ */ new Map();
|
|
58
|
+
const terminalRuns = /* @__PURE__ */ new Map();
|
|
59
|
+
function markRunTerminal(runId, outcome) {
|
|
60
|
+
const now = Date.now();
|
|
61
|
+
for (const [trackedRunId, terminal] of terminalRuns) if (terminal.expiresAt <= now) terminalRuns.delete(trackedRunId);
|
|
62
|
+
terminalRuns.set(runId, {
|
|
63
|
+
expiresAt: now + TERMINAL_TOMBSTONE_TTL_MS,
|
|
64
|
+
outcome
|
|
65
|
+
});
|
|
66
|
+
if (terminalRuns.size > 4096) {
|
|
67
|
+
const oldest = terminalRuns.keys().next().value;
|
|
68
|
+
if (oldest) terminalRuns.delete(oldest);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function terminalOutcome(runId) {
|
|
72
|
+
const terminal = terminalRuns.get(runId);
|
|
73
|
+
if (!terminal) return;
|
|
74
|
+
if (terminal.expiresAt <= Date.now()) {
|
|
75
|
+
terminalRuns.delete(runId);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
return terminal.outcome;
|
|
79
|
+
}
|
|
80
|
+
function logFailure(api, action, error) {
|
|
81
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
82
|
+
api.logger.debug?.(`discord subagent progress ${action} failed: ${message}`);
|
|
83
|
+
}
|
|
84
|
+
function getProgressStore(api) {
|
|
85
|
+
const cached = progressStores.get(api);
|
|
86
|
+
if (cached !== void 0) return cached ?? void 0;
|
|
87
|
+
if (!api.runtime?.state) {
|
|
88
|
+
progressStores.set(api, null);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const store = api.runtime.state.openKeyedStore({
|
|
93
|
+
namespace: "subagent-progress",
|
|
94
|
+
maxEntries: MAX_TRACKED_RUNS,
|
|
95
|
+
overflowPolicy: "reject-new",
|
|
96
|
+
defaultTtlMs: PROGRESS_STORE_TTL_MS
|
|
97
|
+
});
|
|
98
|
+
progressStores.set(api, store);
|
|
99
|
+
return store;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
logFailure(api, "state store open", error);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function persistedProgressRunFromTracker(tracker) {
|
|
106
|
+
return {
|
|
107
|
+
key: `${tracker.accountId}:${tracker.channelId}:${tracker.messageId}`,
|
|
108
|
+
accountId: tracker.accountId,
|
|
109
|
+
channelId: tracker.channelId,
|
|
110
|
+
messageId: tracker.messageId,
|
|
111
|
+
status: "active",
|
|
112
|
+
...tracker.runningEmoji ? { runningEmoji: tracker.runningEmoji } : {}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
async function persistProgressRun(api, runId, tracker) {
|
|
116
|
+
const store = getProgressStore(api);
|
|
117
|
+
if (!store) return "error";
|
|
118
|
+
const value = persistedProgressRunFromTracker(tracker);
|
|
119
|
+
try {
|
|
120
|
+
if (await store.registerIfAbsent(runId, value)) return "persisted";
|
|
121
|
+
const existing = await store.lookup(runId);
|
|
122
|
+
if (!existing) return "error";
|
|
123
|
+
if (existing.status === "cleanup") return "terminal";
|
|
124
|
+
return existing.key === value.key ? "persisted" : "conflict";
|
|
125
|
+
} catch (error) {
|
|
126
|
+
logFailure(api, "state store write", error);
|
|
127
|
+
return "error";
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async function markProgressRunForCleanup(api, runId, persisted, outcome) {
|
|
131
|
+
try {
|
|
132
|
+
await getProgressStore(api)?.register(runId, {
|
|
133
|
+
...persisted,
|
|
134
|
+
status: "cleanup",
|
|
135
|
+
outcome
|
|
136
|
+
});
|
|
137
|
+
return true;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
logFailure(api, "state store cleanup mark", error);
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async function lookupProgressRun(api, runId) {
|
|
144
|
+
const store = getProgressStore(api);
|
|
145
|
+
if (!store) return { status: "error" };
|
|
146
|
+
try {
|
|
147
|
+
const value = await store.lookup(runId);
|
|
148
|
+
return value ? {
|
|
149
|
+
status: "found",
|
|
150
|
+
value
|
|
151
|
+
} : { status: "missing" };
|
|
152
|
+
} catch (error) {
|
|
153
|
+
logFailure(api, "state store read", error);
|
|
154
|
+
return { status: "error" };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function consumeProgressRun(api, runId) {
|
|
158
|
+
try {
|
|
159
|
+
return await getProgressStore(api)?.consume(runId);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
logFailure(api, "state store consume", error);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async function listProgressStateForKey(api, key) {
|
|
166
|
+
const store = getProgressStore(api);
|
|
167
|
+
if (!store) return { ok: false };
|
|
168
|
+
try {
|
|
169
|
+
const matching = (await store.entries()).filter((entry) => entry.value.key === key);
|
|
170
|
+
const cleanupRuns = matching.flatMap((entry) => entry.value.status === "cleanup" ? [{
|
|
171
|
+
runId: entry.key,
|
|
172
|
+
value: entry.value
|
|
173
|
+
}] : []);
|
|
174
|
+
return {
|
|
175
|
+
ok: true,
|
|
176
|
+
activeRunIds: matching.filter((entry) => entry.value.status === "active").map((entry) => entry.key),
|
|
177
|
+
cleanupRuns,
|
|
178
|
+
ownedEmojis: Array.from(new Set(matching.flatMap((entry) => entry.value.runningEmoji ?? [])))
|
|
179
|
+
};
|
|
180
|
+
} catch (error) {
|
|
181
|
+
logFailure(api, "state store list", error);
|
|
182
|
+
return { ok: false };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async function runQueued(key, task) {
|
|
186
|
+
const current = (trackerQueues.get(key) ?? Promise.resolve()).catch(() => void 0).then(task);
|
|
187
|
+
trackerQueues.set(key, current);
|
|
188
|
+
try {
|
|
189
|
+
await current;
|
|
190
|
+
} finally {
|
|
191
|
+
if (trackerQueues.get(key) === current) trackerQueues.delete(key);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function resetDiscordSubagentProgressStateForTest() {
|
|
195
|
+
trackerQueues.clear();
|
|
196
|
+
terminalRuns.clear();
|
|
197
|
+
progressStores = /* @__PURE__ */ new WeakMap();
|
|
198
|
+
}
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region extensions/discord/src/subagent-progress.ts
|
|
201
|
+
const TYPING_INTERVAL_MS = 8500;
|
|
202
|
+
const TYPING_TTL_MS = 60 * 6e4;
|
|
203
|
+
const TERMINAL_LOOKUP_RETRY_MS = 1e3;
|
|
204
|
+
const TERMINAL_RETRY_MAX_DELAY_MS = 60 * 6e4;
|
|
205
|
+
const TERMINAL_RETRY_MAX_ATTEMPTS = 12;
|
|
206
|
+
const STARTUP_RETRY_MAX_ATTEMPTS = 12;
|
|
207
|
+
const trackers = /* @__PURE__ */ new Map();
|
|
208
|
+
const trackerKeyByRunId = /* @__PURE__ */ new Map();
|
|
209
|
+
const terminalRetryTimers = /* @__PURE__ */ new Map();
|
|
210
|
+
const terminalRetryExpiresAt = /* @__PURE__ */ new Map();
|
|
211
|
+
const terminalRetryAttempts = /* @__PURE__ */ new Map();
|
|
212
|
+
const startupRecoveryRetries = /* @__PURE__ */ new Map();
|
|
213
|
+
function clearTerminalRetry(runId) {
|
|
214
|
+
const timer = terminalRetryTimers.get(runId);
|
|
215
|
+
if (timer) clearTimeout(timer);
|
|
216
|
+
terminalRetryTimers.delete(runId);
|
|
217
|
+
terminalRetryExpiresAt.delete(runId);
|
|
218
|
+
terminalRetryAttempts.delete(runId);
|
|
219
|
+
}
|
|
220
|
+
function cancelTerminalRetryTimer(runId) {
|
|
221
|
+
const timer = terminalRetryTimers.get(runId);
|
|
222
|
+
if (timer) clearTimeout(timer);
|
|
223
|
+
terminalRetryTimers.delete(runId);
|
|
224
|
+
}
|
|
225
|
+
async function setReaction(api, tracker, emoji) {
|
|
226
|
+
try {
|
|
227
|
+
return (await reactMessageDiscord(tracker.channelId, tracker.messageId, emoji, {
|
|
228
|
+
cfg: api.config,
|
|
229
|
+
accountId: tracker.accountId
|
|
230
|
+
})).ok;
|
|
231
|
+
} catch (error) {
|
|
232
|
+
logFailure(api, "reaction add", error);
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
async function clearReaction(api, tracker, emoji) {
|
|
237
|
+
if (!emoji) return true;
|
|
238
|
+
try {
|
|
239
|
+
return (await removeReactionDiscord(tracker.channelId, tracker.messageId, emoji, {
|
|
240
|
+
cfg: api.config,
|
|
241
|
+
accountId: tracker.accountId
|
|
242
|
+
})).ok;
|
|
243
|
+
} catch (error) {
|
|
244
|
+
logFailure(api, "reaction remove", error);
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async function clearRunningReactions(api, tracker, emojis) {
|
|
249
|
+
return (await Promise.all(emojis.map((emoji) => clearReaction(api, tracker, emoji)))).every(Boolean);
|
|
250
|
+
}
|
|
251
|
+
async function persistTrackerRunningEmoji(api, tracker) {
|
|
252
|
+
const store = getProgressStore(api);
|
|
253
|
+
if (!store) return false;
|
|
254
|
+
try {
|
|
255
|
+
await Promise.all(Array.from(tracker.persistedRunIds, (runId) => store.register(runId, persistedProgressRunFromTracker(tracker))));
|
|
256
|
+
return true;
|
|
257
|
+
} catch (error) {
|
|
258
|
+
logFailure(api, "reaction ownership write", error);
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
async function updateRunningReaction(api, tracker) {
|
|
263
|
+
if (!tracker.reactionsEnabled) return true;
|
|
264
|
+
const nextEmoji = tracker.activeRunIds.size > 0 ? RUNNING_EMOJIS[Math.min(tracker.activeRunIds.size, RUNNING_EMOJIS.length) - 1] : void 0;
|
|
265
|
+
if (nextEmoji === tracker.runningEmoji) {
|
|
266
|
+
if (!nextEmoji || tracker.runningEmojiConfirmed) return true;
|
|
267
|
+
if (!await persistTrackerRunningEmoji(api, tracker)) return false;
|
|
268
|
+
tracker.runningEmojiConfirmed = await setReaction(api, tracker, nextEmoji);
|
|
269
|
+
return tracker.runningEmojiConfirmed;
|
|
270
|
+
}
|
|
271
|
+
if (!await clearReaction(api, tracker, tracker.runningEmoji)) return false;
|
|
272
|
+
tracker.runningEmoji = void 0;
|
|
273
|
+
tracker.runningEmojiConfirmed = false;
|
|
274
|
+
if (nextEmoji) {
|
|
275
|
+
tracker.runningEmoji = nextEmoji;
|
|
276
|
+
if (!await persistTrackerRunningEmoji(api, tracker)) {
|
|
277
|
+
tracker.runningEmoji = void 0;
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
tracker.runningEmojiConfirmed = await setReaction(api, tracker, nextEmoji);
|
|
281
|
+
return tracker.runningEmojiConfirmed;
|
|
282
|
+
}
|
|
283
|
+
await persistTrackerRunningEmoji(api, tracker);
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
async function disableTrackerReactionsOnCollision(api, tracker, ackReaction) {
|
|
287
|
+
if (!tracker.reactionsEnabled || reactionsAreAvailable(api.config, ackReaction)) return true;
|
|
288
|
+
const reserved = reservedReactionEmojis(api.config, ackReaction);
|
|
289
|
+
if (tracker.runningEmoji && !reserved.has(tracker.runningEmoji)) {
|
|
290
|
+
if (!await clearReaction(api, tracker, tracker.runningEmoji)) {
|
|
291
|
+
tracker.reactionsEnabled = false;
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
tracker.runningEmoji = void 0;
|
|
296
|
+
tracker.runningEmojiConfirmed = false;
|
|
297
|
+
tracker.reactionsEnabled = false;
|
|
298
|
+
await persistTrackerRunningEmoji(api, tracker);
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
async function sendTyping(api, tracker) {
|
|
302
|
+
try {
|
|
303
|
+
await sendTypingDiscord(tracker.channelId, {
|
|
304
|
+
cfg: api.config,
|
|
305
|
+
accountId: tracker.accountId
|
|
306
|
+
});
|
|
307
|
+
} catch (error) {
|
|
308
|
+
logFailure(api, "typing", error);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function startTyping(api, tracker) {
|
|
312
|
+
tracker.typingExpiresAt = Date.now() + TYPING_TTL_MS;
|
|
313
|
+
sendTyping(api, tracker);
|
|
314
|
+
if (tracker.typingTimer) return;
|
|
315
|
+
tracker.typingTimer = setInterval(() => {
|
|
316
|
+
if (tracker.activeRunIds.size === 0 || Date.now() >= tracker.typingExpiresAt) {
|
|
317
|
+
stopTyping(tracker);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
sendTyping(api, tracker);
|
|
321
|
+
}, TYPING_INTERVAL_MS);
|
|
322
|
+
tracker.typingTimer.unref?.();
|
|
323
|
+
}
|
|
324
|
+
function stopTyping(tracker) {
|
|
325
|
+
if (tracker.typingTimer) {
|
|
326
|
+
clearInterval(tracker.typingTimer);
|
|
327
|
+
tracker.typingTimer = void 0;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async function handleStarted(api, event) {
|
|
331
|
+
const runId = event.runId.trim();
|
|
332
|
+
const target = resolveDiscordProgressTarget(event.requester);
|
|
333
|
+
if (!runId || !target || terminalOutcome(runId)) return;
|
|
334
|
+
const account = resolveDiscordAccount({
|
|
335
|
+
cfg: api.config,
|
|
336
|
+
accountId: event.requester?.accountId
|
|
337
|
+
});
|
|
338
|
+
const key = `${account.accountId}:${target.channelId}:${target.messageId}`;
|
|
339
|
+
if (!account.enabled || account.config.subagentProgress !== true) {
|
|
340
|
+
await runQueued(key, async () => {
|
|
341
|
+
const tracker = trackers.get(key);
|
|
342
|
+
if (!tracker) return;
|
|
343
|
+
stopTyping(tracker);
|
|
344
|
+
tracker.reactionsEnabled = false;
|
|
345
|
+
if (!account.enabled || !tracker.runningEmoji) return;
|
|
346
|
+
if (!reservedReactionEmojis(api.config, account.config.ackReaction).has(tracker.runningEmoji) && await clearReaction(api, tracker, tracker.runningEmoji)) {
|
|
347
|
+
tracker.runningEmoji = void 0;
|
|
348
|
+
tracker.runningEmojiConfirmed = false;
|
|
349
|
+
await persistTrackerRunningEmoji(api, tracker);
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
await runQueued(key, async () => {
|
|
355
|
+
let tracker = trackers.get(key);
|
|
356
|
+
let restoredCurrentRunWasTerminal = false;
|
|
357
|
+
if (!tracker) {
|
|
358
|
+
const reactionsEnabled = reactionsAreAvailable(api.config, account.config.ackReaction);
|
|
359
|
+
const restored = await listProgressStateForKey(api, key);
|
|
360
|
+
if (!restored.ok) return;
|
|
361
|
+
restoredCurrentRunWasTerminal = restored.cleanupRuns.some((cleanup) => cleanup.runId === runId);
|
|
362
|
+
tracker = {
|
|
363
|
+
accountId: account.accountId,
|
|
364
|
+
channelId: target.channelId,
|
|
365
|
+
messageId: target.messageId,
|
|
366
|
+
activeRunIds: new Set(restored.activeRunIds),
|
|
367
|
+
persistedRunIds: new Set(restored.activeRunIds),
|
|
368
|
+
runningEmojiConfirmed: false,
|
|
369
|
+
reactionsEnabled,
|
|
370
|
+
typingExpiresAt: 0
|
|
371
|
+
};
|
|
372
|
+
if (restored.activeRunIds.length > 0 || restored.cleanupRuns.length > 0) {
|
|
373
|
+
const reserved = reservedReactionEmojis(api.config, account.config.ackReaction);
|
|
374
|
+
const cleanupEmojis = restored.ownedEmojis.filter((emoji) => !reserved.has(emoji));
|
|
375
|
+
const countsCleared = await clearRunningReactions(api, tracker, cleanupEmojis);
|
|
376
|
+
const failurePresented = restored.cleanupRuns.filter((cleanup) => cleanup.value.outcome !== "ok").length === 0 || !reactionsEnabled || countsCleared && await setReaction(api, tracker, "🔴");
|
|
377
|
+
if (countsCleared && failurePresented) {
|
|
378
|
+
for (const cleanup of restored.cleanupRuns) markRunTerminal(cleanup.runId, cleanup.value.outcome);
|
|
379
|
+
await Promise.all(restored.cleanupRuns.map((cleanup) => consumeProgressRun(api, cleanup.runId)));
|
|
380
|
+
} else {
|
|
381
|
+
tracker.reactionsEnabled = false;
|
|
382
|
+
for (const cleanup of restored.cleanupRuns) scheduleTerminalLookupRetry(api, {
|
|
383
|
+
phase: "ended",
|
|
384
|
+
runId: cleanup.runId,
|
|
385
|
+
outcome: cleanup.value.outcome
|
|
386
|
+
}, cleanup.value);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
trackers.set(key, tracker);
|
|
390
|
+
}
|
|
391
|
+
if (!await disableTrackerReactionsOnCollision(api, tracker, account.config.ackReaction)) return;
|
|
392
|
+
if (restoredCurrentRunWasTerminal) {
|
|
393
|
+
if (tracker.activeRunIds.size > 0) {
|
|
394
|
+
await updateRunningReaction(api, tracker);
|
|
395
|
+
startTyping(api, tracker);
|
|
396
|
+
} else trackers.delete(key);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
if (tracker.activeRunIds.has(runId)) {
|
|
400
|
+
trackerKeyByRunId.set(runId, key);
|
|
401
|
+
await updateRunningReaction(api, tracker);
|
|
402
|
+
startTyping(api, tracker);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
let persistResult = "error";
|
|
406
|
+
if (tracker.reactionsEnabled) {
|
|
407
|
+
persistResult = await persistProgressRun(api, runId, tracker);
|
|
408
|
+
if (persistResult === "terminal") {
|
|
409
|
+
markRunTerminal(runId, "unknown");
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
if (persistResult === "conflict") {
|
|
413
|
+
api.logger.debug?.(`discord subagent progress ignored conflicting run id: ${runId}`);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (persistResult === "error") {
|
|
417
|
+
await clearReaction(api, tracker, tracker.runningEmoji);
|
|
418
|
+
tracker.runningEmoji = void 0;
|
|
419
|
+
tracker.runningEmojiConfirmed = false;
|
|
420
|
+
tracker.reactionsEnabled = false;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
tracker.activeRunIds.add(runId);
|
|
424
|
+
trackerKeyByRunId.set(runId, key);
|
|
425
|
+
if (persistResult === "persisted") tracker.persistedRunIds.add(runId);
|
|
426
|
+
const endedOutcome = terminalOutcome(runId);
|
|
427
|
+
if (endedOutcome) {
|
|
428
|
+
const owned = persistedProgressRunFromTracker(tracker);
|
|
429
|
+
await markProgressRunForCleanup(api, runId, owned, endedOutcome);
|
|
430
|
+
if (endedOutcome === "ok" || !tracker.reactionsEnabled || await setReaction(api, tracker, "🔴")) await consumeProgressRun(api, runId);
|
|
431
|
+
else scheduleTerminalLookupRetry(api, {
|
|
432
|
+
phase: "ended",
|
|
433
|
+
runId,
|
|
434
|
+
outcome: endedOutcome,
|
|
435
|
+
requester: event.requester
|
|
436
|
+
}, {
|
|
437
|
+
...owned,
|
|
438
|
+
status: "cleanup",
|
|
439
|
+
outcome: endedOutcome
|
|
440
|
+
});
|
|
441
|
+
tracker.activeRunIds.delete(runId);
|
|
442
|
+
tracker.persistedRunIds.delete(runId);
|
|
443
|
+
trackerKeyByRunId.delete(runId);
|
|
444
|
+
await updateRunningReaction(api, tracker);
|
|
445
|
+
if (tracker.activeRunIds.size === 0) {
|
|
446
|
+
stopTyping(tracker);
|
|
447
|
+
trackers.delete(key);
|
|
448
|
+
}
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
await updateRunningReaction(api, tracker);
|
|
452
|
+
startTyping(api, tracker);
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
async function reconcilePersistedTracker(api, persisted, outcome, endingRunId) {
|
|
456
|
+
const store = getProgressStore(api);
|
|
457
|
+
let activeRunIds = [];
|
|
458
|
+
if (store) try {
|
|
459
|
+
activeRunIds = (await store.entries()).filter((entry) => entry.key !== endingRunId && entry.value.key === persisted.key && entry.value.status === "active").map((entry) => entry.key);
|
|
460
|
+
} catch (error) {
|
|
461
|
+
logFailure(api, "state store list", error);
|
|
462
|
+
return { ok: false };
|
|
463
|
+
}
|
|
464
|
+
const tracker = {
|
|
465
|
+
accountId: persisted.accountId,
|
|
466
|
+
channelId: persisted.channelId,
|
|
467
|
+
messageId: persisted.messageId,
|
|
468
|
+
activeRunIds: new Set(activeRunIds),
|
|
469
|
+
persistedRunIds: new Set(activeRunIds),
|
|
470
|
+
runningEmojiConfirmed: false,
|
|
471
|
+
reactionsEnabled: true,
|
|
472
|
+
typingExpiresAt: 0
|
|
473
|
+
};
|
|
474
|
+
const account = resolveDiscordAccount({
|
|
475
|
+
cfg: api.config,
|
|
476
|
+
accountId: persisted.accountId
|
|
477
|
+
});
|
|
478
|
+
const typingEnabled = account.enabled && account.config.subagentProgress === true;
|
|
479
|
+
const reserved = reservedReactionEmojis(api.config, account.config.ackReaction);
|
|
480
|
+
const cleanupEmojis = persisted.runningEmoji && !reserved.has(persisted.runningEmoji) ? [persisted.runningEmoji] : [];
|
|
481
|
+
const reactionsEnabled = typingEnabled && reactionsAreAvailable(api.config, account.config.ackReaction);
|
|
482
|
+
const reactionsCleared = account.enabled && await clearRunningReactions(api, tracker, cleanupEmojis);
|
|
483
|
+
const nextEmoji = RUNNING_EMOJIS[Math.min(activeRunIds.length, RUNNING_EMOJIS.length) - 1];
|
|
484
|
+
let countPresented = true;
|
|
485
|
+
if (reactionsEnabled && reactionsCleared && nextEmoji) {
|
|
486
|
+
tracker.runningEmoji = nextEmoji;
|
|
487
|
+
countPresented = await persistTrackerRunningEmoji(api, tracker) && await setReaction(api, tracker, nextEmoji);
|
|
488
|
+
tracker.runningEmojiConfirmed = countPresented;
|
|
489
|
+
}
|
|
490
|
+
const outcomePresented = outcome === "ok" || !reactionsEnabled || reactionsCleared && countPresented && await setReaction(api, tracker, "🔴");
|
|
491
|
+
if (!reactionsCleared || !countPresented || !outcomePresented) return { ok: false };
|
|
492
|
+
return {
|
|
493
|
+
ok: true,
|
|
494
|
+
activeRunIds,
|
|
495
|
+
reactionsEnabled,
|
|
496
|
+
typingEnabled,
|
|
497
|
+
...reactionsEnabled && nextEmoji ? { runningEmoji: nextEmoji } : {}
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
function scheduleTerminalLookupRetry(api, event, owned) {
|
|
501
|
+
const runId = event.runId.trim();
|
|
502
|
+
if (!runId || terminalRetryTimers.has(runId)) return;
|
|
503
|
+
if (!owned) {
|
|
504
|
+
const target = resolveDiscordProgressTarget(event.requester);
|
|
505
|
+
const account = resolveDiscordAccount({
|
|
506
|
+
cfg: api.config,
|
|
507
|
+
accountId: event.requester?.accountId
|
|
508
|
+
});
|
|
509
|
+
if (!target || !account.enabled || account.config.subagentProgress !== true) return;
|
|
510
|
+
}
|
|
511
|
+
if (terminalRetryTimers.size >= 4096) return;
|
|
512
|
+
const expiresAt = terminalRetryExpiresAt.get(runId) ?? Date.now() + 6048e5;
|
|
513
|
+
const attempts = terminalRetryAttempts.get(runId) ?? 0;
|
|
514
|
+
if (expiresAt <= Date.now() || attempts >= TERMINAL_RETRY_MAX_ATTEMPTS) {
|
|
515
|
+
clearTerminalRetry(runId);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
terminalRetryExpiresAt.set(runId, expiresAt);
|
|
519
|
+
terminalRetryAttempts.set(runId, attempts + 1);
|
|
520
|
+
const retryDelayMs = Math.min(TERMINAL_LOOKUP_RETRY_MS * 2 ** Math.min(attempts, 12), TERMINAL_RETRY_MAX_DELAY_MS);
|
|
521
|
+
const timer = setTimeout(() => {
|
|
522
|
+
terminalRetryTimers.delete(runId);
|
|
523
|
+
handleEnded(api, event, owned);
|
|
524
|
+
}, retryDelayMs);
|
|
525
|
+
timer.unref?.();
|
|
526
|
+
terminalRetryTimers.set(runId, timer);
|
|
527
|
+
}
|
|
528
|
+
async function handleEnded(api, event, persistedHint) {
|
|
529
|
+
const runId = event.runId.trim();
|
|
530
|
+
if (!runId) return;
|
|
531
|
+
markRunTerminal(runId, event.outcome);
|
|
532
|
+
const lookup = await lookupProgressRun(api, runId);
|
|
533
|
+
const persisted = lookup.status === "found" ? lookup.value : persistedHint;
|
|
534
|
+
const key = trackerKeyByRunId.get(runId) ?? persisted?.key;
|
|
535
|
+
if (!key) {
|
|
536
|
+
if (lookup.status === "error") scheduleTerminalLookupRetry(api, event);
|
|
537
|
+
else clearTerminalRetry(runId);
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
cancelTerminalRetryTimer(runId);
|
|
541
|
+
await runQueued(key, async () => {
|
|
542
|
+
const tracker = trackers.get(key);
|
|
543
|
+
const currentLookup = await lookupProgressRun(api, runId);
|
|
544
|
+
const currentPersisted = currentLookup.status === "found" ? currentLookup.value : currentLookup.status === "error" ? persistedHint ?? persisted : persistedHint ?? (tracker ? void 0 : persisted);
|
|
545
|
+
const outcome = persistedTerminalOutcome(currentPersisted) ?? persistedTerminalOutcome(persisted) ?? event.outcome;
|
|
546
|
+
const retryEvent = outcome === event.outcome ? event : {
|
|
547
|
+
...event,
|
|
548
|
+
outcome
|
|
549
|
+
};
|
|
550
|
+
trackerKeyByRunId.delete(runId);
|
|
551
|
+
const owned = tracker?.persistedRunIds.has(runId) && currentPersisted?.status !== "cleanup" ? persistedProgressRunFromTracker(tracker) : currentPersisted;
|
|
552
|
+
const cleanupMarked = owned ? owned.status === "cleanup" || await markProgressRunForCleanup(api, runId, owned, outcome) : true;
|
|
553
|
+
if (tracker) {
|
|
554
|
+
const currentAccount = resolveDiscordAccount({
|
|
555
|
+
cfg: api.config,
|
|
556
|
+
accountId: tracker.accountId
|
|
557
|
+
});
|
|
558
|
+
if (!currentAccount.enabled || currentAccount.config.subagentProgress !== true) {
|
|
559
|
+
tracker.reactionsEnabled = false;
|
|
560
|
+
stopTyping(tracker);
|
|
561
|
+
} else await disableTrackerReactionsOnCollision(api, tracker, currentAccount.config.ackReaction);
|
|
562
|
+
}
|
|
563
|
+
if (!tracker) {
|
|
564
|
+
const reconciliation = owned ? await reconcilePersistedTracker(api, owned, outcome, runId) : { ok: false };
|
|
565
|
+
if (reconciliation.ok && owned) {
|
|
566
|
+
if (!await consumeProgressRun(api, runId)) scheduleTerminalLookupRetry(api, retryEvent, owned);
|
|
567
|
+
else clearTerminalRetry(runId);
|
|
568
|
+
if (reconciliation.typingEnabled && reconciliation.activeRunIds.length > 0) {
|
|
569
|
+
const restoredTracker = {
|
|
570
|
+
accountId: owned.accountId,
|
|
571
|
+
channelId: owned.channelId,
|
|
572
|
+
messageId: owned.messageId,
|
|
573
|
+
activeRunIds: new Set(reconciliation.activeRunIds),
|
|
574
|
+
persistedRunIds: new Set(reconciliation.activeRunIds),
|
|
575
|
+
runningEmojiConfirmed: Boolean(reconciliation.runningEmoji),
|
|
576
|
+
reactionsEnabled: reconciliation.reactionsEnabled,
|
|
577
|
+
...reconciliation.runningEmoji ? { runningEmoji: reconciliation.runningEmoji } : {},
|
|
578
|
+
typingExpiresAt: 0
|
|
579
|
+
};
|
|
580
|
+
trackers.set(key, restoredTracker);
|
|
581
|
+
for (const activeRunId of reconciliation.activeRunIds) trackerKeyByRunId.set(activeRunId, key);
|
|
582
|
+
startTyping(api, restoredTracker);
|
|
583
|
+
}
|
|
584
|
+
} else if (owned) scheduleTerminalLookupRetry(api, retryEvent, owned);
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
tracker.activeRunIds.delete(runId);
|
|
588
|
+
tracker.persistedRunIds.delete(runId);
|
|
589
|
+
const countReconciled = tracker.reactionsEnabled ? await updateRunningReaction(api, tracker) : owned ? (await reconcilePersistedTracker(api, owned, outcome, runId)).ok : true;
|
|
590
|
+
const outcomePresented = outcome === "ok" || !tracker.reactionsEnabled || countReconciled && await setReaction(api, tracker, "🔴");
|
|
591
|
+
if (countReconciled && outcomePresented && owned) {
|
|
592
|
+
const consumed = await consumeProgressRun(api, runId);
|
|
593
|
+
if (!consumed) scheduleTerminalLookupRetry(api, retryEvent, owned);
|
|
594
|
+
else clearTerminalRetry(runId);
|
|
595
|
+
if (!consumed && !cleanupMarked) await markProgressRunForCleanup(api, runId, owned, outcome);
|
|
596
|
+
} else if (owned) scheduleTerminalLookupRetry(api, retryEvent, owned);
|
|
597
|
+
if (tracker.activeRunIds.size === 0) {
|
|
598
|
+
stopTyping(tracker);
|
|
599
|
+
trackers.delete(key);
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
async function handleDiscordSubagentProgressImpl(api, event) {
|
|
604
|
+
if (event.phase === "started") {
|
|
605
|
+
await handleStarted(api, event);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
await handleEnded(api, event);
|
|
609
|
+
}
|
|
610
|
+
function clearStartupRecoveryRetry(api) {
|
|
611
|
+
const retry = startupRecoveryRetries.get(api);
|
|
612
|
+
if (retry?.timer) clearTimeout(retry.timer);
|
|
613
|
+
startupRecoveryRetries.delete(api);
|
|
614
|
+
}
|
|
615
|
+
function scheduleStartupRecoveryRetry(api) {
|
|
616
|
+
const retry = startupRecoveryRetries.get(api) ?? { attempts: 0 };
|
|
617
|
+
if (retry.timer || retry.attempts >= STARTUP_RETRY_MAX_ATTEMPTS) return;
|
|
618
|
+
const delayMs = Math.min(TERMINAL_LOOKUP_RETRY_MS * 2 ** retry.attempts, TERMINAL_RETRY_MAX_DELAY_MS);
|
|
619
|
+
retry.attempts += 1;
|
|
620
|
+
retry.timer = setTimeout(() => {
|
|
621
|
+
retry.timer = void 0;
|
|
622
|
+
recoverDiscordSubagentProgress(api);
|
|
623
|
+
}, delayMs);
|
|
624
|
+
retry.timer.unref?.();
|
|
625
|
+
startupRecoveryRetries.set(api, retry);
|
|
626
|
+
}
|
|
627
|
+
async function recoverDiscordSubagentProgress(api) {
|
|
628
|
+
const store = getProgressStore(api);
|
|
629
|
+
if (!store) {
|
|
630
|
+
if (api.runtime?.state) scheduleStartupRecoveryRetry(api);
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
let persistedRuns;
|
|
634
|
+
try {
|
|
635
|
+
persistedRuns = await store.entries();
|
|
636
|
+
} catch (error) {
|
|
637
|
+
logFailure(api, "startup recovery list", error);
|
|
638
|
+
scheduleStartupRecoveryRetry(api);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
clearStartupRecoveryRetry(api);
|
|
642
|
+
for (const entry of persistedRuns) await handleEnded(api, {
|
|
643
|
+
phase: "ended",
|
|
644
|
+
runId: entry.key,
|
|
645
|
+
outcome: persistedTerminalOutcome(entry.value) ?? "unknown"
|
|
646
|
+
}, entry.value);
|
|
647
|
+
}
|
|
648
|
+
function resetDiscordSubagentProgressForTest() {
|
|
649
|
+
for (const tracker of trackers.values()) stopTyping(tracker);
|
|
650
|
+
trackers.clear();
|
|
651
|
+
trackerKeyByRunId.clear();
|
|
652
|
+
for (const timer of terminalRetryTimers.values()) clearTimeout(timer);
|
|
653
|
+
terminalRetryTimers.clear();
|
|
654
|
+
terminalRetryExpiresAt.clear();
|
|
655
|
+
terminalRetryAttempts.clear();
|
|
656
|
+
for (const retry of startupRecoveryRetries.values()) if (retry.timer) clearTimeout(retry.timer);
|
|
657
|
+
startupRecoveryRetries.clear();
|
|
658
|
+
resetDiscordSubagentProgressStateForTest();
|
|
659
|
+
}
|
|
660
|
+
const handleDiscordSubagentProgress = Object.assign(handleDiscordSubagentProgressImpl, { resetForTest: resetDiscordSubagentProgressForTest });
|
|
661
|
+
//#endregion
|
|
662
|
+
export { handleDiscordSubagentProgress, recoverDiscordSubagentProgress };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { x as formatDiscordUserTag } from "./allow-list-
|
|
1
|
+
import { d as discord_exports } from "./chunk-CWzmeWf_.js";
|
|
2
|
+
import { x as formatDiscordUserTag } from "./allow-list-DzKrtfah.js";
|
|
3
3
|
//#region extensions/discord/src/monitor/system-events.ts
|
|
4
4
|
function resolveDiscordSystemEvent(message, location) {
|
|
5
5
|
switch (message.type) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Yt as __exportAll, a as allowFromContainsDiscordUserId, l as parseDiscordTarget } from "./chunk-CWzmeWf_.js";
|
|
2
2
|
import { c as resolveDiscordAccountAllowFrom, s as resolveDiscordAccount } from "./accounts-CMd4nzLB.js";
|
|
3
|
-
import { i as rememberDiscordDirectoryUser, r as listDiscordDirectoryPeersLive } from "./directory-live-
|
|
3
|
+
import { i as rememberDiscordDirectoryUser, r as listDiscordDirectoryPeersLive } from "./directory-live-BYlCRO__.js";
|
|
4
4
|
import { buildMessagingTarget } from "openclaw/plugin-sdk/channel-targets";
|
|
5
5
|
//#region extensions/discord/src/send-target-parsing.ts
|
|
6
6
|
const parseDiscordSendTarget = (raw, options = {}) => parseDiscordTarget(raw, options);
|