@tloncorp/openclaw 0.6.0 → 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.
@@ -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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"}
@@ -0,0 +1,44 @@
1
+ import { buildChannelOutboundSessionRoute, } from 'openclaw/plugin-sdk/core';
2
+ import { parseTlonTarget } from './targets.js';
3
+ /**
4
+ * Channel-native outbound session-route builder for Tlon.
5
+ *
6
+ * Lets explicit outbound sends to a Tlon target (e.g. the shared `message` tool
7
+ * with `to: tlon:~ship`) derive a stable Tlon session route, so they resolve
8
+ * to Tlon even when the current turn's surface is something else. Returns
9
+ * `null` for non-Tlon / unparseable targets so core can fall through.
10
+ */
11
+ export function resolveTlonOutboundSessionRoute(params) {
12
+ const parsed = parseTlonTarget(params.target);
13
+ if (!parsed) {
14
+ return null;
15
+ }
16
+ const threadId = params.threadId != null && params.threadId !== ''
17
+ ? params.threadId
18
+ : undefined;
19
+ if (parsed.kind === 'dm') {
20
+ return buildChannelOutboundSessionRoute({
21
+ cfg: params.cfg,
22
+ agentId: params.agentId,
23
+ channel: 'tlon',
24
+ accountId: params.accountId,
25
+ peer: { kind: 'direct', id: parsed.ship },
26
+ chatType: 'direct',
27
+ from: `tlon:${parsed.ship}`,
28
+ to: `tlon:${parsed.ship}`,
29
+ ...(threadId !== undefined ? { threadId } : {}),
30
+ });
31
+ }
32
+ return buildChannelOutboundSessionRoute({
33
+ cfg: params.cfg,
34
+ agentId: params.agentId,
35
+ channel: 'tlon',
36
+ accountId: params.accountId,
37
+ peer: { kind: 'group', id: parsed.nest },
38
+ chatType: 'group',
39
+ from: `tlon:group:${parsed.nest}`,
40
+ to: `tlon:${parsed.nest}`,
41
+ ...(threadId !== undefined ? { threadId } : {}),
42
+ });
43
+ }
44
+ //# sourceMappingURL=session-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-route.js","sourceRoot":"","sources":["../../src/session-route.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,gCAAgC,GACjC,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,UAAU,+BAA+B,CAC7C,MAAyC;IAEzC,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GACZ,MAAM,CAAC,QAAQ,IAAI,IAAI,IAAI,MAAM,CAAC,QAAQ,KAAK,EAAE;QAC/C,CAAC,CAAC,MAAM,CAAC,QAAQ;QACjB,CAAC,CAAC,SAAS,CAAC;IAEhB,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACzB,OAAO,gCAAgC,CAAC;YACtC,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,OAAO,EAAE,MAAM;YACf,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE;YACzC,QAAQ,EAAE,QAAQ;YAClB,IAAI,EAAE,QAAQ,MAAM,CAAC,IAAI,EAAE;YAC3B,EAAE,EAAE,QAAQ,MAAM,CAAC,IAAI,EAAE;YACzB,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChD,CAAC,CAAC;IACL,CAAC;IAED,OAAO,gCAAgC,CAAC;QACtC,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE;QACxC,QAAQ,EAAE,OAAO;QACjB,IAAI,EAAE,cAAc,MAAM,CAAC,IAAI,EAAE;QACjC,EAAE,EAAE,QAAQ,MAAM,CAAC,IAAI,EAAE;QACzB,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChD,CAAC,CAAC;AACL,CAAC"}