@aspalagin/openclaw-max 0.7.2 → 0.8.0

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 (83) hide show
  1. package/LICENSE +2 -0
  2. package/README.md +412 -344
  3. package/README_EN.md +492 -0
  4. package/SKILL.md +45 -37
  5. package/assets/icon.png +0 -0
  6. package/dist/secret-contract-api.js +6 -0
  7. package/dist/src/access-policy.js +123 -0
  8. package/dist/src/accounts.js +170 -23
  9. package/dist/src/action-scope.js +213 -0
  10. package/dist/src/actions.js +159 -31
  11. package/dist/src/api.js +99 -83
  12. package/dist/src/callbacks.js +27 -7
  13. package/dist/src/channel-agent-prompt.js +8 -19
  14. package/dist/src/channel-config.js +25 -3
  15. package/dist/src/channel-lifecycle.js +28 -5
  16. package/dist/src/channel-outbound.js +43 -9
  17. package/dist/src/channel.js +16 -0
  18. package/dist/src/command-menu.js +199 -0
  19. package/dist/src/config-schema.js +106 -10
  20. package/dist/src/deliver.js +144 -48
  21. package/dist/src/dispatch.js +12 -11
  22. package/dist/src/gateway-drain.js +40 -0
  23. package/dist/src/group-mention.js +129 -0
  24. package/dist/src/inbound-attachments.js +162 -20
  25. package/dist/src/inbound.js +388 -134
  26. package/dist/src/media-temp.js +31 -20
  27. package/dist/src/network.js +216 -0
  28. package/dist/src/onboarding.js +4 -2
  29. package/dist/src/polling.js +180 -12
  30. package/dist/src/presentation.js +11 -0
  31. package/dist/src/progress-draft.js +168 -0
  32. package/dist/src/runtime.js +15 -0
  33. package/dist/src/secret-contract.js +149 -0
  34. package/dist/src/send.js +250 -46
  35. package/dist/src/stream-draft.js +83 -41
  36. package/dist/src/turn-adoption.js +24 -0
  37. package/dist/src/typing.js +55 -0
  38. package/dist/src/update-journal.js +267 -0
  39. package/dist/src/webhook-queue.js +151 -26
  40. package/dist/src/webhook-runner.js +61 -9
  41. package/dist/src/webhook.js +34 -15
  42. package/openclaw.plugin.json +986 -13
  43. package/package.json +25 -5
  44. package/src/access-policy.ts +154 -0
  45. package/src/accounts.ts +260 -25
  46. package/src/action-scope.ts +274 -0
  47. package/src/actions.ts +188 -35
  48. package/src/api.ts +158 -101
  49. package/src/callbacks.ts +26 -7
  50. package/src/channel-agent-prompt.ts +8 -19
  51. package/src/channel-config.ts +32 -3
  52. package/src/channel-lifecycle.ts +33 -6
  53. package/src/channel-outbound.ts +50 -7
  54. package/src/channel.ts +16 -0
  55. package/src/command-menu.ts +277 -0
  56. package/src/config-schema.ts +111 -10
  57. package/src/deliver.ts +174 -55
  58. package/src/dispatch.ts +12 -11
  59. package/src/gateway-drain.ts +45 -0
  60. package/src/group-mention.ts +191 -0
  61. package/src/inbound-attachments.ts +173 -22
  62. package/src/inbound.ts +443 -130
  63. package/src/media-temp.ts +35 -23
  64. package/src/network.ts +233 -0
  65. package/src/onboarding.ts +4 -2
  66. package/src/polling.ts +221 -15
  67. package/src/presentation.ts +14 -1
  68. package/src/progress-draft.ts +226 -0
  69. package/src/runtime.ts +23 -0
  70. package/src/secret-contract.ts +183 -0
  71. package/src/send.ts +346 -59
  72. package/src/stream-draft.ts +102 -41
  73. package/src/turn-adoption.ts +33 -0
  74. package/src/types.ts +9 -3
  75. package/src/typing.ts +77 -0
  76. package/src/update-journal.ts +302 -0
  77. package/src/webhook-queue.ts +205 -33
  78. package/src/webhook-runner.ts +65 -8
  79. package/src/webhook.ts +39 -18
  80. package/scripts/check-cycles.mjs +0 -38
  81. package/scripts/test-api.mjs +0 -169
  82. package/scripts/test-send.mjs +0 -162
  83. package/scripts/update-schema.mjs +0 -48
