@tloncorp/openclaw 0.4.3 → 0.6.1

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 (112) hide show
  1. package/README.md +130 -141
  2. package/dist/index.js +703 -152
  3. package/dist/index.js.map +1 -1
  4. package/dist/setup-api.js +2 -2
  5. package/dist/setup-entry.js +2 -2
  6. package/dist/setup-entry.js.map +1 -1
  7. package/dist/src/account-fields.js +7 -3
  8. package/dist/src/account-fields.js.map +1 -1
  9. package/dist/src/actions.js +73 -52
  10. package/dist/src/actions.js.map +1 -1
  11. package/dist/src/channel.js +63 -39
  12. package/dist/src/channel.js.map +1 -1
  13. package/dist/src/channel.runtime.js +61 -32
  14. package/dist/src/channel.runtime.js.map +1 -1
  15. package/dist/src/config-schema.js +24 -4
  16. package/dist/src/config-schema.js.map +1 -1
  17. package/dist/src/diagnostic-subscriptions.js +49 -0
  18. package/dist/src/diagnostic-subscriptions.js.map +1 -0
  19. package/dist/src/effective-owner.js.map +1 -1
  20. package/dist/src/gateway-status.js +55 -7
  21. package/dist/src/gateway-status.js.map +1 -1
  22. package/dist/src/monitor/approval.js +71 -62
  23. package/dist/src/monitor/approval.js.map +1 -1
  24. package/dist/src/monitor/command-auth.js +7 -7
  25. package/dist/src/monitor/command-auth.js.map +1 -1
  26. package/dist/src/monitor/command-bridge.js +3 -2
  27. package/dist/src/monitor/command-bridge.js.map +1 -1
  28. package/dist/src/monitor/computing-presence.js +76 -12
  29. package/dist/src/monitor/computing-presence.js.map +1 -1
  30. package/dist/src/monitor/discovery.js +16 -9
  31. package/dist/src/monitor/discovery.js.map +1 -1
  32. package/dist/src/monitor/history.js +58 -26
  33. package/dist/src/monitor/history.js.map +1 -1
  34. package/dist/src/monitor/index.js +3018 -2496
  35. package/dist/src/monitor/index.js.map +1 -1
  36. package/dist/src/monitor/media.js +106 -78
  37. package/dist/src/monitor/media.js.map +1 -1
  38. package/dist/src/monitor/nudge-runner.js +36 -27
  39. package/dist/src/monitor/nudge-runner.js.map +1 -1
  40. package/dist/src/monitor/nudge-state.js +7 -11
  41. package/dist/src/monitor/nudge-state.js.map +1 -1
  42. package/dist/src/monitor/owner-reply-persistence.js +27 -26
  43. package/dist/src/monitor/owner-reply-persistence.js.map +1 -1
  44. package/dist/src/monitor/processed-messages.js.map +1 -1
  45. package/dist/src/monitor/session-routing.js +261 -0
  46. package/dist/src/monitor/session-routing.js.map +1 -0
  47. package/dist/src/monitor/settings-sync.js +1 -8
  48. package/dist/src/monitor/settings-sync.js.map +1 -1
  49. package/dist/src/monitor/utils.js +77 -71
  50. package/dist/src/monitor/utils.js.map +1 -1
  51. package/dist/src/nudge-decision.js +40 -43
  52. package/dist/src/nudge-decision.js.map +1 -1
  53. package/dist/src/nudge-messages.js +9 -9
  54. package/dist/src/nudge-scheduler.js.map +1 -1
  55. package/dist/src/owner-listen-command.js +38 -28
  56. package/dist/src/owner-listen-command.js.map +1 -1
  57. package/dist/src/pending-nudge.js.map +1 -1
  58. package/dist/src/runtime.js +10 -6
  59. package/dist/src/runtime.js.map +1 -1
  60. package/dist/src/session-roles.js +2 -1
  61. package/dist/src/session-roles.js.map +1 -1
  62. package/dist/src/session-route.js +44 -0
  63. package/dist/src/session-route.js.map +1 -0
  64. package/dist/src/settings.js +233 -102
  65. package/dist/src/settings.js.map +1 -1
  66. package/dist/src/setup-core.js +32 -32
  67. package/dist/src/setup-core.js.map +1 -1
  68. package/dist/src/setup-surface.js +19 -19
  69. package/dist/src/setup-surface.js.map +1 -1
  70. package/dist/src/shared-state.js +46 -0
  71. package/dist/src/shared-state.js.map +1 -0
  72. package/dist/src/targets.js +17 -10
  73. package/dist/src/targets.js.map +1 -1
  74. package/dist/src/telemetry.js +764 -34
  75. package/dist/src/telemetry.js.map +1 -1
  76. package/dist/src/tlon-binary.js +20 -12
  77. package/dist/src/tlon-binary.js.map +1 -1
  78. package/dist/src/tlon-tool-guard.js +5 -5
  79. package/dist/src/tool-trace.js +17 -13
  80. package/dist/src/tool-trace.js.map +1 -1
  81. package/dist/src/types.js +30 -12
  82. package/dist/src/types.js.map +1 -1
  83. package/dist/src/urbit/api-client.js +16 -12
  84. package/dist/src/urbit/api-client.js.map +1 -1
  85. package/dist/src/urbit/auth.js +9 -9
  86. package/dist/src/urbit/auth.js.map +1 -1
  87. package/dist/src/urbit/base-url.js +11 -11
  88. package/dist/src/urbit/base-url.js.map +1 -1
  89. package/dist/src/urbit/channel-ops.js +25 -19
  90. package/dist/src/urbit/channel-ops.js.map +1 -1
  91. package/dist/src/urbit/context.js +8 -8
  92. package/dist/src/urbit/context.js.map +1 -1
  93. package/dist/src/urbit/errors.js +33 -7
  94. package/dist/src/urbit/errors.js.map +1 -1
  95. package/dist/src/urbit/fetch.js +3 -3
  96. package/dist/src/urbit/fetch.js.map +1 -1
  97. package/dist/src/urbit/http-poke.js +10 -10
  98. package/dist/src/urbit/http-poke.js.map +1 -1
  99. package/dist/src/urbit/send.js +27 -23
  100. package/dist/src/urbit/send.js.map +1 -1
  101. package/dist/src/urbit/sse-client.js +45 -41
  102. package/dist/src/urbit/sse-client.js.map +1 -1
  103. package/dist/src/urbit/story.js +31 -30
  104. package/dist/src/urbit/story.js.map +1 -1
  105. package/dist/src/urbit/upload.js +8 -8
  106. package/dist/src/urbit/upload.js.map +1 -1
  107. package/dist/src/version.generated.js +2 -1
  108. package/dist/src/version.generated.js.map +1 -1
  109. package/dist/src/version.js +134 -0
  110. package/dist/src/version.js.map +1 -0
  111. package/openclaw.plugin.json +37 -0
  112. package/package.json +9 -15
