@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.
- package/README.md +130 -141
- package/dist/index.js +703 -152
- package/dist/index.js.map +1 -1
- package/dist/setup-api.js +2 -2
- package/dist/setup-entry.js +2 -2
- package/dist/setup-entry.js.map +1 -1
- package/dist/src/account-fields.js +7 -3
- package/dist/src/account-fields.js.map +1 -1
- package/dist/src/actions.js +73 -52
- package/dist/src/actions.js.map +1 -1
- package/dist/src/channel.js +63 -39
- package/dist/src/channel.js.map +1 -1
- package/dist/src/channel.runtime.js +61 -32
- package/dist/src/channel.runtime.js.map +1 -1
- package/dist/src/config-schema.js +24 -4
- package/dist/src/config-schema.js.map +1 -1
- package/dist/src/diagnostic-subscriptions.js +49 -0
- package/dist/src/diagnostic-subscriptions.js.map +1 -0
- package/dist/src/effective-owner.js.map +1 -1
- package/dist/src/gateway-status.js +55 -7
- package/dist/src/gateway-status.js.map +1 -1
- package/dist/src/monitor/approval.js +71 -62
- package/dist/src/monitor/approval.js.map +1 -1
- package/dist/src/monitor/command-auth.js +7 -7
- package/dist/src/monitor/command-auth.js.map +1 -1
- package/dist/src/monitor/command-bridge.js +3 -2
- package/dist/src/monitor/command-bridge.js.map +1 -1
- package/dist/src/monitor/computing-presence.js +76 -12
- package/dist/src/monitor/computing-presence.js.map +1 -1
- package/dist/src/monitor/discovery.js +16 -9
- package/dist/src/monitor/discovery.js.map +1 -1
- package/dist/src/monitor/history.js +58 -26
- package/dist/src/monitor/history.js.map +1 -1
- package/dist/src/monitor/index.js +3018 -2496
- package/dist/src/monitor/index.js.map +1 -1
- package/dist/src/monitor/media.js +106 -78
- package/dist/src/monitor/media.js.map +1 -1
- package/dist/src/monitor/nudge-runner.js +36 -27
- package/dist/src/monitor/nudge-runner.js.map +1 -1
- package/dist/src/monitor/nudge-state.js +7 -11
- package/dist/src/monitor/nudge-state.js.map +1 -1
- package/dist/src/monitor/owner-reply-persistence.js +27 -26
- package/dist/src/monitor/owner-reply-persistence.js.map +1 -1
- package/dist/src/monitor/processed-messages.js.map +1 -1
- package/dist/src/monitor/session-routing.js +261 -0
- package/dist/src/monitor/session-routing.js.map +1 -0
- package/dist/src/monitor/settings-sync.js +1 -8
- package/dist/src/monitor/settings-sync.js.map +1 -1
- package/dist/src/monitor/utils.js +77 -71
- package/dist/src/monitor/utils.js.map +1 -1
- package/dist/src/nudge-decision.js +40 -43
- package/dist/src/nudge-decision.js.map +1 -1
- package/dist/src/nudge-messages.js +9 -9
- package/dist/src/nudge-scheduler.js.map +1 -1
- package/dist/src/owner-listen-command.js +38 -28
- package/dist/src/owner-listen-command.js.map +1 -1
- package/dist/src/pending-nudge.js.map +1 -1
- package/dist/src/runtime.js +10 -6
- package/dist/src/runtime.js.map +1 -1
- package/dist/src/session-roles.js +2 -1
- package/dist/src/session-roles.js.map +1 -1
- package/dist/src/session-route.js +44 -0
- package/dist/src/session-route.js.map +1 -0
- package/dist/src/settings.js +233 -102
- package/dist/src/settings.js.map +1 -1
- package/dist/src/setup-core.js +32 -32
- package/dist/src/setup-core.js.map +1 -1
- package/dist/src/setup-surface.js +19 -19
- package/dist/src/setup-surface.js.map +1 -1
- package/dist/src/shared-state.js +46 -0
- package/dist/src/shared-state.js.map +1 -0
- package/dist/src/targets.js +17 -10
- package/dist/src/targets.js.map +1 -1
- package/dist/src/telemetry.js +764 -34
- package/dist/src/telemetry.js.map +1 -1
- package/dist/src/tlon-binary.js +20 -12
- package/dist/src/tlon-binary.js.map +1 -1
- package/dist/src/tlon-tool-guard.js +5 -5
- package/dist/src/tool-trace.js +17 -13
- package/dist/src/tool-trace.js.map +1 -1
- package/dist/src/types.js +30 -12
- package/dist/src/types.js.map +1 -1
- package/dist/src/urbit/api-client.js +16 -12
- package/dist/src/urbit/api-client.js.map +1 -1
- package/dist/src/urbit/auth.js +9 -9
- package/dist/src/urbit/auth.js.map +1 -1
- package/dist/src/urbit/base-url.js +11 -11
- package/dist/src/urbit/base-url.js.map +1 -1
- package/dist/src/urbit/channel-ops.js +25 -19
- package/dist/src/urbit/channel-ops.js.map +1 -1
- package/dist/src/urbit/context.js +8 -8
- package/dist/src/urbit/context.js.map +1 -1
- package/dist/src/urbit/errors.js +33 -7
- package/dist/src/urbit/errors.js.map +1 -1
- package/dist/src/urbit/fetch.js +3 -3
- package/dist/src/urbit/fetch.js.map +1 -1
- package/dist/src/urbit/http-poke.js +10 -10
- package/dist/src/urbit/http-poke.js.map +1 -1
- package/dist/src/urbit/send.js +27 -23
- package/dist/src/urbit/send.js.map +1 -1
- package/dist/src/urbit/sse-client.js +45 -41
- package/dist/src/urbit/sse-client.js.map +1 -1
- package/dist/src/urbit/story.js +31 -30
- package/dist/src/urbit/story.js.map +1 -1
- package/dist/src/urbit/upload.js +8 -8
- package/dist/src/urbit/upload.js.map +1 -1
- package/dist/src/version.generated.js +2 -1
- package/dist/src/version.generated.js.map +1 -1
- package/dist/src/version.js +134 -0
- package/dist/src/version.js.map +1 -0
- package/openclaw.plugin.json +37 -0
- package/package.json +9 -15
package/dist/src/telemetry.js
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
|
-
import { PostHog } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const
|
|
1
|
+
import { PostHog } from 'posthog-node';
|
|
2
|
+
import { sharedMap, sharedSlot } from './shared-state.js';
|
|
3
|
+
import { getTlonVersionIdentity } from './version.js';
|
|
4
|
+
const TLON_TELEMETRY_EVENT_NAME = 'TlonBot Reply Handled';
|
|
5
|
+
const TLON_GATEWAY_CONNECTED_EVENT = 'TlonBot Gateway Connected';
|
|
6
|
+
const TLON_OUTBOUND_ROUTED_EVENT = 'TlonBot Outbound Routed';
|
|
7
|
+
const TLON_SESSION_LIFECYCLE_EVENT = 'TlonBot Session Lifecycle';
|
|
8
|
+
const TLON_SESSION_WATCHDOG_EVENT = 'TlonBot Session Watchdog';
|
|
9
|
+
const TLON_SESSION_RECOVERY_EVENT = 'TlonBot Session Recovery';
|
|
10
|
+
const TLON_HARNESS_ERROR_EVENT = 'TlonBot Harness Error';
|
|
11
|
+
const TLON_PLUGIN_ERROR_EVENT = 'TlonBot Plugin Error';
|
|
12
|
+
const TLON_TELEMETRY_ERROR_EVENT = 'TlonBot Telemetry Error';
|
|
13
|
+
const TLON_HEARTBEAT_NUDGE_EVENT = 'TlonBot Heartbeat Nudge Sent';
|
|
14
|
+
const TLON_HEARTBEAT_REENGAGED_EVENT = 'TlonBot Heartbeat Nudge Reengaged';
|
|
15
|
+
const TLON_TELEMETRY_LOG_SOURCE = 'openclawPlugin';
|
|
6
16
|
const TOOL_TRACE_TTL_MS = 60 * 60 * 1000;
|
|
7
17
|
const MAX_TOOL_CALLS_PER_SESSION = 200;
|
|
8
|
-
const
|
|
18
|
+
const REPLY_TRACE_TTL_MS = 60 * 60 * 1000;
|
|
19
|
+
const MAX_ACTIVE_REPLY_TRACES = 50;
|
|
20
|
+
const SESSION_CONTEXT_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
21
|
+
const MAX_SESSION_CONTEXTS = 5_000;
|
|
22
|
+
const toolCallsBySession = sharedMap('telemetry.toolCallsBySession');
|
|
23
|
+
const sessionContextsBySessionKey = sharedMap('telemetry.sessionContextsBySessionKey');
|
|
9
24
|
function cleanupToolCalls(now = Date.now()) {
|
|
10
25
|
for (const [sessionKey, trace] of toolCallsBySession) {
|
|
11
26
|
if (now - trace.updatedAt > TOOL_TRACE_TTL_MS) {
|
|
@@ -28,7 +43,7 @@ export function recordToolCall(params) {
|
|
|
28
43
|
trace.updatedAt = now;
|
|
29
44
|
trace.calls.push({
|
|
30
45
|
toolName: params.toolName,
|
|
31
|
-
durationMs: typeof params.durationMs ===
|
|
46
|
+
durationMs: typeof params.durationMs === 'number' ? params.durationMs : null,
|
|
32
47
|
error: params.error ?? null,
|
|
33
48
|
recordedAt: now,
|
|
34
49
|
});
|
|
@@ -58,16 +73,141 @@ function collectToolUsageSince(sessionKey, cursor) {
|
|
|
58
73
|
errorCount: calls.filter((call) => call.error).length,
|
|
59
74
|
};
|
|
60
75
|
}
|
|
76
|
+
function cleanupSessionContexts(now = Date.now()) {
|
|
77
|
+
for (const [sessionKey, context] of sessionContextsBySessionKey) {
|
|
78
|
+
if (now - context.updatedAt > SESSION_CONTEXT_TTL_MS) {
|
|
79
|
+
sessionContextsBySessionKey.delete(sessionKey);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
while (sessionContextsBySessionKey.size > MAX_SESSION_CONTEXTS) {
|
|
83
|
+
const oldestKey = [...sessionContextsBySessionKey.entries()].sort((a, b) => a[1].updatedAt - b[1].updatedAt)[0]?.[0];
|
|
84
|
+
if (!oldestKey) {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
sessionContextsBySessionKey.delete(oldestKey);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function rememberTlonSessionContext(params) {
|
|
91
|
+
const sessionKey = params.sessionKey.trim();
|
|
92
|
+
if (!sessionKey) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const now = Date.now();
|
|
96
|
+
cleanupSessionContexts(now);
|
|
97
|
+
const existing = sessionContextsBySessionKey.get(sessionKey);
|
|
98
|
+
sessionContextsBySessionKey.set(sessionKey, {
|
|
99
|
+
sessionKey,
|
|
100
|
+
sessionId: optionalString(params.sessionId) ?? existing?.sessionId ?? null,
|
|
101
|
+
runId: optionalString(params.runId) ?? existing?.runId ?? null,
|
|
102
|
+
ownerShip: params.ownerShip,
|
|
103
|
+
botShip: params.botShip,
|
|
104
|
+
accountId: params.accountId ?? null,
|
|
105
|
+
agentId: params.agentId ?? null,
|
|
106
|
+
destinationKind: params.destinationKind,
|
|
107
|
+
updatedAt: now,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function lookupTlonSessionContext(sessionKey) {
|
|
111
|
+
const normalized = sessionKey?.trim();
|
|
112
|
+
if (!normalized) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
cleanupSessionContexts();
|
|
116
|
+
const context = sessionContextsBySessionKey.get(normalized);
|
|
117
|
+
if (!context) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
context.updatedAt = Date.now();
|
|
121
|
+
sessionContextsBySessionKey.set(normalized, context);
|
|
122
|
+
return context;
|
|
123
|
+
}
|
|
124
|
+
function updateTlonSessionContextSessionId(context, sessionId) {
|
|
125
|
+
const normalizedSessionId = optionalString(sessionId);
|
|
126
|
+
if (!normalizedSessionId || context.sessionId === normalizedSessionId) {
|
|
127
|
+
return context;
|
|
128
|
+
}
|
|
129
|
+
const updated = {
|
|
130
|
+
...context,
|
|
131
|
+
sessionId: normalizedSessionId,
|
|
132
|
+
updatedAt: Date.now(),
|
|
133
|
+
};
|
|
134
|
+
sessionContextsBySessionKey.set(updated.sessionKey, updated);
|
|
135
|
+
return updated;
|
|
136
|
+
}
|
|
137
|
+
function updateTlonSessionContextRuntime(context, params) {
|
|
138
|
+
const normalizedSessionId = optionalString(params.sessionId);
|
|
139
|
+
const normalizedRunId = optionalString(params.runId);
|
|
140
|
+
const normalizedAgentId = optionalString(params.agentId);
|
|
141
|
+
if ((!normalizedSessionId || context.sessionId === normalizedSessionId) &&
|
|
142
|
+
(!normalizedRunId || context.runId === normalizedRunId) &&
|
|
143
|
+
(!normalizedAgentId || context.agentId === normalizedAgentId)) {
|
|
144
|
+
return context;
|
|
145
|
+
}
|
|
146
|
+
const updated = {
|
|
147
|
+
...context,
|
|
148
|
+
sessionId: normalizedSessionId ?? context.sessionId,
|
|
149
|
+
runId: normalizedRunId ?? context.runId,
|
|
150
|
+
agentId: normalizedAgentId ?? context.agentId,
|
|
151
|
+
updatedAt: Date.now(),
|
|
152
|
+
};
|
|
153
|
+
sessionContextsBySessionKey.set(updated.sessionKey, updated);
|
|
154
|
+
return updated;
|
|
155
|
+
}
|
|
156
|
+
function countDispatchFailures(counts, kind) {
|
|
157
|
+
const value = counts?.[kind];
|
|
158
|
+
return typeof value === 'number' && Number.isFinite(value)
|
|
159
|
+
? Math.max(0, value)
|
|
160
|
+
: 0;
|
|
161
|
+
}
|
|
162
|
+
function classifyError(error) {
|
|
163
|
+
if (!error) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
if (error instanceof Error) {
|
|
167
|
+
return error.name || 'Error';
|
|
168
|
+
}
|
|
169
|
+
if (typeof error === 'object' && error !== null) {
|
|
170
|
+
const code = error.code;
|
|
171
|
+
if (typeof code === 'string' && code.trim()) {
|
|
172
|
+
return code.trim().slice(0, 80);
|
|
173
|
+
}
|
|
174
|
+
const name = error.name;
|
|
175
|
+
if (typeof name === 'string' && name.trim()) {
|
|
176
|
+
return name.trim().slice(0, 80);
|
|
177
|
+
}
|
|
178
|
+
return 'object';
|
|
179
|
+
}
|
|
180
|
+
return typeof error;
|
|
181
|
+
}
|
|
61
182
|
function resolveReplyOutcome(params) {
|
|
62
183
|
if (params.deliveredMessageCount > 0) {
|
|
63
|
-
return
|
|
184
|
+
return 'responded';
|
|
64
185
|
}
|
|
65
|
-
|
|
186
|
+
const failedReplyCount = countDispatchFailures(params.failedCounts, 'tool') +
|
|
187
|
+
countDispatchFailures(params.failedCounts, 'block') +
|
|
188
|
+
countDispatchFailures(params.failedCounts, 'final');
|
|
189
|
+
return params.dispatchError || params.sendError || failedReplyCount > 0
|
|
190
|
+
? 'error'
|
|
191
|
+
: 'no_reply';
|
|
192
|
+
}
|
|
193
|
+
function resolveDeliverySkipReason(params) {
|
|
194
|
+
if (params.outcome !== 'no_reply') {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
if (params.deliverySkipReason) {
|
|
198
|
+
return params.deliverySkipReason;
|
|
199
|
+
}
|
|
200
|
+
if (params.sourceReplyDeliveryMode === 'message_tool_only') {
|
|
201
|
+
return 'source_reply_delivery_mode_message_tool_only';
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
66
204
|
}
|
|
67
205
|
class PostHogTlonTelemetry {
|
|
68
206
|
client;
|
|
69
207
|
runtime;
|
|
208
|
+
versionIdentity = getTlonVersionIdentity();
|
|
70
209
|
identifiedOwners = new Set();
|
|
210
|
+
activeReplyTraces = new Map();
|
|
71
211
|
missingOwnerWarningLogged = false;
|
|
72
212
|
constructor(params) {
|
|
73
213
|
this.runtime = params.runtime;
|
|
@@ -80,23 +220,109 @@ class PostHogTlonTelemetry {
|
|
|
80
220
|
disableRemoteConfig: true,
|
|
81
221
|
});
|
|
82
222
|
}
|
|
223
|
+
properties(props) {
|
|
224
|
+
return {
|
|
225
|
+
logSource: TLON_TELEMETRY_LOG_SOURCE,
|
|
226
|
+
...this.versionIdentity,
|
|
227
|
+
...props,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
captureGatewayConnected(event) {
|
|
231
|
+
const ownerShip = event.ownerShip ?? '';
|
|
232
|
+
if (!this.ensureIdentified(ownerShip, event.botShip)) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
this.client.capture({
|
|
236
|
+
distinctId: ownerShip,
|
|
237
|
+
event: TLON_GATEWAY_CONNECTED_EVENT,
|
|
238
|
+
properties: this.properties({
|
|
239
|
+
botShip: event.botShip,
|
|
240
|
+
ownerShip: event.ownerShip,
|
|
241
|
+
tlonSkillVersion: event.tlonSkillVersion,
|
|
242
|
+
accountId: event.accountId,
|
|
243
|
+
configured: event.configured,
|
|
244
|
+
watchedChannelCount: event.watchedChannelCount,
|
|
245
|
+
dmAllowlistCount: event.dmAllowlistCount,
|
|
246
|
+
defaultAuthorizedShipsCount: event.defaultAuthorizedShipsCount,
|
|
247
|
+
pendingApprovalCount: event.pendingApprovalCount,
|
|
248
|
+
autoDiscoverChannels: event.autoDiscoverChannels,
|
|
249
|
+
ownerListenEnabled: event.ownerListenEnabled,
|
|
250
|
+
}),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
83
253
|
startReply(params) {
|
|
254
|
+
const normalizedParams = {
|
|
255
|
+
...params,
|
|
256
|
+
sessionId: optionalString(params.sessionId),
|
|
257
|
+
runId: params.runId ?? null,
|
|
258
|
+
accountId: params.accountId ?? null,
|
|
259
|
+
agentId: params.agentId ?? null,
|
|
260
|
+
destinationKind: params.destinationKind ?? params.chatType,
|
|
261
|
+
};
|
|
84
262
|
const toolTraceCursor = createToolTraceCursor(params.sessionKey);
|
|
263
|
+
rememberTlonSessionContext({
|
|
264
|
+
sessionKey: normalizedParams.sessionKey,
|
|
265
|
+
sessionId: normalizedParams.sessionId,
|
|
266
|
+
runId: normalizedParams.runId,
|
|
267
|
+
ownerShip: normalizedParams.ownerShip,
|
|
268
|
+
botShip: normalizedParams.botShip,
|
|
269
|
+
accountId: normalizedParams.accountId,
|
|
270
|
+
agentId: normalizedParams.agentId,
|
|
271
|
+
destinationKind: normalizedParams.destinationKind,
|
|
272
|
+
});
|
|
273
|
+
this.pruneActiveReplyTraces();
|
|
274
|
+
const token = Symbol(normalizedParams.sessionKey);
|
|
275
|
+
const trace = {
|
|
276
|
+
params: normalizedParams,
|
|
277
|
+
startedAt: Date.now(),
|
|
278
|
+
toolTraceCursor,
|
|
279
|
+
timeout: setTimeout(() => {
|
|
280
|
+
this.captureAbandonedReplyTrace(token, 'stale');
|
|
281
|
+
}, REPLY_TRACE_TTL_MS),
|
|
282
|
+
};
|
|
283
|
+
trace.timeout.unref?.();
|
|
284
|
+
this.activeReplyTraces.set(token, trace);
|
|
285
|
+
this.pruneActiveReplyTraces();
|
|
85
286
|
return {
|
|
86
287
|
capture: async (result) => {
|
|
288
|
+
const activeTrace = this.activeReplyTraces.get(token);
|
|
289
|
+
if (!activeTrace) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
this.activeReplyTraces.delete(token);
|
|
293
|
+
clearTimeout(activeTrace.timeout);
|
|
87
294
|
// Yield once so after_tool_call hooks for the just-finished reply have time to run.
|
|
88
295
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
296
|
+
const failedToolCount = countDispatchFailures(result.failedCounts, 'tool');
|
|
297
|
+
const failedBlockCount = countDispatchFailures(result.failedCounts, 'block');
|
|
298
|
+
const failedFinalCount = countDispatchFailures(result.failedCounts, 'final');
|
|
299
|
+
const sendErrorCount = Math.max(0, result.sendErrorCount ?? 0);
|
|
300
|
+
const sessionContext = lookupTlonSessionContext(activeTrace.params.sessionKey);
|
|
301
|
+
const outcome = resolveReplyOutcome({
|
|
302
|
+
deliveredMessageCount: result.deliveredMessageCount,
|
|
303
|
+
sendError: sendErrorCount > 0,
|
|
304
|
+
failedCounts: result.failedCounts,
|
|
305
|
+
dispatchError: result.dispatchError,
|
|
306
|
+
});
|
|
307
|
+
const sourceReplyDeliveryMode = result.sourceReplyDeliveryMode ?? null;
|
|
89
308
|
this.captureReplyOutcome({
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
309
|
+
sessionKey: activeTrace.params.sessionKey,
|
|
310
|
+
sessionId: sessionContext?.sessionId ?? activeTrace.params.sessionId,
|
|
311
|
+
runId: activeTrace.params.runId,
|
|
312
|
+
accountId: activeTrace.params.accountId,
|
|
313
|
+
agentId: activeTrace.params.agentId,
|
|
314
|
+
ownerShip: activeTrace.params.ownerShip,
|
|
315
|
+
botShip: activeTrace.params.botShip,
|
|
316
|
+
outcome,
|
|
317
|
+
chatType: activeTrace.params.chatType,
|
|
318
|
+
destinationKind: activeTrace.params.destinationKind,
|
|
319
|
+
isThreadReply: activeTrace.params.isThreadReply,
|
|
320
|
+
senderRole: activeTrace.params.senderRole,
|
|
321
|
+
attachmentCount: activeTrace.params.attachmentCount,
|
|
322
|
+
sendAttemptCount: Math.max(0, result.sendAttemptCount ?? 0),
|
|
323
|
+
sendError: sendErrorCount > 0,
|
|
324
|
+
sendErrorCount,
|
|
325
|
+
sendErrorKind: result.sendErrorKind ?? null,
|
|
100
326
|
deliveredMessageCount: result.deliveredMessageCount,
|
|
101
327
|
replyCharCount: result.replyCharCount,
|
|
102
328
|
replyWordCount: result.replyWordCount,
|
|
@@ -105,32 +331,119 @@ class PostHogTlonTelemetry {
|
|
|
105
331
|
queuedFinal: result.queuedFinal,
|
|
106
332
|
queuedFinalCount: result.queuedFinalCount,
|
|
107
333
|
queuedBlockCount: result.queuedBlockCount,
|
|
334
|
+
failedToolCount,
|
|
335
|
+
failedBlockCount,
|
|
336
|
+
failedFinalCount,
|
|
337
|
+
failedReplyCount: failedToolCount + failedBlockCount + failedFinalCount,
|
|
338
|
+
deliverySkipReason: resolveDeliverySkipReason({
|
|
339
|
+
outcome,
|
|
340
|
+
deliverySkipReason: result.deliverySkipReason,
|
|
341
|
+
sourceReplyDeliveryMode,
|
|
342
|
+
}),
|
|
343
|
+
sourceReplyDeliveryMode,
|
|
344
|
+
beforeAgentRunBlocked: result.beforeAgentRunBlocked === true,
|
|
345
|
+
dispatchError: Boolean(result.dispatchError),
|
|
346
|
+
dispatchErrorKind: classifyError(result.dispatchError),
|
|
347
|
+
abandonedReason: null,
|
|
108
348
|
provider: result.provider,
|
|
109
349
|
model: result.model,
|
|
110
350
|
thinkLevel: result.thinkLevel,
|
|
111
|
-
toolUsage: collectToolUsageSince(params.sessionKey, toolTraceCursor),
|
|
351
|
+
toolUsage: collectToolUsageSince(activeTrace.params.sessionKey, activeTrace.toolTraceCursor),
|
|
112
352
|
});
|
|
113
353
|
},
|
|
114
354
|
};
|
|
115
355
|
}
|
|
356
|
+
captureAbandonedReplyTrace(token, reason) {
|
|
357
|
+
const trace = this.activeReplyTraces.get(token);
|
|
358
|
+
if (!trace) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
this.activeReplyTraces.delete(token);
|
|
362
|
+
clearTimeout(trace.timeout);
|
|
363
|
+
const sessionContext = lookupTlonSessionContext(trace.params.sessionKey);
|
|
364
|
+
this.captureReplyOutcome({
|
|
365
|
+
sessionKey: trace.params.sessionKey,
|
|
366
|
+
sessionId: sessionContext?.sessionId ?? trace.params.sessionId,
|
|
367
|
+
runId: trace.params.runId,
|
|
368
|
+
accountId: trace.params.accountId,
|
|
369
|
+
agentId: trace.params.agentId,
|
|
370
|
+
ownerShip: trace.params.ownerShip,
|
|
371
|
+
botShip: trace.params.botShip,
|
|
372
|
+
outcome: 'abandoned',
|
|
373
|
+
chatType: trace.params.chatType,
|
|
374
|
+
destinationKind: trace.params.destinationKind,
|
|
375
|
+
isThreadReply: trace.params.isThreadReply,
|
|
376
|
+
senderRole: trace.params.senderRole,
|
|
377
|
+
attachmentCount: trace.params.attachmentCount,
|
|
378
|
+
sendAttemptCount: 0,
|
|
379
|
+
sendError: false,
|
|
380
|
+
sendErrorCount: 0,
|
|
381
|
+
sendErrorKind: null,
|
|
382
|
+
deliveredMessageCount: 0,
|
|
383
|
+
replyCharCount: 0,
|
|
384
|
+
replyWordCount: 0,
|
|
385
|
+
replyMediaCount: 0,
|
|
386
|
+
dispatchDurationMs: Date.now() - trace.startedAt,
|
|
387
|
+
queuedFinal: false,
|
|
388
|
+
queuedFinalCount: 0,
|
|
389
|
+
queuedBlockCount: 0,
|
|
390
|
+
failedToolCount: 0,
|
|
391
|
+
failedBlockCount: 0,
|
|
392
|
+
failedFinalCount: 0,
|
|
393
|
+
failedReplyCount: 0,
|
|
394
|
+
deliverySkipReason: null,
|
|
395
|
+
sourceReplyDeliveryMode: null,
|
|
396
|
+
beforeAgentRunBlocked: false,
|
|
397
|
+
dispatchError: false,
|
|
398
|
+
dispatchErrorKind: null,
|
|
399
|
+
abandonedReason: reason,
|
|
400
|
+
provider: null,
|
|
401
|
+
model: null,
|
|
402
|
+
thinkLevel: null,
|
|
403
|
+
toolUsage: collectToolUsageSince(trace.params.sessionKey, trace.toolTraceCursor),
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
pruneActiveReplyTraces(now = Date.now()) {
|
|
407
|
+
for (const [token, trace] of this.activeReplyTraces) {
|
|
408
|
+
if (now - trace.startedAt > REPLY_TRACE_TTL_MS) {
|
|
409
|
+
this.captureAbandonedReplyTrace(token, 'stale');
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
while (this.activeReplyTraces.size > MAX_ACTIVE_REPLY_TRACES) {
|
|
413
|
+
const oldest = [...this.activeReplyTraces.entries()].sort((a, b) => a[1].startedAt - b[1].startedAt)[0];
|
|
414
|
+
if (!oldest) {
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
this.captureAbandonedReplyTrace(oldest[0], 'max_traces');
|
|
418
|
+
}
|
|
419
|
+
}
|
|
116
420
|
captureReplyOutcome(event) {
|
|
117
|
-
const ownerShip = event.ownerShip ??
|
|
421
|
+
const ownerShip = event.ownerShip ?? '';
|
|
118
422
|
if (!this.ensureIdentified(ownerShip, event.botShip)) {
|
|
119
423
|
return;
|
|
120
424
|
}
|
|
121
425
|
this.client.capture({
|
|
122
426
|
distinctId: ownerShip,
|
|
123
427
|
event: TLON_TELEMETRY_EVENT_NAME,
|
|
124
|
-
properties: {
|
|
125
|
-
|
|
428
|
+
properties: this.properties({
|
|
429
|
+
sessionKey: event.sessionKey,
|
|
430
|
+
sessionId: event.sessionId,
|
|
431
|
+
runId: event.runId,
|
|
432
|
+
accountId: event.accountId,
|
|
433
|
+
agentId: event.agentId,
|
|
126
434
|
botShip: event.botShip,
|
|
127
435
|
ownerShip: event.ownerShip,
|
|
128
436
|
outcome: event.outcome,
|
|
129
437
|
chatType: event.chatType,
|
|
438
|
+
destinationKind: event.destinationKind,
|
|
130
439
|
isThreadReply: event.isThreadReply,
|
|
131
440
|
senderRole: event.senderRole,
|
|
132
441
|
attachmentCount: event.attachmentCount,
|
|
133
442
|
hasAttachments: event.attachmentCount > 0,
|
|
443
|
+
sendAttemptCount: event.sendAttemptCount,
|
|
444
|
+
sendError: event.sendError,
|
|
445
|
+
sendErrorCount: event.sendErrorCount,
|
|
446
|
+
sendErrorKind: event.sendErrorKind,
|
|
134
447
|
deliveredMessageCount: event.deliveredMessageCount,
|
|
135
448
|
replyCharCount: event.replyCharCount,
|
|
136
449
|
replyWordCount: event.replyWordCount,
|
|
@@ -139,6 +452,16 @@ class PostHogTlonTelemetry {
|
|
|
139
452
|
queuedFinal: event.queuedFinal,
|
|
140
453
|
queuedFinalCount: event.queuedFinalCount,
|
|
141
454
|
queuedBlockCount: event.queuedBlockCount,
|
|
455
|
+
failedToolCount: event.failedToolCount,
|
|
456
|
+
failedBlockCount: event.failedBlockCount,
|
|
457
|
+
failedFinalCount: event.failedFinalCount,
|
|
458
|
+
failedReplyCount: event.failedReplyCount,
|
|
459
|
+
deliverySkipReason: event.deliverySkipReason,
|
|
460
|
+
sourceReplyDeliveryMode: event.sourceReplyDeliveryMode,
|
|
461
|
+
beforeAgentRunBlocked: event.beforeAgentRunBlocked,
|
|
462
|
+
dispatchError: event.dispatchError,
|
|
463
|
+
dispatchErrorKind: event.dispatchErrorKind,
|
|
464
|
+
abandonedReason: event.abandonedReason,
|
|
142
465
|
provider: event.provider,
|
|
143
466
|
model: event.model,
|
|
144
467
|
thinkLevel: event.thinkLevel,
|
|
@@ -151,14 +474,195 @@ class PostHogTlonTelemetry {
|
|
|
151
474
|
durationMs: call.durationMs,
|
|
152
475
|
error: call.error,
|
|
153
476
|
})),
|
|
154
|
-
},
|
|
477
|
+
}),
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
captureSessionLifecycle(event) {
|
|
481
|
+
const ownerShip = event.ownerShip ?? '';
|
|
482
|
+
if (!this.ensureIdentified(ownerShip, event.botShip)) {
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
this.client.capture({
|
|
486
|
+
distinctId: ownerShip,
|
|
487
|
+
event: TLON_SESSION_LIFECYCLE_EVENT,
|
|
488
|
+
properties: this.properties({
|
|
489
|
+
botShip: event.botShip,
|
|
490
|
+
ownerShip: event.ownerShip,
|
|
491
|
+
accountId: event.accountId,
|
|
492
|
+
agentId: event.agentId,
|
|
493
|
+
sessionKey: event.sessionKey,
|
|
494
|
+
sessionId: event.sessionId,
|
|
495
|
+
lifecycleEvent: event.lifecycleEvent,
|
|
496
|
+
destinationKind: event.destinationKind,
|
|
497
|
+
reason: event.reason,
|
|
498
|
+
messageCount: event.messageCount,
|
|
499
|
+
durationMs: event.durationMs,
|
|
500
|
+
transcriptArchived: event.transcriptArchived,
|
|
501
|
+
hasNextSession: event.hasNextSession,
|
|
502
|
+
}),
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
captureSessionWatchdog(event) {
|
|
506
|
+
const ownerShip = event.ownerShip ?? '';
|
|
507
|
+
if (!this.ensureIdentified(ownerShip, event.botShip)) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
this.client.capture({
|
|
511
|
+
distinctId: ownerShip,
|
|
512
|
+
event: TLON_SESSION_WATCHDOG_EVENT,
|
|
513
|
+
properties: this.properties({
|
|
514
|
+
botShip: event.botShip,
|
|
515
|
+
ownerShip: event.ownerShip,
|
|
516
|
+
accountId: event.accountId,
|
|
517
|
+
agentId: event.agentId,
|
|
518
|
+
sessionKey: event.sessionKey,
|
|
519
|
+
sessionId: event.sessionId,
|
|
520
|
+
diagnosticType: event.diagnosticType,
|
|
521
|
+
destinationKind: event.destinationKind,
|
|
522
|
+
state: event.state,
|
|
523
|
+
ageMs: event.ageMs,
|
|
524
|
+
queueDepth: event.queueDepth,
|
|
525
|
+
reason: event.reason,
|
|
526
|
+
classification: event.classification,
|
|
527
|
+
activeWorkKind: event.activeWorkKind,
|
|
528
|
+
lastProgressAgeMs: event.lastProgressAgeMs,
|
|
529
|
+
lastProgressReason: event.lastProgressReason,
|
|
530
|
+
activeToolName: event.activeToolName,
|
|
531
|
+
activeToolAgeMs: event.activeToolAgeMs,
|
|
532
|
+
terminalProgressStale: event.terminalProgressStale,
|
|
533
|
+
}),
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
captureSessionRecovery(event) {
|
|
537
|
+
const ownerShip = event.ownerShip ?? '';
|
|
538
|
+
if (!this.ensureIdentified(ownerShip, event.botShip)) {
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
this.client.capture({
|
|
542
|
+
distinctId: ownerShip,
|
|
543
|
+
event: TLON_SESSION_RECOVERY_EVENT,
|
|
544
|
+
properties: this.properties({
|
|
545
|
+
botShip: event.botShip,
|
|
546
|
+
ownerShip: event.ownerShip,
|
|
547
|
+
accountId: event.accountId,
|
|
548
|
+
agentId: event.agentId,
|
|
549
|
+
sessionKey: event.sessionKey,
|
|
550
|
+
sessionId: event.sessionId,
|
|
551
|
+
diagnosticType: event.diagnosticType,
|
|
552
|
+
destinationKind: event.destinationKind,
|
|
553
|
+
state: event.state,
|
|
554
|
+
stateGeneration: event.stateGeneration,
|
|
555
|
+
ageMs: event.ageMs,
|
|
556
|
+
queueDepth: event.queueDepth,
|
|
557
|
+
reason: event.reason,
|
|
558
|
+
activeWorkKind: event.activeWorkKind,
|
|
559
|
+
allowActiveAbort: event.allowActiveAbort,
|
|
560
|
+
status: event.status,
|
|
561
|
+
action: event.action,
|
|
562
|
+
outcomeReason: event.outcomeReason,
|
|
563
|
+
released: event.released,
|
|
564
|
+
stale: event.stale,
|
|
565
|
+
}),
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
captureOutboundRoute(event) {
|
|
569
|
+
const ownerShip = event.ownerShip ?? '';
|
|
570
|
+
if (!this.ensureIdentified(ownerShip, event.botShip)) {
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
this.client.capture({
|
|
574
|
+
distinctId: ownerShip,
|
|
575
|
+
event: TLON_OUTBOUND_ROUTED_EVENT,
|
|
576
|
+
properties: this.properties({
|
|
577
|
+
botShip: event.botShip,
|
|
578
|
+
ownerShip: event.ownerShip,
|
|
579
|
+
resolvedChannel: event.resolvedChannel,
|
|
580
|
+
routedToTlon: event.routedToTlon,
|
|
581
|
+
targetKind: event.targetKind,
|
|
582
|
+
}),
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
captureHarnessError(event) {
|
|
586
|
+
const ownerShip = event.ownerShip ?? '';
|
|
587
|
+
if (!this.ensureIdentified(ownerShip, event.botShip)) {
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
this.client.capture({
|
|
591
|
+
distinctId: ownerShip,
|
|
592
|
+
event: TLON_HARNESS_ERROR_EVENT,
|
|
593
|
+
properties: this.properties({
|
|
594
|
+
harness: event.harness,
|
|
595
|
+
harnessEventType: event.harnessEventType,
|
|
596
|
+
errorScope: event.errorScope,
|
|
597
|
+
sessionKey: event.sessionKey,
|
|
598
|
+
sessionId: event.sessionId,
|
|
599
|
+
runId: event.runId,
|
|
600
|
+
accountId: event.accountId,
|
|
601
|
+
agentId: event.agentId,
|
|
602
|
+
ownerShip: event.ownerShip,
|
|
603
|
+
botShip: event.botShip,
|
|
604
|
+
destinationKind: event.destinationKind,
|
|
605
|
+
provider: event.provider,
|
|
606
|
+
model: event.model,
|
|
607
|
+
toolName: event.toolName,
|
|
608
|
+
phase: event.phase,
|
|
609
|
+
outcome: event.outcome,
|
|
610
|
+
errorCategory: event.errorCategory,
|
|
611
|
+
failureKind: event.failureKind,
|
|
612
|
+
durationMs: event.durationMs,
|
|
613
|
+
errorText: event.errorText,
|
|
614
|
+
}),
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
capturePluginError(event) {
|
|
618
|
+
const ownerShip = event.ownerShip ?? '';
|
|
619
|
+
if (!this.ensureIdentified(ownerShip, event.botShip)) {
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
this.client.capture({
|
|
623
|
+
distinctId: ownerShip,
|
|
624
|
+
event: TLON_PLUGIN_ERROR_EVENT,
|
|
625
|
+
properties: this.properties({
|
|
626
|
+
harness: event.harness,
|
|
627
|
+
pluginErrorSource: event.pluginErrorSource,
|
|
628
|
+
accountId: event.accountId,
|
|
629
|
+
ownerShip: event.ownerShip,
|
|
630
|
+
botShip: event.botShip,
|
|
631
|
+
errorKind: event.errorKind,
|
|
632
|
+
errorText: event.errorText,
|
|
633
|
+
attempt: event.attempt,
|
|
634
|
+
}),
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
captureTelemetryError(event) {
|
|
638
|
+
const ownerShip = event.ownerShip ?? '';
|
|
639
|
+
if (!this.ensureIdentified(ownerShip, event.botShip)) {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
this.client.capture({
|
|
643
|
+
distinctId: ownerShip,
|
|
644
|
+
event: TLON_TELEMETRY_ERROR_EVENT,
|
|
645
|
+
properties: this.properties({
|
|
646
|
+
harness: event.harness,
|
|
647
|
+
telemetrySource: event.telemetrySource,
|
|
648
|
+
sourceEventName: event.sourceEventName,
|
|
649
|
+
sessionKey: event.sessionKey,
|
|
650
|
+
sessionId: event.sessionId,
|
|
651
|
+
runId: event.runId,
|
|
652
|
+
accountId: event.accountId,
|
|
653
|
+
agentId: event.agentId,
|
|
654
|
+
ownerShip: event.ownerShip,
|
|
655
|
+
botShip: event.botShip,
|
|
656
|
+
errorKind: event.errorKind,
|
|
657
|
+
errorText: event.errorText,
|
|
658
|
+
}),
|
|
155
659
|
});
|
|
156
660
|
}
|
|
157
661
|
ensureIdentified(ownerShip, botShip) {
|
|
158
662
|
if (!ownerShip) {
|
|
159
663
|
if (!this.missingOwnerWarningLogged) {
|
|
160
664
|
this.missingOwnerWarningLogged = true;
|
|
161
|
-
this.runtime?.log?.(
|
|
665
|
+
this.runtime?.log?.('[tlon] Telemetry is enabled but ownerShip is not configured; skipping telemetry events');
|
|
162
666
|
}
|
|
163
667
|
return false;
|
|
164
668
|
}
|
|
@@ -168,6 +672,7 @@ class PostHogTlonTelemetry {
|
|
|
168
672
|
distinctId: ownerShip,
|
|
169
673
|
properties: {
|
|
170
674
|
logSource: TLON_TELEMETRY_LOG_SOURCE,
|
|
675
|
+
...this.versionIdentity,
|
|
171
676
|
tlonOwnerShip: ownerShip,
|
|
172
677
|
tlonBotShip: botShip,
|
|
173
678
|
},
|
|
@@ -182,17 +687,18 @@ class PostHogTlonTelemetry {
|
|
|
182
687
|
this.client.capture({
|
|
183
688
|
distinctId: event.ownerShip,
|
|
184
689
|
event: TLON_HEARTBEAT_NUDGE_EVENT,
|
|
185
|
-
properties: {
|
|
186
|
-
logSource: TLON_TELEMETRY_LOG_SOURCE,
|
|
690
|
+
properties: this.properties({
|
|
187
691
|
botShip: event.botShip,
|
|
188
692
|
ownerShip: event.ownerShip,
|
|
189
|
-
trigger:
|
|
693
|
+
trigger: 'heartbeat',
|
|
190
694
|
nudgeStage: event.nudgeStage,
|
|
191
695
|
nudgeTarget: event.nudgeTarget,
|
|
192
696
|
channel: event.channel,
|
|
193
697
|
success: event.success,
|
|
194
698
|
accountId: event.accountId,
|
|
195
|
-
|
|
699
|
+
messageId: event.messageId,
|
|
700
|
+
nudgeSentAtMs: event.nudgeSentAtMs,
|
|
701
|
+
}),
|
|
196
702
|
});
|
|
197
703
|
}
|
|
198
704
|
captureHeartbeatReengagement(event) {
|
|
@@ -202,8 +708,7 @@ class PostHogTlonTelemetry {
|
|
|
202
708
|
this.client.capture({
|
|
203
709
|
distinctId: event.ownerShip,
|
|
204
710
|
event: TLON_HEARTBEAT_REENGAGED_EVENT,
|
|
205
|
-
properties: {
|
|
206
|
-
logSource: TLON_TELEMETRY_LOG_SOURCE,
|
|
711
|
+
properties: this.properties({
|
|
207
712
|
botShip: event.botShip,
|
|
208
713
|
ownerShip: event.ownerShip,
|
|
209
714
|
nudgeStage: event.nudgeStage,
|
|
@@ -212,10 +717,13 @@ class PostHogTlonTelemetry {
|
|
|
212
717
|
reengagementDelayMs: event.reengagementDelayMs,
|
|
213
718
|
channel: event.channel,
|
|
214
719
|
accountId: event.accountId,
|
|
215
|
-
},
|
|
720
|
+
}),
|
|
216
721
|
});
|
|
217
722
|
}
|
|
218
723
|
async close() {
|
|
724
|
+
for (const token of [...this.activeReplyTraces.keys()]) {
|
|
725
|
+
this.captureAbandonedReplyTrace(token, 'telemetry_close');
|
|
726
|
+
}
|
|
219
727
|
try {
|
|
220
728
|
await this.client.flush();
|
|
221
729
|
}
|
|
@@ -235,18 +743,240 @@ export function createTlonTelemetry(params) {
|
|
|
235
743
|
return null;
|
|
236
744
|
}
|
|
237
745
|
if (!params.config.apiKey) {
|
|
238
|
-
params.runtime?.log?.(
|
|
746
|
+
params.runtime?.log?.('[tlon] Telemetry is enabled but telemetry.apiKey is missing; telemetry disabled');
|
|
239
747
|
return null;
|
|
240
748
|
}
|
|
241
|
-
params.runtime?.log?.(`[tlon] Telemetry enabled${params.config.host ? ` (${params.config.host})` :
|
|
749
|
+
params.runtime?.log?.(`[tlon] Telemetry enabled${params.config.host ? ` (${params.config.host})` : ''}`);
|
|
242
750
|
return new PostHogTlonTelemetry({
|
|
243
751
|
apiKey: params.config.apiKey,
|
|
244
752
|
host: params.config.host,
|
|
245
753
|
runtime: params.runtime,
|
|
246
754
|
});
|
|
247
755
|
}
|
|
756
|
+
const outboundRouteReporterSlot = sharedSlot('telemetry.outboundRouteReporter');
|
|
757
|
+
const sessionTelemetryReporterSlot = sharedSlot('telemetry.sessionTelemetryReporter');
|
|
758
|
+
const errorTelemetryReporterSlot = sharedSlot('telemetry.errorTelemetryReporter');
|
|
759
|
+
export function setOutboundRouteReporter(reporter) {
|
|
760
|
+
outboundRouteReporterSlot.set(reporter);
|
|
761
|
+
}
|
|
762
|
+
export function reportOutboundRoute(event) {
|
|
763
|
+
outboundRouteReporterSlot.get()?.(event);
|
|
764
|
+
}
|
|
765
|
+
export function setSessionTelemetryReporter(reporter) {
|
|
766
|
+
sessionTelemetryReporterSlot.set(reporter);
|
|
767
|
+
}
|
|
768
|
+
export function setErrorTelemetryReporter(reporter) {
|
|
769
|
+
errorTelemetryReporterSlot.set(reporter);
|
|
770
|
+
}
|
|
771
|
+
function optionalString(value) {
|
|
772
|
+
const normalized = value?.trim();
|
|
773
|
+
return normalized ? normalized : null;
|
|
774
|
+
}
|
|
775
|
+
function optionalErrorText(value) {
|
|
776
|
+
return typeof value === 'string' && value.trim() ? value : null;
|
|
777
|
+
}
|
|
778
|
+
function optionalNumber(value) {
|
|
779
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
780
|
+
}
|
|
781
|
+
export function formatTlonTelemetryErrorText(error) {
|
|
782
|
+
if (error instanceof Error) {
|
|
783
|
+
return error.stack || error.message || String(error);
|
|
784
|
+
}
|
|
785
|
+
if (typeof error === 'string') {
|
|
786
|
+
return error;
|
|
787
|
+
}
|
|
788
|
+
if (error === null) {
|
|
789
|
+
return 'null';
|
|
790
|
+
}
|
|
791
|
+
if (error === undefined) {
|
|
792
|
+
return 'undefined';
|
|
793
|
+
}
|
|
794
|
+
try {
|
|
795
|
+
const json = JSON.stringify(error);
|
|
796
|
+
if (json) {
|
|
797
|
+
return json;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
catch {
|
|
801
|
+
// Fall through to String(error).
|
|
802
|
+
}
|
|
803
|
+
return String(error);
|
|
804
|
+
}
|
|
805
|
+
export function reportSessionTurnCreated(event) {
|
|
806
|
+
const context = lookupTlonSessionContext(event.sessionKey);
|
|
807
|
+
if (!context) {
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
updateTlonSessionContextRuntime(context, {
|
|
811
|
+
sessionId: event.sessionId,
|
|
812
|
+
runId: event.runId,
|
|
813
|
+
agentId: event.agentId,
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
export function reportHarnessError(event) {
|
|
817
|
+
const rememberedContext = lookupTlonSessionContext(event.sessionKey);
|
|
818
|
+
const context = rememberedContext
|
|
819
|
+
? updateTlonSessionContextRuntime(rememberedContext, {
|
|
820
|
+
sessionId: event.sessionId,
|
|
821
|
+
runId: event.runId,
|
|
822
|
+
agentId: event.agentId,
|
|
823
|
+
})
|
|
824
|
+
: null;
|
|
825
|
+
if (!context && event.sessionKey) {
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
errorTelemetryReporterSlot.get()?.({
|
|
829
|
+
kind: 'harness',
|
|
830
|
+
event: {
|
|
831
|
+
harness: 'openclaw',
|
|
832
|
+
harnessEventType: event.harnessEventType,
|
|
833
|
+
errorScope: event.errorScope,
|
|
834
|
+
sessionKey: context?.sessionKey ?? null,
|
|
835
|
+
sessionId: context?.sessionId ?? optionalString(event.sessionId),
|
|
836
|
+
runId: optionalString(event.runId) ?? context?.runId ?? null,
|
|
837
|
+
accountId: context?.accountId ?? optionalString(event.accountId),
|
|
838
|
+
agentId: optionalString(event.agentId) ?? context?.agentId ?? null,
|
|
839
|
+
ownerShip: context?.ownerShip ?? optionalString(event.ownerShip),
|
|
840
|
+
botShip: context?.botShip ?? optionalString(event.botShip) ?? '',
|
|
841
|
+
destinationKind: context?.destinationKind ?? event.destinationKind ?? null,
|
|
842
|
+
provider: optionalString(event.provider),
|
|
843
|
+
model: optionalString(event.model),
|
|
844
|
+
toolName: optionalString(event.toolName),
|
|
845
|
+
phase: optionalString(event.phase),
|
|
846
|
+
outcome: optionalString(event.outcome),
|
|
847
|
+
errorCategory: optionalString(event.errorCategory),
|
|
848
|
+
failureKind: optionalString(event.failureKind),
|
|
849
|
+
durationMs: optionalNumber(event.durationMs),
|
|
850
|
+
errorText: optionalErrorText(event.errorText),
|
|
851
|
+
},
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
export function reportPluginError(event) {
|
|
855
|
+
errorTelemetryReporterSlot.get()?.({
|
|
856
|
+
kind: 'plugin',
|
|
857
|
+
event,
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
export function reportTelemetryError(event) {
|
|
861
|
+
const rememberedContext = lookupTlonSessionContext(event.sessionKey);
|
|
862
|
+
const context = rememberedContext
|
|
863
|
+
? updateTlonSessionContextRuntime(rememberedContext, {
|
|
864
|
+
sessionId: event.sessionId,
|
|
865
|
+
runId: event.runId,
|
|
866
|
+
agentId: event.agentId,
|
|
867
|
+
})
|
|
868
|
+
: null;
|
|
869
|
+
errorTelemetryReporterSlot.get()?.({
|
|
870
|
+
kind: 'telemetry',
|
|
871
|
+
event: {
|
|
872
|
+
...event,
|
|
873
|
+
sessionKey: context?.sessionKey ?? optionalString(event.sessionKey),
|
|
874
|
+
sessionId: context?.sessionId ?? optionalString(event.sessionId),
|
|
875
|
+
runId: optionalString(event.runId) ?? context?.runId ?? null,
|
|
876
|
+
agentId: optionalString(event.agentId) ?? context?.agentId ?? null,
|
|
877
|
+
accountId: event.accountId ?? context?.accountId ?? null,
|
|
878
|
+
ownerShip: event.ownerShip ?? context?.ownerShip ?? null,
|
|
879
|
+
botShip: event.botShip ?? context?.botShip ?? null,
|
|
880
|
+
sourceEventName: optionalString(event.sourceEventName),
|
|
881
|
+
errorKind: optionalString(event.errorKind),
|
|
882
|
+
},
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
export function reportSessionLifecycle(event) {
|
|
886
|
+
const rememberedContext = lookupTlonSessionContext(event.sessionKey);
|
|
887
|
+
const context = rememberedContext
|
|
888
|
+
? updateTlonSessionContextSessionId(rememberedContext, event.sessionId)
|
|
889
|
+
: null;
|
|
890
|
+
if (!context) {
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
sessionTelemetryReporterSlot.get()?.({
|
|
894
|
+
kind: 'lifecycle',
|
|
895
|
+
event: {
|
|
896
|
+
lifecycleEvent: event.lifecycleEvent,
|
|
897
|
+
sessionKey: context.sessionKey,
|
|
898
|
+
sessionId: context.sessionId,
|
|
899
|
+
accountId: context.accountId,
|
|
900
|
+
agentId: optionalString(event.agentId) ?? context.agentId,
|
|
901
|
+
ownerShip: context.ownerShip,
|
|
902
|
+
botShip: context.botShip,
|
|
903
|
+
destinationKind: context.destinationKind,
|
|
904
|
+
reason: optionalString(event.reason),
|
|
905
|
+
messageCount: optionalNumber(event.messageCount),
|
|
906
|
+
durationMs: optionalNumber(event.durationMs),
|
|
907
|
+
transcriptArchived: typeof event.transcriptArchived === 'boolean'
|
|
908
|
+
? event.transcriptArchived
|
|
909
|
+
: null,
|
|
910
|
+
hasNextSession: event.hasNextSession === true,
|
|
911
|
+
},
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
export function reportSessionDiagnostic(event) {
|
|
915
|
+
const rememberedContext = lookupTlonSessionContext(event.sessionKey);
|
|
916
|
+
const context = rememberedContext
|
|
917
|
+
? updateTlonSessionContextSessionId(rememberedContext, event.sessionId)
|
|
918
|
+
: null;
|
|
919
|
+
if (!context) {
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
if (event.type === 'session.stalled' || event.type === 'session.stuck') {
|
|
923
|
+
sessionTelemetryReporterSlot.get()?.({
|
|
924
|
+
kind: 'watchdog',
|
|
925
|
+
event: {
|
|
926
|
+
diagnosticType: event.type,
|
|
927
|
+
sessionKey: context.sessionKey,
|
|
928
|
+
sessionId: context.sessionId,
|
|
929
|
+
accountId: context.accountId,
|
|
930
|
+
agentId: context.agentId,
|
|
931
|
+
ownerShip: context.ownerShip,
|
|
932
|
+
botShip: context.botShip,
|
|
933
|
+
destinationKind: context.destinationKind,
|
|
934
|
+
state: event.state,
|
|
935
|
+
ageMs: event.ageMs,
|
|
936
|
+
queueDepth: optionalNumber(event.queueDepth),
|
|
937
|
+
reason: optionalString(event.reason),
|
|
938
|
+
classification: event.classification,
|
|
939
|
+
activeWorkKind: optionalString(event.activeWorkKind),
|
|
940
|
+
lastProgressAgeMs: optionalNumber(event.lastProgressAgeMs),
|
|
941
|
+
lastProgressReason: optionalString(event.lastProgressReason),
|
|
942
|
+
activeToolName: optionalString(event.activeToolName),
|
|
943
|
+
activeToolAgeMs: optionalNumber(event.activeToolAgeMs),
|
|
944
|
+
terminalProgressStale: event.terminalProgressStale === true,
|
|
945
|
+
},
|
|
946
|
+
});
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
const recoveryEvent = event;
|
|
950
|
+
sessionTelemetryReporterSlot.get()?.({
|
|
951
|
+
kind: 'recovery',
|
|
952
|
+
event: {
|
|
953
|
+
diagnosticType: recoveryEvent.type,
|
|
954
|
+
sessionKey: context.sessionKey,
|
|
955
|
+
sessionId: context.sessionId,
|
|
956
|
+
accountId: context.accountId,
|
|
957
|
+
agentId: context.agentId,
|
|
958
|
+
ownerShip: context.ownerShip,
|
|
959
|
+
botShip: context.botShip,
|
|
960
|
+
destinationKind: context.destinationKind,
|
|
961
|
+
state: recoveryEvent.state,
|
|
962
|
+
stateGeneration: optionalNumber(recoveryEvent.stateGeneration),
|
|
963
|
+
ageMs: recoveryEvent.ageMs,
|
|
964
|
+
queueDepth: optionalNumber(recoveryEvent.queueDepth),
|
|
965
|
+
reason: optionalString(recoveryEvent.reason),
|
|
966
|
+
activeWorkKind: optionalString(recoveryEvent.activeWorkKind),
|
|
967
|
+
allowActiveAbort: recoveryEvent.allowActiveAbort === true,
|
|
968
|
+
status: optionalString(recoveryEvent.status),
|
|
969
|
+
action: optionalString(recoveryEvent.action),
|
|
970
|
+
outcomeReason: optionalString(recoveryEvent.outcomeReason),
|
|
971
|
+
released: optionalNumber(recoveryEvent.released),
|
|
972
|
+
stale: recoveryEvent.stale === true,
|
|
973
|
+
},
|
|
974
|
+
});
|
|
975
|
+
}
|
|
248
976
|
export const _testing = {
|
|
249
977
|
clearToolCalls: () => toolCallsBySession.clear(),
|
|
978
|
+
clearSessionContexts: () => sessionContextsBySessionKey.clear(),
|
|
979
|
+
getReplyTraceTtlMs: () => REPLY_TRACE_TTL_MS,
|
|
250
980
|
getToolTraceTtlMs: () => TOOL_TRACE_TTL_MS,
|
|
251
981
|
};
|
|
252
982
|
//# sourceMappingURL=telemetry.js.map
|