@agimon-ai/doompi-log 0.0.1-alpha.23 → 0.0.1-alpha.25

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 CHANGED
@@ -15,7 +15,8 @@ The extension aggregates telemetry it observes in-process and can query historic
15
15
 
16
16
  ## Install
17
17
 
18
- DoomPi loads Log as core. For standalone Pi:
18
+ DoomPi includes Log in every composition. Pi discovers its sole extension entry through
19
+ `package.json.pi.extensions`. For standalone Pi:
19
20
 
20
21
  ```bash
21
22
  pi install npm:@agimon-ai/doompi-log
@@ -49,14 +50,14 @@ The overlay requires a TUI. The aggregator, findings, and metrics-source APIs ca
49
50
 
50
51
  ```ts
51
52
  import {
52
- createDoomLogExtension,
53
+ installDoomLogRuntime,
53
54
  deriveFindings,
54
55
  LogMetricsAggregator,
55
56
  openLogMetricsOverlay,
56
57
  } from '@agimon-ai/doompi-log';
57
58
  ```
58
59
 
59
- Focused exports provide extension options, metrics types, the metrics source, and the overlay component.
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.
60
61
 
61
62
  ## Development
62
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/leader"),i=require("@agimon-ai/doompi-telemetry");const a=`log-metrics`,o=new Set([`error`,`aborted`]);function s(e){return e===`1`||e?.toLowerCase()===`true`||e?.toLowerCase()===`yes`}function c(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 l(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 u(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 d(e){return{service:e.serviceName,backend:e.backend,endpoint:e.endpoint??`none`,endpointSource:e.endpointSource,traces:e.traces,redaction:!0,fileFallback:e.fileFallback}}function f(e={}){return function(t){let n=e.env??process.env,r=e.metrics,a=new Map,f,p,m=async(e,t,n)=>{p&&(e===`debug`?await p.recordDebug(t,n):e===`warn`?await p.recordWarning(t,void 0,n):e===`error`?await p.recordError(t,void 0,n):await p.recordEvent(t,n))},h=t=>(p??=(0,i.createDoomTelemetry)({serviceName:e.serviceName??`pi`,packageName:`@agimon-ai/doompi-log`,cwd:t.cwd,env:n,allowFileFallback:e.allowFileFallback,telemetryFactory:e.telemetryFactory,warn:e.onDiagnostic,enableLogs:!0,enableTraces:n.AGENT_OTEL_TRACES===void 0||s(n.AGENT_OTEL_TRACES),onRecord:e=>r?.record(e.event,e.attributes),onStatus:t=>e.onSinkStatus?.(d(t))}),e.onSinkStatus?.(d(p.status())),p);t.on(`session_start`,async(e,t)=>{h(t),await m(`info`,`pi.session.started`,{...c(t),"pi.session.reason":e.reason,"pi.session.previous":e.previousSessionFile!==void 0})}),t.on(`before_agent_start`,async(e,t)=>{h(t),await m(`info`,`pi.user_prompt`,{...c(t),"pi.user_message.length":e.prompt.length,"pi.user_message.image_count":e.images?.length??0})}),t.on(`agent_start`,async(e,t)=>{f=Date.now(),h(t),await m(`info`,`pi.agent.started`,c(t))}),t.on(`turn_start`,async(e,t)=>{h(t),await m(`debug`,`pi.turn.started`,{...c(t),"pi.turn.index":e.turnIndex})}),t.on(`turn_end`,async(e,t)=>{h(t);let n=l(e.message);if(await m(`info`,`pi.turn.finished`,{...c(t),"pi.turn.index":e.turnIndex,"pi.tool_result.count":e.toolResults.length,...n}),e.message.role===`assistant`&&o.has(e.message.stopReason)&&await m(e.message.stopReason===`aborted`?`warn`:`error`,`pi.turn.failed`,{...c(t),"pi.turn.index":e.turnIndex,...u(e.message),...n}),n)for(let r of e.toolResults)await m(`debug`,`pi.tool_token_sample`,{...c(t),...n,"tool.name":r.toolName,token_attribution:`toolCallTurn`})}),t.on(`tool_execution_start`,async(e,t)=>{a.set(e.toolCallId,Date.now()),h(t),await m(`debug`,`pi.tool_call`,{...c(t),"tool.name":e.toolName,"tool.call.id":e.toolCallId})}),t.on(`tool_execution_end`,async(e,t)=>{let n=a.get(e.toolCallId);a.delete(e.toolCallId),h(t),await m(`info`,`pi.tool_result`,{...c(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`,async(e,t)=>{h(t);let n={...c(t),"http.response.status_code":e.status};e.status>=400?await m(`error`,`pi.api_error`,n):await m(`debug`,`pi.api_response`,n)}),t.on(`model_select`,async(e,t)=>{h(t),await m(`info`,`pi.model.selected`,{...c(t),"pi.model.source":e.source,"gen_ai.provider.name":e.model.provider,"gen_ai.request.model":e.model.id})}),t.on(`agent_end`,async(e,t)=>{h(t),await m(`info`,`pi.agent.finished`,{...c(t),"pi.message.count":e.messages.length,...f===void 0?{}:{"pi.agent.duration_ms":Date.now()-f}}),f=void 0,await p?.flush()}),t.on(`agent_settled`,async(e,t)=>{h(t),await m(`debug`,`pi.agent.settled`,c(t))}),t.on(`session_shutdown`,async(e,t)=>{h(t),await m(`info`,`pi.session.finished`,{...c(t),"pi.session.reason":e.reason}),await p?.shutdown()})}}function p(e,t={}){return(0,r.registerDoomLeaderContribution)(e,{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:a}}]})}async function m(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 h(t={}){return function(r){let i=t.env??process.env,o=new e.LogMetricsAggregator,c=t.metricsSource??n.createMetricsSource({env:i}),l,u;f({...t,metrics:o,onDiagnostic:e=>{u=e,t.onDiagnostic?t.onDiagnostic(e):i.AGENT_OTEL_DIAGNOSTICS===`stderr`&&process.emitWarning(e)},onSinkStatus:e=>{l=e,t.onSinkStatus?.(e)}})(r),p(r,{source:t.leaderSource}),r.registerCommand(a,{description:`Show session-local log metrics`,handler:async(e,t)=>{await m(t,()=>({disabled:s(i.AGENT_TELEMETRY_DISABLED)||s(i.OTEL_SDK_DISABLED),snapshot:o.snapshot(),sink:l,transport:c.lastTransport(),lastDiagnostic:u}),c.query)}})}}const g=h();exports.createDoomLogExtension=h,exports.createPiTelemetryExtension=f,exports.doomLogExtension=g,exports.openLogMetricsOverlay=m,exports.registerLogMetricsLeaderBinding=p;
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"],"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 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\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 createPiTelemetryExtension(options: PiTelemetryExtensionOptions = {}) {\n return function registerPiTelemetry(pi: ExtensionAPI): void {\n const env = options.env ?? process.env;\n const metrics = options.metrics;\n const toolStartedAt = new Map<string, number>();\n let agentStartedAt: number | undefined;\n let telemetry: DoomTelemetry | undefined;\n\n const emit = async (level: RecordLevel, name: string, attributes: TelemetryAttributes): Promise<void> => {\n if (!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 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) => metrics?.record(record.event, record.attributes),\n onStatus: (status) => options.onSinkStatus?.(statusFromTelemetry(status)),\n });\n options.onSinkStatus?.(statusFromTelemetry(telemetry.status()));\n return telemetry;\n };\n\n pi.on('session_start', async (event, ctx) => {\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 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 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 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 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 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 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 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 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 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 agentStartedAt = undefined;\n await telemetry?.flush();\n });\n\n pi.on('agent_settled', async (_event, ctx) => {\n getTelemetry(ctx);\n await emit('debug', 'pi.agent.settled', contextAttributes(ctx));\n });\n\n pi.on('session_shutdown', async (event, ctx) => {\n getTelemetry(ctx);\n await emit('info', 'pi.session.finished', {\n ...contextAttributes(ctx),\n 'pi.session.reason': event.reason,\n });\n await telemetry?.shutdown();\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 createDoomLogExtension(options: PiTelemetryExtensionOptions = {}) {\n return function registerDoomLog(pi: ExtensionAPI): void {\n const env = options.env ?? process.env;\n const metrics = new LogMetricsAggregator();\n const metricsSource = options.metricsSource ?? createMetricsSource({ env });\n let sink: SinkStatus | undefined;\n let lastDiagnostic: string | undefined;\n\n createPiTelemetryExtension({\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 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 sink = status;\n options.onSinkStatus?.(status);\n },\n })(pi);\n\n registerLogMetricsLeaderBinding(pi, { source: options.leaderSource });\n pi.registerCommand(LOG_METRICS_COMMAND, {\n description: LOG_METRICS_DESCRIPTION,\n handler: async (_args, ctx) => {\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 };\n}\n\nexport const doomLogExtension = createDoomLogExtension();\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,7 +1,9 @@
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";
6
+ import { Context } from "@deepseek-ai/cordis";
5
7
  import { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
6
8
  //#region src/adapters/pi/extension.d.ts
7
9
  type TelemetryFactory = NonNullable<DoomTelemetryOptions['telemetryFactory']>;
@@ -22,13 +24,17 @@ interface PiTelemetryExtensionOptions {
22
24
  /** Receives telemetry lifecycle failures without writing into the TUI. */
23
25
  onDiagnostic?: (message: string) => void;
24
26
  }
25
- declare function createPiTelemetryExtension(options?: PiTelemetryExtensionOptions): (pi: ExtensionAPI) => void;
26
- declare function registerLogMetricsLeaderBinding(pi: Pick<ExtensionAPI, 'events' | 'on'>, options?: {
27
+ interface PiTelemetryRuntimeHandle {
28
+ waitForSession(ctx: ExtensionContext): Promise<void>;
29
+ finishSession(reason: string, ctx: ExtensionContext): Promise<void>;
30
+ }
31
+ declare function registerLogMetricsLeaderBinding(hub: DoomUiHubService, options?: {
27
32
  source?: string;
28
33
  }): () => void;
29
34
  declare function openLogMetricsOverlay(ctx: ExtensionContext, getView: () => LogMetricsView, query?: MetricsQuery): Promise<void>;
30
- declare function createDoomLogExtension(options?: PiTelemetryExtensionOptions): (pi: ExtensionAPI) => void;
31
- declare const doomLogExtension: (pi: ExtensionAPI) => void;
35
+ declare function installDoomLogRuntime(cordis: Context, pi: ExtensionAPI, options?: PiTelemetryExtensionOptions, splitReadiness?: boolean): PiTelemetryRuntimeHandle;
36
+ /** The package's single standard Pi factory. */
37
+ declare function doomLogExtension(pi: ExtensionAPI): Promise<void>;
32
38
  //#endregion
33
- export { PiTelemetryExtensionOptions, createDoomLogExtension, createPiTelemetryExtension, doomLogExtension, openLogMetricsOverlay, registerLogMetricsLeaderBinding };
39
+ export { PiTelemetryExtensionOptions, PiTelemetryRuntimeHandle, doomLogExtension, installDoomLogRuntime, openLogMetricsOverlay, registerLogMetricsLeaderBinding };
34
40
  //# sourceMappingURL=extension.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;;;KA4BK,mBAAmB,YAAY;UAInB;EACf;;EAEA;;EAEA;EACA,MAAM,OAAO;EACb,mBAAmB;;EAEnB,UAAU;;EAEV,gBAAgB,QAAQ;;EAExB,gBAAgB;;EAEhB,gBAAgB;;iBA0DF,2BAA2B,UAAS,+BACd,IAAI;iBAoK1B,gCACd,IAAI,KAAK,gCACT;EAAW;;iBAiBS,sBACpB,KAAK,kBACL,eAAe,gBACf,QAAQ,eACP;iBAea,uBAAuB,UAAS,+BACd,IAAI;cA4CzB,mBAAgB,IA5CS"}
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,7 +1,9 @@
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";
6
+ import { Context } from "@deepseek-ai/cordis";
5
7
  import { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
6
8
  //#region src/adapters/pi/extension.d.ts
7
9
  type TelemetryFactory = NonNullable<DoomTelemetryOptions['telemetryFactory']>;
@@ -22,13 +24,17 @@ interface PiTelemetryExtensionOptions {
22
24
  /** Receives telemetry lifecycle failures without writing into the TUI. */
23
25
  onDiagnostic?: (message: string) => void;
24
26
  }
25
- declare function createPiTelemetryExtension(options?: PiTelemetryExtensionOptions): (pi: ExtensionAPI) => void;
26
- declare function registerLogMetricsLeaderBinding(pi: Pick<ExtensionAPI, 'events' | 'on'>, options?: {
27
+ interface PiTelemetryRuntimeHandle {
28
+ waitForSession(ctx: ExtensionContext): Promise<void>;
29
+ finishSession(reason: string, ctx: ExtensionContext): Promise<void>;
30
+ }
31
+ declare function registerLogMetricsLeaderBinding(hub: DoomUiHubService, options?: {
27
32
  source?: string;
28
33
  }): () => void;
29
34
  declare function openLogMetricsOverlay(ctx: ExtensionContext, getView: () => LogMetricsView, query?: MetricsQuery): Promise<void>;
30
- declare function createDoomLogExtension(options?: PiTelemetryExtensionOptions): (pi: ExtensionAPI) => void;
31
- declare const doomLogExtension: (pi: ExtensionAPI) => void;
35
+ declare function installDoomLogRuntime(cordis: Context, pi: ExtensionAPI, options?: PiTelemetryExtensionOptions, splitReadiness?: boolean): PiTelemetryRuntimeHandle;
36
+ /** The package's single standard Pi factory. */
37
+ declare function doomLogExtension(pi: ExtensionAPI): Promise<void>;
32
38
  //#endregion
33
- export { PiTelemetryExtensionOptions, createDoomLogExtension, createPiTelemetryExtension, doomLogExtension, openLogMetricsOverlay, registerLogMetricsLeaderBinding };
39
+ export { PiTelemetryExtensionOptions, PiTelemetryRuntimeHandle, doomLogExtension, installDoomLogRuntime, openLogMetricsOverlay, registerLogMetricsLeaderBinding };
34
40
  //# sourceMappingURL=extension.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;;;KA4BK,mBAAmB,YAAY;UAInB;EACf;;EAEA;;EAEA;EACA,MAAM,OAAO;EACb,mBAAmB;;EAEnB,UAAU;;EAEV,gBAAgB,QAAQ;;EAExB,gBAAgB;;EAEhB,gBAAgB;;iBA0DF,2BAA2B,UAAS,+BACd,IAAI;iBAoK1B,gCACd,IAAI,KAAK,gCACT;EAAW;;iBAiBS,sBACpB,KAAK,kBACL,eAAe,gBACf,QAAQ,eACP;iBAea,uBAAuB,UAAS,+BACd,IAAI;cA4CzB,mBAAgB,IA5CS"}
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{registerDoomLeaderContribution as r}from"@agimon-ai/doompi-extension-contracts/leader";import{createDoomTelemetry as i}from"@agimon-ai/doompi-telemetry";const a=`log-metrics`,o=new Set([`error`,`aborted`]);function s(e){return e===`1`||e?.toLowerCase()===`true`||e?.toLowerCase()===`yes`}function c(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 l(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 u(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 d(e){return{service:e.serviceName,backend:e.backend,endpoint:e.endpoint??`none`,endpointSource:e.endpointSource,traces:e.traces,redaction:!0,fileFallback:e.fileFallback}}function f(e={}){return function(t){let n=e.env??process.env,r=e.metrics,a=new Map,f,p,m=async(e,t,n)=>{p&&(e===`debug`?await p.recordDebug(t,n):e===`warn`?await p.recordWarning(t,void 0,n):e===`error`?await p.recordError(t,void 0,n):await p.recordEvent(t,n))},h=t=>(p??=i({serviceName:e.serviceName??`pi`,packageName:`@agimon-ai/doompi-log`,cwd:t.cwd,env:n,allowFileFallback:e.allowFileFallback,telemetryFactory:e.telemetryFactory,warn:e.onDiagnostic,enableLogs:!0,enableTraces:n.AGENT_OTEL_TRACES===void 0||s(n.AGENT_OTEL_TRACES),onRecord:e=>r?.record(e.event,e.attributes),onStatus:t=>e.onSinkStatus?.(d(t))}),e.onSinkStatus?.(d(p.status())),p);t.on(`session_start`,async(e,t)=>{h(t),await m(`info`,`pi.session.started`,{...c(t),"pi.session.reason":e.reason,"pi.session.previous":e.previousSessionFile!==void 0})}),t.on(`before_agent_start`,async(e,t)=>{h(t),await m(`info`,`pi.user_prompt`,{...c(t),"pi.user_message.length":e.prompt.length,"pi.user_message.image_count":e.images?.length??0})}),t.on(`agent_start`,async(e,t)=>{f=Date.now(),h(t),await m(`info`,`pi.agent.started`,c(t))}),t.on(`turn_start`,async(e,t)=>{h(t),await m(`debug`,`pi.turn.started`,{...c(t),"pi.turn.index":e.turnIndex})}),t.on(`turn_end`,async(e,t)=>{h(t);let n=l(e.message);if(await m(`info`,`pi.turn.finished`,{...c(t),"pi.turn.index":e.turnIndex,"pi.tool_result.count":e.toolResults.length,...n}),e.message.role===`assistant`&&o.has(e.message.stopReason)&&await m(e.message.stopReason===`aborted`?`warn`:`error`,`pi.turn.failed`,{...c(t),"pi.turn.index":e.turnIndex,...u(e.message),...n}),n)for(let r of e.toolResults)await m(`debug`,`pi.tool_token_sample`,{...c(t),...n,"tool.name":r.toolName,token_attribution:`toolCallTurn`})}),t.on(`tool_execution_start`,async(e,t)=>{a.set(e.toolCallId,Date.now()),h(t),await m(`debug`,`pi.tool_call`,{...c(t),"tool.name":e.toolName,"tool.call.id":e.toolCallId})}),t.on(`tool_execution_end`,async(e,t)=>{let n=a.get(e.toolCallId);a.delete(e.toolCallId),h(t),await m(`info`,`pi.tool_result`,{...c(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`,async(e,t)=>{h(t);let n={...c(t),"http.response.status_code":e.status};e.status>=400?await m(`error`,`pi.api_error`,n):await m(`debug`,`pi.api_response`,n)}),t.on(`model_select`,async(e,t)=>{h(t),await m(`info`,`pi.model.selected`,{...c(t),"pi.model.source":e.source,"gen_ai.provider.name":e.model.provider,"gen_ai.request.model":e.model.id})}),t.on(`agent_end`,async(e,t)=>{h(t),await m(`info`,`pi.agent.finished`,{...c(t),"pi.message.count":e.messages.length,...f===void 0?{}:{"pi.agent.duration_ms":Date.now()-f}}),f=void 0,await p?.flush()}),t.on(`agent_settled`,async(e,t)=>{h(t),await m(`debug`,`pi.agent.settled`,c(t))}),t.on(`session_shutdown`,async(e,t)=>{h(t),await m(`info`,`pi.session.finished`,{...c(t),"pi.session.reason":e.reason}),await p?.shutdown()})}}function p(e,t={}){return r(e,{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:a}}]})}async function m(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 h(t={}){return function(r){let i=t.env??process.env,o=new e,c=t.metricsSource??n({env:i}),l,u;f({...t,metrics:o,onDiagnostic:e=>{u=e,t.onDiagnostic?t.onDiagnostic(e):i.AGENT_OTEL_DIAGNOSTICS===`stderr`&&process.emitWarning(e)},onSinkStatus:e=>{l=e,t.onSinkStatus?.(e)}})(r),p(r,{source:t.leaderSource}),r.registerCommand(a,{description:`Show session-local log metrics`,handler:async(e,t)=>{await m(t,()=>({disabled:s(i.AGENT_TELEMETRY_DISABLED)||s(i.OTEL_SDK_DISABLED),snapshot:o.snapshot(),sink:l,transport:c.lastTransport(),lastDiagnostic:u}),c.query)}})}}const g=h();export{h as createDoomLogExtension,f as createPiTelemetryExtension,g as doomLogExtension,m as openLogMetricsOverlay,p as registerLogMetricsLeaderBinding};
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 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\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 createPiTelemetryExtension(options: PiTelemetryExtensionOptions = {}) {\n return function registerPiTelemetry(pi: ExtensionAPI): void {\n const env = options.env ?? process.env;\n const metrics = options.metrics;\n const toolStartedAt = new Map<string, number>();\n let agentStartedAt: number | undefined;\n let telemetry: DoomTelemetry | undefined;\n\n const emit = async (level: RecordLevel, name: string, attributes: TelemetryAttributes): Promise<void> => {\n if (!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 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) => metrics?.record(record.event, record.attributes),\n onStatus: (status) => options.onSinkStatus?.(statusFromTelemetry(status)),\n });\n options.onSinkStatus?.(statusFromTelemetry(telemetry.status()));\n return telemetry;\n };\n\n pi.on('session_start', async (event, ctx) => {\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 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 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 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 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 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 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 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 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 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 agentStartedAt = undefined;\n await telemetry?.flush();\n });\n\n pi.on('agent_settled', async (_event, ctx) => {\n getTelemetry(ctx);\n await emit('debug', 'pi.agent.settled', contextAttributes(ctx));\n });\n\n pi.on('session_shutdown', async (event, ctx) => {\n getTelemetry(ctx);\n await emit('info', 'pi.session.finished', {\n ...contextAttributes(ctx),\n 'pi.session.reason': event.reason,\n });\n await telemetry?.shutdown();\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 createDoomLogExtension(options: PiTelemetryExtensionOptions = {}) {\n return function registerDoomLog(pi: ExtensionAPI): void {\n const env = options.env ?? process.env;\n const metrics = new LogMetricsAggregator();\n const metricsSource = options.metricsSource ?? createMetricsSource({ env });\n let sink: SinkStatus | undefined;\n let lastDiagnostic: string | undefined;\n\n createPiTelemetryExtension({\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 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 sink = status;\n options.onSinkStatus?.(status);\n },\n })(pi);\n\n registerLogMetricsLeaderBinding(pi, { source: options.leaderSource });\n pi.registerCommand(LOG_METRICS_COMMAND, {\n description: LOG_METRICS_DESCRIPTION,\n handler: async (_args, ctx) => {\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 };\n}\n\nexport const doomLogExtension = createDoomLogExtension();\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/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./services/metrics.cjs"),t=require("./services/findings.cjs"),n=require("./tui/logMetricsOverlay.cjs"),r=require("./adapters/pi/extension.cjs");exports.API_ERROR_RECORD=e.API_ERROR_RECORD,exports.LogMetricsAggregator=e.LogMetricsAggregator,exports.LogMetricsOverlayComponent=n.LogMetricsOverlayComponent,exports.MAX_RETAINED_STATE_PER_TOOL=e.MAX_RETAINED_STATE_PER_TOOL,exports.MAX_RETAINED_TOKEN_BUCKETS=e.MAX_RETAINED_TOKEN_BUCKETS,exports.TOOL_RESULT_RECORD=e.TOOL_RESULT_RECORD,exports.TOOL_TOKEN_SAMPLE_RECORD=e.TOOL_TOKEN_SAMPLE_RECORD,exports.TURN_FAILED_RECORD=e.TURN_FAILED_RECORD,exports.TURN_FINISHED_RECORD=e.TURN_FINISHED_RECORD,exports.createDoomLogExtension=r.createDoomLogExtension,exports.createPiTelemetryExtension=r.createPiTelemetryExtension,exports.deriveFindings=t.deriveFindings,exports.openLogMetricsOverlay=r.openLogMetricsOverlay,exports.registerLogMetricsLeaderBinding=r.registerLogMetricsLeaderBinding;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./services/metrics.cjs"),t=require("./services/findings.cjs"),n=require("./tui/logMetricsOverlay.cjs"),r=require("./adapters/pi/extension.cjs");exports.API_ERROR_RECORD=e.API_ERROR_RECORD,exports.LogMetricsAggregator=e.LogMetricsAggregator,exports.LogMetricsOverlayComponent=n.LogMetricsOverlayComponent,exports.MAX_RETAINED_STATE_PER_TOOL=e.MAX_RETAINED_STATE_PER_TOOL,exports.MAX_RETAINED_TOKEN_BUCKETS=e.MAX_RETAINED_TOKEN_BUCKETS,exports.TOOL_RESULT_RECORD=e.TOOL_RESULT_RECORD,exports.TOOL_TOKEN_SAMPLE_RECORD=e.TOOL_TOKEN_SAMPLE_RECORD,exports.TURN_FAILED_RECORD=e.TURN_FAILED_RECORD,exports.TURN_FINISHED_RECORD=e.TURN_FINISHED_RECORD,exports.deriveFindings=t.deriveFindings,exports.installDoomLogRuntime=r.installDoomLogRuntime,exports.openLogMetricsOverlay=r.openLogMetricsOverlay,exports.registerLogMetricsLeaderBinding=r.registerLogMetricsLeaderBinding;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { API_ERROR_RECORD, LogMetricsAggregator, LogMetricsAggregatorOptions, LogMetricsError, LogMetricsEventCount, LogMetricsOperationDuration, LogMetricsRecorder, LogMetricsSnapshot, LogMetricsTokenTotals, LogMetricsToolCost, LogMetricsToolLatency, MAX_RETAINED_STATE_PER_TOOL, MAX_RETAINED_TOKEN_BUCKETS, TOOL_RESULT_RECORD, TOOL_TOKEN_SAMPLE_RECORD, TURN_FAILED_RECORD, TURN_FINISHED_RECORD } from "./services/metrics.cjs";
2
2
  import { LogMetricsOverlayComponent, LogMetricsView, SinkStatus } from "./tui/logMetricsOverlay.cjs";
3
- import { PiTelemetryExtensionOptions, createDoomLogExtension, createPiTelemetryExtension, openLogMetricsOverlay, registerLogMetricsLeaderBinding } from "./adapters/pi/extension.cjs";
3
+ import { PiTelemetryExtensionOptions, installDoomLogRuntime, openLogMetricsOverlay, registerLogMetricsLeaderBinding } from "./adapters/pi/extension.cjs";
4
4
  import { LogMetricsFinding, LogMetricsFindingSeverity, deriveFindings } from "./services/findings.cjs";
5
- export { API_ERROR_RECORD, LogMetricsAggregator, type LogMetricsAggregatorOptions, type LogMetricsError, type LogMetricsEventCount, type LogMetricsFinding, type LogMetricsFindingSeverity, type LogMetricsOperationDuration, LogMetricsOverlayComponent, type LogMetricsRecorder, type LogMetricsSnapshot, type LogMetricsTokenTotals, type LogMetricsToolCost, type LogMetricsToolLatency, type LogMetricsView, MAX_RETAINED_STATE_PER_TOOL, MAX_RETAINED_TOKEN_BUCKETS, type PiTelemetryExtensionOptions, type SinkStatus, TOOL_RESULT_RECORD, TOOL_TOKEN_SAMPLE_RECORD, TURN_FAILED_RECORD, TURN_FINISHED_RECORD, createDoomLogExtension, createPiTelemetryExtension, deriveFindings, openLogMetricsOverlay, registerLogMetricsLeaderBinding };
5
+ export { API_ERROR_RECORD, LogMetricsAggregator, type LogMetricsAggregatorOptions, type LogMetricsError, type LogMetricsEventCount, type LogMetricsFinding, type LogMetricsFindingSeverity, type LogMetricsOperationDuration, LogMetricsOverlayComponent, type LogMetricsRecorder, type LogMetricsSnapshot, type LogMetricsTokenTotals, type LogMetricsToolCost, type LogMetricsToolLatency, type LogMetricsView, MAX_RETAINED_STATE_PER_TOOL, MAX_RETAINED_TOKEN_BUCKETS, type PiTelemetryExtensionOptions, type SinkStatus, TOOL_RESULT_RECORD, TOOL_TOKEN_SAMPLE_RECORD, TURN_FAILED_RECORD, TURN_FINISHED_RECORD, deriveFindings, installDoomLogRuntime, openLogMetricsOverlay, registerLogMetricsLeaderBinding };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { API_ERROR_RECORD, LogMetricsAggregator, LogMetricsAggregatorOptions, LogMetricsError, LogMetricsEventCount, LogMetricsOperationDuration, LogMetricsRecorder, LogMetricsSnapshot, LogMetricsTokenTotals, LogMetricsToolCost, LogMetricsToolLatency, MAX_RETAINED_STATE_PER_TOOL, MAX_RETAINED_TOKEN_BUCKETS, TOOL_RESULT_RECORD, TOOL_TOKEN_SAMPLE_RECORD, TURN_FAILED_RECORD, TURN_FINISHED_RECORD } from "./services/metrics.mjs";
2
2
  import { LogMetricsOverlayComponent, LogMetricsView, SinkStatus } from "./tui/logMetricsOverlay.mjs";
3
- import { PiTelemetryExtensionOptions, createDoomLogExtension, createPiTelemetryExtension, openLogMetricsOverlay, registerLogMetricsLeaderBinding } from "./adapters/pi/extension.mjs";
3
+ import { PiTelemetryExtensionOptions, installDoomLogRuntime, openLogMetricsOverlay, registerLogMetricsLeaderBinding } from "./adapters/pi/extension.mjs";
4
4
  import { LogMetricsFinding, LogMetricsFindingSeverity, deriveFindings } from "./services/findings.mjs";
5
- export { API_ERROR_RECORD, LogMetricsAggregator, type LogMetricsAggregatorOptions, type LogMetricsError, type LogMetricsEventCount, type LogMetricsFinding, type LogMetricsFindingSeverity, type LogMetricsOperationDuration, LogMetricsOverlayComponent, type LogMetricsRecorder, type LogMetricsSnapshot, type LogMetricsTokenTotals, type LogMetricsToolCost, type LogMetricsToolLatency, type LogMetricsView, MAX_RETAINED_STATE_PER_TOOL, MAX_RETAINED_TOKEN_BUCKETS, type PiTelemetryExtensionOptions, type SinkStatus, TOOL_RESULT_RECORD, TOOL_TOKEN_SAMPLE_RECORD, TURN_FAILED_RECORD, TURN_FINISHED_RECORD, createDoomLogExtension, createPiTelemetryExtension, deriveFindings, openLogMetricsOverlay, registerLogMetricsLeaderBinding };
5
+ export { API_ERROR_RECORD, LogMetricsAggregator, type LogMetricsAggregatorOptions, type LogMetricsError, type LogMetricsEventCount, type LogMetricsFinding, type LogMetricsFindingSeverity, type LogMetricsOperationDuration, LogMetricsOverlayComponent, type LogMetricsRecorder, type LogMetricsSnapshot, type LogMetricsTokenTotals, type LogMetricsToolCost, type LogMetricsToolLatency, type LogMetricsView, MAX_RETAINED_STATE_PER_TOOL, MAX_RETAINED_TOKEN_BUCKETS, type PiTelemetryExtensionOptions, type SinkStatus, TOOL_RESULT_RECORD, TOOL_TOKEN_SAMPLE_RECORD, TURN_FAILED_RECORD, TURN_FINISHED_RECORD, deriveFindings, installDoomLogRuntime, openLogMetricsOverlay, registerLogMetricsLeaderBinding };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{API_ERROR_RECORD as e,LogMetricsAggregator as t,MAX_RETAINED_STATE_PER_TOOL as n,MAX_RETAINED_TOKEN_BUCKETS as r,TOOL_RESULT_RECORD as i,TOOL_TOKEN_SAMPLE_RECORD as a,TURN_FAILED_RECORD as o,TURN_FINISHED_RECORD as s}from"./services/metrics.mjs";import{deriveFindings as c}from"./services/findings.mjs";import{LogMetricsOverlayComponent as l}from"./tui/logMetricsOverlay.mjs";import{createDoomLogExtension as u,createPiTelemetryExtension as d,openLogMetricsOverlay as f,registerLogMetricsLeaderBinding as p}from"./adapters/pi/extension.mjs";export{e as API_ERROR_RECORD,t as LogMetricsAggregator,l as LogMetricsOverlayComponent,n as MAX_RETAINED_STATE_PER_TOOL,r as MAX_RETAINED_TOKEN_BUCKETS,i as TOOL_RESULT_RECORD,a as TOOL_TOKEN_SAMPLE_RECORD,o as TURN_FAILED_RECORD,s as TURN_FINISHED_RECORD,u as createDoomLogExtension,d as createPiTelemetryExtension,c as deriveFindings,f as openLogMetricsOverlay,p as registerLogMetricsLeaderBinding};
1
+ import{API_ERROR_RECORD as e,LogMetricsAggregator as t,MAX_RETAINED_STATE_PER_TOOL as n,MAX_RETAINED_TOKEN_BUCKETS as r,TOOL_RESULT_RECORD as i,TOOL_TOKEN_SAMPLE_RECORD as a,TURN_FAILED_RECORD as o,TURN_FINISHED_RECORD as s}from"./services/metrics.mjs";import{deriveFindings as c}from"./services/findings.mjs";import{LogMetricsOverlayComponent as l}from"./tui/logMetricsOverlay.mjs";import{installDoomLogRuntime as u,openLogMetricsOverlay as d,registerLogMetricsLeaderBinding as f}from"./adapters/pi/extension.mjs";export{e as API_ERROR_RECORD,t as LogMetricsAggregator,l as LogMetricsOverlayComponent,n as MAX_RETAINED_STATE_PER_TOOL,r as MAX_RETAINED_TOKEN_BUCKETS,i as TOOL_RESULT_RECORD,a as TOOL_TOKEN_SAMPLE_RECORD,o as TURN_FAILED_RECORD,s as TURN_FINISHED_RECORD,c as deriveFindings,u as installDoomLogRuntime,d as openLogMetricsOverlay,f as registerLogMetricsLeaderBinding};
package/package.json CHANGED
@@ -1,14 +1,18 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-log",
3
- "version": "0.0.1-alpha.23",
3
+ "version": "0.0.1-alpha.25",
4
4
  "description": "Pi session metrics, findings, sink status, and a Log Metrics overlay for agent observability.",
5
5
  "keywords": [
6
6
  "agent-observability",
7
+ "ai",
8
+ "coding-agent",
9
+ "developer-tools",
7
10
  "doompi",
8
11
  "log-sink",
9
12
  "logging",
10
13
  "metrics",
11
14
  "pi-extension",
15
+ "pi-package",
12
16
  "telemetry"
13
17
  ],
14
18
  "homepage": "https://agimon.ai",
@@ -27,11 +31,6 @@
27
31
  "import": "./dist/index.mjs",
28
32
  "require": "./dist/index.cjs"
29
33
  },
30
- "./extension": {
31
- "types": "./dist/extension.d.mts",
32
- "import": "./dist/extension.mjs",
33
- "require": "./dist/extension.cjs"
34
- },
35
34
  "./extensions/pi": {
36
35
  "types": "./dist/extensions/pi.d.mts",
37
36
  "import": "./dist/extensions/pi.mjs",
@@ -59,9 +58,10 @@
59
58
  "registry": "https://registry.npmjs.org/"
60
59
  },
61
60
  "dependencies": {
62
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.23",
63
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.23",
64
- "@agimon-ai/log-sink-mcp": "0.29.13"
61
+ "@deepseek-ai/cordis": "4.0.1",
62
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.25",
63
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.25",
64
+ "@agimon-ai/log-sink-mcp": "0.29.15"
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.23"
74
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.25"
75
75
  },
76
76
  "peerDependencies": {
77
77
  "@earendil-works/pi-coding-agent": "0.84.2",
@@ -1 +0,0 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./adapters/pi/extension.cjs");exports.createDoomLogExtension=e.createDoomLogExtension,exports.createPiTelemetryExtension=e.createPiTelemetryExtension,exports.openLogMetricsOverlay=e.openLogMetricsOverlay,exports.registerLogMetricsLeaderBinding=e.registerLogMetricsLeaderBinding;
@@ -1,2 +0,0 @@
1
- import { PiTelemetryExtensionOptions, createDoomLogExtension, createPiTelemetryExtension, openLogMetricsOverlay, registerLogMetricsLeaderBinding } from "./adapters/pi/extension.cjs";
2
- export { type PiTelemetryExtensionOptions, createDoomLogExtension, createPiTelemetryExtension, openLogMetricsOverlay, registerLogMetricsLeaderBinding };
@@ -1,2 +0,0 @@
1
- import { PiTelemetryExtensionOptions, createDoomLogExtension, createPiTelemetryExtension, openLogMetricsOverlay, registerLogMetricsLeaderBinding } from "./adapters/pi/extension.mjs";
2
- export { type PiTelemetryExtensionOptions, createDoomLogExtension, createPiTelemetryExtension, openLogMetricsOverlay, registerLogMetricsLeaderBinding };
@@ -1 +0,0 @@
1
- import{createDoomLogExtension as e,createPiTelemetryExtension as t,openLogMetricsOverlay as n,registerLogMetricsLeaderBinding as r}from"./adapters/pi/extension.mjs";export{e as createDoomLogExtension,t as createPiTelemetryExtension,n as openLogMetricsOverlay,r as registerLogMetricsLeaderBinding};