@@ -1 +1 @@
1
- {"version":3,"file":"processed-messages.js","sourceRoot":"","sources":["../../../src/monitor/processed-messages.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,6BAA6B,CAAC,KAAK,GAAG,IAAI;IACxD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,MAAM,IAAI,GAAG,CAAC,EAAkB,EAAE,EAAE;QAClC,MAAM,OAAO,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACtB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAClB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;YACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrC,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;gBAC7B,IAAI,MAAM,EAAE,CAAC;oBACX,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACtB,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,MAAM,GAAG,GAAG,CAAC,EAAkB,EAAE,EAAE;QACjC,MAAM,OAAO,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC,CAAC;IAEF,OAAO;QACL,IAAI;QACJ,GAAG;QACH,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI;KACtB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"processed-messages.js","sourceRoot":"","sources":["../../../src/monitor/processed-messages.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,6BAA6B,CAC3C,KAAK,GAAG,IAAI;IAEZ,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,MAAM,IAAI,GAAG,CAAC,EAAkB,EAAE,EAAE;QAClC,MAAM,OAAO,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACtB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAClB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;YACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrC,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;gBAC7B,IAAI,MAAM,EAAE,CAAC;oBACX,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACtB,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,MAAM,GAAG,GAAG,CAAC,EAAkB,EAAE,EAAE;QACjC,MAAM,OAAO,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC,CAAC;IAEF,OAAO;QACL,IAAI;QACJ,GAAG;QACH,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI;KACtB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,261 @@
1
+ import { resolvePinnedMainDmOwnerFromAllowlist } from 'openclaw/plugin-sdk/conversation-runtime';
2
+ import { resolveInboundLastRouteSessionKey, } from 'openclaw/plugin-sdk/routing';
3
+ import { canonicalizeNest, normalizeShip } from '../targets.js';
4
+ /**
5
+ * Build the parameters for persisting a Tlon inbound turn's durable session
6
+ * route. Pure: no I/O, no SDK store access. The caller passes the result to
7
+ * `recordInboundSession` (see `recordTlonInboundRoute`).
8
+ *
9
+ * Routing rules:
10
+ * - DM: persist `tlon:<senderShip>`. When the last-route session is the agent
11
+ * main session and a single owner is pinned, attach `mainDmOwnerPin` so a
12
+ * non-owner DM cannot silently overwrite the owner/main route.
13
+ * - Group/channel: persist `tlon:<groupChannel>` only when the last-route
14
+ * session is NOT the main session (avoid clobbering a broad shared session).
15
+ * Skip — but still record origin metadata — when `groupChannel` is missing.
16
+ */
17
+ export function buildTlonInboundRouteRecord(input) {
18
+ const { cfg, route, ctxSessionKey, isGroup, groupChannel, senderShip, parentId, deliverParentId, effectiveOwnerShip, effectiveDmAllowlist, } = input;
19
+ const recordSessionKey = ctxSessionKey ?? route.sessionKey;
20
+ const lastRouteSessionKey = resolveInboundLastRouteSessionKey({
21
+ route,
22
+ sessionKey: recordSessionKey,
23
+ });
24
+ const rawThread = deliverParentId ?? parentId;
25
+ const threadId = rawThread != null && rawThread !== '' ? String(rawThread) : undefined;
26
+ const base = { recordSessionKey, lastRouteSessionKey };
27
+ if (isGroup) {
28
+ const nest = groupChannel?.trim();
29
+ if (!nest) {
30
+ return { ...base, target: null, skippedReason: 'group-missing-channel' };
31
+ }
32
+ // Validate/canonicalize the nest before storing. Firehose nests are already
33
+ // canonical, but config/settings-watched values can be malformed; a bad
34
+ // nest would otherwise persist an unusable `lastTo`.
35
+ const canonicalNest = canonicalizeNest(nest);
36
+ if (!canonicalNest) {
37
+ return { ...base, target: null, skippedReason: 'group-invalid-channel' };
38
+ }
39
+ const target = `tlon:${canonicalNest}`;
40
+ // Don't overwrite a broad shared main session with a per-group route.
41
+ if (lastRouteSessionKey === route.mainSessionKey) {
42
+ return {
43
+ ...base,
44
+ target,
45
+ skippedReason: 'group-route-targets-main-session',
46
+ };
47
+ }
48
+ return {
49
+ ...base,
50
+ target,
51
+ updateLastRoute: {
52
+ sessionKey: lastRouteSessionKey,
53
+ channel: 'tlon',
54
+ to: target,
55
+ ...(route.accountId ? { accountId: route.accountId } : {}),
56
+ ...(threadId !== undefined ? { threadId } : {}),
57
+ },
58
+ };
59
+ }
60
+ // DM
61
+ const target = `tlon:${senderShip}`;
62
+ const mainDmOwnerPin = resolveMainDmOwnerPin({
63
+ cfg,
64
+ route,
65
+ lastRouteSessionKey,
66
+ senderShip,
67
+ effectiveOwnerShip,
68
+ effectiveDmAllowlist,
69
+ });
70
+ return {
71
+ ...base,
72
+ target,
73
+ updateLastRoute: {
74
+ sessionKey: lastRouteSessionKey,
75
+ channel: 'tlon',
76
+ to: target,
77
+ ...(route.accountId ? { accountId: route.accountId } : {}),
78
+ ...(threadId !== undefined ? { threadId } : {}),
79
+ ...(mainDmOwnerPin ? { mainDmOwnerPin } : {}),
80
+ },
81
+ };
82
+ }
83
+ function resolveMainDmOwnerPin(params) {
84
+ const { cfg, route, lastRouteSessionKey, senderShip, effectiveOwnerShip, effectiveDmAllowlist, } = params;
85
+ // Only the main session can be clobbered by another DM sender; isolated
86
+ // per-peer sessions need no pin.
87
+ if (lastRouteSessionKey !== route.mainSessionKey) {
88
+ return undefined;
89
+ }
90
+ const sender = senderShip.trim();
91
+ if (!sender) {
92
+ return undefined;
93
+ }
94
+ // Tlon's configured owner (`ownerShip`) is separate from `dmAllowlist`, and
95
+ // owner DMs are valid even when the owner isn't allowlisted. Prefer pinning
96
+ // the configured owner; otherwise fall back to the SDK's allowlist semantics
97
+ // (which only pin when there is exactly one allowed sender).
98
+ const pinnedOwner = resolvePinnedMainDmOwnerFromAllowlist({
99
+ dmScope: cfg.session?.dmScope,
100
+ allowFrom: effectiveOwnerShip ? [effectiveOwnerShip] : effectiveDmAllowlist,
101
+ normalizeEntry: normalizeShip,
102
+ });
103
+ if (!pinnedOwner) {
104
+ return undefined;
105
+ }
106
+ return {
107
+ ownerRecipient: pinnedOwner,
108
+ senderRecipient: normalizeShip(sender),
109
+ };
110
+ }
111
+ /**
112
+ * Persist a Tlon inbound turn's session route. Always records origin metadata
113
+ * (even when `record.updateLastRoute` is omitted) and fails open: a persistence
114
+ * failure is logged but never blocks the live reply.
115
+ */
116
+ export async function recordTlonInboundRoute(deps) {
117
+ const { session, storePath, ctxPayload, record, messageId, accountId } = deps;
118
+ if (record.skippedReason === 'group-missing-channel' ||
119
+ record.skippedReason === 'group-invalid-channel') {
120
+ // Anomalous (a group turn with no/malformed channel nest); surface outside
121
+ // debug too.
122
+ deps.logError?.(`[tlon] route persistence skipped (${record.skippedReason}): ` +
123
+ `messageId=${messageId ?? '?'} lastRouteSessionKey=${record.lastRouteSessionKey}`);
124
+ }
125
+ else if (record.skippedReason) {
126
+ // Normal policy outcome (e.g. a group route that targets the shared main
127
+ // session); debug-only to avoid high-volume logs for an expected case.
128
+ deps.logDebug?.(`[tlon] route persistence skipped (${record.skippedReason}): ` +
129
+ `messageId=${messageId ?? '?'} lastRouteSessionKey=${record.lastRouteSessionKey} ` +
130
+ `target=${record.target ?? '(none)'}`);
131
+ }
132
+ // When a main-session DM is pinned to an owner, the SDK skips the durable
133
+ // route update for a non-owner sender. Wire an onSkip so that decision is
134
+ // observable instead of silently looking like a successful write.
135
+ const pin = record.updateLastRoute?.mainDmOwnerPin;
136
+ const updateLastRoute = record.updateLastRoute
137
+ ? {
138
+ ...record.updateLastRoute,
139
+ ...(pin
140
+ ? {
141
+ mainDmOwnerPin: {
142
+ ...pin,
143
+ onSkip: ({ ownerRecipient, senderRecipient }) => deps.logDebug?.(`[tlon] durable route update skipped by owner pin: ` +
144
+ `messageId=${messageId ?? '?'} owner=${ownerRecipient} ` +
145
+ `sender=${senderRecipient} lastRouteSessionKey=${record.lastRouteSessionKey}`),
146
+ },
147
+ }
148
+ : {}),
149
+ }
150
+ : undefined;
151
+ try {
152
+ await session.recordInboundSession({
153
+ storePath,
154
+ sessionKey: record.recordSessionKey,
155
+ ctx: ctxPayload,
156
+ updateLastRoute,
157
+ onRecordError: (err) => {
158
+ deps.logError?.(`[tlon] failed updating session meta: ${String(err)}`);
159
+ },
160
+ });
161
+ }
162
+ catch (err) {
163
+ deps.logError?.(`[tlon] failed recording inbound session route: ${String(err)} ` +
164
+ `(messageId=${messageId ?? '?'} storePath=${storePath} ` +
165
+ `recordSessionKey=${record.recordSessionKey} ` +
166
+ `lastRouteSessionKey=${record.lastRouteSessionKey} ` +
167
+ `target=${record.target ?? '(none)'} accountId=${accountId ?? '(none)'} ` +
168
+ `hadUpdateLastRoute=${Boolean(record.updateLastRoute)})`);
169
+ }
170
+ }
171
+ /**
172
+ * Whether the SDK will skip the durable route update because a main-session DM
173
+ * is pinned to an owner different from the sender. A built `updateLastRoute`
174
+ * does not guarantee a write, so diagnostics should report this rather than
175
+ * imply the route was persisted.
176
+ */
177
+ export function routeUpdateWillSkipByPin(update) {
178
+ const pin = update?.mainDmOwnerPin;
179
+ if (!pin) {
180
+ return false;
181
+ }
182
+ return pin.ownerRecipient !== pin.senderRecipient;
183
+ }
184
+ /**
185
+ * Run the durable route-record step before reply dispatch. The bug this fixes
186
+ * lives at exactly this boundary: route-dependent sends that happen during
187
+ * dispatch must observe the persisted route, so recording must complete first.
188
+ * Keeping this ordering in one place gives the monitor a testable guard.
189
+ */
190
+ export async function recordRouteThenDispatch(steps) {
191
+ await steps.record();
192
+ return steps.dispatch();
193
+ }
194
+ /**
195
+ * The monitor's "build ctx → record route → dispatch" boundary, as a single
196
+ * testable unit (this is where the original webchat-leak bug lived). Builds the
197
+ * Tlon route record, persists it, and only then runs `dispatch`. The monitor
198
+ * delegates its entire record+dispatch to this so the ordering is covered by a
199
+ * unit test rather than only by the monitor's structure.
200
+ */
201
+ export async function recordTlonRouteAndDispatch(params) {
202
+ const record = buildTlonInboundRouteRecord({
203
+ cfg: params.cfg,
204
+ route: params.route,
205
+ ctxSessionKey: params.ctxSessionKey,
206
+ isGroup: params.isGroup,
207
+ groupChannel: params.groupChannel,
208
+ senderShip: params.senderShip,
209
+ parentId: params.parentId,
210
+ deliverParentId: params.deliverParentId,
211
+ effectiveOwnerShip: params.effectiveOwnerShip,
212
+ effectiveDmAllowlist: params.effectiveDmAllowlist,
213
+ });
214
+ params.onRecord?.(record);
215
+ return recordRouteThenDispatch({
216
+ // Fail open across the *entire* persistence step — including
217
+ // resolveStorePath, which can throw on bad session-store config — so a
218
+ // persistence failure never suppresses the live Tlon reply.
219
+ record: async () => {
220
+ let storePath;
221
+ try {
222
+ storePath = params.session.resolveStorePath(params.sessionStore, {
223
+ agentId: params.route.agentId,
224
+ });
225
+ }
226
+ catch (err) {
227
+ params.logError?.(`[tlon] failed resolving session store path: ${String(err)} ` +
228
+ `(messageId=${params.messageId ?? '?'})`);
229
+ return;
230
+ }
231
+ await recordTlonInboundRoute({
232
+ session: params.session,
233
+ storePath,
234
+ ctxPayload: params.ctxPayload,
235
+ record,
236
+ messageId: params.messageId,
237
+ accountId: params.route.accountId,
238
+ logError: params.logError,
239
+ logDebug: params.logDebug,
240
+ });
241
+ },
242
+ dispatch: params.dispatch,
243
+ });
244
+ }
245
+ /**
246
+ * Build a Tlon `deliveryContext` for an enqueued system event (reactions,
247
+ * group-join notices). System-event turns resolve delivery from the event's
248
+ * `deliveryContext` or the session store; attaching this ensures a later
249
+ * system/heartbeat turn driven by the event routes to Tlon instead of falling
250
+ * back to webchat, even on a session that hasn't persisted an inbound route yet.
251
+ * `to` is the provider-qualified target (`tlon:~ship` for DMs, `tlon:<nest>` for
252
+ * channels), matching `OriginatingTo` and the durable route.
253
+ */
254
+ export function tlonDeliveryContext(to, accountId) {
255
+ return { channel: 'tlon', to, ...(accountId ? { accountId } : {}) };
256
+ }
257
+ /** Route-debug gate: enabled via `TLON_OPENCLAW_ROUTE_DEBUG=1`. */
258
+ export function isRouteDebugEnabled() {
259
+ return process.env.TLON_OPENCLAW_ROUTE_DEBUG === '1';
260
+ }
261
+ //# sourceMappingURL=session-routing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-routing.js","sourceRoot":"","sources":["../../../src/monitor/session-routing.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,4 @@
1
- /**
2
- * Pure helper for settings mirror synchronization.
3
- *
4
- * Detects ownerShip and pendingNudge transitions from the settings mirror.
5
- * The scheduler no longer routes through this helper for lastNudgeStage —
6
- * the authoritative stage lives in a fresh scry plus a local shadow.
7
- */
8
- import { normalizeShip } from "../targets.js";
1
+ import { normalizeShip } from '../targets.js';
9
2
  export function resolveSettingsMirrorSync(params) {
10
3
  const { prevSettings, newSettings, fileConfigOwnerShip } = params;
11
4
  // ownerShip: compare string values (normalize for comparison)
@@ -1 +1 @@
1
- {"version":3,"file":"settings-sync.js","sourceRoot":"","sources":["../../../src/monitor/settings-sync.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAS9C,MAAM,UAAU,yBAAyB,CAAC,MAIzC;IACC,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,mBAAmB,EAAE,GAAG,MAAM,CAAC;IAElE,8DAA8D;IAC9D,MAAM,SAAS,GAAG,YAAY,CAAC,SAAS,CAAC;IACzC,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,CAAC;IACvC,MAAM,mBAAmB,GAAG,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACxE,MAAM,kBAAkB,GAAG,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,MAAM,gBAAgB,GAAG,mBAAmB,KAAK,kBAAkB,CAAC;IACpE,MAAM,kBAAkB,GAAkB,kBAAkB,IAAI,mBAAmB,CAAC;IAEpF,iFAAiF;IACjF,MAAM,mBAAmB,GAAG,YAAY,CAAC,YAAY,KAAK,WAAW,CAAC,YAAY,CAAC;IACnF,MAAM,YAAY,GAAwB,WAAW,CAAC,YAAY,IAAI,IAAI,CAAC;IAE3E,OAAO;QACL,gBAAgB;QAChB,kBAAkB;QAClB,mBAAmB;QACnB,YAAY;KACb,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"settings-sync.js","sourceRoot":"","sources":["../../../src/monitor/settings-sync.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAS9C,MAAM,UAAU,yBAAyB,CAAC,MAIzC;IACC,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,mBAAmB,EAAE,GAAG,MAAM,CAAC;IAElE,8DAA8D;IAC9D,MAAM,SAAS,GAAG,YAAY,CAAC,SAAS,CAAC;IACzC,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,CAAC;IACvC,MAAM,mBAAmB,GAAG,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACxE,MAAM,kBAAkB,GAAG,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,MAAM,gBAAgB,GAAG,mBAAmB,KAAK,kBAAkB,CAAC;IACpE,MAAM,kBAAkB,GACtB,kBAAkB,IAAI,mBAAmB,CAAC;IAE5C,iFAAiF;IACjF,MAAM,mBAAmB,GACvB,YAAY,CAAC,YAAY,KAAK,WAAW,CAAC,YAAY,CAAC;IACzD,MAAM,YAAY,GAAwB,WAAW,CAAC,YAAY,IAAI,IAAI,CAAC;IAE3E,OAAO;QACL,gBAAgB;QAChB,kBAAkB;QAClB,mBAAmB;QACnB,YAAY;KACb,CAAC;AACJ,CAAC"}
@@ -1,4 +1,4 @@
1
- import { normalizeShip } from "../targets.js";
1
+ import { normalizeShip } from '../targets.js';
2
2
  // Extract all cites from message content
3
3
  export function extractCites(content) {
4
4
  if (!content || !Array.isArray(content)) {
@@ -6,32 +6,32 @@ export function extractCites(content) {
6
6
  }
7
7
  const cites = [];
8
8
  for (const verse of content) {
9
- if (verse?.block?.cite && typeof verse.block.cite === "object") {
9
+ if (verse?.block?.cite && typeof verse.block.cite === 'object') {
10
10
  const cite = verse.block.cite;
11
- if (cite.chan && typeof cite.chan === "object") {
11
+ if (cite.chan && typeof cite.chan === 'object') {
12
12
  const { nest, where } = cite.chan;
13
13
  const whereMatch = where?.match(/\/msg\/(~[a-z-]+)\/(.+)/);
14
14
  cites.push({
15
- type: "chan",
15
+ type: 'chan',
16
16
  nest,
17
17
  where,
18
18
  author: whereMatch?.[1],
19
19
  postId: whereMatch?.[2],
20
20
  });
21
21
  }
22
- else if (cite.group && typeof cite.group === "string") {
23
- cites.push({ type: "group", group: cite.group });
22
+ else if (cite.group && typeof cite.group === 'string') {
23
+ cites.push({ type: 'group', group: cite.group });
24
24
  }
25
- else if (cite.desk && typeof cite.desk === "object") {
25
+ else if (cite.desk && typeof cite.desk === 'object') {
26
26
  cites.push({
27
- type: "desk",
27
+ type: 'desk',
28
28
  flag: cite.desk.flag,
29
29
  where: cite.desk.where,
30
30
  });
31
31
  }
32
- else if (cite.bait && typeof cite.bait === "object") {
32
+ else if (cite.bait && typeof cite.bait === 'object') {
33
33
  cites.push({
34
- type: "bait",
34
+ type: 'bait',
35
35
  group: cite.bait.group,
36
36
  nest: cite.bait.graph,
37
37
  where: cite.bait.where,
@@ -43,27 +43,29 @@ export function extractCites(content) {
43
43
  }
44
44
  export function formatModelName(modelString) {
45
45
  if (!modelString) {
46
- return "AI";
46
+ return 'AI';
47
47
  }
48
- const modelName = modelString.includes("/") ? modelString.split("/")[1] : modelString;
48
+ const modelName = modelString.includes('/')
49
+ ? modelString.split('/')[1]
50
+ : modelString;
49
51
  const modelMappings = {
50
- "claude-opus-4-5": "Claude Opus 4.5",
51
- "claude-sonnet-4-5": "Claude Sonnet 4.5",
52
- "claude-sonnet-3-5": "Claude Sonnet 3.5",
53
- "gpt-4o": "GPT-4o",
54
- "gpt-4-turbo": "GPT-4 Turbo",
55
- "gpt-4": "GPT-4",
56
- "gemini-2.0-flash": "Gemini 2.0 Flash",
57
- "gemini-pro": "Gemini Pro",
52
+ 'claude-opus-4-5': 'Claude Opus 4.5',
53
+ 'claude-sonnet-4-5': 'Claude Sonnet 4.5',
54
+ 'claude-sonnet-3-5': 'Claude Sonnet 3.5',
55
+ 'gpt-4o': 'GPT-4o',
56
+ 'gpt-4-turbo': 'GPT-4 Turbo',
57
+ 'gpt-4': 'GPT-4',
58
+ 'gemini-2.0-flash': 'Gemini 2.0 Flash',
59
+ 'gemini-pro': 'Gemini Pro',
58
60
  };
59
61
  if (modelMappings[modelName]) {
60
62
  return modelMappings[modelName];
61
63
  }
62
64
  return modelName
63
- .replace(/-/g, " ")
64
- .split(" ")
65
+ .replace(/-/g, ' ')
66
+ .split(' ')
65
67
  .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
66
- .join(" ");
68
+ .join(' ');
67
69
  }
68
70
  export function isBotMentioned(messageText, botShipName, nickname) {
69
71
  if (!messageText || !botShipName) {
@@ -71,15 +73,15 @@ export function isBotMentioned(messageText, botShipName, nickname) {
71
73
  }
72
74
  // Check for ship mention
73
75
  const normalizedBotShip = normalizeShip(botShipName);
74
- const escapedShip = normalizedBotShip.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
75
- const mentionPattern = new RegExp(`(^|\\s)${escapedShip}(?=\\s|$)`, "i");
76
+ const escapedShip = normalizedBotShip.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
77
+ const mentionPattern = new RegExp(`(^|\\s)${escapedShip}(?=\\s|$)`, 'i');
76
78
  if (mentionPattern.test(messageText)) {
77
79
  return true;
78
80
  }
79
81
  // Check for nickname mention (case-insensitive, word boundary)
80
82
  if (nickname) {
81
- const escapedNickname = nickname.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
82
- const nicknamePattern = new RegExp(`(^|\\s)${escapedNickname}(?=\\s|$|[,!?.])`, "i");
83
+ const escapedNickname = nickname.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
84
+ const nicknamePattern = new RegExp(`(^|\\s)${escapedNickname}(?=\\s|$|[,!?.])`, 'i');
83
85
  if (nicknamePattern.test(messageText)) {
84
86
  return true;
85
87
  }
@@ -98,25 +100,25 @@ export function isBotMentioned(messageText, botShipName, nickname) {
98
100
  */
99
101
  export function shouldEngageInGroup(opts) {
100
102
  if (opts.mentioned) {
101
- return { engage: true, reason: "mention" };
103
+ return { engage: true, reason: 'mention' };
102
104
  }
103
105
  if (opts.inParticipatedThread) {
104
- return { engage: true, reason: "thread" };
106
+ return { engage: true, reason: 'thread' };
105
107
  }
106
108
  if (opts.isOwnerBlob) {
107
- return { engage: true, reason: "owner-blob" };
109
+ return { engage: true, reason: 'owner-blob' };
108
110
  }
109
111
  if (!opts.ownerListenEnabled) {
110
- return { engage: false, reason: "skip" };
112
+ return { engage: false, reason: 'skip' };
111
113
  }
112
114
  const isOwner = opts.ownerShip !== null && opts.senderShip === opts.ownerShip;
113
115
  const isOwned = opts.groupHost !== null &&
114
116
  (opts.groupHost === opts.ownerShip || opts.groupHost === opts.botShipName);
115
117
  const disabled = opts.ownerListenDisabledChannels.has(opts.channelNest);
116
118
  if (isOwner && isOwned && !disabled) {
117
- return { engage: true, reason: "owner-owned" };
119
+ return { engage: true, reason: 'owner-owned' };
118
120
  }
119
- return { engage: false, reason: "skip" };
121
+ return { engage: false, reason: 'skip' };
120
122
  }
121
123
  /** Parse "tlon:group:<nest>" → "<nest>", else null. */
122
124
  export function nestFromCtxFrom(from) {
@@ -138,14 +140,16 @@ export function stripBotMention(messageText, botShipName) {
138
140
  if (!messageText || !botShipName) {
139
141
  return messageText;
140
142
  }
141
- return messageText.replace(normalizeShip(botShipName), "").trim();
143
+ return messageText.replace(normalizeShip(botShipName), '').trim();
142
144
  }
143
145
  export function isDmAllowed(senderShip, allowlist) {
144
146
  if (!allowlist || allowlist.length === 0) {
145
147
  return false;
146
148
  }
147
149
  const normalizedSender = normalizeShip(senderShip);
148
- return allowlist.map((ship) => normalizeShip(ship)).some((ship) => ship === normalizedSender);
150
+ return allowlist
151
+ .map((ship) => normalizeShip(ship))
152
+ .some((ship) => ship === normalizedSender);
149
153
  }
150
154
  /**
151
155
  * Check if a group invite from a ship should be auto-accepted.
@@ -160,24 +164,26 @@ export function isGroupInviteAllowed(inviterShip, allowlist) {
160
164
  return false;
161
165
  }
162
166
  const normalizedInviter = normalizeShip(inviterShip);
163
- return allowlist.map((ship) => normalizeShip(ship)).some((ship) => ship === normalizedInviter);
167
+ return allowlist
168
+ .map((ship) => normalizeShip(ship))
169
+ .some((ship) => ship === normalizedInviter);
164
170
  }
165
171
  // Helper to recursively extract text from inline content
166
172
  function extractInlineText(items) {
167
173
  return items
168
174
  .map((item) => {
169
- if (typeof item === "string") {
175
+ if (typeof item === 'string') {
170
176
  return item;
171
177
  }
172
- if (item && typeof item === "object") {
178
+ if (item && typeof item === 'object') {
173
179
  if (item.ship) {
174
180
  return item.ship;
175
181
  }
176
- if ("sect" in item) {
177
- return `@${item.sect || "all"}`;
182
+ if ('sect' in item) {
183
+ return `@${item.sect || 'all'}`;
178
184
  }
179
- if (item["inline-code"]) {
180
- return `\`${item["inline-code"]}\``;
185
+ if (item['inline-code']) {
186
+ return `\`${item['inline-code']}\``;
181
187
  }
182
188
  if (item.code) {
183
189
  return `\`${item.code}\``;
@@ -195,13 +201,13 @@ function extractInlineText(items) {
195
201
  return `~~${extractInlineText(item.strike)}~~`;
196
202
  }
197
203
  }
198
- return "";
204
+ return '';
199
205
  })
200
- .join("");
206
+ .join('');
201
207
  }
202
208
  export function extractMessageText(content) {
203
209
  if (!content || !Array.isArray(content)) {
204
- return "";
210
+ return '';
205
211
  }
206
212
  return content
207
213
  .map((verse) => {
@@ -209,26 +215,26 @@ export function extractMessageText(content) {
209
215
  if (verse.inline && Array.isArray(verse.inline)) {
210
216
  return verse.inline
211
217
  .map((item) => {
212
- if (typeof item === "string") {
218
+ if (typeof item === 'string') {
213
219
  return item;
214
220
  }
215
- if (item && typeof item === "object") {
221
+ if (item && typeof item === 'object') {
216
222
  if (item.ship) {
217
223
  return item.ship;
218
224
  }
219
225
  // Handle sect (role mentions like @all)
220
- if ("sect" in item) {
221
- return `@${item.sect || "all"}`;
226
+ if ('sect' in item) {
227
+ return `@${item.sect || 'all'}`;
222
228
  }
223
229
  if (item.break !== undefined) {
224
- return "\n";
230
+ return '\n';
225
231
  }
226
232
  if (item.link && item.link.href) {
227
233
  return item.link.href;
228
234
  }
229
235
  // Handle inline code (Tlon uses "inline-code" key)
230
- if (item["inline-code"]) {
231
- return `\`${item["inline-code"]}\``;
236
+ if (item['inline-code']) {
237
+ return `\`${item['inline-code']}\``;
232
238
  }
233
239
  if (item.code) {
234
240
  return `\`${item.code}\``;
@@ -248,36 +254,36 @@ export function extractMessageText(content) {
248
254
  return `> ${extractInlineText(item.blockquote)}`;
249
255
  }
250
256
  }
251
- return "";
257
+ return '';
252
258
  })
253
- .join("");
259
+ .join('');
254
260
  }
255
261
  // Handle block content (images, code blocks, etc.)
256
- if (verse.block && typeof verse.block === "object") {
262
+ if (verse.block && typeof verse.block === 'object') {
257
263
  const block = verse.block;
258
264
  // Image blocks
259
265
  if (block.image && block.image.src) {
260
- const alt = block.image.alt ? ` (${block.image.alt})` : "";
266
+ const alt = block.image.alt ? ` (${block.image.alt})` : '';
261
267
  return `\n${block.image.src}${alt}\n`;
262
268
  }
263
269
  // Code blocks
264
- if (block.code && typeof block.code === "object") {
265
- const lang = block.code.lang || "";
266
- const code = block.code.code || "";
270
+ if (block.code && typeof block.code === 'object') {
271
+ const lang = block.code.lang || '';
272
+ const code = block.code.code || '';
267
273
  return `\n\`\`\`${lang}\n${code}\n\`\`\`\n`;
268
274
  }
269
275
  // Header blocks
270
- if (block.header && typeof block.header === "object") {
276
+ if (block.header && typeof block.header === 'object') {
271
277
  const text = block.header.content
272
- ?.map((item) => (typeof item === "string" ? item : ""))
273
- .join("") || "";
278
+ ?.map((item) => (typeof item === 'string' ? item : ''))
279
+ .join('') || '';
274
280
  return `\n## ${text}\n`;
275
281
  }
276
282
  // Cite/quote blocks - parse the reference structure
277
- if (block.cite && typeof block.cite === "object") {
283
+ if (block.cite && typeof block.cite === 'object') {
278
284
  const cite = block.cite;
279
285
  // ChanCite - reference to a channel message
280
- if (cite.chan && typeof cite.chan === "object") {
286
+ if (cite.chan && typeof cite.chan === 'object') {
281
287
  const { nest, where } = cite.chan;
282
288
  // where is typically /msg/~author/timestamp
283
289
  const whereMatch = where?.match(/\/msg\/(~[a-z-]+)\/(.+)/);
@@ -288,23 +294,23 @@ export function extractMessageText(content) {
288
294
  return `\n> [quoted from ${nest}]\n`;
289
295
  }
290
296
  // GroupCite - reference to a group
291
- if (cite.group && typeof cite.group === "string") {
297
+ if (cite.group && typeof cite.group === 'string') {
292
298
  return `\n> [ref: group ${cite.group}]\n`;
293
299
  }
294
300
  // DeskCite - reference to an app/desk
295
- if (cite.desk && typeof cite.desk === "object") {
301
+ if (cite.desk && typeof cite.desk === 'object') {
296
302
  return `\n> [ref: ${cite.desk.flag}]\n`;
297
303
  }
298
304
  // BaitCite - reference with group+graph context
299
- if (cite.bait && typeof cite.bait === "object") {
305
+ if (cite.bait && typeof cite.bait === 'object') {
300
306
  return `\n> [ref: ${cite.bait.graph} in ${cite.bait.group}]\n`;
301
307
  }
302
308
  return `\n> [quoted message]\n`;
303
309
  }
304
310
  }
305
- return "";
311
+ return '';
306
312
  })
307
- .join("\n")
313
+ .join('\n')
308
314
  .trim();
309
315
  }
310
316
  export function isSummarizationRequest(messageText) {
@@ -342,9 +348,9 @@ export function sanitizeMessageText(text) {
342
348
  return text;
343
349
  }
344
350
  // Strip [BLOCK_USER: ~ship | reason] directives entirely
345
- let sanitized = text.replace(/\[BLOCK_USER:\s*~[\w-]+\s*\|\s*.+?\]/gi, "");
351
+ let sanitized = text.replace(/\[BLOCK_USER:\s*~[\w-]+\s*\|\s*.+?\]/gi, '');
346
352
  // Convert role tags from [brackets] to (parentheses)
347
- sanitized = sanitized.replace(/\[(owner|user|admin|system)\]/gi, "($1)");
353
+ sanitized = sanitized.replace(/\[(owner|user|admin|system)\]/gi, '($1)');
348
354
  return sanitized;
349
355
  }
350
356
  //# sourceMappingURL=utils.js.map