@latitude-data/openclaw-telemetry 0.0.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/LICENSE +157 -0
- package/README.md +141 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +272 -0
- package/dist/cli.js.map +1 -0
- package/dist/plugin.d.ts +117 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +646 -0
- package/dist/plugin.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","names":[],"sources":["../src/client.ts","../src/config.ts","../src/logger.ts","../src/otlp.ts","../src/turn-builder.ts","../src/plugin.ts"],"sourcesContent":["import type { Logger } from \"./logger.ts\"\nimport type { OtlpExportRequest } from \"./types.ts\"\n\nexport async function postTraces({\n baseUrl,\n apiKey,\n project,\n payload,\n logger,\n timeoutMs = 10_000,\n}: {\n baseUrl: string\n apiKey: string\n project: string\n payload: OtlpExportRequest\n logger: Logger\n timeoutMs?: number\n}): Promise<void> {\n const url = `${baseUrl.replace(/\\/+$/, \"\")}/v1/traces`\n const bodyText = JSON.stringify(payload)\n logger.debug(`POST ${url} (project=${project}, ${bodyText.length} bytes)`)\n\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n \"X-Latitude-Project\": project,\n },\n body: bodyText,\n signal: controller.signal,\n })\n if (!res.ok) {\n const text = await res.text().catch(() => \"\")\n logger.warn(`ingest HTTP ${res.status}: ${text.slice(0, 500)}`)\n } else {\n logger.debug(`ingest HTTP ${res.status}`)\n }\n } catch (err) {\n logger.warn(`ingest failed: ${String(err)}`)\n } finally {\n clearTimeout(timer)\n }\n}\n","export interface Config {\n apiKey: string\n baseUrl: string\n project: string\n enabled: boolean\n debug: boolean\n}\n\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): Config {\n const apiKey = env.LATITUDE_API_KEY ?? \"\"\n const baseUrl = env.LATITUDE_BASE_URL ?? \"https://ingest.latitude.so\"\n const project = env.LATITUDE_PROJECT ?? \"\"\n const enabled = (env.LATITUDE_OPENCLAW_ENABLED ?? \"1\") !== \"0\" && apiKey !== \"\" && project !== \"\"\n const debug = env.LATITUDE_DEBUG === \"1\"\n return { apiKey, baseUrl, project, enabled, debug }\n}\n","const PREFIX = \"[latitude-openclaw]\"\n\nexport interface Logger {\n debug: (msg: string) => void\n warn: (msg: string) => void\n}\n\nexport function createLogger(debugEnabled: boolean): Logger {\n return {\n debug: debugEnabled ? (msg) => process.stderr.write(`${PREFIX} ${msg}\\n`) : () => {},\n warn: (msg) => process.stderr.write(`${PREFIX} ${msg}\\n`),\n }\n}\n","import { createHash } from \"node:crypto\"\nimport { arch, hostname, platform, release } from \"node:os\"\nimport type {\n LlmCallRecord,\n OtlpExportRequest,\n OtlpKeyValue,\n OtlpResourceSpans,\n OtlpSpan,\n RunRecord,\n ToolCallRecord,\n} from \"./types.ts\"\n\nconst SCOPE_NAME = \"@latitude-data/openclaw-telemetry\"\nconst SCOPE_VERSION = \"0.0.1\"\n\n/** Build an OTLP export request for a single completed agent run. */\nexport function buildOtlpRequest(run: RunRecord): OtlpExportRequest {\n const spans = buildRunSpans(run)\n const rs: OtlpResourceSpans = {\n resource: { attributes: resourceAttrs() },\n scopeSpans: [{ scope: { name: SCOPE_NAME, version: SCOPE_VERSION }, spans }],\n }\n return { resourceSpans: [rs] }\n}\n\nfunction buildRunSpans(run: RunRecord): OtlpSpan[] {\n const traceId = hashHex(`${run.sessionId ?? \"session\"}:${run.runId}`, 32)\n const interactionSpanId = hashHex(`${traceId}:run`, 16)\n const out: OtlpSpan[] = [buildInteractionSpan(traceId, interactionSpanId, run)]\n\n run.llmCalls.forEach((call, idx) => {\n const callSpanId = hashHex(`${traceId}:call:${idx}`, 16)\n out.push(buildLlmSpan(traceId, interactionSpanId, callSpanId, call, idx, run))\n // Tool spans are siblings of the llm_request, parented on the interaction\n // span so the run timeline renders as: llm → tool → llm → tool → ...\n call.toolCalls.forEach((tool, tIdx) => {\n const toolSpanId = hashHex(`${traceId}:call:${idx}:tool:${tIdx}`, 16)\n out.push(buildToolSpan(traceId, interactionSpanId, toolSpanId, tool, run))\n })\n })\n run.orphanTools.forEach((tool, idx) => {\n const toolSpanId = hashHex(`${traceId}:orphan-tool:${idx}`, 16)\n out.push(buildToolSpan(traceId, interactionSpanId, toolSpanId, tool, run))\n })\n\n return out\n}\n\nfunction buildInteractionSpan(traceId: string, spanId: string, run: RunRecord): OtlpSpan {\n const startNs = msToNs(run.startMs)\n const endNs = msToNs(run.endMs ?? run.startMs)\n const totalTools = run.llmCalls.reduce((sum, c) => sum + c.toolCalls.length, 0) + run.orphanTools.length\n const totalUsage = aggregateUsage(run.llmCalls)\n\n return {\n traceId,\n spanId,\n parentSpanId: \"\",\n name: \"interaction\",\n kind: 1,\n startTimeUnixNano: startNs,\n endTimeUnixNano: endNs,\n attributes: stripUndef([\n str(\"span.type\", \"interaction\"),\n str(\"interaction.kind\", \"agent_run\"),\n str(\"openclaw.run.id\", run.runId),\n run.sessionId ? str(\"openclaw.session.id\", run.sessionId) : undefined,\n run.sessionId ? str(\"session.id\", run.sessionId) : undefined,\n run.sessionKey ? str(\"openclaw.session.key\", run.sessionKey) : undefined,\n run.agentId ? str(\"openclaw.agent.id\", run.agentId) : undefined,\n run.agentId ? str(\"openclaw.agent.name\", run.agentId) : undefined,\n run.workspaceDir ? str(\"openclaw.workspace.dir\", run.workspaceDir) : undefined,\n run.messageProvider ? str(\"openclaw.message.provider\", run.messageProvider) : undefined,\n run.channelId ? str(\"openclaw.channel.id\", run.channelId) : undefined,\n run.trigger ? str(\"openclaw.trigger\", run.trigger) : undefined,\n run.modelProviderId ? str(\"openclaw.model.provider.id\", run.modelProviderId) : undefined,\n run.modelId ? str(\"openclaw.model.id\", run.modelId) : undefined,\n int(\"interaction.duration_ms\", durationMs(run.startMs, run.endMs)),\n int(\"interaction.call_count\", run.llmCalls.length),\n int(\"interaction.tool_call_count\", totalTools),\n run.success !== undefined ? bool(\"openclaw.run.success\", run.success) : undefined,\n run.error ? str(\"openclaw.run.error\", run.error) : undefined,\n totalUsage.input !== undefined ? int(\"gen_ai.usage.input_tokens\", totalUsage.input) : undefined,\n totalUsage.output !== undefined ? int(\"gen_ai.usage.output_tokens\", totalUsage.output) : undefined,\n totalUsage.cacheRead !== undefined\n ? int(\"gen_ai.usage.cache_read_input_tokens\", totalUsage.cacheRead)\n : undefined,\n totalUsage.cacheWrite !== undefined\n ? int(\"gen_ai.usage.cache_creation_input_tokens\", totalUsage.cacheWrite)\n : undefined,\n totalUsage.total !== undefined ? int(\"gen_ai.usage.total_tokens\", totalUsage.total) : undefined,\n // Surface the first user prompt so the Latitude UI has something\n // recognisable in the interaction list.\n run.llmCalls[0]?.prompt ? str(\"user_prompt\", run.llmCalls[0].prompt) : undefined,\n ]),\n status: { code: run.success === false ? 2 : 1 },\n }\n}\n\nfunction buildLlmSpan(\n traceId: string,\n parentSpanId: string,\n spanId: string,\n call: LlmCallRecord,\n callIdx: number,\n run: RunRecord,\n): OtlpSpan {\n const startNs = msToNs(call.startMs)\n const endNs = msToNs(call.endMs ?? call.startMs)\n\n const inputMessages = buildInputMessages(call)\n const outputMessages = buildOutputMessages(call)\n const systemInstructions = call.systemPrompt\n ? JSON.stringify([{ type: \"text\", content: call.systemPrompt }])\n : undefined\n\n return {\n traceId,\n spanId,\n parentSpanId,\n name: \"llm_request\",\n kind: 3,\n startTimeUnixNano: startNs,\n endTimeUnixNano: endNs,\n attributes: stripUndef([\n str(\"span.type\", \"llm_request\"),\n str(\"gen_ai.operation.name\", \"chat\"),\n str(\"llm_request.context\", \"interaction\"),\n int(\"llm_request.call_index\", callIdx),\n // Provider/model — capture every variant OpenClaw exposes so consumers\n // can filter on whichever form they already use.\n str(\"gen_ai.system\", call.provider),\n str(\"openclaw.provider\", call.provider),\n str(\"gen_ai.request.model\", call.requestModel),\n str(\"model\", call.requestModel),\n call.responseModel ? str(\"gen_ai.response.model\", call.responseModel) : undefined,\n call.resolvedRef ? str(\"openclaw.resolved.ref\", call.resolvedRef) : undefined,\n // Identity — the agent name tag the user specifically asked for is here\n // under both canonical and convenience keys.\n run.sessionId ? str(\"session.id\", run.sessionId) : undefined,\n run.sessionKey ? str(\"openclaw.session.key\", run.sessionKey) : undefined,\n str(\"openclaw.run.id\", call.runId),\n call.agentId ? str(\"openclaw.agent.id\", call.agentId) : undefined,\n call.agentId ? str(\"openclaw.agent.name\", call.agentId) : undefined,\n // Token usage — input / output / cache / total, in both gen_ai.* and\n // legacy aliases for backwards compatibility with existing dashboards.\n call.usage?.input !== undefined ? int(\"gen_ai.usage.input_tokens\", call.usage.input) : undefined,\n call.usage?.input !== undefined ? int(\"input_tokens\", call.usage.input) : undefined,\n call.usage?.output !== undefined ? int(\"gen_ai.usage.output_tokens\", call.usage.output) : undefined,\n call.usage?.output !== undefined ? int(\"output_tokens\", call.usage.output) : undefined,\n call.usage?.cacheRead !== undefined\n ? int(\"gen_ai.usage.cache_read_input_tokens\", call.usage.cacheRead)\n : undefined,\n call.usage?.cacheRead !== undefined ? int(\"cache_read_tokens\", call.usage.cacheRead) : undefined,\n call.usage?.cacheWrite !== undefined\n ? int(\"gen_ai.usage.cache_creation_input_tokens\", call.usage.cacheWrite)\n : undefined,\n call.usage?.cacheWrite !== undefined ? int(\"cache_creation_tokens\", call.usage.cacheWrite) : undefined,\n call.usage?.total !== undefined ? int(\"gen_ai.usage.total_tokens\", call.usage.total) : undefined,\n // Content — system prompt + full message arrays.\n systemInstructions ? str(\"gen_ai.system_instructions\", systemInstructions) : undefined,\n str(\"gen_ai.input.messages\", JSON.stringify(inputMessages)),\n str(\"gen_ai.output.messages\", JSON.stringify(outputMessages)),\n // Misc signals.\n int(\"openclaw.images.count\", call.imagesCount),\n int(\"llm_request.tool_call_count\", call.toolCalls.length),\n int(\"llm_request.duration_ms\", durationMs(call.startMs, call.endMs)),\n call.error ? str(\"error.type\", \"llm_error\") : undefined,\n call.error ? str(\"error.message\", call.error) : undefined,\n str(\"success\", call.error ? \"false\" : \"true\"),\n str(\"llm_request.captured\", \"true\"),\n ]),\n status: { code: call.error ? 2 : 1 },\n }\n}\n\nfunction buildToolSpan(\n traceId: string,\n parentSpanId: string,\n spanId: string,\n tool: ToolCallRecord,\n run: RunRecord,\n): OtlpSpan {\n const startNs = msToNs(tool.startMs)\n const endNs = msToNs(tool.endMs ?? tool.startMs)\n const isError = Boolean(tool.error)\n return {\n traceId,\n spanId,\n parentSpanId,\n name: `tool:${tool.toolName}`,\n kind: 1,\n startTimeUnixNano: startNs,\n endTimeUnixNano: endNs,\n attributes: stripUndef([\n str(\"span.type\", \"tool_execution\"),\n str(\"gen_ai.operation.name\", \"execute_tool\"),\n str(\"gen_ai.tool.name\", tool.toolName),\n str(\"gen_ai.tool.call.id\", tool.toolCallId),\n str(\"gen_ai.tool.call.arguments\", safeJson(tool.params)),\n tool.result !== undefined ? str(\"gen_ai.tool.call.result\", safeJson(tool.result)) : undefined,\n isError ? str(\"error.type\", \"tool_error\") : undefined,\n isError ? str(\"error.message\", tool.error ?? \"\") : undefined,\n bool(\"tool.is_error\", isError),\n str(\"success\", isError ? \"false\" : \"true\"),\n tool.durationMs !== undefined ? int(\"tool.duration_ms\", tool.durationMs) : undefined,\n run.sessionId ? str(\"session.id\", run.sessionId) : undefined,\n run.sessionKey ? str(\"openclaw.session.key\", run.sessionKey) : undefined,\n str(\"openclaw.run.id\", run.runId),\n tool.agentId ? str(\"openclaw.agent.id\", tool.agentId) : undefined,\n tool.agentId ? str(\"openclaw.agent.name\", tool.agentId) : undefined,\n ]),\n status: { code: isError ? 2 : 1 },\n }\n}\n\n// ─── Message shape helpers ──────────────────────────────────────────────────\n//\n// Latitude UI expects `{ role, parts: [{ type, content|... }] }` objects.\n// Build both the input (history + current prompt) and output (assistant + any\n// tool_calls from this call) arrays in that shape, passing through whatever\n// OpenClaw handed us as-is where the shape is already usable.\n\ninterface MessagePart {\n type: string\n [key: string]: unknown\n}\n\ninterface Message {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\"\n parts: MessagePart[]\n}\n\nfunction buildInputMessages(call: LlmCallRecord): Message[] {\n const out: Message[] = []\n // OpenClaw's `historyMessages` is typed `unknown[]`; we trust it enough to\n // pass through but normalize simple shapes so the Latitude UI has something\n // to render. Complex objects fall back to a JSON stringification.\n for (const msg of call.historyMessages) {\n const normalized = normalizeHistoryMessage(msg)\n if (normalized) out.push(normalized)\n }\n if (call.prompt.length > 0) {\n out.push({ role: \"user\", parts: [{ type: \"text\", content: call.prompt }] })\n }\n return out\n}\n\nconst ALLOWED_ROLES: ReadonlySet<Message[\"role\"]> = new Set([\"system\", \"user\", \"assistant\", \"tool\"])\n\nfunction normalizeRole(raw: unknown): Message[\"role\"] {\n // Provider adapters occasionally emit roles outside the canonical set\n // (e.g. OpenAI's \"developer\"). Coerce unknown roles to \"user\" so the\n // downstream Latitude UI gets a payload it can render.\n if (typeof raw !== \"string\") return \"user\"\n return ALLOWED_ROLES.has(raw as Message[\"role\"]) ? (raw as Message[\"role\"]) : \"user\"\n}\n\nfunction normalizeHistoryMessage(raw: unknown): Message | undefined {\n if (!raw || typeof raw !== \"object\") return undefined\n const obj = raw as Record<string, unknown>\n const role = normalizeRole(obj.role)\n const content = obj.content ?? obj.text ?? obj.message\n if (typeof content === \"string\") {\n return { role, parts: [{ type: \"text\", content }] }\n }\n if (Array.isArray(content)) {\n const parts: MessagePart[] = []\n for (const block of content) {\n const part = normalizeContentBlock(block)\n if (part) parts.push(part)\n }\n return { role, parts: parts.length > 0 ? parts : [{ type: \"text\", content: JSON.stringify(content) }] }\n }\n // Unknown shape — dump it as JSON so nothing is silently lost.\n return { role, parts: [{ type: \"text\", content: safeJson(raw) }] }\n}\n\nfunction normalizeContentBlock(raw: unknown): MessagePart | undefined {\n if (typeof raw === \"string\") return { type: \"text\", content: raw }\n if (!raw || typeof raw !== \"object\") return undefined\n const obj = raw as Record<string, unknown>\n const type = typeof obj.type === \"string\" ? obj.type : \"text\"\n if (type === \"text\" && typeof obj.text === \"string\") return { type: \"text\", content: obj.text }\n if (type === \"tool_use\") {\n return {\n type: \"tool_call\",\n id: typeof obj.id === \"string\" ? obj.id : \"\",\n name: typeof obj.name === \"string\" ? obj.name : \"\",\n arguments: obj.input ?? {},\n }\n }\n if (type === \"tool_result\") {\n return {\n type: \"tool_call_response\",\n id: typeof obj.tool_use_id === \"string\" ? obj.tool_use_id : \"\",\n response: obj.content ?? \"\",\n }\n }\n if (type === \"image\") {\n return { type: \"uri\", modality: \"image\", uri: safeJson(obj.source ?? obj) }\n }\n return { type, content: safeJson(raw) }\n}\n\nfunction buildOutputMessages(call: LlmCallRecord): Message[] {\n const parts: MessagePart[] = []\n for (const text of call.assistantTexts) {\n if (text.length > 0) parts.push({ type: \"text\", content: text })\n }\n // Attach tool_call parts from tools invoked during this call so the output\n // message reads like the assistant message the model actually produced.\n for (const tool of call.toolCalls) {\n parts.push({\n type: \"tool_call\",\n id: tool.toolCallId,\n name: tool.toolName,\n arguments: tool.params,\n })\n }\n // Fall back to `lastAssistant` if we have no text/tools (edge case — empty\n // SSE response, failed run).\n if (parts.length === 0 && call.lastAssistant !== undefined) {\n parts.push({ type: \"text\", content: safeJson(call.lastAssistant) })\n }\n return [{ role: \"assistant\", parts }]\n}\n\n// ─── Utilities ──────────────────────────────────────────────────────────────\n\nfunction aggregateUsage(calls: LlmCallRecord[]): Required<Partial<import(\"./types.ts\").OpenClawLlmUsage>> {\n const agg = {\n input: undefined as number | undefined,\n output: undefined as number | undefined,\n cacheRead: undefined as number | undefined,\n cacheWrite: undefined as number | undefined,\n total: undefined as number | undefined,\n }\n const add = (k: keyof typeof agg, v: number | undefined): void => {\n if (v === undefined) return\n agg[k] = (agg[k] ?? 0) + v\n }\n for (const c of calls) {\n if (!c.usage) continue\n add(\"input\", c.usage.input)\n add(\"output\", c.usage.output)\n add(\"cacheRead\", c.usage.cacheRead)\n add(\"cacheWrite\", c.usage.cacheWrite)\n add(\"total\", c.usage.total)\n }\n return agg as Required<Partial<import(\"./types.ts\").OpenClawLlmUsage>>\n}\n\nfunction resourceAttrs(): OtlpKeyValue[] {\n return [\n str(\"service.name\", \"openclaw\"),\n str(\"service.version\", SCOPE_VERSION),\n str(\"host.name\", hostname()),\n str(\"host.arch\", arch()),\n str(\"os.type\", platform()),\n str(\"os.version\", release()),\n ]\n}\n\nfunction str(key: string, value: string): OtlpKeyValue {\n return { key, value: { stringValue: value } }\n}\n\nfunction int(key: string, value: number): OtlpKeyValue {\n return { key, value: { intValue: String(Math.trunc(value)) } }\n}\n\nfunction bool(key: string, value: boolean): OtlpKeyValue {\n return { key, value: { boolValue: value } }\n}\n\nfunction stripUndef(items: Array<OtlpKeyValue | undefined>): OtlpKeyValue[] {\n return items.filter((x): x is OtlpKeyValue => x !== undefined)\n}\n\nfunction hashHex(input: string, length: number): string {\n return createHash(\"sha256\").update(input).digest(\"hex\").slice(0, length)\n}\n\nfunction msToNs(ms: number): string {\n return (BigInt(Math.trunc(ms)) * 1_000_000n).toString()\n}\n\nfunction durationMs(startMs: number, endMs: number | undefined): number {\n if (endMs === undefined) return 0\n return Math.max(0, endMs - startMs)\n}\n\nfunction safeJson(value: unknown): string {\n try {\n if (typeof value === \"string\") return value\n return JSON.stringify(value)\n } catch {\n return \"\"\n }\n}\n","import { randomUUID } from \"node:crypto\"\nimport type {\n LlmCallRecord,\n OpenClawAfterToolCallEvent,\n OpenClawAgentContext,\n OpenClawAgentEndEvent,\n OpenClawBeforeToolCallEvent,\n OpenClawLlmInputEvent,\n OpenClawLlmOutputEvent,\n OpenClawSessionStartEvent,\n RunRecord,\n ToolCallRecord,\n} from \"./types.ts\"\n\n/**\n * Accumulates OpenClaw hook events per agent run (keyed by `runId`) into a\n * `RunRecord` ready to be converted to OTLP spans. All mutation is synchronous\n * and non-blocking so the hook runner can stay fire-and-forget.\n *\n * Event ordering assumptions (verified against OpenClaw\n * src/agents/pi-embedded-runner/run/attempt.ts):\n *\n * session_start? -> [ llm_input -> (before_tool_call -> after_tool_call)* -> llm_output ]+ -> agent_end\n *\n * Tool calls arriving between an `llm_input` and its `llm_output` are attached\n * to the currently-open LLM call. Tools arriving outside that window (e.g.\n * `after_tool_call` fires after `llm_output` has already closed the call) are\n * stored on the run's `orphanTools` list so we don't drop them.\n */\nexport class TurnBuilder {\n private readonly runs = new Map<string, RunRecord>()\n\n onSessionStart(_evt: OpenClawSessionStartEvent, _ctx: OpenClawAgentContext): void {\n // No-op for now — we lazily create RunRecords on the first `llm_input` for\n // a given runId. Kept as a hook point so we can later emit a\n // session-level span or capture `resumedFrom` metadata.\n }\n\n onLlmInput(evt: OpenClawLlmInputEvent, ctx: OpenClawAgentContext): LlmCallRecord {\n const run = this.ensureRun(evt.runId, ctx)\n const call: LlmCallRecord = {\n runId: evt.runId,\n sessionId: evt.sessionId,\n sessionKey: ctx.sessionKey,\n agentId: ctx.agentId,\n provider: evt.provider,\n requestModel: evt.model,\n responseModel: undefined,\n resolvedRef: undefined,\n systemPrompt: evt.systemPrompt,\n prompt: evt.prompt,\n historyMessages: evt.historyMessages,\n imagesCount: evt.imagesCount,\n assistantTexts: [],\n lastAssistant: undefined,\n usage: undefined,\n startMs: Date.now(),\n endMs: undefined,\n error: undefined,\n toolCalls: [],\n }\n run.llmCalls.push(call)\n return call\n }\n\n onBeforeToolCall(evt: OpenClawBeforeToolCallEvent, ctx: OpenClawAgentContext): void {\n if (!evt.runId) return\n // Create the run record lazily if a tool fires before we've seen llm_input\n // for this runId — rare but possible, and we prefer capturing an orphan\n // tool over dropping the event.\n const run = this.runs.get(evt.runId) ?? this.ensureRun(evt.runId, ctx)\n\n const tool: ToolCallRecord = {\n // Use a UUID when OpenClaw elides the id — name+timestamp can collide for\n // multiple invocations of the same tool within the same millisecond,\n // which would then cause `after_tool_call` to update the wrong record.\n toolCallId: evt.toolCallId ?? `${evt.toolName}:${randomUUID()}`,\n toolName: evt.toolName,\n params: evt.params,\n result: undefined,\n error: undefined,\n startMs: Date.now(),\n endMs: undefined,\n durationMs: undefined,\n agentId: ctx.agentId,\n }\n const openCall = this.currentOpenCall(run)\n if (openCall) {\n openCall.toolCalls.push(tool)\n } else {\n run.orphanTools.push(tool)\n }\n }\n\n onAfterToolCall(evt: OpenClawAfterToolCallEvent, _ctx: OpenClawAgentContext): void {\n if (!evt.runId) return\n const run = this.runs.get(evt.runId)\n if (!run) return\n const tool = this.findToolRecord(run, evt.toolCallId, evt.toolName)\n if (!tool) return\n tool.result = evt.result\n tool.error = evt.error\n tool.durationMs = evt.durationMs\n tool.endMs = Date.now()\n }\n\n onLlmOutput(evt: OpenClawLlmOutputEvent, _ctx: OpenClawAgentContext): LlmCallRecord | undefined {\n const run = this.runs.get(evt.runId)\n if (!run) return undefined\n // Close the most recently-opened call that doesn't yet have an endMs —\n // the LLM loop is sequential, so this pairs 1:1 with `llm_input`.\n const openCall = this.currentOpenCall(run)\n if (!openCall) return undefined\n openCall.endMs = Date.now()\n openCall.assistantTexts = evt.assistantTexts\n openCall.lastAssistant = evt.lastAssistant\n openCall.usage = evt.usage\n openCall.responseModel = evt.model\n openCall.resolvedRef = evt.resolvedRef\n return openCall\n }\n\n onAgentEnd(evt: OpenClawAgentEndEvent, ctx: OpenClawAgentContext): RunRecord | undefined {\n const runId = ctx.runId\n if (!runId) return undefined\n const run = this.runs.get(runId)\n if (!run) return undefined\n run.endMs = Date.now()\n run.success = evt.success\n run.error = evt.error\n // Best-effort: close any still-open LLM call that never saw an `llm_output`\n // (e.g. when the run errored mid-call) so the span still has an end time.\n for (const call of run.llmCalls) {\n if (call.endMs === undefined) {\n call.endMs = run.endMs\n if (evt.error && call.error === undefined) call.error = evt.error\n }\n for (const tool of call.toolCalls) {\n if (tool.endMs === undefined) tool.endMs = run.endMs\n }\n }\n for (const tool of run.orphanTools) {\n if (tool.endMs === undefined) tool.endMs = run.endMs\n }\n this.runs.delete(runId)\n return run\n }\n\n /** Drop a run without emitting — used on errors from the emit path. */\n abandon(runId: string): void {\n this.runs.delete(runId)\n }\n\n /** Active runs count, for debug logging. */\n inflightCount(): number {\n return this.runs.size\n }\n\n private ensureRun(runId: string, ctx: OpenClawAgentContext): RunRecord {\n let run = this.runs.get(runId)\n if (run) return run\n run = {\n runId,\n sessionId: ctx.sessionId,\n sessionKey: ctx.sessionKey,\n agentId: ctx.agentId,\n workspaceDir: ctx.workspaceDir,\n messageProvider: ctx.messageProvider,\n trigger: ctx.trigger,\n channelId: ctx.channelId,\n modelProviderId: ctx.modelProviderId,\n modelId: ctx.modelId,\n startMs: Date.now(),\n endMs: undefined,\n success: undefined,\n error: undefined,\n llmCalls: [],\n orphanTools: [],\n }\n this.runs.set(runId, run)\n return run\n }\n\n private currentOpenCall(run: RunRecord): LlmCallRecord | undefined {\n for (let i = run.llmCalls.length - 1; i >= 0; i--) {\n const call = run.llmCalls[i]\n if (call && call.endMs === undefined) return call\n }\n return undefined\n }\n\n private findToolRecord(run: RunRecord, toolCallId: string | undefined, toolName: string): ToolCallRecord | undefined {\n // Try matching by toolCallId first since it's unique. Fall back to the\n // most recent unfinished record for the same name if the id is missing\n // or no record matches — defensive coverage for OpenClaw versions that\n // elide toolCallId on after_tool_call.\n const matchesId = (t: ToolCallRecord): boolean => Boolean(toolCallId && t.toolCallId === toolCallId)\n\n for (const call of run.llmCalls) {\n for (const t of call.toolCalls) if (matchesId(t)) return t\n }\n for (const t of run.orphanTools) if (matchesId(t)) return t\n\n for (let i = run.llmCalls.length - 1; i >= 0; i--) {\n const call = run.llmCalls[i]\n if (!call) continue\n for (let j = call.toolCalls.length - 1; j >= 0; j--) {\n const t = call.toolCalls[j]\n if (t && t.toolName === toolName && t.endMs === undefined) return t\n }\n }\n for (let i = run.orphanTools.length - 1; i >= 0; i--) {\n const t = run.orphanTools[i]\n if (t && t.toolName === toolName && t.endMs === undefined) return t\n }\n return undefined\n }\n}\n","import { postTraces } from \"./client.ts\"\nimport { type Config, loadConfig } from \"./config.ts\"\nimport { createLogger, type Logger } from \"./logger.ts\"\nimport { buildOtlpRequest } from \"./otlp.ts\"\nimport { TurnBuilder } from \"./turn-builder.ts\"\nimport type {\n OpenClawAfterToolCallEvent,\n OpenClawAgentContext,\n OpenClawAgentEndEvent,\n OpenClawBeforeToolCallEvent,\n OpenClawLlmInputEvent,\n OpenClawLlmOutputEvent,\n OpenClawSessionStartEvent,\n RunRecord,\n} from \"./types.ts\"\n\n/**\n * Minimal structural type for OpenClaw's plugin API — only the fields we\n * touch. We avoid importing from `openclaw/plugin-sdk` so the package stays\n * usable when OpenClaw isn't installed (the CLI and tests don't need it),\n * and so we're robust to small signature changes across OpenClaw versions.\n */\nexport interface OpenClawPluginApiLike {\n logger?: Logger\n on: <K extends string>(\n hookName: K,\n handler: (event: unknown, ctx: unknown) => unknown,\n opts?: { priority?: number },\n ) => void\n}\n\nexport interface RegisterOptions {\n /** Override the config, mostly for tests. */\n config?: Config\n /** Override the logger. */\n logger?: Logger\n /**\n * Hook to observe the emitted run right before it's posted. Used by tests;\n * not a stable public API.\n */\n onEmit?: (run: RunRecord) => void\n}\n\n/**\n * Register the Latitude plugin against an OpenClaw plugin API. OpenClaw calls\n * this once at plugin activation; we wire up `llm_input`, `llm_output`, tool\n * and lifecycle hooks to stream traces to Latitude.\n *\n * Every handler is fire-and-forget on OpenClaw's side (see\n * `src/plugins/hooks.ts` — runLlmInput/runLlmOutput are documented as\n * parallel and wrapped with `.catch()` at the call site in attempt.ts), so\n * nothing we do here can slow the agent loop.\n */\nexport default function registerLatitudePlugin(api: OpenClawPluginApiLike, opts: RegisterOptions = {}): void {\n const config = opts.config ?? loadConfig()\n const logger = opts.logger ?? createLogger(config.debug)\n\n if (!config.enabled) {\n if (config.apiKey === \"\") logger.debug(\"disabled: LATITUDE_API_KEY is empty\")\n if (config.project === \"\") logger.debug(\"disabled: LATITUDE_PROJECT is empty\")\n return\n }\n logger.debug(`enabled: project=${config.project} base=${config.baseUrl}`)\n\n const builder = new TurnBuilder()\n\n api.on(\"session_start\", (evt, ctx) => {\n builder.onSessionStart(evt as OpenClawSessionStartEvent, ctx as OpenClawAgentContext)\n })\n\n api.on(\"llm_input\", (evt, ctx) => {\n try {\n builder.onLlmInput(evt as OpenClawLlmInputEvent, ctx as OpenClawAgentContext)\n } catch (err) {\n logger.warn(`llm_input handler failed: ${String(err)}`)\n }\n })\n\n api.on(\"before_tool_call\", (evt, ctx) => {\n try {\n builder.onBeforeToolCall(evt as OpenClawBeforeToolCallEvent, ctx as OpenClawAgentContext)\n } catch (err) {\n logger.warn(`before_tool_call handler failed: ${String(err)}`)\n }\n })\n\n api.on(\"after_tool_call\", (evt, ctx) => {\n try {\n builder.onAfterToolCall(evt as OpenClawAfterToolCallEvent, ctx as OpenClawAgentContext)\n } catch (err) {\n logger.warn(`after_tool_call handler failed: ${String(err)}`)\n }\n })\n\n api.on(\"llm_output\", (evt, ctx) => {\n try {\n builder.onLlmOutput(evt as OpenClawLlmOutputEvent, ctx as OpenClawAgentContext)\n } catch (err) {\n logger.warn(`llm_output handler failed: ${String(err)}`)\n }\n })\n\n api.on(\"agent_end\", (evt, ctx) => {\n try {\n const run = builder.onAgentEnd(evt as OpenClawAgentEndEvent, ctx as OpenClawAgentContext)\n if (!run) {\n logger.debug(\"agent_end fired without a matching run in flight\")\n return\n }\n opts.onEmit?.(run)\n const payload = buildOtlpRequest(run)\n void postTraces({\n baseUrl: config.baseUrl,\n apiKey: config.apiKey,\n project: config.project,\n payload,\n logger,\n })\n } catch (err) {\n logger.warn(`agent_end handler failed: ${String(err)}`)\n }\n })\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@latitude-data/openclaw-telemetry",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "OpenClaw plugin that streams LLM calls, tool executions, and agent runs to Latitude as OTLP traces",
|
|
5
|
+
"author": "Latitude Data SL <hello@latitude.so>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"openclaw",
|
|
9
|
+
"telemetry",
|
|
10
|
+
"otlp",
|
|
11
|
+
"opentelemetry",
|
|
12
|
+
"latitude",
|
|
13
|
+
"observability"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/latitude-dev/latitude-llm/tree/main/packages/telemetry/openclaw"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/latitude-dev/latitude-llm/tree/main/packages/telemetry/openclaw#readme",
|
|
20
|
+
"type": "module",
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"bin": {
|
|
25
|
+
"latitude-openclaw": "./dist/cli.js"
|
|
26
|
+
},
|
|
27
|
+
"main": "./dist/plugin.js",
|
|
28
|
+
"types": "./dist/plugin.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/plugin.d.ts",
|
|
32
|
+
"default": "./dist/plugin.js"
|
|
33
|
+
},
|
|
34
|
+
"./plugin": {
|
|
35
|
+
"types": "./dist/plugin.d.ts",
|
|
36
|
+
"default": "./dist/plugin.js"
|
|
37
|
+
},
|
|
38
|
+
"./cli": {
|
|
39
|
+
"types": "./dist/cli.d.ts",
|
|
40
|
+
"default": "./dist/cli.js"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@clack/prompts": "^1.2.0",
|
|
45
|
+
"picocolors": "^1.1.1"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"typescript": "^6.0.3"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@biomejs/biome": "2.4.6",
|
|
52
|
+
"@types/node": "22.14.1",
|
|
53
|
+
"tsdown": "0.21.9",
|
|
54
|
+
"@typescript/native-preview": "7.0.0-dev.20260421.2",
|
|
55
|
+
"typescript": "6.0.3",
|
|
56
|
+
"vitest": "4.1.0"
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "tsdown",
|
|
60
|
+
"check": "biome check src",
|
|
61
|
+
"format": "biome format --write src && biome check --fix src",
|
|
62
|
+
"typecheck": "tsgo -p tsconfig.json --noEmit",
|
|
63
|
+
"test": "vitest run --pool=forks --passWithNoTests --dir src"
|
|
64
|
+
}
|
|
65
|
+
}
|