@@ -75,11 +75,21 @@ export function registerMaxWebhookRoute(params) {
75
75
  export function maxUpdateDedupeKey(update) {
76
76
  const id = update.callback?.callback_id ??
77
77
  update.message?.body?.mid ??
78
- (typeof update.message_id === 'string' ? update.message_id : undefined);
78
+ // A forward-only message has no body; its link carries the original mid.
79
+ update.message?.link?.message?.mid ??
80
+ (typeof update.message_id === 'string' ? update.message_id : undefined) ??
81
+ // Registry updates (bot_started, bot_added, …) carry no message: their chat
82
+ // and user tell apart two of them in the same millisecond.
83
+ registryUpdateId(update);
79
84
  if (!id && update.timestamp == null)
80
85
  return undefined;
81
86
  return `${update.update_type}:${update.timestamp ?? ''}:${id ?? ''}`;
82
87
  }
88
+ function registryUpdateId(update) {
89
+ const userId = update.user?.user_id ?? update.user_id;
90
+ const parts = [update.chat_id, userId].filter((part) => part != null);
91
+ return parts.length > 0 ? parts.join('/') : undefined;
92
+ }
83
93
  /** Returns true when the key was already seen (LRU bounded by DEDUPE_CAPACITY). */
84
94
  function rememberUpdate(seen, key) {
85
95
  if (seen.has(key)) {
@@ -143,25 +153,34 @@ export async function handleMaxWebhookRequest(req, res) {
143
153
  return true;
144
154
  }
145
155
  const update = raw;
146
- // MAX needs 200 within 30 s while an agent turn can take minutes: ack first,
147
- // then hand the update to the account task. Errors are logged, never turned
148
- // into a non-200 (that would only make MAX redeliver and eventually
149
- // unsubscribe the bot). onUpdate must only enqueue: this request's work
150
- // admission is released when the handler returns, so agent/session work
151
- // started from here would fail with GatewayDrainingError.
152
- res.statusCode = 200;
153
- res.setHeader('Content-Type', 'application/json');
154
- res.end(JSON.stringify({ ok: true }));
155
156
  const dedupeKey = maxUpdateDedupeKey(update);
156
157
  if (dedupeKey && rememberUpdate(matchedTarget.seen, dedupeKey)) {
157
158
  matchedTarget.log?.(`[${matchedTarget.account.accountId}] MAX webhook: duplicate ${dedupeKey} skipped`);
159
+ res.statusCode = 200;
160
+ res.setHeader('Content-Type', 'application/json');
161
+ res.end(JSON.stringify({ ok: true }));
158
162
  return true;
159
163
  }
160
- void Promise.resolve()
161
- .then(() => matchedTarget.onUpdate(update))
162
- .catch((err) => {
163
- matchedTarget.error?.(`[${matchedTarget.account.accountId}] Webhook update processing failed: ${String(err)}`);
164
- });
164
+ // MAX needs 200 within 30 s while an agent turn can take minutes: onUpdate
165
+ // only records the update (durable journal) and enqueues it for the account
166
+ // task, then MAX gets its 200. It must not dispatch: this request's work
167
+ // admission is released when the handler returns, so agent/session work
168
+ // started from here would fail with GatewayDrainingError. When the update
169
+ // could not be recorded, 503 makes MAX redeliver it instead of losing it.
170
+ try {
171
+ await matchedTarget.onUpdate(update);
172
+ }
173
+ catch (err) {
174
+ if (dedupeKey)
175
+ matchedTarget.seen.delete(dedupeKey);
176
+ matchedTarget.error?.(`[${matchedTarget.account.accountId}] MAX webhook: ${update.update_type} not queued, answering 503 for redelivery: ${String(err)}`);
177
+ res.statusCode = 503;
178
+ res.end('Service Unavailable');
179
+ return true;
180
+ }
181
+ res.statusCode = 200;
182
+ res.setHeader('Content-Type', 'application/json');
183
+ res.end(JSON.stringify({ ok: true }));
165
184
  return true;
166
185
  }
167
186
  /**