@agimon-ai/doompi-log 0.0.1-alpha.24 → 0.0.1-alpha.26
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 +1 -1
- package/dist/adapters/pi/extension.cjs +1 -1
- package/dist/adapters/pi/extension.cjs.map +1 -1
- package/dist/adapters/pi/extension.d.cts +4 -2
- package/dist/adapters/pi/extension.d.cts.map +1 -1
- package/dist/adapters/pi/extension.d.mts +4 -2
- package/dist/adapters/pi/extension.d.mts.map +1 -1
- package/dist/adapters/pi/extension.mjs +1 -1
- package/dist/adapters/pi/extension.mjs.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
} from '@agimon-ai/doompi-log';
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
`installDoomLogRuntime` installs package resources into
|
|
60
|
+
`installDoomLogRuntime` installs package resources into the package plugin fiber supplied by the shared runner-scoped Cordis host. Normal Pi activation should use the discovered `extensions/pi` entry, which connects to that host and disposes its plugin fiber before releasing the host lease. Focused exports provide metrics types, the metrics source, and the overlay component.
|
|
61
61
|
|
|
62
62
|
## Development
|
|
63
63
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../services/metrics.cjs"),t=require("../../tui/logMetricsOverlay.cjs"),n=require("../node/metricsSource.cjs");let r=require("@agimon-ai/doompi-extension-contracts/
|
|
1
|
+
const e=require("../../services/metrics.cjs"),t=require("../../tui/logMetricsOverlay.cjs"),n=require("../node/metricsSource.cjs");let r=require("@agimon-ai/doompi-extension-contracts/cordis-host"),i=require("@agimon-ai/doompi-extension-contracts/readiness"),a=require("@agimon-ai/doompi-extension-contracts/ui-hub"),o=require("@agimon-ai/doompi-telemetry");const s=`@agimon-ai/doompi-log`,c=`log-metrics`,l=new Set([`error`,`aborted`]);function u(e){return e===`1`||e?.toLowerCase()===`true`||e?.toLowerCase()===`yes`}function d(e){let t={"pi.mode":e.mode,"pi.session.id":e.sessionManager.getSessionId()};return e.thinkingLevel&&(t[`pi.thinking_level`]=e.thinkingLevel),e.model&&(t[`gen_ai.provider.name`]=e.model.provider,t[`gen_ai.request.model`]=e.model.id),t}function f(e){if(e.role!==`assistant`)return;let t={"gen_ai.response.model":e.responseModel??e.model,"gen_ai.response.finish_reasons":e.stopReason,"gen_ai.usage.input_tokens":e.usage.input,"gen_ai.usage.output_tokens":e.usage.output,"gen_ai.usage.cache_read_tokens":e.usage.cacheRead,"gen_ai.usage.cache_write_tokens":e.usage.cacheWrite,"gen_ai.usage.total_tokens":e.usage.totalTokens,"gen_ai.usage.cost":e.usage.cost.total};return e.usage.reasoning!==void 0&&(t[`gen_ai.usage.reasoning_tokens`]=e.usage.reasoning),t}function p(e){if(e.role!==`assistant`)return{"error.type":`AgentError`,outcome:`error`};let t=e.stopReason===`aborted`;return{"error.type":`ProviderError`,"error.code":e.stopReason,outcome:t?`aborted`:`error`}}function m(e){return{service:e.serviceName,backend:e.backend,endpoint:e.endpoint??`none`,endpointSource:e.endpointSource,traces:e.traces,redaction:!0,fileFallback:e.fileFallback}}function h(e,t,n={},r=!1){let a=n.env??process.env,c=n.metrics,h=new Map,g=!0,_=0,v,y,b,x=Promise.resolve(),S=e=>{x=x.then(e).catch(()=>void 0)};e.effect(()=>async()=>{g=!1,_+=1,b=void 0,h.clear(),v=void 0;let e=y;y=void 0,await e?.shutdown()},`${s}/telemetry`);let C=async(e,t,n)=>{!g||!y||(e===`debug`?await y.recordDebug(t,n):e===`warn`?await y.recordWarning(t,void 0,n):e===`error`?await y.recordError(t,void 0,n):await y.recordEvent(t,n))},w=e=>{if(!g)throw Error(`Log telemetry runtime is disposed.`);return y??=(0,o.createDoomTelemetry)({serviceName:n.serviceName??`pi`,packageName:s,cwd:e.cwd,env:a,allowFileFallback:n.allowFileFallback,telemetryFactory:n.telemetryFactory,warn:n.onDiagnostic,enableLogs:!0,enableTraces:a.AGENT_OTEL_TRACES===void 0||u(a.AGENT_OTEL_TRACES),onRecord:e=>{g&&c?.record(e.event,e.attributes)},onStatus:e=>{g&&n.onSinkStatus?.(m(e))}}),g&&n.onSinkStatus?.(m(y.status())),y},T=async e=>{let t=b;if(t){if(t.sessionManager!==e.sessionManager)throw Error(`Log readiness belongs to a stale Pi session.`);if(await t.operation,!g||t!==b)throw Error(`Log readiness belongs to a stale extension generation.`)}},E=(e,t)=>{let n=b,i=async()=>{if(n){if(!await n.operation.then(()=>!0,()=>!1)||!g||n!==b||n.sessionManager!==e.sessionManager)return}else if(!g)return;await t()};if(!r)return i();S(i)};return t.on(`session_start`,(t,n)=>{if(!g)return;let a=++_;h.clear(),v=void 0;let o=async e=>{e?.throwIfAborted(),w(n),await C(`info`,`pi.session.started`,{...d(n),"pi.session.reason":t.reason,"pi.session.previous":t.previousSessionFile!==void 0}),e?.throwIfAborted()},c=r?(0,i.readDoomReadinessCoordinator)(e):void 0;if(!c)return o();let l=b,u=(async()=>{l?.coordinator===c&&await l.operation.catch(()=>void 0),!(!g||a!==_)&&await c.start(s,`${n.sessionManager.getSessionId()}:${a}`,async e=>(await o(e),{value:void 0})).wait()})();u.catch(()=>void 0),b={sessionManager:n.sessionManager,coordinator:c,operation:u}}),t.on(`before_agent_start`,(e,t)=>{if(g)return E(t,async()=>{w(t),await C(`info`,`pi.user_prompt`,{...d(t),"pi.user_message.length":e.prompt.length,"pi.user_message.image_count":e.images?.length??0})})}),t.on(`agent_start`,(e,t)=>{if(g)return E(t,async()=>{v=Date.now(),w(t),await C(`info`,`pi.agent.started`,d(t))})}),t.on(`turn_start`,(e,t)=>{if(g)return E(t,async()=>{w(t),await C(`debug`,`pi.turn.started`,{...d(t),"pi.turn.index":e.turnIndex})})}),t.on(`turn_end`,(e,t)=>{if(g)return E(t,async()=>{w(t);let n=f(e.message);if(await C(`info`,`pi.turn.finished`,{...d(t),"pi.turn.index":e.turnIndex,"pi.tool_result.count":e.toolResults.length,...n}),e.message.role===`assistant`&&l.has(e.message.stopReason)&&await C(e.message.stopReason===`aborted`?`warn`:`error`,`pi.turn.failed`,{...d(t),"pi.turn.index":e.turnIndex,...p(e.message),...n}),n)for(let r of e.toolResults)await C(`debug`,`pi.tool_token_sample`,{...d(t),...n,"tool.name":r.toolName,token_attribution:`toolCallTurn`})})}),t.on(`tool_execution_start`,(e,t)=>{if(g)return E(t,async()=>{h.set(e.toolCallId,Date.now()),w(t),await C(`debug`,`pi.tool_call`,{...d(t),"tool.name":e.toolName,"tool.call.id":e.toolCallId})})}),t.on(`tool_execution_end`,(e,t)=>{if(g)return E(t,async()=>{let n=h.get(e.toolCallId);h.delete(e.toolCallId),w(t),await C(`info`,`pi.tool_result`,{...d(t),success:!e.isError,"tool.name":e.toolName,"tool.call.id":e.toolCallId,"tool.result.error":e.isError,...n===void 0?{}:{"tool.duration_ms":Date.now()-n}})})}),t.on(`after_provider_response`,(e,t)=>{if(g)return E(t,async()=>{w(t);let n={...d(t),"http.response.status_code":e.status};e.status>=400?await C(`error`,`pi.api_error`,n):await C(`debug`,`pi.api_response`,n)})}),t.on(`model_select`,(e,t)=>{if(g)return E(t,async()=>{w(t),await C(`info`,`pi.model.selected`,{...d(t),"pi.model.source":e.source,"gen_ai.provider.name":e.model.provider,"gen_ai.request.model":e.model.id})})}),t.on(`agent_end`,(e,t)=>{if(!g)return;let n=_;return E(t,async()=>{w(t),await C(`info`,`pi.agent.finished`,{...d(t),"pi.message.count":e.messages.length,...v===void 0?{}:{"pi.agent.duration_ms":Date.now()-v}}),!(!g||n!==_)&&(v=void 0,await y?.flush())})}),t.on(`agent_settled`,(e,t)=>{if(g)return E(t,async()=>{w(t),await C(`debug`,`pi.agent.settled`,d(t))})}),{waitForSession:T,async finishSession(e,t){if(!g)return;await x;let n=b;n&&await n.operation.catch(()=>void 0),g&&(n&&n!==b||y&&(w(t),await C(`info`,`pi.session.finished`,{...d(t),"pi.session.reason":e})))}}}function g(e,t={}){let n=e.registerLeader({source:t.source??`@agimon-ai/doompi-log`,bindings:[{id:`log.metrics`,path:[{key:`h`,label:`help`,order:70},{key:`l`,label:`logs`,detail:`telemetry`}],command:{name:c}}]});return()=>n.dispose()}async function _(e,n,r){await e.ui.custom((e,i,a,o)=>new t.LogMetricsOverlayComponent(e,i,n,o,r),{overlay:!0,overlayOptions:{anchor:`top-left`,width:`100%`,maxHeight:`100%`,margin:0}})}function v(t,r,i={},o=!1){let l=i.env??process.env,d=new e.LogMetricsAggregator,f=i.metricsSource??n.createMetricsSource({env:l}),p=!0,m,v;t.effect(()=>()=>{p=!1,m=void 0,v=void 0},`${s}/runtime`);let y=h(t,r,{...i,metrics:d,onDiagnostic:e=>{p&&(v=e,i.onDiagnostic?i.onDiagnostic(e):l.AGENT_OTEL_DIAGNOSTICS===`stderr`&&process.emitWarning(e))},onSinkStatus:e=>{p&&(m=e,i.onSinkStatus?.(e))}},o);return t.inject([a.DOOM_UI_HUB_SERVICE],e=>g((0,a.requireDoomUiHub)(e),{source:i.leaderSource})),r.registerCommand(c,{description:`Show session-local log metrics`,handler:async(e,t)=>{p&&(await y.waitForSession(t),p&&await _(t,()=>({disabled:u(l.AGENT_TELEMETRY_DISABLED)||u(l.OTEL_SDK_DISABLED),snapshot:d.snapshot(),sink:m,transport:f.lastTransport(),lastDiagnostic:v}),f.query))}}),y}function y(e,{pi:t}){let n=v(e,t,{},!0),r;t.on(`session_shutdown`,(e,t)=>(r??=n.finishSession(e.reason,t),r))}async function b(e){let t=await(0,r.connectDoomCordisHost)(e,s),n=t.root.plugin(y,{pi:e});try{await n}catch(e){try{await n.dispose()}finally{await t.dispose()}throw e}let i;e.on(`session_shutdown`,()=>i??=(async()=>{try{await n.dispose()}finally{await t.dispose()}})())}exports.doomLogExtension=b,exports.installDoomLogRuntime=v,exports.installPiTelemetryRuntime=h,exports.openLogMetricsOverlay=_,exports.registerLogMetricsLeaderBinding=g;
|
|
2
2
|
//# sourceMappingURL=extension.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.cjs","names":["createDoomTelemetry","registerDoomLeaderContribution","LogMetricsOverlayComponent","LogMetricsAggregator","createMetricsSource","Context"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["/**\n * Pi telemetry wiring for the Doom harness.\n *\n * This extension deliberately emits metadata-only records. Prompt, message,\n * tool payload, file path, diagnostic text, exception, and stack data never\n * cross the shared telemetry boundary.\n */\n\nimport { registerDoomLeaderContribution } from '@agimon-ai/doompi-extension-contracts/leader';\nimport { createDoomTelemetry, type DoomTelemetry, type DoomTelemetryOptions } from '@agimon-ai/doompi-telemetry';\nimport { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext, TurnEndEvent } from '@earendil-works/pi-coding-agent';\nimport { LogMetricsAggregator, type LogMetricsRecorder } from '../../services/metrics.ts';\nimport { LogMetricsOverlayComponent, type LogMetricsView, type SinkStatus } from '../../tui/logMetricsOverlay.ts';\nimport type { MetricsQuery, MetricsSource } from '../../types/metricsSource.ts';\nimport { createMetricsSource } from '../node/metricsSource.ts';\n\nconst SERVICE_NAME = 'pi';\nconst PACKAGE_NAME = '@agimon-ai/doompi-log';\n/** Opt back into stderr diagnostics when debugging outside a live TUI. */\nconst DIAGNOSTICS_STDERR = 'stderr';\nconst LEADER_SOURCE = '@agimon-ai/doompi-log';\nconst LOG_METRICS_COMMAND = 'log-metrics';\nconst LOG_METRICS_DESCRIPTION = 'Show session-local log metrics';\nconst HELP_GROUP_ORDER = 70;\nconst NO_ENDPOINT = 'none';\nconst API_ERROR_STATUS = 400;\nconst FAILURE_STOP_REASONS = new Set(['error', 'aborted']);\n\ntype TelemetryFactory = NonNullable<DoomTelemetryOptions['telemetryFactory']>;\ntype TelemetryAttributes = Record<string, string | number | boolean>;\ntype RecordLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport interface PiTelemetryExtensionOptions {\n allowFileFallback?: boolean;\n /** Overrides the telemetry service identity. */\n serviceName?: string;\n /** Overrides the leader contribution owner. */\n leaderSource?: string;\n env?: NodeJS.ProcessEnv;\n telemetryFactory?: TelemetryFactory;\n /** Receives every sanitized record, whether or not a sink is live. */\n metrics?: LogMetricsRecorder;\n /** Called once the sink backend is resolved, so the overlay can report it. */\n onSinkStatus?: (status: SinkStatus | undefined) => void;\n /** Overrides the sink-history transport; injected in tests. */\n metricsSource?: MetricsSource;\n /** Receives telemetry lifecycle failures without writing into the TUI. */\n onDiagnostic?: (message: string) => void;\n}\n\nexport interface PiTelemetryRuntimeHandle {\n finishSession(reason: string, ctx: ExtensionContext): Promise<void>;\n}\n\nfunction isEnabled(value: string | undefined): boolean {\n return value === '1' || value?.toLowerCase() === 'true' || value?.toLowerCase() === 'yes';\n}\n\nfunction contextAttributes(ctx: ExtensionContext): TelemetryAttributes {\n const attributes: TelemetryAttributes = {\n 'pi.mode': ctx.mode,\n 'pi.session.id': ctx.sessionManager.getSessionId(),\n };\n if (ctx.thinkingLevel) attributes['pi.thinking_level'] = ctx.thinkingLevel;\n if (ctx.model) {\n attributes['gen_ai.provider.name'] = ctx.model.provider;\n attributes['gen_ai.request.model'] = ctx.model.id;\n }\n return attributes;\n}\n\nfunction usageAttributes(message: TurnEndEvent['message']): TelemetryAttributes | undefined {\n if (message.role !== 'assistant') return undefined;\n const attributes: TelemetryAttributes = {\n 'gen_ai.response.model': message.responseModel ?? message.model,\n 'gen_ai.response.finish_reasons': message.stopReason,\n 'gen_ai.usage.input_tokens': message.usage.input,\n 'gen_ai.usage.output_tokens': message.usage.output,\n 'gen_ai.usage.cache_read_tokens': message.usage.cacheRead,\n 'gen_ai.usage.cache_write_tokens': message.usage.cacheWrite,\n 'gen_ai.usage.total_tokens': message.usage.totalTokens,\n 'gen_ai.usage.cost': message.usage.cost.total,\n };\n if (message.usage.reasoning !== undefined) attributes['gen_ai.usage.reasoning_tokens'] = message.usage.reasoning;\n return attributes;\n}\n\nfunction failureAttributes(message: TurnEndEvent['message']): TelemetryAttributes {\n if (message.role !== 'assistant') return { 'error.type': 'AgentError', outcome: 'error' };\n const aborted = message.stopReason === 'aborted';\n return {\n 'error.type': 'ProviderError',\n 'error.code': message.stopReason,\n outcome: aborted ? 'aborted' : 'error',\n };\n}\n\nfunction statusFromTelemetry(status: ReturnType<DoomTelemetry['status']>): SinkStatus {\n return {\n service: status.serviceName,\n backend: status.backend,\n endpoint: status.endpoint ?? NO_ENDPOINT,\n endpointSource: status.endpointSource,\n traces: status.traces,\n redaction: true,\n fileFallback: status.fileFallback,\n };\n}\n\nexport function installPiTelemetryRuntime(\n cordis: Context,\n pi: ExtensionAPI,\n options: PiTelemetryExtensionOptions = {},\n): PiTelemetryRuntimeHandle {\n const env = options.env ?? process.env;\n const metrics = options.metrics;\n const toolStartedAt = new Map<string, number>();\n let active = true;\n let sessionGeneration = 0;\n let agentStartedAt: number | undefined;\n let telemetry: DoomTelemetry | undefined;\n\n cordis.effect(\n () => async () => {\n active = false;\n sessionGeneration += 1;\n toolStartedAt.clear();\n agentStartedAt = undefined;\n const ownedTelemetry = telemetry;\n telemetry = undefined;\n await ownedTelemetry?.shutdown();\n },\n `${PACKAGE_NAME}/telemetry`,\n );\n\n const emit = async (level: RecordLevel, name: string, attributes: TelemetryAttributes): Promise<void> => {\n if (!active || !telemetry) return;\n if (level === 'debug') await telemetry.recordDebug(name, attributes);\n else if (level === 'warn') await telemetry.recordWarning(name, undefined, attributes);\n else if (level === 'error') await telemetry.recordError(name, undefined, attributes);\n else await telemetry.recordEvent(name, attributes);\n };\n\n const getTelemetry = (ctx: ExtensionContext): DoomTelemetry => {\n if (!active) throw new Error('Log telemetry runtime is disposed.');\n telemetry ??= createDoomTelemetry({\n serviceName: options.serviceName ?? SERVICE_NAME,\n packageName: PACKAGE_NAME,\n cwd: ctx.cwd,\n env,\n allowFileFallback: options.allowFileFallback,\n telemetryFactory: options.telemetryFactory,\n warn: options.onDiagnostic,\n enableLogs: true,\n enableTraces: env.AGENT_OTEL_TRACES === undefined || isEnabled(env.AGENT_OTEL_TRACES),\n onRecord: (record) => {\n if (active) metrics?.record(record.event, record.attributes);\n },\n onStatus: (status) => {\n if (active) options.onSinkStatus?.(statusFromTelemetry(status));\n },\n });\n if (active) options.onSinkStatus?.(statusFromTelemetry(telemetry.status()));\n return telemetry;\n };\n\n pi.on('session_start', async (event, ctx) => {\n if (!active) return;\n sessionGeneration += 1;\n toolStartedAt.clear();\n agentStartedAt = undefined;\n getTelemetry(ctx);\n await emit('info', 'pi.session.started', {\n ...contextAttributes(ctx),\n 'pi.session.reason': event.reason,\n 'pi.session.previous': event.previousSessionFile !== undefined,\n });\n });\n\n pi.on('before_agent_start', async (event, ctx) => {\n if (!active) return;\n getTelemetry(ctx);\n await emit('info', 'pi.user_prompt', {\n ...contextAttributes(ctx),\n 'pi.user_message.length': event.prompt.length,\n 'pi.user_message.image_count': event.images?.length ?? 0,\n });\n });\n\n pi.on('agent_start', async (_event, ctx) => {\n if (!active) return;\n agentStartedAt = Date.now();\n getTelemetry(ctx);\n await emit('info', 'pi.agent.started', contextAttributes(ctx));\n });\n\n pi.on('turn_start', async (event, ctx) => {\n if (!active) return;\n getTelemetry(ctx);\n await emit('debug', 'pi.turn.started', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n });\n });\n\n pi.on('turn_end', async (event, ctx) => {\n if (!active) return;\n getTelemetry(ctx);\n const usage = usageAttributes(event.message);\n await emit('info', 'pi.turn.finished', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n 'pi.tool_result.count': event.toolResults.length,\n ...usage,\n });\n if (event.message.role === 'assistant' && FAILURE_STOP_REASONS.has(event.message.stopReason)) {\n await emit(event.message.stopReason === 'aborted' ? 'warn' : 'error', 'pi.turn.failed', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n ...failureAttributes(event.message),\n ...usage,\n });\n }\n if (!usage) return;\n for (const result of event.toolResults) {\n await emit('debug', 'pi.tool_token_sample', {\n ...contextAttributes(ctx),\n ...usage,\n 'tool.name': result.toolName,\n token_attribution: 'toolCallTurn',\n });\n }\n });\n\n pi.on('tool_execution_start', async (event, ctx) => {\n if (!active) return;\n toolStartedAt.set(event.toolCallId, Date.now());\n getTelemetry(ctx);\n await emit('debug', 'pi.tool_call', {\n ...contextAttributes(ctx),\n 'tool.name': event.toolName,\n 'tool.call.id': event.toolCallId,\n });\n });\n\n pi.on('tool_execution_end', async (event, ctx) => {\n if (!active) return;\n const startedAt = toolStartedAt.get(event.toolCallId);\n toolStartedAt.delete(event.toolCallId);\n getTelemetry(ctx);\n await emit('info', 'pi.tool_result', {\n ...contextAttributes(ctx),\n success: !event.isError,\n 'tool.name': event.toolName,\n 'tool.call.id': event.toolCallId,\n 'tool.result.error': event.isError,\n ...(startedAt === undefined ? {} : { 'tool.duration_ms': Date.now() - startedAt }),\n });\n });\n\n pi.on('after_provider_response', async (event, ctx) => {\n if (!active) return;\n getTelemetry(ctx);\n const attributes = {\n ...contextAttributes(ctx),\n 'http.response.status_code': event.status,\n };\n if (event.status >= API_ERROR_STATUS) await emit('error', 'pi.api_error', attributes);\n else await emit('debug', 'pi.api_response', attributes);\n });\n\n pi.on('model_select', async (event, ctx) => {\n if (!active) return;\n getTelemetry(ctx);\n await emit('info', 'pi.model.selected', {\n ...contextAttributes(ctx),\n 'pi.model.source': event.source,\n 'gen_ai.provider.name': event.model.provider,\n 'gen_ai.request.model': event.model.id,\n });\n });\n\n pi.on('agent_end', async (event, ctx) => {\n if (!active) return;\n const generation = sessionGeneration;\n getTelemetry(ctx);\n await emit('info', 'pi.agent.finished', {\n ...contextAttributes(ctx),\n 'pi.message.count': event.messages.length,\n ...(agentStartedAt === undefined ? {} : { 'pi.agent.duration_ms': Date.now() - agentStartedAt }),\n });\n if (!active || generation !== sessionGeneration) return;\n agentStartedAt = undefined;\n await telemetry?.flush();\n });\n\n pi.on('agent_settled', async (_event, ctx) => {\n if (!active) return;\n getTelemetry(ctx);\n await emit('debug', 'pi.agent.settled', contextAttributes(ctx));\n });\n\n return {\n async finishSession(reason, ctx) {\n if (!active) return;\n getTelemetry(ctx);\n await emit('info', 'pi.session.finished', {\n ...contextAttributes(ctx),\n 'pi.session.reason': reason,\n });\n },\n };\n}\n\nexport function registerLogMetricsLeaderBinding(\n pi: Pick<ExtensionAPI, 'events' | 'on'>,\n options: { source?: string } = {},\n): () => void {\n return registerDoomLeaderContribution(pi, {\n source: options.source ?? LEADER_SOURCE,\n bindings: [\n {\n id: 'log.metrics',\n path: [\n { key: 'h', label: 'help', order: HELP_GROUP_ORDER },\n { key: 'l', label: 'logs', detail: 'telemetry' },\n ],\n command: { name: LOG_METRICS_COMMAND },\n },\n ],\n });\n}\n\nexport async function openLogMetricsOverlay(\n ctx: ExtensionContext,\n getView: () => LogMetricsView,\n query?: MetricsQuery,\n): Promise<void> {\n await ctx.ui.custom<undefined>(\n (tui, theme, _keybindings, done) => new LogMetricsOverlayComponent(tui, theme, getView, done, query),\n {\n overlay: true,\n overlayOptions: {\n anchor: 'top-left',\n width: '100%',\n maxHeight: '100%',\n margin: 0,\n },\n },\n );\n}\n\nexport function installDoomLogRuntime(\n cordis: Context,\n pi: ExtensionAPI,\n options: PiTelemetryExtensionOptions = {},\n): PiTelemetryRuntimeHandle {\n const env = options.env ?? process.env;\n const metrics = new LogMetricsAggregator();\n const metricsSource = options.metricsSource ?? createMetricsSource({ env });\n let active = true;\n let sink: SinkStatus | undefined;\n let lastDiagnostic: string | undefined;\n let disposeLeader: (() => void) | undefined;\n\n cordis.effect(\n () => () => {\n active = false;\n sink = undefined;\n lastDiagnostic = undefined;\n disposeLeader?.();\n disposeLeader = undefined;\n },\n `${PACKAGE_NAME}/runtime`,\n );\n\n const telemetryRuntime = installPiTelemetryRuntime(cordis, pi, {\n ...options,\n metrics,\n // Always supply a sink so doom-telemetry never falls back to\n // process.emitWarning, which writes raw stderr over the TUI frame.\n // Diagnostics surface in the Log Metrics overlay instead.\n onDiagnostic: (message) => {\n if (!active) return;\n lastDiagnostic = message;\n if (options.onDiagnostic) options.onDiagnostic(message);\n else if (env.AGENT_OTEL_DIAGNOSTICS === DIAGNOSTICS_STDERR) process.emitWarning(message);\n },\n onSinkStatus: (status) => {\n if (!active) return;\n sink = status;\n options.onSinkStatus?.(status);\n },\n });\n\n disposeLeader = registerLogMetricsLeaderBinding(pi, { source: options.leaderSource });\n pi.registerCommand(LOG_METRICS_COMMAND, {\n description: LOG_METRICS_DESCRIPTION,\n handler: async (_args, ctx) => {\n if (!active) return;\n await openLogMetricsOverlay(\n ctx,\n () => ({\n disabled: isEnabled(env.AGENT_TELEMETRY_DISABLED) || isEnabled(env.OTEL_SDK_DISABLED),\n snapshot: metrics.snapshot(),\n sink,\n transport: metricsSource.lastTransport(),\n lastDiagnostic,\n }),\n metricsSource.query,\n );\n },\n });\n return telemetryRuntime;\n}\n\n/** The package's single standard Pi factory. */\nexport async function doomLogExtension(pi: ExtensionAPI): Promise<void> {\n const cordis = new Context();\n let disposal: Promise<void> | undefined;\n const dispose = (): Promise<void> => {\n disposal ??= cordis.fiber.dispose();\n return disposal;\n };\n let runtime: PiTelemetryRuntimeHandle;\n try {\n runtime = installDoomLogRuntime(cordis, pi);\n } catch (error) {\n await dispose();\n throw error;\n }\n let shutdown: Promise<void> | undefined;\n pi.on('session_shutdown', (event, ctx) => {\n shutdown ??= (async () => {\n await runtime.finishSession(event.reason, ctx);\n await dispose();\n })();\n return shutdown;\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extension.cjs","names":["createDoomTelemetry","readDoomReadinessCoordinator","LogMetricsOverlayComponent","LogMetricsAggregator","createMetricsSource","DOOM_UI_HUB_SERVICE","requireDoomUiHub","connectDoomCordisHost"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["/**\n * Pi telemetry wiring for the Doom harness.\n *\n * This extension deliberately emits metadata-only records. Prompt, message,\n * tool payload, file path, diagnostic text, exception, and stack data never\n * cross the shared telemetry boundary.\n */\n\nimport { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport {\n type DoomReadinessCoordinator,\n readDoomReadinessCoordinator,\n} from '@agimon-ai/doompi-extension-contracts/readiness';\nimport {\n DOOM_UI_HUB_SERVICE,\n type DoomUiHubService,\n requireDoomUiHub,\n} from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport { createDoomTelemetry, type DoomTelemetry, type DoomTelemetryOptions } from '@agimon-ai/doompi-telemetry';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext, TurnEndEvent } from '@earendil-works/pi-coding-agent';\nimport { LogMetricsAggregator, type LogMetricsRecorder } from '../../services/metrics.ts';\nimport { LogMetricsOverlayComponent, type LogMetricsView, type SinkStatus } from '../../tui/logMetricsOverlay.ts';\nimport type { MetricsQuery, MetricsSource } from '../../types/metricsSource.ts';\nimport { createMetricsSource } from '../node/metricsSource.ts';\n\nconst SERVICE_NAME = 'pi';\nconst PACKAGE_NAME = '@agimon-ai/doompi-log';\n/** Opt back into stderr diagnostics when debugging outside a live TUI. */\nconst DIAGNOSTICS_STDERR = 'stderr';\nconst LEADER_SOURCE = '@agimon-ai/doompi-log';\nconst LOG_METRICS_COMMAND = 'log-metrics';\nconst LOG_METRICS_DESCRIPTION = 'Show session-local log metrics';\nconst HELP_GROUP_ORDER = 70;\nconst NO_ENDPOINT = 'none';\nconst API_ERROR_STATUS = 400;\nconst FAILURE_STOP_REASONS = new Set(['error', 'aborted']);\n\ntype TelemetryFactory = NonNullable<DoomTelemetryOptions['telemetryFactory']>;\ntype TelemetryAttributes = Record<string, string | number | boolean>;\ntype RecordLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport interface PiTelemetryExtensionOptions {\n allowFileFallback?: boolean;\n /** Overrides the telemetry service identity. */\n serviceName?: string;\n /** Overrides the leader contribution owner. */\n leaderSource?: string;\n env?: NodeJS.ProcessEnv;\n telemetryFactory?: TelemetryFactory;\n /** Receives every sanitized record, whether or not a sink is live. */\n metrics?: LogMetricsRecorder;\n /** Called once the sink backend is resolved, so the overlay can report it. */\n onSinkStatus?: (status: SinkStatus | undefined) => void;\n /** Overrides the sink-history transport; injected in tests. */\n metricsSource?: MetricsSource;\n /** Receives telemetry lifecycle failures without writing into the TUI. */\n onDiagnostic?: (message: string) => void;\n}\n\nexport interface PiTelemetryRuntimeHandle {\n waitForSession(ctx: ExtensionContext): Promise<void>;\n finishSession(reason: string, ctx: ExtensionContext): Promise<void>;\n}\n\nfunction isEnabled(value: string | undefined): boolean {\n return value === '1' || value?.toLowerCase() === 'true' || value?.toLowerCase() === 'yes';\n}\n\nfunction contextAttributes(ctx: ExtensionContext): TelemetryAttributes {\n const attributes: TelemetryAttributes = {\n 'pi.mode': ctx.mode,\n 'pi.session.id': ctx.sessionManager.getSessionId(),\n };\n if (ctx.thinkingLevel) attributes['pi.thinking_level'] = ctx.thinkingLevel;\n if (ctx.model) {\n attributes['gen_ai.provider.name'] = ctx.model.provider;\n attributes['gen_ai.request.model'] = ctx.model.id;\n }\n return attributes;\n}\n\nfunction usageAttributes(message: TurnEndEvent['message']): TelemetryAttributes | undefined {\n if (message.role !== 'assistant') return undefined;\n const attributes: TelemetryAttributes = {\n 'gen_ai.response.model': message.responseModel ?? message.model,\n 'gen_ai.response.finish_reasons': message.stopReason,\n 'gen_ai.usage.input_tokens': message.usage.input,\n 'gen_ai.usage.output_tokens': message.usage.output,\n 'gen_ai.usage.cache_read_tokens': message.usage.cacheRead,\n 'gen_ai.usage.cache_write_tokens': message.usage.cacheWrite,\n 'gen_ai.usage.total_tokens': message.usage.totalTokens,\n 'gen_ai.usage.cost': message.usage.cost.total,\n };\n if (message.usage.reasoning !== undefined) attributes['gen_ai.usage.reasoning_tokens'] = message.usage.reasoning;\n return attributes;\n}\n\nfunction failureAttributes(message: TurnEndEvent['message']): TelemetryAttributes {\n if (message.role !== 'assistant') return { 'error.type': 'AgentError', outcome: 'error' };\n const aborted = message.stopReason === 'aborted';\n return {\n 'error.type': 'ProviderError',\n 'error.code': message.stopReason,\n outcome: aborted ? 'aborted' : 'error',\n };\n}\n\nfunction statusFromTelemetry(status: ReturnType<DoomTelemetry['status']>): SinkStatus {\n return {\n service: status.serviceName,\n backend: status.backend,\n endpoint: status.endpoint ?? NO_ENDPOINT,\n endpointSource: status.endpointSource,\n traces: status.traces,\n redaction: true,\n fileFallback: status.fileFallback,\n };\n}\n\nexport function installPiTelemetryRuntime(\n cordis: Context,\n pi: ExtensionAPI,\n options: PiTelemetryExtensionOptions = {},\n splitReadiness = false,\n): PiTelemetryRuntimeHandle {\n const env = options.env ?? process.env;\n const metrics = options.metrics;\n const toolStartedAt = new Map<string, number>();\n let active = true;\n let sessionGeneration = 0;\n let agentStartedAt: number | undefined;\n let telemetry: DoomTelemetry | undefined;\n let sessionReadiness:\n | {\n readonly sessionManager: object;\n readonly coordinator: DoomReadinessCoordinator;\n readonly operation: Promise<void>;\n }\n | undefined;\n let backgroundQueue = Promise.resolve();\n\n const enqueue = (operation: () => Promise<void>): void => {\n const scheduled = backgroundQueue.then(operation);\n // Telemetry is advisory and the coordinator owns startup failure notification.\n backgroundQueue = scheduled.catch(() => undefined);\n };\n\n cordis.effect(\n () => async () => {\n active = false;\n sessionGeneration += 1;\n sessionReadiness = undefined;\n toolStartedAt.clear();\n agentStartedAt = undefined;\n const ownedTelemetry = telemetry;\n telemetry = undefined;\n await ownedTelemetry?.shutdown();\n },\n `${PACKAGE_NAME}/telemetry`,\n );\n\n const emit = async (level: RecordLevel, name: string, attributes: TelemetryAttributes): Promise<void> => {\n if (!active || !telemetry) return;\n if (level === 'debug') await telemetry.recordDebug(name, attributes);\n else if (level === 'warn') await telemetry.recordWarning(name, undefined, attributes);\n else if (level === 'error') await telemetry.recordError(name, undefined, attributes);\n else await telemetry.recordEvent(name, attributes);\n };\n\n const getTelemetry = (ctx: ExtensionContext): DoomTelemetry => {\n if (!active) throw new Error('Log telemetry runtime is disposed.');\n telemetry ??= createDoomTelemetry({\n serviceName: options.serviceName ?? SERVICE_NAME,\n packageName: PACKAGE_NAME,\n cwd: ctx.cwd,\n env,\n allowFileFallback: options.allowFileFallback,\n telemetryFactory: options.telemetryFactory,\n warn: options.onDiagnostic,\n enableLogs: true,\n enableTraces: env.AGENT_OTEL_TRACES === undefined || isEnabled(env.AGENT_OTEL_TRACES),\n onRecord: (record) => {\n if (active) metrics?.record(record.event, record.attributes);\n },\n onStatus: (status) => {\n if (active) options.onSinkStatus?.(statusFromTelemetry(status));\n },\n });\n if (active) options.onSinkStatus?.(statusFromTelemetry(telemetry.status()));\n return telemetry;\n };\n\n const waitForSession = async (ctx: ExtensionContext): Promise<void> => {\n const current = sessionReadiness;\n if (!current) return;\n if (current.sessionManager !== ctx.sessionManager) {\n throw new Error('Log readiness belongs to a stale Pi session.');\n }\n await current.operation;\n if (!active || current !== sessionReadiness) {\n throw new Error('Log readiness belongs to a stale extension generation.');\n }\n };\n\n const enqueueForSession = (ctx: ExtensionContext, operation: () => Promise<void>): void | Promise<void> => {\n const expected = sessionReadiness;\n const run = async (): Promise<void> => {\n if (expected) {\n const ready = await expected.operation.then(\n () => true,\n () => false,\n );\n if (!ready || !active || expected !== sessionReadiness || expected.sessionManager !== ctx.sessionManager) {\n return;\n }\n } else if (!active) {\n return;\n }\n await operation();\n };\n if (!splitReadiness) return run();\n enqueue(run);\n return undefined;\n };\n\n pi.on('session_start', (event, ctx) => {\n if (!active) return undefined;\n const ownGeneration = ++sessionGeneration;\n toolStartedAt.clear();\n agentStartedAt = undefined;\n const initialize = async (signal?: AbortSignal): Promise<void> => {\n signal?.throwIfAborted();\n getTelemetry(ctx);\n await emit('info', 'pi.session.started', {\n ...contextAttributes(ctx),\n 'pi.session.reason': event.reason,\n 'pi.session.previous': event.previousSessionFile !== undefined,\n });\n signal?.throwIfAborted();\n };\n const coordinator = splitReadiness ? readDoomReadinessCoordinator(cordis) : undefined;\n if (!coordinator) return initialize();\n\n const previous = sessionReadiness;\n const operation = (async (): Promise<void> => {\n if (previous?.coordinator === coordinator) await previous.operation.catch(() => undefined);\n if (!active || ownGeneration !== sessionGeneration) return;\n const handle = coordinator.start(\n PACKAGE_NAME,\n `${ctx.sessionManager.getSessionId()}:${ownGeneration}`,\n async (signal) => {\n await initialize(signal);\n return { value: undefined };\n },\n );\n await handle.wait();\n })();\n // Config's coordinator owns the single user-facing failure notification.\n void operation.catch(() => undefined);\n sessionReadiness = { sessionManager: ctx.sessionManager, coordinator, operation };\n return undefined;\n });\n\n pi.on('before_agent_start', (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('info', 'pi.user_prompt', {\n ...contextAttributes(ctx),\n 'pi.user_message.length': event.prompt.length,\n 'pi.user_message.image_count': event.images?.length ?? 0,\n });\n });\n });\n\n pi.on('agent_start', (_event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n agentStartedAt = Date.now();\n getTelemetry(ctx);\n await emit('info', 'pi.agent.started', contextAttributes(ctx));\n });\n });\n\n pi.on('turn_start', (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('debug', 'pi.turn.started', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n });\n });\n });\n\n pi.on('turn_end', (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n const usage = usageAttributes(event.message);\n await emit('info', 'pi.turn.finished', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n 'pi.tool_result.count': event.toolResults.length,\n ...usage,\n });\n if (event.message.role === 'assistant' && FAILURE_STOP_REASONS.has(event.message.stopReason)) {\n await emit(event.message.stopReason === 'aborted' ? 'warn' : 'error', 'pi.turn.failed', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n ...failureAttributes(event.message),\n ...usage,\n });\n }\n if (!usage) return;\n for (const result of event.toolResults) {\n await emit('debug', 'pi.tool_token_sample', {\n ...contextAttributes(ctx),\n ...usage,\n 'tool.name': result.toolName,\n token_attribution: 'toolCallTurn',\n });\n }\n });\n });\n\n pi.on('tool_execution_start', (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n toolStartedAt.set(event.toolCallId, Date.now());\n getTelemetry(ctx);\n await emit('debug', 'pi.tool_call', {\n ...contextAttributes(ctx),\n 'tool.name': event.toolName,\n 'tool.call.id': event.toolCallId,\n });\n });\n });\n\n pi.on('tool_execution_end', (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n const startedAt = toolStartedAt.get(event.toolCallId);\n toolStartedAt.delete(event.toolCallId);\n getTelemetry(ctx);\n await emit('info', 'pi.tool_result', {\n ...contextAttributes(ctx),\n success: !event.isError,\n 'tool.name': event.toolName,\n 'tool.call.id': event.toolCallId,\n 'tool.result.error': event.isError,\n ...(startedAt === undefined ? {} : { 'tool.duration_ms': Date.now() - startedAt }),\n });\n });\n });\n\n pi.on('after_provider_response', (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n const attributes = {\n ...contextAttributes(ctx),\n 'http.response.status_code': event.status,\n };\n if (event.status >= API_ERROR_STATUS) await emit('error', 'pi.api_error', attributes);\n else await emit('debug', 'pi.api_response', attributes);\n });\n });\n\n pi.on('model_select', (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('info', 'pi.model.selected', {\n ...contextAttributes(ctx),\n 'pi.model.source': event.source,\n 'gen_ai.provider.name': event.model.provider,\n 'gen_ai.request.model': event.model.id,\n });\n });\n });\n\n pi.on('agent_end', (event, ctx) => {\n if (!active) return;\n const generation = sessionGeneration;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('info', 'pi.agent.finished', {\n ...contextAttributes(ctx),\n 'pi.message.count': event.messages.length,\n ...(agentStartedAt === undefined ? {} : { 'pi.agent.duration_ms': Date.now() - agentStartedAt }),\n });\n if (!active || generation !== sessionGeneration) return;\n agentStartedAt = undefined;\n await telemetry?.flush();\n });\n });\n\n pi.on('agent_settled', (_event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('debug', 'pi.agent.settled', contextAttributes(ctx));\n });\n });\n\n return {\n waitForSession,\n async finishSession(reason, ctx) {\n if (!active) return;\n await backgroundQueue;\n const current = sessionReadiness;\n if (current) await current.operation.catch(() => undefined);\n if (!active) return;\n if (current && current !== sessionReadiness) return;\n if (!telemetry) return;\n getTelemetry(ctx);\n await emit('info', 'pi.session.finished', {\n ...contextAttributes(ctx),\n 'pi.session.reason': reason,\n });\n },\n };\n}\n\nexport function registerLogMetricsLeaderBinding(hub: DoomUiHubService, options: { source?: string } = {}): () => void {\n const contribution = hub.registerLeader({\n source: options.source ?? LEADER_SOURCE,\n bindings: [\n {\n id: 'log.metrics',\n path: [\n { key: 'h', label: 'help', order: HELP_GROUP_ORDER },\n { key: 'l', label: 'logs', detail: 'telemetry' },\n ],\n command: { name: LOG_METRICS_COMMAND },\n },\n ],\n });\n return () => contribution.dispose();\n}\n\nexport async function openLogMetricsOverlay(\n ctx: ExtensionContext,\n getView: () => LogMetricsView,\n query?: MetricsQuery,\n): Promise<void> {\n await ctx.ui.custom<undefined>(\n (tui, theme, _keybindings, done) => new LogMetricsOverlayComponent(tui, theme, getView, done, query),\n {\n overlay: true,\n overlayOptions: {\n anchor: 'top-left',\n width: '100%',\n maxHeight: '100%',\n margin: 0,\n },\n },\n );\n}\n\nexport function installDoomLogRuntime(\n cordis: Context,\n pi: ExtensionAPI,\n options: PiTelemetryExtensionOptions = {},\n splitReadiness = false,\n): PiTelemetryRuntimeHandle {\n const env = options.env ?? process.env;\n const metrics = new LogMetricsAggregator();\n const metricsSource = options.metricsSource ?? createMetricsSource({ env });\n let active = true;\n let sink: SinkStatus | undefined;\n let lastDiagnostic: string | undefined;\n\n cordis.effect(\n () => () => {\n active = false;\n sink = undefined;\n lastDiagnostic = undefined;\n },\n `${PACKAGE_NAME}/runtime`,\n );\n\n const telemetryRuntime = installPiTelemetryRuntime(\n cordis,\n pi,\n {\n ...options,\n metrics,\n // Always supply a sink so doom-telemetry never falls back to\n // process.emitWarning, which writes raw stderr over the TUI frame.\n // Diagnostics surface in the Log Metrics overlay instead.\n onDiagnostic: (message) => {\n if (!active) return;\n lastDiagnostic = message;\n if (options.onDiagnostic) options.onDiagnostic(message);\n else if (env.AGENT_OTEL_DIAGNOSTICS === DIAGNOSTICS_STDERR) process.emitWarning(message);\n },\n onSinkStatus: (status) => {\n if (!active) return;\n sink = status;\n options.onSinkStatus?.(status);\n },\n },\n splitReadiness,\n );\n\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n return registerLogMetricsLeaderBinding(requireDoomUiHub(uiContext), { source: options.leaderSource });\n });\n pi.registerCommand(LOG_METRICS_COMMAND, {\n description: LOG_METRICS_DESCRIPTION,\n handler: async (_args, ctx) => {\n if (!active) return;\n await telemetryRuntime.waitForSession(ctx);\n if (!active) return;\n await openLogMetricsOverlay(\n ctx,\n () => ({\n disabled: isEnabled(env.AGENT_TELEMETRY_DISABLED) || isEnabled(env.OTEL_SDK_DISABLED),\n snapshot: metrics.snapshot(),\n sink,\n transport: metricsSource.lastTransport(),\n lastDiagnostic,\n }),\n metricsSource.query,\n );\n },\n });\n return telemetryRuntime;\n}\n\ninterface LogPluginConfig {\n readonly pi: ExtensionAPI;\n}\n\nfunction logPlugin(cordis: Context, { pi }: LogPluginConfig): void {\n const runtime = installDoomLogRuntime(cordis, pi, {}, true);\n let shutdown: Promise<void> | undefined;\n pi.on('session_shutdown', (event, ctx) => {\n shutdown ??= runtime.finishSession(event.reason, ctx);\n return shutdown;\n });\n}\n\n/** The package's single standard Pi factory. */\nexport async function doomLogExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, PACKAGE_NAME);\n const fiber = connection.root.plugin(logPlugin, { pi });\n try {\n await fiber;\n } catch (error) {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n throw error;\n }\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LogMetricsRecorder } from "../../services/metrics.cjs";
|
|
2
2
|
import { MetricsQuery, MetricsSource } from "../../types/metricsSource.cjs";
|
|
3
3
|
import { LogMetricsView, SinkStatus } from "../../tui/logMetricsOverlay.cjs";
|
|
4
|
+
import { DoomUiHubService } from "@agimon-ai/doompi-extension-contracts/ui-hub";
|
|
4
5
|
import { DoomTelemetryOptions } from "@agimon-ai/doompi-telemetry";
|
|
5
6
|
import { Context } from "@deepseek-ai/cordis";
|
|
6
7
|
import { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
@@ -24,13 +25,14 @@ interface PiTelemetryExtensionOptions {
|
|
|
24
25
|
onDiagnostic?: (message: string) => void;
|
|
25
26
|
}
|
|
26
27
|
interface PiTelemetryRuntimeHandle {
|
|
28
|
+
waitForSession(ctx: ExtensionContext): Promise<void>;
|
|
27
29
|
finishSession(reason: string, ctx: ExtensionContext): Promise<void>;
|
|
28
30
|
}
|
|
29
|
-
declare function registerLogMetricsLeaderBinding(
|
|
31
|
+
declare function registerLogMetricsLeaderBinding(hub: DoomUiHubService, options?: {
|
|
30
32
|
source?: string;
|
|
31
33
|
}): () => void;
|
|
32
34
|
declare function openLogMetricsOverlay(ctx: ExtensionContext, getView: () => LogMetricsView, query?: MetricsQuery): Promise<void>;
|
|
33
|
-
declare function installDoomLogRuntime(cordis: Context, pi: ExtensionAPI, options?: PiTelemetryExtensionOptions): PiTelemetryRuntimeHandle;
|
|
35
|
+
declare function installDoomLogRuntime(cordis: Context, pi: ExtensionAPI, options?: PiTelemetryExtensionOptions, splitReadiness?: boolean): PiTelemetryRuntimeHandle;
|
|
34
36
|
/** The package's single standard Pi factory. */
|
|
35
37
|
declare function doomLogExtension(pi: ExtensionAPI): Promise<void>;
|
|
36
38
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;;;;;KAsCK,mBAAmB,YAAY;UAInB;EACf;;EAEA;;EAEA;EACA,MAAM,OAAO;EACb,mBAAmB;;EAEnB,UAAU;;EAEV,gBAAgB,QAAQ;;EAExB,gBAAgB;;EAEhB,gBAAgB;;UAGD;EACf,eAAe,KAAK,mBAAmB;EACvC,cAAc,gBAAgB,KAAK,mBAAmB;;iBA4WxC,gCAAgC,KAAK,kBAAkB;EAAW;;iBAiB5D,sBACpB,KAAK,kBACL,eAAe,gBACf,QAAQ,eACP;iBAea,sBACd,QAAQ,SACR,IAAI,cACJ,UAAS,6BACT,2BACC;;iBAgFmB,iBAAiB,IAAI,eAAe"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LogMetricsRecorder } from "../../services/metrics.mjs";
|
|
2
2
|
import { MetricsQuery, MetricsSource } from "../../types/metricsSource.mjs";
|
|
3
3
|
import { LogMetricsView, SinkStatus } from "../../tui/logMetricsOverlay.mjs";
|
|
4
|
+
import { DoomUiHubService } from "@agimon-ai/doompi-extension-contracts/ui-hub";
|
|
4
5
|
import { DoomTelemetryOptions } from "@agimon-ai/doompi-telemetry";
|
|
5
6
|
import { Context } from "@deepseek-ai/cordis";
|
|
6
7
|
import { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
@@ -24,13 +25,14 @@ interface PiTelemetryExtensionOptions {
|
|
|
24
25
|
onDiagnostic?: (message: string) => void;
|
|
25
26
|
}
|
|
26
27
|
interface PiTelemetryRuntimeHandle {
|
|
28
|
+
waitForSession(ctx: ExtensionContext): Promise<void>;
|
|
27
29
|
finishSession(reason: string, ctx: ExtensionContext): Promise<void>;
|
|
28
30
|
}
|
|
29
|
-
declare function registerLogMetricsLeaderBinding(
|
|
31
|
+
declare function registerLogMetricsLeaderBinding(hub: DoomUiHubService, options?: {
|
|
30
32
|
source?: string;
|
|
31
33
|
}): () => void;
|
|
32
34
|
declare function openLogMetricsOverlay(ctx: ExtensionContext, getView: () => LogMetricsView, query?: MetricsQuery): Promise<void>;
|
|
33
|
-
declare function installDoomLogRuntime(cordis: Context, pi: ExtensionAPI, options?: PiTelemetryExtensionOptions): PiTelemetryRuntimeHandle;
|
|
35
|
+
declare function installDoomLogRuntime(cordis: Context, pi: ExtensionAPI, options?: PiTelemetryExtensionOptions, splitReadiness?: boolean): PiTelemetryRuntimeHandle;
|
|
34
36
|
/** The package's single standard Pi factory. */
|
|
35
37
|
declare function doomLogExtension(pi: ExtensionAPI): Promise<void>;
|
|
36
38
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;;;;;KAsCK,mBAAmB,YAAY;UAInB;EACf;;EAEA;;EAEA;EACA,MAAM,OAAO;EACb,mBAAmB;;EAEnB,UAAU;;EAEV,gBAAgB,QAAQ;;EAExB,gBAAgB;;EAEhB,gBAAgB;;UAGD;EACf,eAAe,KAAK,mBAAmB;EACvC,cAAc,gBAAgB,KAAK,mBAAmB;;iBA4WxC,gCAAgC,KAAK,kBAAkB;EAAW;;iBAiB5D,sBACpB,KAAK,kBACL,eAAe,gBACf,QAAQ,eACP;iBAea,sBACd,QAAQ,SACR,IAAI,cACJ,UAAS,6BACT,2BACC;;iBAgFmB,iBAAiB,IAAI,eAAe"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{LogMetricsAggregator as e}from"../../services/metrics.mjs";import{LogMetricsOverlayComponent as t}from"../../tui/logMetricsOverlay.mjs";import{createMetricsSource as n}from"../node/metricsSource.mjs";import{
|
|
1
|
+
import{LogMetricsAggregator as e}from"../../services/metrics.mjs";import{LogMetricsOverlayComponent as t}from"../../tui/logMetricsOverlay.mjs";import{createMetricsSource as n}from"../node/metricsSource.mjs";import{connectDoomCordisHost as r}from"@agimon-ai/doompi-extension-contracts/cordis-host";import{readDoomReadinessCoordinator as i}from"@agimon-ai/doompi-extension-contracts/readiness";import{DOOM_UI_HUB_SERVICE as a,requireDoomUiHub as o}from"@agimon-ai/doompi-extension-contracts/ui-hub";import{createDoomTelemetry as s}from"@agimon-ai/doompi-telemetry";const c=`@agimon-ai/doompi-log`,l=`log-metrics`,u=new Set([`error`,`aborted`]);function d(e){return e===`1`||e?.toLowerCase()===`true`||e?.toLowerCase()===`yes`}function f(e){let t={"pi.mode":e.mode,"pi.session.id":e.sessionManager.getSessionId()};return e.thinkingLevel&&(t[`pi.thinking_level`]=e.thinkingLevel),e.model&&(t[`gen_ai.provider.name`]=e.model.provider,t[`gen_ai.request.model`]=e.model.id),t}function p(e){if(e.role!==`assistant`)return;let t={"gen_ai.response.model":e.responseModel??e.model,"gen_ai.response.finish_reasons":e.stopReason,"gen_ai.usage.input_tokens":e.usage.input,"gen_ai.usage.output_tokens":e.usage.output,"gen_ai.usage.cache_read_tokens":e.usage.cacheRead,"gen_ai.usage.cache_write_tokens":e.usage.cacheWrite,"gen_ai.usage.total_tokens":e.usage.totalTokens,"gen_ai.usage.cost":e.usage.cost.total};return e.usage.reasoning!==void 0&&(t[`gen_ai.usage.reasoning_tokens`]=e.usage.reasoning),t}function m(e){if(e.role!==`assistant`)return{"error.type":`AgentError`,outcome:`error`};let t=e.stopReason===`aborted`;return{"error.type":`ProviderError`,"error.code":e.stopReason,outcome:t?`aborted`:`error`}}function h(e){return{service:e.serviceName,backend:e.backend,endpoint:e.endpoint??`none`,endpointSource:e.endpointSource,traces:e.traces,redaction:!0,fileFallback:e.fileFallback}}function g(e,t,n={},r=!1){let a=n.env??process.env,o=n.metrics,l=new Map,g=!0,_=0,v,y,b,x=Promise.resolve(),S=e=>{x=x.then(e).catch(()=>void 0)};e.effect(()=>async()=>{g=!1,_+=1,b=void 0,l.clear(),v=void 0;let e=y;y=void 0,await e?.shutdown()},`${c}/telemetry`);let C=async(e,t,n)=>{!g||!y||(e===`debug`?await y.recordDebug(t,n):e===`warn`?await y.recordWarning(t,void 0,n):e===`error`?await y.recordError(t,void 0,n):await y.recordEvent(t,n))},w=e=>{if(!g)throw Error(`Log telemetry runtime is disposed.`);return y??=s({serviceName:n.serviceName??`pi`,packageName:c,cwd:e.cwd,env:a,allowFileFallback:n.allowFileFallback,telemetryFactory:n.telemetryFactory,warn:n.onDiagnostic,enableLogs:!0,enableTraces:a.AGENT_OTEL_TRACES===void 0||d(a.AGENT_OTEL_TRACES),onRecord:e=>{g&&o?.record(e.event,e.attributes)},onStatus:e=>{g&&n.onSinkStatus?.(h(e))}}),g&&n.onSinkStatus?.(h(y.status())),y},T=async e=>{let t=b;if(t){if(t.sessionManager!==e.sessionManager)throw Error(`Log readiness belongs to a stale Pi session.`);if(await t.operation,!g||t!==b)throw Error(`Log readiness belongs to a stale extension generation.`)}},E=(e,t)=>{let n=b,i=async()=>{if(n){if(!await n.operation.then(()=>!0,()=>!1)||!g||n!==b||n.sessionManager!==e.sessionManager)return}else if(!g)return;await t()};if(!r)return i();S(i)};return t.on(`session_start`,(t,n)=>{if(!g)return;let a=++_;l.clear(),v=void 0;let o=async e=>{e?.throwIfAborted(),w(n),await C(`info`,`pi.session.started`,{...f(n),"pi.session.reason":t.reason,"pi.session.previous":t.previousSessionFile!==void 0}),e?.throwIfAborted()},s=r?i(e):void 0;if(!s)return o();let u=b,d=(async()=>{u?.coordinator===s&&await u.operation.catch(()=>void 0),!(!g||a!==_)&&await s.start(c,`${n.sessionManager.getSessionId()}:${a}`,async e=>(await o(e),{value:void 0})).wait()})();d.catch(()=>void 0),b={sessionManager:n.sessionManager,coordinator:s,operation:d}}),t.on(`before_agent_start`,(e,t)=>{if(g)return E(t,async()=>{w(t),await C(`info`,`pi.user_prompt`,{...f(t),"pi.user_message.length":e.prompt.length,"pi.user_message.image_count":e.images?.length??0})})}),t.on(`agent_start`,(e,t)=>{if(g)return E(t,async()=>{v=Date.now(),w(t),await C(`info`,`pi.agent.started`,f(t))})}),t.on(`turn_start`,(e,t)=>{if(g)return E(t,async()=>{w(t),await C(`debug`,`pi.turn.started`,{...f(t),"pi.turn.index":e.turnIndex})})}),t.on(`turn_end`,(e,t)=>{if(g)return E(t,async()=>{w(t);let n=p(e.message);if(await C(`info`,`pi.turn.finished`,{...f(t),"pi.turn.index":e.turnIndex,"pi.tool_result.count":e.toolResults.length,...n}),e.message.role===`assistant`&&u.has(e.message.stopReason)&&await C(e.message.stopReason===`aborted`?`warn`:`error`,`pi.turn.failed`,{...f(t),"pi.turn.index":e.turnIndex,...m(e.message),...n}),n)for(let r of e.toolResults)await C(`debug`,`pi.tool_token_sample`,{...f(t),...n,"tool.name":r.toolName,token_attribution:`toolCallTurn`})})}),t.on(`tool_execution_start`,(e,t)=>{if(g)return E(t,async()=>{l.set(e.toolCallId,Date.now()),w(t),await C(`debug`,`pi.tool_call`,{...f(t),"tool.name":e.toolName,"tool.call.id":e.toolCallId})})}),t.on(`tool_execution_end`,(e,t)=>{if(g)return E(t,async()=>{let n=l.get(e.toolCallId);l.delete(e.toolCallId),w(t),await C(`info`,`pi.tool_result`,{...f(t),success:!e.isError,"tool.name":e.toolName,"tool.call.id":e.toolCallId,"tool.result.error":e.isError,...n===void 0?{}:{"tool.duration_ms":Date.now()-n}})})}),t.on(`after_provider_response`,(e,t)=>{if(g)return E(t,async()=>{w(t);let n={...f(t),"http.response.status_code":e.status};e.status>=400?await C(`error`,`pi.api_error`,n):await C(`debug`,`pi.api_response`,n)})}),t.on(`model_select`,(e,t)=>{if(g)return E(t,async()=>{w(t),await C(`info`,`pi.model.selected`,{...f(t),"pi.model.source":e.source,"gen_ai.provider.name":e.model.provider,"gen_ai.request.model":e.model.id})})}),t.on(`agent_end`,(e,t)=>{if(!g)return;let n=_;return E(t,async()=>{w(t),await C(`info`,`pi.agent.finished`,{...f(t),"pi.message.count":e.messages.length,...v===void 0?{}:{"pi.agent.duration_ms":Date.now()-v}}),!(!g||n!==_)&&(v=void 0,await y?.flush())})}),t.on(`agent_settled`,(e,t)=>{if(g)return E(t,async()=>{w(t),await C(`debug`,`pi.agent.settled`,f(t))})}),{waitForSession:T,async finishSession(e,t){if(!g)return;await x;let n=b;n&&await n.operation.catch(()=>void 0),g&&(n&&n!==b||y&&(w(t),await C(`info`,`pi.session.finished`,{...f(t),"pi.session.reason":e})))}}}function _(e,t={}){let n=e.registerLeader({source:t.source??`@agimon-ai/doompi-log`,bindings:[{id:`log.metrics`,path:[{key:`h`,label:`help`,order:70},{key:`l`,label:`logs`,detail:`telemetry`}],command:{name:l}}]});return()=>n.dispose()}async function v(e,n,r){await e.ui.custom((e,i,a,o)=>new t(e,i,n,o,r),{overlay:!0,overlayOptions:{anchor:`top-left`,width:`100%`,maxHeight:`100%`,margin:0}})}function y(t,r,i={},s=!1){let u=i.env??process.env,f=new e,p=i.metricsSource??n({env:u}),m=!0,h,y;t.effect(()=>()=>{m=!1,h=void 0,y=void 0},`${c}/runtime`);let b=g(t,r,{...i,metrics:f,onDiagnostic:e=>{m&&(y=e,i.onDiagnostic?i.onDiagnostic(e):u.AGENT_OTEL_DIAGNOSTICS===`stderr`&&process.emitWarning(e))},onSinkStatus:e=>{m&&(h=e,i.onSinkStatus?.(e))}},s);return t.inject([a],e=>_(o(e),{source:i.leaderSource})),r.registerCommand(l,{description:`Show session-local log metrics`,handler:async(e,t)=>{m&&(await b.waitForSession(t),m&&await v(t,()=>({disabled:d(u.AGENT_TELEMETRY_DISABLED)||d(u.OTEL_SDK_DISABLED),snapshot:f.snapshot(),sink:h,transport:p.lastTransport(),lastDiagnostic:y}),p.query))}}),b}function b(e,{pi:t}){let n=y(e,t,{},!0),r;t.on(`session_shutdown`,(e,t)=>(r??=n.finishSession(e.reason,t),r))}async function x(e){let t=await r(e,c),n=t.root.plugin(b,{pi:e});try{await n}catch(e){try{await n.dispose()}finally{await t.dispose()}throw e}let i;e.on(`session_shutdown`,()=>i??=(async()=>{try{await n.dispose()}finally{await t.dispose()}})())}export{x as doomLogExtension,y as installDoomLogRuntime,g as installPiTelemetryRuntime,v as openLogMetricsOverlay,_ as registerLogMetricsLeaderBinding};
|
|
2
2
|
//# sourceMappingURL=extension.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["/**\n * Pi telemetry wiring for the Doom harness.\n *\n * This extension deliberately emits metadata-only records. Prompt, message,\n * tool payload, file path, diagnostic text, exception, and stack data never\n * cross the shared telemetry boundary.\n */\n\nimport { registerDoomLeaderContribution } from '@agimon-ai/doompi-extension-contracts/leader';\nimport { createDoomTelemetry, type DoomTelemetry, type DoomTelemetryOptions } from '@agimon-ai/doompi-telemetry';\nimport { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext, TurnEndEvent } from '@earendil-works/pi-coding-agent';\nimport { LogMetricsAggregator, type LogMetricsRecorder } from '../../services/metrics.ts';\nimport { LogMetricsOverlayComponent, type LogMetricsView, type SinkStatus } from '../../tui/logMetricsOverlay.ts';\nimport type { MetricsQuery, MetricsSource } from '../../types/metricsSource.ts';\nimport { createMetricsSource } from '../node/metricsSource.ts';\n\nconst SERVICE_NAME = 'pi';\nconst PACKAGE_NAME = '@agimon-ai/doompi-log';\n/** Opt back into stderr diagnostics when debugging outside a live TUI. */\nconst DIAGNOSTICS_STDERR = 'stderr';\nconst LEADER_SOURCE = '@agimon-ai/doompi-log';\nconst LOG_METRICS_COMMAND = 'log-metrics';\nconst LOG_METRICS_DESCRIPTION = 'Show session-local log metrics';\nconst HELP_GROUP_ORDER = 70;\nconst NO_ENDPOINT = 'none';\nconst API_ERROR_STATUS = 400;\nconst FAILURE_STOP_REASONS = new Set(['error', 'aborted']);\n\ntype TelemetryFactory = NonNullable<DoomTelemetryOptions['telemetryFactory']>;\ntype TelemetryAttributes = Record<string, string | number | boolean>;\ntype RecordLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport interface PiTelemetryExtensionOptions {\n allowFileFallback?: boolean;\n /** Overrides the telemetry service identity. */\n serviceName?: string;\n /** Overrides the leader contribution owner. */\n leaderSource?: string;\n env?: NodeJS.ProcessEnv;\n telemetryFactory?: TelemetryFactory;\n /** Receives every sanitized record, whether or not a sink is live. */\n metrics?: LogMetricsRecorder;\n /** Called once the sink backend is resolved, so the overlay can report it. */\n onSinkStatus?: (status: SinkStatus | undefined) => void;\n /** Overrides the sink-history transport; injected in tests. */\n metricsSource?: MetricsSource;\n /** Receives telemetry lifecycle failures without writing into the TUI. */\n onDiagnostic?: (message: string) => void;\n}\n\nexport interface PiTelemetryRuntimeHandle {\n finishSession(reason: string, ctx: ExtensionContext): Promise<void>;\n}\n\nfunction isEnabled(value: string | undefined): boolean {\n return value === '1' || value?.toLowerCase() === 'true' || value?.toLowerCase() === 'yes';\n}\n\nfunction contextAttributes(ctx: ExtensionContext): TelemetryAttributes {\n const attributes: TelemetryAttributes = {\n 'pi.mode': ctx.mode,\n 'pi.session.id': ctx.sessionManager.getSessionId(),\n };\n if (ctx.thinkingLevel) attributes['pi.thinking_level'] = ctx.thinkingLevel;\n if (ctx.model) {\n attributes['gen_ai.provider.name'] = ctx.model.provider;\n attributes['gen_ai.request.model'] = ctx.model.id;\n }\n return attributes;\n}\n\nfunction usageAttributes(message: TurnEndEvent['message']): TelemetryAttributes | undefined {\n if (message.role !== 'assistant') return undefined;\n const attributes: TelemetryAttributes = {\n 'gen_ai.response.model': message.responseModel ?? message.model,\n 'gen_ai.response.finish_reasons': message.stopReason,\n 'gen_ai.usage.input_tokens': message.usage.input,\n 'gen_ai.usage.output_tokens': message.usage.output,\n 'gen_ai.usage.cache_read_tokens': message.usage.cacheRead,\n 'gen_ai.usage.cache_write_tokens': message.usage.cacheWrite,\n 'gen_ai.usage.total_tokens': message.usage.totalTokens,\n 'gen_ai.usage.cost': message.usage.cost.total,\n };\n if (message.usage.reasoning !== undefined) attributes['gen_ai.usage.reasoning_tokens'] = message.usage.reasoning;\n return attributes;\n}\n\nfunction failureAttributes(message: TurnEndEvent['message']): TelemetryAttributes {\n if (message.role !== 'assistant') return { 'error.type': 'AgentError', outcome: 'error' };\n const aborted = message.stopReason === 'aborted';\n return {\n 'error.type': 'ProviderError',\n 'error.code': message.stopReason,\n outcome: aborted ? 'aborted' : 'error',\n };\n}\n\nfunction statusFromTelemetry(status: ReturnType<DoomTelemetry['status']>): SinkStatus {\n return {\n service: status.serviceName,\n backend: status.backend,\n endpoint: status.endpoint ?? NO_ENDPOINT,\n endpointSource: status.endpointSource,\n traces: status.traces,\n redaction: true,\n fileFallback: status.fileFallback,\n };\n}\n\nexport function installPiTelemetryRuntime(\n cordis: Context,\n pi: ExtensionAPI,\n options: PiTelemetryExtensionOptions = {},\n): PiTelemetryRuntimeHandle {\n const env = options.env ?? process.env;\n const metrics = options.metrics;\n const toolStartedAt = new Map<string, number>();\n let active = true;\n let sessionGeneration = 0;\n let agentStartedAt: number | undefined;\n let telemetry: DoomTelemetry | undefined;\n\n cordis.effect(\n () => async () => {\n active = false;\n sessionGeneration += 1;\n toolStartedAt.clear();\n agentStartedAt = undefined;\n const ownedTelemetry = telemetry;\n telemetry = undefined;\n await ownedTelemetry?.shutdown();\n },\n `${PACKAGE_NAME}/telemetry`,\n );\n\n const emit = async (level: RecordLevel, name: string, attributes: TelemetryAttributes): Promise<void> => {\n if (!active || !telemetry) return;\n if (level === 'debug') await telemetry.recordDebug(name, attributes);\n else if (level === 'warn') await telemetry.recordWarning(name, undefined, attributes);\n else if (level === 'error') await telemetry.recordError(name, undefined, attributes);\n else await telemetry.recordEvent(name, attributes);\n };\n\n const getTelemetry = (ctx: ExtensionContext): DoomTelemetry => {\n if (!active) throw new Error('Log telemetry runtime is disposed.');\n telemetry ??= createDoomTelemetry({\n serviceName: options.serviceName ?? SERVICE_NAME,\n packageName: PACKAGE_NAME,\n cwd: ctx.cwd,\n env,\n allowFileFallback: options.allowFileFallback,\n telemetryFactory: options.telemetryFactory,\n warn: options.onDiagnostic,\n enableLogs: true,\n enableTraces: env.AGENT_OTEL_TRACES === undefined || isEnabled(env.AGENT_OTEL_TRACES),\n onRecord: (record) => {\n if (active) metrics?.record(record.event, record.attributes);\n },\n onStatus: (status) => {\n if (active) options.onSinkStatus?.(statusFromTelemetry(status));\n },\n });\n if (active) options.onSinkStatus?.(statusFromTelemetry(telemetry.status()));\n return telemetry;\n };\n\n pi.on('session_start', async (event, ctx) => {\n if (!active) return;\n sessionGeneration += 1;\n toolStartedAt.clear();\n agentStartedAt = undefined;\n getTelemetry(ctx);\n await emit('info', 'pi.session.started', {\n ...contextAttributes(ctx),\n 'pi.session.reason': event.reason,\n 'pi.session.previous': event.previousSessionFile !== undefined,\n });\n });\n\n pi.on('before_agent_start', async (event, ctx) => {\n if (!active) return;\n getTelemetry(ctx);\n await emit('info', 'pi.user_prompt', {\n ...contextAttributes(ctx),\n 'pi.user_message.length': event.prompt.length,\n 'pi.user_message.image_count': event.images?.length ?? 0,\n });\n });\n\n pi.on('agent_start', async (_event, ctx) => {\n if (!active) return;\n agentStartedAt = Date.now();\n getTelemetry(ctx);\n await emit('info', 'pi.agent.started', contextAttributes(ctx));\n });\n\n pi.on('turn_start', async (event, ctx) => {\n if (!active) return;\n getTelemetry(ctx);\n await emit('debug', 'pi.turn.started', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n });\n });\n\n pi.on('turn_end', async (event, ctx) => {\n if (!active) return;\n getTelemetry(ctx);\n const usage = usageAttributes(event.message);\n await emit('info', 'pi.turn.finished', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n 'pi.tool_result.count': event.toolResults.length,\n ...usage,\n });\n if (event.message.role === 'assistant' && FAILURE_STOP_REASONS.has(event.message.stopReason)) {\n await emit(event.message.stopReason === 'aborted' ? 'warn' : 'error', 'pi.turn.failed', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n ...failureAttributes(event.message),\n ...usage,\n });\n }\n if (!usage) return;\n for (const result of event.toolResults) {\n await emit('debug', 'pi.tool_token_sample', {\n ...contextAttributes(ctx),\n ...usage,\n 'tool.name': result.toolName,\n token_attribution: 'toolCallTurn',\n });\n }\n });\n\n pi.on('tool_execution_start', async (event, ctx) => {\n if (!active) return;\n toolStartedAt.set(event.toolCallId, Date.now());\n getTelemetry(ctx);\n await emit('debug', 'pi.tool_call', {\n ...contextAttributes(ctx),\n 'tool.name': event.toolName,\n 'tool.call.id': event.toolCallId,\n });\n });\n\n pi.on('tool_execution_end', async (event, ctx) => {\n if (!active) return;\n const startedAt = toolStartedAt.get(event.toolCallId);\n toolStartedAt.delete(event.toolCallId);\n getTelemetry(ctx);\n await emit('info', 'pi.tool_result', {\n ...contextAttributes(ctx),\n success: !event.isError,\n 'tool.name': event.toolName,\n 'tool.call.id': event.toolCallId,\n 'tool.result.error': event.isError,\n ...(startedAt === undefined ? {} : { 'tool.duration_ms': Date.now() - startedAt }),\n });\n });\n\n pi.on('after_provider_response', async (event, ctx) => {\n if (!active) return;\n getTelemetry(ctx);\n const attributes = {\n ...contextAttributes(ctx),\n 'http.response.status_code': event.status,\n };\n if (event.status >= API_ERROR_STATUS) await emit('error', 'pi.api_error', attributes);\n else await emit('debug', 'pi.api_response', attributes);\n });\n\n pi.on('model_select', async (event, ctx) => {\n if (!active) return;\n getTelemetry(ctx);\n await emit('info', 'pi.model.selected', {\n ...contextAttributes(ctx),\n 'pi.model.source': event.source,\n 'gen_ai.provider.name': event.model.provider,\n 'gen_ai.request.model': event.model.id,\n });\n });\n\n pi.on('agent_end', async (event, ctx) => {\n if (!active) return;\n const generation = sessionGeneration;\n getTelemetry(ctx);\n await emit('info', 'pi.agent.finished', {\n ...contextAttributes(ctx),\n 'pi.message.count': event.messages.length,\n ...(agentStartedAt === undefined ? {} : { 'pi.agent.duration_ms': Date.now() - agentStartedAt }),\n });\n if (!active || generation !== sessionGeneration) return;\n agentStartedAt = undefined;\n await telemetry?.flush();\n });\n\n pi.on('agent_settled', async (_event, ctx) => {\n if (!active) return;\n getTelemetry(ctx);\n await emit('debug', 'pi.agent.settled', contextAttributes(ctx));\n });\n\n return {\n async finishSession(reason, ctx) {\n if (!active) return;\n getTelemetry(ctx);\n await emit('info', 'pi.session.finished', {\n ...contextAttributes(ctx),\n 'pi.session.reason': reason,\n });\n },\n };\n}\n\nexport function registerLogMetricsLeaderBinding(\n pi: Pick<ExtensionAPI, 'events' | 'on'>,\n options: { source?: string } = {},\n): () => void {\n return registerDoomLeaderContribution(pi, {\n source: options.source ?? LEADER_SOURCE,\n bindings: [\n {\n id: 'log.metrics',\n path: [\n { key: 'h', label: 'help', order: HELP_GROUP_ORDER },\n { key: 'l', label: 'logs', detail: 'telemetry' },\n ],\n command: { name: LOG_METRICS_COMMAND },\n },\n ],\n });\n}\n\nexport async function openLogMetricsOverlay(\n ctx: ExtensionContext,\n getView: () => LogMetricsView,\n query?: MetricsQuery,\n): Promise<void> {\n await ctx.ui.custom<undefined>(\n (tui, theme, _keybindings, done) => new LogMetricsOverlayComponent(tui, theme, getView, done, query),\n {\n overlay: true,\n overlayOptions: {\n anchor: 'top-left',\n width: '100%',\n maxHeight: '100%',\n margin: 0,\n },\n },\n );\n}\n\nexport function installDoomLogRuntime(\n cordis: Context,\n pi: ExtensionAPI,\n options: PiTelemetryExtensionOptions = {},\n): PiTelemetryRuntimeHandle {\n const env = options.env ?? process.env;\n const metrics = new LogMetricsAggregator();\n const metricsSource = options.metricsSource ?? createMetricsSource({ env });\n let active = true;\n let sink: SinkStatus | undefined;\n let lastDiagnostic: string | undefined;\n let disposeLeader: (() => void) | undefined;\n\n cordis.effect(\n () => () => {\n active = false;\n sink = undefined;\n lastDiagnostic = undefined;\n disposeLeader?.();\n disposeLeader = undefined;\n },\n `${PACKAGE_NAME}/runtime`,\n );\n\n const telemetryRuntime = installPiTelemetryRuntime(cordis, pi, {\n ...options,\n metrics,\n // Always supply a sink so doom-telemetry never falls back to\n // process.emitWarning, which writes raw stderr over the TUI frame.\n // Diagnostics surface in the Log Metrics overlay instead.\n onDiagnostic: (message) => {\n if (!active) return;\n lastDiagnostic = message;\n if (options.onDiagnostic) options.onDiagnostic(message);\n else if (env.AGENT_OTEL_DIAGNOSTICS === DIAGNOSTICS_STDERR) process.emitWarning(message);\n },\n onSinkStatus: (status) => {\n if (!active) return;\n sink = status;\n options.onSinkStatus?.(status);\n },\n });\n\n disposeLeader = registerLogMetricsLeaderBinding(pi, { source: options.leaderSource });\n pi.registerCommand(LOG_METRICS_COMMAND, {\n description: LOG_METRICS_DESCRIPTION,\n handler: async (_args, ctx) => {\n if (!active) return;\n await openLogMetricsOverlay(\n ctx,\n () => ({\n disabled: isEnabled(env.AGENT_TELEMETRY_DISABLED) || isEnabled(env.OTEL_SDK_DISABLED),\n snapshot: metrics.snapshot(),\n sink,\n transport: metricsSource.lastTransport(),\n lastDiagnostic,\n }),\n metricsSource.query,\n );\n },\n });\n return telemetryRuntime;\n}\n\n/** The package's single standard Pi factory. */\nexport async function doomLogExtension(pi: ExtensionAPI): Promise<void> {\n const cordis = new Context();\n let disposal: Promise<void> | undefined;\n const dispose = (): Promise<void> => {\n disposal ??= cordis.fiber.dispose();\n return disposal;\n };\n let runtime: PiTelemetryRuntimeHandle;\n try {\n runtime = installDoomLogRuntime(cordis, pi);\n } catch (error) {\n await dispose();\n throw error;\n }\n let shutdown: Promise<void> | undefined;\n pi.on('session_shutdown', (event, ctx) => {\n shutdown ??= (async () => {\n await runtime.finishSession(event.reason, ctx);\n await dispose();\n })();\n return shutdown;\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["/**\n * Pi telemetry wiring for the Doom harness.\n *\n * This extension deliberately emits metadata-only records. Prompt, message,\n * tool payload, file path, diagnostic text, exception, and stack data never\n * cross the shared telemetry boundary.\n */\n\nimport { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport {\n type DoomReadinessCoordinator,\n readDoomReadinessCoordinator,\n} from '@agimon-ai/doompi-extension-contracts/readiness';\nimport {\n DOOM_UI_HUB_SERVICE,\n type DoomUiHubService,\n requireDoomUiHub,\n} from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport { createDoomTelemetry, type DoomTelemetry, type DoomTelemetryOptions } from '@agimon-ai/doompi-telemetry';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext, TurnEndEvent } from '@earendil-works/pi-coding-agent';\nimport { LogMetricsAggregator, type LogMetricsRecorder } from '../../services/metrics.ts';\nimport { LogMetricsOverlayComponent, type LogMetricsView, type SinkStatus } from '../../tui/logMetricsOverlay.ts';\nimport type { MetricsQuery, MetricsSource } from '../../types/metricsSource.ts';\nimport { createMetricsSource } from '../node/metricsSource.ts';\n\nconst SERVICE_NAME = 'pi';\nconst PACKAGE_NAME = '@agimon-ai/doompi-log';\n/** Opt back into stderr diagnostics when debugging outside a live TUI. */\nconst DIAGNOSTICS_STDERR = 'stderr';\nconst LEADER_SOURCE = '@agimon-ai/doompi-log';\nconst LOG_METRICS_COMMAND = 'log-metrics';\nconst LOG_METRICS_DESCRIPTION = 'Show session-local log metrics';\nconst HELP_GROUP_ORDER = 70;\nconst NO_ENDPOINT = 'none';\nconst API_ERROR_STATUS = 400;\nconst FAILURE_STOP_REASONS = new Set(['error', 'aborted']);\n\ntype TelemetryFactory = NonNullable<DoomTelemetryOptions['telemetryFactory']>;\ntype TelemetryAttributes = Record<string, string | number | boolean>;\ntype RecordLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport interface PiTelemetryExtensionOptions {\n allowFileFallback?: boolean;\n /** Overrides the telemetry service identity. */\n serviceName?: string;\n /** Overrides the leader contribution owner. */\n leaderSource?: string;\n env?: NodeJS.ProcessEnv;\n telemetryFactory?: TelemetryFactory;\n /** Receives every sanitized record, whether or not a sink is live. */\n metrics?: LogMetricsRecorder;\n /** Called once the sink backend is resolved, so the overlay can report it. */\n onSinkStatus?: (status: SinkStatus | undefined) => void;\n /** Overrides the sink-history transport; injected in tests. */\n metricsSource?: MetricsSource;\n /** Receives telemetry lifecycle failures without writing into the TUI. */\n onDiagnostic?: (message: string) => void;\n}\n\nexport interface PiTelemetryRuntimeHandle {\n waitForSession(ctx: ExtensionContext): Promise<void>;\n finishSession(reason: string, ctx: ExtensionContext): Promise<void>;\n}\n\nfunction isEnabled(value: string | undefined): boolean {\n return value === '1' || value?.toLowerCase() === 'true' || value?.toLowerCase() === 'yes';\n}\n\nfunction contextAttributes(ctx: ExtensionContext): TelemetryAttributes {\n const attributes: TelemetryAttributes = {\n 'pi.mode': ctx.mode,\n 'pi.session.id': ctx.sessionManager.getSessionId(),\n };\n if (ctx.thinkingLevel) attributes['pi.thinking_level'] = ctx.thinkingLevel;\n if (ctx.model) {\n attributes['gen_ai.provider.name'] = ctx.model.provider;\n attributes['gen_ai.request.model'] = ctx.model.id;\n }\n return attributes;\n}\n\nfunction usageAttributes(message: TurnEndEvent['message']): TelemetryAttributes | undefined {\n if (message.role !== 'assistant') return undefined;\n const attributes: TelemetryAttributes = {\n 'gen_ai.response.model': message.responseModel ?? message.model,\n 'gen_ai.response.finish_reasons': message.stopReason,\n 'gen_ai.usage.input_tokens': message.usage.input,\n 'gen_ai.usage.output_tokens': message.usage.output,\n 'gen_ai.usage.cache_read_tokens': message.usage.cacheRead,\n 'gen_ai.usage.cache_write_tokens': message.usage.cacheWrite,\n 'gen_ai.usage.total_tokens': message.usage.totalTokens,\n 'gen_ai.usage.cost': message.usage.cost.total,\n };\n if (message.usage.reasoning !== undefined) attributes['gen_ai.usage.reasoning_tokens'] = message.usage.reasoning;\n return attributes;\n}\n\nfunction failureAttributes(message: TurnEndEvent['message']): TelemetryAttributes {\n if (message.role !== 'assistant') return { 'error.type': 'AgentError', outcome: 'error' };\n const aborted = message.stopReason === 'aborted';\n return {\n 'error.type': 'ProviderError',\n 'error.code': message.stopReason,\n outcome: aborted ? 'aborted' : 'error',\n };\n}\n\nfunction statusFromTelemetry(status: ReturnType<DoomTelemetry['status']>): SinkStatus {\n return {\n service: status.serviceName,\n backend: status.backend,\n endpoint: status.endpoint ?? NO_ENDPOINT,\n endpointSource: status.endpointSource,\n traces: status.traces,\n redaction: true,\n fileFallback: status.fileFallback,\n };\n}\n\nexport function installPiTelemetryRuntime(\n cordis: Context,\n pi: ExtensionAPI,\n options: PiTelemetryExtensionOptions = {},\n splitReadiness = false,\n): PiTelemetryRuntimeHandle {\n const env = options.env ?? process.env;\n const metrics = options.metrics;\n const toolStartedAt = new Map<string, number>();\n let active = true;\n let sessionGeneration = 0;\n let agentStartedAt: number | undefined;\n let telemetry: DoomTelemetry | undefined;\n let sessionReadiness:\n | {\n readonly sessionManager: object;\n readonly coordinator: DoomReadinessCoordinator;\n readonly operation: Promise<void>;\n }\n | undefined;\n let backgroundQueue = Promise.resolve();\n\n const enqueue = (operation: () => Promise<void>): void => {\n const scheduled = backgroundQueue.then(operation);\n // Telemetry is advisory and the coordinator owns startup failure notification.\n backgroundQueue = scheduled.catch(() => undefined);\n };\n\n cordis.effect(\n () => async () => {\n active = false;\n sessionGeneration += 1;\n sessionReadiness = undefined;\n toolStartedAt.clear();\n agentStartedAt = undefined;\n const ownedTelemetry = telemetry;\n telemetry = undefined;\n await ownedTelemetry?.shutdown();\n },\n `${PACKAGE_NAME}/telemetry`,\n );\n\n const emit = async (level: RecordLevel, name: string, attributes: TelemetryAttributes): Promise<void> => {\n if (!active || !telemetry) return;\n if (level === 'debug') await telemetry.recordDebug(name, attributes);\n else if (level === 'warn') await telemetry.recordWarning(name, undefined, attributes);\n else if (level === 'error') await telemetry.recordError(name, undefined, attributes);\n else await telemetry.recordEvent(name, attributes);\n };\n\n const getTelemetry = (ctx: ExtensionContext): DoomTelemetry => {\n if (!active) throw new Error('Log telemetry runtime is disposed.');\n telemetry ??= createDoomTelemetry({\n serviceName: options.serviceName ?? SERVICE_NAME,\n packageName: PACKAGE_NAME,\n cwd: ctx.cwd,\n env,\n allowFileFallback: options.allowFileFallback,\n telemetryFactory: options.telemetryFactory,\n warn: options.onDiagnostic,\n enableLogs: true,\n enableTraces: env.AGENT_OTEL_TRACES === undefined || isEnabled(env.AGENT_OTEL_TRACES),\n onRecord: (record) => {\n if (active) metrics?.record(record.event, record.attributes);\n },\n onStatus: (status) => {\n if (active) options.onSinkStatus?.(statusFromTelemetry(status));\n },\n });\n if (active) options.onSinkStatus?.(statusFromTelemetry(telemetry.status()));\n return telemetry;\n };\n\n const waitForSession = async (ctx: ExtensionContext): Promise<void> => {\n const current = sessionReadiness;\n if (!current) return;\n if (current.sessionManager !== ctx.sessionManager) {\n throw new Error('Log readiness belongs to a stale Pi session.');\n }\n await current.operation;\n if (!active || current !== sessionReadiness) {\n throw new Error('Log readiness belongs to a stale extension generation.');\n }\n };\n\n const enqueueForSession = (ctx: ExtensionContext, operation: () => Promise<void>): void | Promise<void> => {\n const expected = sessionReadiness;\n const run = async (): Promise<void> => {\n if (expected) {\n const ready = await expected.operation.then(\n () => true,\n () => false,\n );\n if (!ready || !active || expected !== sessionReadiness || expected.sessionManager !== ctx.sessionManager) {\n return;\n }\n } else if (!active) {\n return;\n }\n await operation();\n };\n if (!splitReadiness) return run();\n enqueue(run);\n return undefined;\n };\n\n pi.on('session_start', (event, ctx) => {\n if (!active) return undefined;\n const ownGeneration = ++sessionGeneration;\n toolStartedAt.clear();\n agentStartedAt = undefined;\n const initialize = async (signal?: AbortSignal): Promise<void> => {\n signal?.throwIfAborted();\n getTelemetry(ctx);\n await emit('info', 'pi.session.started', {\n ...contextAttributes(ctx),\n 'pi.session.reason': event.reason,\n 'pi.session.previous': event.previousSessionFile !== undefined,\n });\n signal?.throwIfAborted();\n };\n const coordinator = splitReadiness ? readDoomReadinessCoordinator(cordis) : undefined;\n if (!coordinator) return initialize();\n\n const previous = sessionReadiness;\n const operation = (async (): Promise<void> => {\n if (previous?.coordinator === coordinator) await previous.operation.catch(() => undefined);\n if (!active || ownGeneration !== sessionGeneration) return;\n const handle = coordinator.start(\n PACKAGE_NAME,\n `${ctx.sessionManager.getSessionId()}:${ownGeneration}`,\n async (signal) => {\n await initialize(signal);\n return { value: undefined };\n },\n );\n await handle.wait();\n })();\n // Config's coordinator owns the single user-facing failure notification.\n void operation.catch(() => undefined);\n sessionReadiness = { sessionManager: ctx.sessionManager, coordinator, operation };\n return undefined;\n });\n\n pi.on('before_agent_start', (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('info', 'pi.user_prompt', {\n ...contextAttributes(ctx),\n 'pi.user_message.length': event.prompt.length,\n 'pi.user_message.image_count': event.images?.length ?? 0,\n });\n });\n });\n\n pi.on('agent_start', (_event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n agentStartedAt = Date.now();\n getTelemetry(ctx);\n await emit('info', 'pi.agent.started', contextAttributes(ctx));\n });\n });\n\n pi.on('turn_start', (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('debug', 'pi.turn.started', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n });\n });\n });\n\n pi.on('turn_end', (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n const usage = usageAttributes(event.message);\n await emit('info', 'pi.turn.finished', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n 'pi.tool_result.count': event.toolResults.length,\n ...usage,\n });\n if (event.message.role === 'assistant' && FAILURE_STOP_REASONS.has(event.message.stopReason)) {\n await emit(event.message.stopReason === 'aborted' ? 'warn' : 'error', 'pi.turn.failed', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n ...failureAttributes(event.message),\n ...usage,\n });\n }\n if (!usage) return;\n for (const result of event.toolResults) {\n await emit('debug', 'pi.tool_token_sample', {\n ...contextAttributes(ctx),\n ...usage,\n 'tool.name': result.toolName,\n token_attribution: 'toolCallTurn',\n });\n }\n });\n });\n\n pi.on('tool_execution_start', (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n toolStartedAt.set(event.toolCallId, Date.now());\n getTelemetry(ctx);\n await emit('debug', 'pi.tool_call', {\n ...contextAttributes(ctx),\n 'tool.name': event.toolName,\n 'tool.call.id': event.toolCallId,\n });\n });\n });\n\n pi.on('tool_execution_end', (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n const startedAt = toolStartedAt.get(event.toolCallId);\n toolStartedAt.delete(event.toolCallId);\n getTelemetry(ctx);\n await emit('info', 'pi.tool_result', {\n ...contextAttributes(ctx),\n success: !event.isError,\n 'tool.name': event.toolName,\n 'tool.call.id': event.toolCallId,\n 'tool.result.error': event.isError,\n ...(startedAt === undefined ? {} : { 'tool.duration_ms': Date.now() - startedAt }),\n });\n });\n });\n\n pi.on('after_provider_response', (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n const attributes = {\n ...contextAttributes(ctx),\n 'http.response.status_code': event.status,\n };\n if (event.status >= API_ERROR_STATUS) await emit('error', 'pi.api_error', attributes);\n else await emit('debug', 'pi.api_response', attributes);\n });\n });\n\n pi.on('model_select', (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('info', 'pi.model.selected', {\n ...contextAttributes(ctx),\n 'pi.model.source': event.source,\n 'gen_ai.provider.name': event.model.provider,\n 'gen_ai.request.model': event.model.id,\n });\n });\n });\n\n pi.on('agent_end', (event, ctx) => {\n if (!active) return;\n const generation = sessionGeneration;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('info', 'pi.agent.finished', {\n ...contextAttributes(ctx),\n 'pi.message.count': event.messages.length,\n ...(agentStartedAt === undefined ? {} : { 'pi.agent.duration_ms': Date.now() - agentStartedAt }),\n });\n if (!active || generation !== sessionGeneration) return;\n agentStartedAt = undefined;\n await telemetry?.flush();\n });\n });\n\n pi.on('agent_settled', (_event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('debug', 'pi.agent.settled', contextAttributes(ctx));\n });\n });\n\n return {\n waitForSession,\n async finishSession(reason, ctx) {\n if (!active) return;\n await backgroundQueue;\n const current = sessionReadiness;\n if (current) await current.operation.catch(() => undefined);\n if (!active) return;\n if (current && current !== sessionReadiness) return;\n if (!telemetry) return;\n getTelemetry(ctx);\n await emit('info', 'pi.session.finished', {\n ...contextAttributes(ctx),\n 'pi.session.reason': reason,\n });\n },\n };\n}\n\nexport function registerLogMetricsLeaderBinding(hub: DoomUiHubService, options: { source?: string } = {}): () => void {\n const contribution = hub.registerLeader({\n source: options.source ?? LEADER_SOURCE,\n bindings: [\n {\n id: 'log.metrics',\n path: [\n { key: 'h', label: 'help', order: HELP_GROUP_ORDER },\n { key: 'l', label: 'logs', detail: 'telemetry' },\n ],\n command: { name: LOG_METRICS_COMMAND },\n },\n ],\n });\n return () => contribution.dispose();\n}\n\nexport async function openLogMetricsOverlay(\n ctx: ExtensionContext,\n getView: () => LogMetricsView,\n query?: MetricsQuery,\n): Promise<void> {\n await ctx.ui.custom<undefined>(\n (tui, theme, _keybindings, done) => new LogMetricsOverlayComponent(tui, theme, getView, done, query),\n {\n overlay: true,\n overlayOptions: {\n anchor: 'top-left',\n width: '100%',\n maxHeight: '100%',\n margin: 0,\n },\n },\n );\n}\n\nexport function installDoomLogRuntime(\n cordis: Context,\n pi: ExtensionAPI,\n options: PiTelemetryExtensionOptions = {},\n splitReadiness = false,\n): PiTelemetryRuntimeHandle {\n const env = options.env ?? process.env;\n const metrics = new LogMetricsAggregator();\n const metricsSource = options.metricsSource ?? createMetricsSource({ env });\n let active = true;\n let sink: SinkStatus | undefined;\n let lastDiagnostic: string | undefined;\n\n cordis.effect(\n () => () => {\n active = false;\n sink = undefined;\n lastDiagnostic = undefined;\n },\n `${PACKAGE_NAME}/runtime`,\n );\n\n const telemetryRuntime = installPiTelemetryRuntime(\n cordis,\n pi,\n {\n ...options,\n metrics,\n // Always supply a sink so doom-telemetry never falls back to\n // process.emitWarning, which writes raw stderr over the TUI frame.\n // Diagnostics surface in the Log Metrics overlay instead.\n onDiagnostic: (message) => {\n if (!active) return;\n lastDiagnostic = message;\n if (options.onDiagnostic) options.onDiagnostic(message);\n else if (env.AGENT_OTEL_DIAGNOSTICS === DIAGNOSTICS_STDERR) process.emitWarning(message);\n },\n onSinkStatus: (status) => {\n if (!active) return;\n sink = status;\n options.onSinkStatus?.(status);\n },\n },\n splitReadiness,\n );\n\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n return registerLogMetricsLeaderBinding(requireDoomUiHub(uiContext), { source: options.leaderSource });\n });\n pi.registerCommand(LOG_METRICS_COMMAND, {\n description: LOG_METRICS_DESCRIPTION,\n handler: async (_args, ctx) => {\n if (!active) return;\n await telemetryRuntime.waitForSession(ctx);\n if (!active) return;\n await openLogMetricsOverlay(\n ctx,\n () => ({\n disabled: isEnabled(env.AGENT_TELEMETRY_DISABLED) || isEnabled(env.OTEL_SDK_DISABLED),\n snapshot: metrics.snapshot(),\n sink,\n transport: metricsSource.lastTransport(),\n lastDiagnostic,\n }),\n metricsSource.query,\n );\n },\n });\n return telemetryRuntime;\n}\n\ninterface LogPluginConfig {\n readonly pi: ExtensionAPI;\n}\n\nfunction logPlugin(cordis: Context, { pi }: LogPluginConfig): void {\n const runtime = installDoomLogRuntime(cordis, pi, {}, true);\n let shutdown: Promise<void> | undefined;\n pi.on('session_shutdown', (event, ctx) => {\n shutdown ??= runtime.finishSession(event.reason, ctx);\n return shutdown;\n });\n}\n\n/** The package's single standard Pi factory. */\nexport async function doomLogExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, PACKAGE_NAME);\n const fiber = connection.root.plugin(logPlugin, { pi });\n try {\n await fiber;\n } catch (error) {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n throw error;\n }\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/doompi-log",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.26",
|
|
4
4
|
"description": "Pi session metrics, findings, sink status, and a Log Metrics overlay for agent observability.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-observability",
|
|
@@ -59,9 +59,9 @@
|
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@deepseek-ai/cordis": "4.0.1",
|
|
62
|
-
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.
|
|
63
|
-
"@agimon-ai/
|
|
64
|
-
"@agimon-ai/
|
|
62
|
+
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.26",
|
|
63
|
+
"@agimon-ai/doompi-telemetry": "0.0.1-alpha.26",
|
|
64
|
+
"@agimon-ai/log-sink-mcp": "0.29.16"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@earendil-works/pi-coding-agent": "0.84.2",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"tsdown": "0.22.14",
|
|
72
72
|
"typescript": "6.0.3",
|
|
73
73
|
"vitest": "4.1.10",
|
|
74
|
-
"@agimon-ai/doompi-ui": "0.0.1-alpha.
|
|
74
|
+
"@agimon-ai/doompi-ui": "0.0.1-alpha.26"
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
77
|
"@earendil-works/pi-coding-agent": "0.84.2",
|