@timefly/opencode-plugin 0.2.7 → 0.2.8

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/dist/cli.js CHANGED
File without changes
@@ -1 +1 @@
1
- {"version":3,"file":"event-handlers.d.ts","sourceRoot":"","sources":["../src/event-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAiBvD,OAAO,EASN,KAAK,gBAAgB,EACrB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAG/D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,SAAS,CAAC,CAAA;AAKxE,eAAO,MAAM,oBAAoB,GAChC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAed,CAAA;AAED,eAAO,MAAM,iBAAiB,GAC7B,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAad,CAAA;AAED,eAAO,MAAM,oBAAoB,GAChC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAwDd,CAAA;AAED,eAAO,MAAM,wBAAwB,GACpC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAqCd,CAAA;AAED,eAAO,MAAM,sBAAsB,GAClC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAWd,CAAA;AAED,eAAO,MAAM,kBAAkB,GAC9B,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAqBd,CAAA;AAED,eAAO,MAAM,qBAAqB,GACjC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAad,CAAA;AAED,eAAO,MAAM,cAAc,GAC1B,OAAO,gBAAgB,EACvB,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAkCd,CAAA"}
1
+ {"version":3,"file":"event-handlers.d.ts","sourceRoot":"","sources":["../src/event-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAiBvD,OAAO,EASN,KAAK,gBAAgB,EACrB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAG/D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,SAAS,CAAC,CAAA;AAKxE,eAAO,MAAM,oBAAoB,GAChC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAed,CAAA;AAED,eAAO,MAAM,iBAAiB,GAC7B,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAad,CAAA;AAED,eAAO,MAAM,oBAAoB,GAChC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CA0Dd,CAAA;AAED,eAAO,MAAM,wBAAwB,GACpC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAqCd,CAAA;AAED,eAAO,MAAM,sBAAsB,GAClC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAWd,CAAA;AAED,eAAO,MAAM,kBAAkB,GAC9B,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAqBd,CAAA;AAED,eAAO,MAAM,qBAAqB,GACjC,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAad,CAAA;AAED,eAAO,MAAM,cAAc,GAC1B,OAAO,gBAAgB,EACvB,SAAS,UAAU,CAAC,OAAO,kBAAkB,CAAC,EAC9C,SAAS,cAAc,KACrB,OAAO,CAAC,IAAI,CAkCd,CAAA"}
@@ -33,8 +33,9 @@ export const handleMessageUpdated = (eventProperties, tracker, publish) => {
33
33
  if (assistantMessage.time.completed === undefined || tracker.hasProcessedMessage(assistantMessage.id)) {
34
34
  return Promise.resolve();
35
35
  }
36
+ tracker.recordSessionStart(assistantMessage.sessionID, assistantMessage.time.created);
36
37
  tracker.markMessageProcessed(assistantMessage.id);
37
- const durationMs = assistantMessage.time.completed - assistantMessage.time.created;
38
+ const durationMs = Math.max(0, assistantMessage.time.completed - assistantMessage.time.created);
38
39
  const tokenMetrics = buildTokenMetrics(assistantMessage.tokens, durationMs);
39
40
  const sessionStats = tracker.getSessionStats(assistantMessage.sessionID);
40
41
  const compactionDelta = tracker.recordTurnTokens(assistantMessage.sessionID, {
@@ -65,6 +66,7 @@ export const handleMessageUpdated = (eventProperties, tracker, publish) => {
65
66
  if (tracker.hasProcessedUserMessage(userMessage.id)) {
66
67
  return Promise.resolve();
67
68
  }
69
+ tracker.recordSessionStart(userMessage.sessionID);
68
70
  tracker.markUserMessageProcessed(userMessage.id);
69
71
  return publish([mapUserMessageInput(userMessage)]);
70
72
  }
@@ -124,7 +126,7 @@ export const handleSessionError = (eventProperties, tracker, publish) => {
124
126
  return publish([
125
127
  mapErrorInput(sessionId, {
126
128
  error_name: errorName,
127
- error_message: errorMessage,
129
+ error_message_length: errorMessage.length,
128
130
  error_scope: 'session'
129
131
  })
130
132
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"event-tracker.d.ts","sourceRoot":"","sources":["../src/event-tracker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"event-tracker.d.ts","sourceRoot":"","sources":["../src/event-tracker.ts"],"names":[],"mappings":"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"}
@@ -21,6 +21,11 @@ const emptySessionStats = () => ({
21
21
  primaryProviderSource: '',
22
22
  aiGenerationMs: 0
23
23
  });
24
+ const INVALID_ID_VALUES = new Set(['', 'unknown', 'undefined', 'null']);
25
+ const cleanIdentity = (value) => {
26
+ const cleanedValue = value.trim();
27
+ return INVALID_ID_VALUES.has(cleanedValue.toLowerCase()) ? '' : cleanedValue;
28
+ };
24
29
  const mergeSessionStats = (currentStats, delta) => ({
25
30
  billedInputTokens: currentStats.billedInputTokens + (delta.billedInputTokens ?? 0),
26
31
  outputTokens: currentStats.outputTokens + (delta.outputTokens ?? 0),
@@ -42,8 +47,8 @@ const mergeSessionStats = (currentStats, delta) => ({
42
47
  : (delta.contextAtStartTokens ?? currentStats.contextAtStartTokens),
43
48
  compactionTokensSaved: currentStats.compactionTokensSaved + (delta.compactionTokensSaved ?? 0),
44
49
  totalToolOutputChars: currentStats.totalToolOutputChars + (delta.totalToolOutputChars ?? 0),
45
- primaryProviderId: delta.primaryProviderId ?? currentStats.primaryProviderId,
46
- primaryProviderSource: delta.primaryProviderSource ?? currentStats.primaryProviderSource,
50
+ primaryProviderId: delta.primaryProviderId !== undefined ? cleanIdentity(delta.primaryProviderId) : currentStats.primaryProviderId,
51
+ primaryProviderSource: delta.primaryProviderSource !== undefined ? cleanIdentity(delta.primaryProviderSource) : currentStats.primaryProviderSource,
47
52
  startedAtMs: currentStats.startedAtMs,
48
53
  aiGenerationMs: currentStats.aiGenerationMs + (delta.aiGenerationMs ?? 0)
49
54
  });
@@ -84,8 +89,8 @@ export const createEventTracker = () => {
84
89
  const currentStats = sessionStatsById.get(sessionId) ?? emptySessionStats();
85
90
  sessionStatsById.set(sessionId, {
86
91
  ...currentStats,
87
- primaryProviderId: providerId,
88
- primaryProviderSource: providerSource
92
+ primaryProviderId: cleanIdentity(providerId),
93
+ primaryProviderSource: cleanIdentity(providerSource)
89
94
  });
90
95
  },
91
96
  recordCompactionPending: (sessionId, contextBeforeTokens) => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAgBjD,eAAO,MAAM,qBAAqB,EAAE,MAsEnC,CAAA;AAED,eAAe,qBAAqB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAgBjD,eAAO,MAAM,qBAAqB,EAAE,MAuEnC,CAAA;AAED,eAAe,qBAAqB,CAAA"}
package/dist/index.js CHANGED
@@ -25,6 +25,7 @@ export const timeflyOpenCodePlugin = ({ client }) => {
25
25
  .then(() => Promise.resolve({
26
26
  event: (input) => runHookSafely(() => handleBusEvent(input.event, tracker, publisher.publish)),
27
27
  'chat.params': (input, output) => runHookSafely(() => {
28
+ tracker.recordSessionStart(input.sessionID);
28
29
  tracker.recordSessionStats(input.sessionID, { requestCount: 1 });
29
30
  const resolvedParams = resolveChatParams(input, output);
30
31
  tracker.recordProviderConnection(resolvedParams.sessionID, resolvedParams.providerId, resolvedParams.providerSource);
@@ -1 +1 @@
1
- {"version":3,"file":"map-opencode-event.d.ts","sourceRoot":"","sources":["../src/map-opencode-event.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"map-opencode-event.d.ts","sourceRoot":"","sources":["../src/map-opencode-event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AAC9D,OAAO,KAAK,EACX,wBAAwB,EACxB,sBAAsB,EACtB,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,EACnB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAEtF,YAAY,EACX,wBAAwB,EACxB,sBAAsB,EACtB,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,EACnB,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,eAAO,MAAM,qBAAqB,GAAI,WAAW,MAAM,KAAG,MAG7C,CAAA;AA8Cb,eAAO,MAAM,YAAY,GAAI,YAAY,MAAM,EAAE,SAAS,MAAM,KAAG,MAKlE,CAAA;AAED,eAAO,MAAM,oBAAoB,GAChC,aAAa,mBAAmB,KAC9B,IAAI,CAAC,uBAAuB,EAAE,WAAW,GAAG,UAAU,CAOvD,CAAA;AAEF,eAAO,MAAM,kBAAkB,GAC9B,WAAW,MAAM,EACjB,cAAc,YAAY,EAC1B,eAAe,aAAa,KAC1B,IAAI,CAAC,uBAAuB,EAAE,WAAW,GAAG,UAAU,GAAG,YAAY,CA6BtE,CAAA;AAEF,eAAO,MAAM,kBAAkB,GAAI,OAAO;IACzC,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,eAAe,CAAC,EAAE,MAAM,CAAA;CACxB,KAAG,uBAiBH,CAAA;AAED,eAAO,MAAM,6BAA6B,GACzC,SAAS,wBAAwB,EACjC,UAAU;IACT,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,eAAe,CAAC,EAAE,eAAe,CAAA;CACjC,KACC,uBAAuB,GAAG,SAqC5B,CAAA;AAED,eAAO,MAAM,4BAA4B,GAAI,SAAS,wBAAwB,KAAG,uBAAuB,GAAG,SA0B1G,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAI,MAAM,sBAAsB,KAAG,uBAiBjE,CAAA;AAED,eAAO,MAAM,mBAAmB,GAAI,SAAS,mBAAmB,KAAG,uBAelE,CAAA;AAED,eAAO,MAAM,kBAAkB,GAC9B,WAAW,MAAM,EACjB,sBAAsB,MAAM,EAC5B,OAAO,OAAO,KACZ,uBASD,CAAA;AAEF,eAAO,MAAM,aAAa,GACzB,WAAW,MAAM,EACjB,eAAe,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,KACtD,uBAID,CAAA;AAEF,eAAO,MAAM,gBAAgB,GAAI,OAAO;IACvC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;CACd,KAAG,uBAOF,CAAA;AAEF,eAAO,MAAM,kBAAkB,GAAI,OAAO;IACzC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;CACpB,KAAG,uBASF,CAAA;AAEF,eAAO,MAAM,uBAAuB,GAAI,OAAO;IAC9C,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CACjB,KAAG,uBASF,CAAA;AAEF,eAAO,MAAM,iBAAiB,GAAI,MAAM,iBAAiB,KAAG,uBAS1D,CAAA;AAEF,eAAO,MAAM,sBAAsB,GAClC,MAAM,sBAAsB,EAC5B,sBAAsB,MAAM,KAC1B,uBAWD,CAAA"}
@@ -2,7 +2,38 @@ import { buildTokenMetadata, buildTokenMetrics } from './token-usage.js';
2
2
  export const readSessionIdOverride = (sessionId) => typeof process !== 'undefined' && process.env.TIMEFLY_OPENCODE_SESSION_ID
3
3
  ? process.env.TIMEFLY_OPENCODE_SESSION_ID
4
4
  : sessionId;
5
- export const buildModelId = (providerId, modelId) => `${providerId}/${modelId}`;
5
+ const INVALID_ID_VALUES = new Set(['', 'unknown', 'undefined', 'null']);
6
+ const cleanText = (value) => value.trim();
7
+ const cleanIdentity = (value) => {
8
+ const cleanedValue = cleanText(value);
9
+ return INVALID_ID_VALUES.has(cleanedValue.toLowerCase()) ? '' : cleanedValue;
10
+ };
11
+ const cleanNumber = (value) => Number.isFinite(value) ? value : 0;
12
+ const cleanMetadata = (metadata) => Object.fromEntries(Object.entries(metadata).filter(([, metadataValue]) => {
13
+ if (metadataValue === undefined) {
14
+ return false;
15
+ }
16
+ if (typeof metadataValue === 'number') {
17
+ return Number.isFinite(metadataValue);
18
+ }
19
+ if (typeof metadataValue === 'string') {
20
+ return cleanText(metadataValue).length > 0;
21
+ }
22
+ return true;
23
+ }));
24
+ const identityMetadata = (providerId, modelId) => {
25
+ const cleanProviderId = cleanIdentity(providerId);
26
+ const cleanModelId = cleanIdentity(modelId);
27
+ return {
28
+ ...(cleanProviderId ? { provider_id: cleanProviderId } : {}),
29
+ ...(cleanModelId ? { model_id: cleanModelId } : {})
30
+ };
31
+ };
32
+ export const buildModelId = (providerId, modelId) => {
33
+ const cleanProviderId = cleanIdentity(providerId);
34
+ const cleanModelId = cleanIdentity(modelId);
35
+ return cleanProviderId && cleanModelId ? `${cleanProviderId}/${cleanModelId}` : cleanModelId;
36
+ };
6
37
  export const mapSessionStartInput = (sessionInfo) => ({
7
38
  sessionId: readSessionIdOverride(sessionInfo.id),
8
39
  metadata: {
@@ -41,43 +72,46 @@ export const mapSessionEndInput = (sessionId, sessionStats, sessionTiming) => ({
41
72
  user_wait_ms: sessionTiming.userWaitMs
42
73
  }
43
74
  });
44
- export const mapLlmRequestInput = (input) => ({
45
- sessionId: readSessionIdOverride(input.sessionID),
46
- eventType: 'llm_request',
47
- modelId: buildModelId(input.providerId, input.modelId),
48
- planMode: input.agent,
49
- metadata: {
50
- provider_id: input.providerId,
51
- model_id: input.modelId,
52
- provider_source: input.providerSource,
53
- agent: input.agent,
54
- temperature: input.temperature,
55
- top_p: input.topP,
56
- ...(input.maxOutputTokens !== undefined ? { max_output_tokens: input.maxOutputTokens } : {})
57
- }
58
- });
75
+ export const mapLlmRequestInput = (input) => {
76
+ const modelId = buildModelId(input.providerId, input.modelId);
77
+ return {
78
+ sessionId: readSessionIdOverride(input.sessionID),
79
+ eventType: 'llm_request',
80
+ ...(modelId ? { modelId } : {}),
81
+ planMode: input.agent,
82
+ metadata: cleanMetadata({
83
+ ...identityMetadata(input.providerId, input.modelId),
84
+ provider_source: cleanIdentity(input.providerSource),
85
+ agent: input.agent,
86
+ temperature: cleanNumber(input.temperature),
87
+ top_p: cleanNumber(input.topP),
88
+ ...(input.maxOutputTokens !== undefined ? { max_output_tokens: cleanNumber(input.maxOutputTokens) } : {})
89
+ })
90
+ };
91
+ };
59
92
  export const mapAssistantTurnCompleteInput = (message, options) => {
60
93
  if (message.time.completed === undefined) {
61
94
  return undefined;
62
95
  }
63
- const durationMs = message.time.completed - message.time.created;
96
+ const durationMs = Math.max(0, message.time.completed - message.time.created);
64
97
  const tokenMetrics = buildTokenMetrics(message.tokens, durationMs);
98
+ const modelId = buildModelId(message.providerID, message.modelID);
65
99
  return {
66
100
  sessionId: readSessionIdOverride(message.sessionID),
67
101
  eventType: 'turn_complete',
68
- modelId: buildModelId(message.providerID, message.modelID),
102
+ ...(modelId ? { modelId } : {}),
69
103
  planMode: message.mode,
70
104
  inputTokens: tokenMetrics.inputTokens,
71
105
  outputTokens: tokenMetrics.outputTokens,
72
106
  totalTokens: tokenMetrics.totalTokens,
73
107
  durationMs,
74
- metadata: buildTokenMetadata(tokenMetrics, {
108
+ eventAtUtc: new Date(message.time.completed),
109
+ metadata: cleanMetadata(buildTokenMetadata(tokenMetrics, {
75
110
  message_id: message.id,
76
- provider_id: message.providerID,
77
- model_id: message.modelID,
78
- ...(options?.providerSource ? { provider_source: options.providerSource } : {}),
111
+ ...identityMetadata(message.providerID, message.modelID),
112
+ ...(options?.providerSource ? { provider_source: cleanIdentity(options.providerSource) } : {}),
79
113
  context_tokens: tokenMetrics.inputTokens,
80
- cost: message.cost,
114
+ cost: cleanNumber(message.cost),
81
115
  ...(message.finish ? { finish_reason: message.finish } : {}),
82
116
  has_error: Boolean(message.error),
83
117
  ...(message.error ? { error_name: message.error.name } : {}),
@@ -88,31 +122,32 @@ export const mapAssistantTurnCompleteInput = (message, options) => {
88
122
  compaction_tokens_saved: options.compactionDelta.tokensSaved
89
123
  }
90
124
  : {})
91
- })
125
+ }))
92
126
  };
93
127
  };
94
128
  export const mapAssistantLlmResponseInput = (message) => {
95
129
  if (message.time.completed === undefined) {
96
130
  return undefined;
97
131
  }
98
- const durationMs = message.time.completed - message.time.created;
132
+ const durationMs = Math.max(0, message.time.completed - message.time.created);
99
133
  const tokenMetrics = buildTokenMetrics(message.tokens, durationMs);
134
+ const modelId = buildModelId(message.providerID, message.modelID);
100
135
  return {
101
136
  sessionId: readSessionIdOverride(message.sessionID),
102
137
  eventType: 'llm_response',
103
- modelId: buildModelId(message.providerID, message.modelID),
138
+ ...(modelId ? { modelId } : {}),
104
139
  planMode: message.mode,
105
140
  inputTokens: tokenMetrics.inputTokens,
106
141
  outputTokens: tokenMetrics.outputTokens,
107
142
  totalTokens: tokenMetrics.totalTokens,
108
143
  durationMs,
109
- metadata: buildTokenMetadata(tokenMetrics, {
144
+ eventAtUtc: new Date(message.time.completed),
145
+ metadata: cleanMetadata(buildTokenMetadata(tokenMetrics, {
110
146
  message_id: message.id,
111
- provider_id: message.providerID,
112
- model_id: message.modelID,
113
- cost: message.cost,
147
+ ...identityMetadata(message.providerID, message.modelID),
148
+ cost: cleanNumber(message.cost),
114
149
  response_scope: 'message'
115
- })
150
+ }))
116
151
  };
117
152
  };
118
153
  export const mapStepFinishInput = (part) => {
@@ -123,28 +158,30 @@ export const mapStepFinishInput = (part) => {
123
158
  inputTokens: tokenMetrics.inputTokens,
124
159
  outputTokens: tokenMetrics.outputTokens,
125
160
  totalTokens: tokenMetrics.totalTokens,
126
- metadata: buildTokenMetadata(tokenMetrics, {
161
+ metadata: cleanMetadata(buildTokenMetadata(tokenMetrics, {
127
162
  message_id: part.messageID,
128
163
  part_id: part.id,
129
164
  step_reason: part.reason,
130
- cost: part.cost,
165
+ cost: cleanNumber(part.cost),
131
166
  response_scope: 'step'
167
+ }))
168
+ };
169
+ };
170
+ export const mapUserMessageInput = (message) => {
171
+ const modelId = buildModelId(message.model.providerID, message.model.modelID);
172
+ return {
173
+ sessionId: readSessionIdOverride(message.sessionID),
174
+ eventType: 'llm_request',
175
+ ...(modelId ? { modelId } : {}),
176
+ planMode: message.agent,
177
+ metadata: cleanMetadata({
178
+ message_id: message.id,
179
+ ...identityMetadata(message.model.providerID, message.model.modelID),
180
+ agent: message.agent,
181
+ request_scope: 'user_message'
132
182
  })
133
183
  };
134
184
  };
135
- export const mapUserMessageInput = (message) => ({
136
- sessionId: readSessionIdOverride(message.sessionID),
137
- eventType: 'llm_request',
138
- modelId: buildModelId(message.model.providerID, message.model.modelID),
139
- planMode: message.agent,
140
- metadata: {
141
- message_id: message.id,
142
- provider_id: message.model.providerID,
143
- model_id: message.model.modelID,
144
- agent: message.agent,
145
- request_scope: 'user_message'
146
- }
147
- });
148
185
  export const mapCompactionInput = (sessionId, contextBeforeTokens, auto) => ({
149
186
  sessionId: readSessionIdOverride(sessionId),
150
187
  eventType: 'compaction',
@@ -184,7 +221,7 @@ export const mapCommandExecutedInput = (input) => ({
184
221
  toolName: `command:${input.name}`,
185
222
  metadata: {
186
223
  command_name: input.name,
187
- command_arguments: input.arguments,
224
+ command_arguments_length: input.arguments.length,
188
225
  message_id: input.messageID
189
226
  }
190
227
  });
@@ -1 +1 @@
1
- {"version":3,"file":"opencode-readers.d.ts","sourceRoot":"","sources":["../src/opencode-readers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"opencode-readers.d.ts","sourceRoot":"","sources":["../src/opencode-readers.ts"],"names":[],"mappings":"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"}
@@ -1,23 +1,30 @@
1
1
  const isRecord = (value) => typeof value === 'object' && value !== null;
2
+ const isFiniteNumber = (value) => typeof value === 'number' && Number.isFinite(value);
3
+ const readTelemetryNumber = (value) => typeof value === 'number' ? (Number.isFinite(value) ? value : 0) : undefined;
2
4
  const readTokenUsage = (value) => {
3
5
  if (!isRecord(value)) {
4
6
  return undefined;
5
7
  }
6
8
  const cacheRecord = isRecord(value.cache) ? value.cache : undefined;
7
- if (typeof value.input !== 'number' ||
8
- typeof value.output !== 'number' ||
9
- typeof value.reasoning !== 'number' ||
10
- typeof cacheRecord?.read !== 'number' ||
11
- typeof cacheRecord?.write !== 'number') {
9
+ const inputTokens = readTelemetryNumber(value.input);
10
+ const outputTokens = readTelemetryNumber(value.output);
11
+ const reasoningTokens = readTelemetryNumber(value.reasoning);
12
+ const cacheReadTokens = readTelemetryNumber(cacheRecord?.read);
13
+ const cacheWriteTokens = readTelemetryNumber(cacheRecord?.write);
14
+ if (inputTokens === undefined ||
15
+ outputTokens === undefined ||
16
+ reasoningTokens === undefined ||
17
+ cacheReadTokens === undefined ||
18
+ cacheWriteTokens === undefined) {
12
19
  return undefined;
13
20
  }
14
21
  return {
15
- input: value.input,
16
- output: value.output,
17
- reasoning: value.reasoning,
22
+ input: inputTokens,
23
+ output: outputTokens,
24
+ reasoning: reasoningTokens,
18
25
  cache: {
19
- read: cacheRecord.read,
20
- write: cacheRecord.write
26
+ read: cacheReadTokens,
27
+ write: cacheWriteTokens
21
28
  }
22
29
  };
23
30
  };
@@ -34,13 +41,14 @@ export const readAssistantMessage = (message) => {
34
41
  }
35
42
  const tokenUsage = readTokenUsage(message.tokens);
36
43
  const timeRecord = isRecord(message.time) ? message.time : undefined;
44
+ const cost = readTelemetryNumber(message.cost);
37
45
  if (typeof message.id !== 'string' ||
38
46
  typeof message.sessionID !== 'string' ||
39
47
  typeof message.modelID !== 'string' ||
40
48
  typeof message.providerID !== 'string' ||
41
49
  typeof message.mode !== 'string' ||
42
- typeof message.cost !== 'number' ||
43
- typeof timeRecord?.created !== 'number' ||
50
+ cost === undefined ||
51
+ !isFiniteNumber(timeRecord?.created) ||
44
52
  !tokenUsage) {
45
53
  return undefined;
46
54
  }
@@ -50,12 +58,12 @@ export const readAssistantMessage = (message) => {
50
58
  role: 'assistant',
51
59
  time: {
52
60
  created: timeRecord.created,
53
- completed: typeof timeRecord.completed === 'number' ? timeRecord.completed : undefined
61
+ completed: isFiniteNumber(timeRecord.completed) ? timeRecord.completed : undefined
54
62
  },
55
63
  modelID: message.modelID,
56
64
  providerID: message.providerID,
57
65
  mode: message.mode,
58
- cost: message.cost,
66
+ cost,
59
67
  tokens: tokenUsage,
60
68
  finish: typeof message.finish === 'string' ? message.finish : undefined,
61
69
  error: isRecord(message.error) && typeof message.error.name === 'string' ? { name: message.error.name } : undefined
@@ -100,11 +108,12 @@ export const readStepFinishPart = (part) => {
100
108
  return undefined;
101
109
  }
102
110
  const tokenUsage = readTokenUsage(part.tokens);
111
+ const cost = readTelemetryNumber(part.cost);
103
112
  if (typeof part.id !== 'string' ||
104
113
  typeof part.sessionID !== 'string' ||
105
114
  typeof part.messageID !== 'string' ||
106
115
  typeof part.reason !== 'string' ||
107
- typeof part.cost !== 'number' ||
116
+ cost === undefined ||
108
117
  !tokenUsage) {
109
118
  return undefined;
110
119
  }
@@ -114,7 +123,7 @@ export const readStepFinishPart = (part) => {
114
123
  messageID: part.messageID,
115
124
  type: 'step-finish',
116
125
  reason: part.reason,
117
- cost: part.cost,
126
+ cost,
118
127
  tokens: tokenUsage
119
128
  };
120
129
  };
@@ -1 +1 @@
1
- {"version":3,"file":"token-usage.d.ts","sourceRoot":"","sources":["../src/token-usage.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE;QACN,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,EAAE,MAAM,CAAA;KACb,CAAA;CACD,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;IACxB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAC7B,CAAA;AAED,eAAO,MAAM,aAAa,GAAI,YAAY,kBAAkB,KAAG,MACH,CAAA;AAE5D,eAAO,MAAM,iBAAiB,GAAI,YAAY,kBAAkB,EAAE,aAAa,MAAM,KAAG,YAmBvF,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAI,cAAc,YAAY,EAAE,QAAO,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAM,KAAG,MAAM,CAC5H,MAAM,EACN,MAAM,GAAG,MAAM,GAAG,OAAO,CAUxB,CAAA"}
1
+ {"version":3,"file":"token-usage.d.ts","sourceRoot":"","sources":["../src/token-usage.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE;QACN,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,EAAE,MAAM,CAAA;KACb,CAAA;CACD,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;IACxB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAC7B,CAAA;AAKD,eAAO,MAAM,aAAa,GAAI,YAAY,kBAAkB,KAAG,MACyD,CAAA;AAExH,eAAO,MAAM,iBAAiB,GAAI,YAAY,kBAAkB,EAAE,aAAa,MAAM,KAAG,YAmBvF,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAI,cAAc,YAAY,EAAE,QAAO,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAM,KAAG,MAAM,CAC5H,MAAM,EACN,MAAM,GAAG,MAAM,GAAG,OAAO,CAUxB,CAAA"}
@@ -1,17 +1,18 @@
1
- export const sumTokenUsage = (tokenUsage) => tokenUsage.input + tokenUsage.output + tokenUsage.reasoning;
1
+ const sanitizeTokenCount = (value) => Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
2
+ export const sumTokenUsage = (tokenUsage) => sanitizeTokenCount(tokenUsage.input) + sanitizeTokenCount(tokenUsage.output) + sanitizeTokenCount(tokenUsage.reasoning);
2
3
  export const buildTokenMetrics = (tokenUsage, durationMs) => {
3
- const inputTokens = tokenUsage.input;
4
- const outputTokens = tokenUsage.output;
5
- const reasoningTokens = tokenUsage.reasoning;
4
+ const inputTokens = sanitizeTokenCount(tokenUsage.input);
5
+ const outputTokens = sanitizeTokenCount(tokenUsage.output);
6
+ const reasoningTokens = sanitizeTokenCount(tokenUsage.reasoning);
6
7
  const totalTokens = sumTokenUsage(tokenUsage);
7
- const durationSeconds = durationMs && durationMs > 0 ? durationMs / 1000 : undefined;
8
+ const durationSeconds = durationMs && Number.isFinite(durationMs) && durationMs > 0 ? durationMs / 1000 : undefined;
8
9
  return {
9
10
  inputTokens,
10
11
  outputTokens,
11
12
  totalTokens,
12
13
  reasoningTokens,
13
- cacheReadTokens: tokenUsage.cache.read,
14
- cacheWriteTokens: tokenUsage.cache.write,
14
+ cacheReadTokens: sanitizeTokenCount(tokenUsage.cache.read),
15
+ cacheWriteTokens: sanitizeTokenCount(tokenUsage.cache.write),
15
16
  outputTokensPerSecond: durationSeconds !== undefined ? Math.round((outputTokens / durationSeconds) * 100) / 100 : undefined,
16
17
  totalTokensPerSecond: durationSeconds !== undefined ? Math.round((totalTokens / durationSeconds) * 100) / 100 : undefined
17
18
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timefly/opencode-plugin",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "TimeFly telemetry plugin for OpenCode — sessions, tokens, models, and tools",
5
5
  "type": "module",
6
6
  "bin": "./dist/cli.js",
@@ -50,14 +50,14 @@
50
50
  },
51
51
  "license": "MIT",
52
52
  "dependencies": {
53
- "@timefly/ai-sdk": "^0.2.0"
53
+ "@timefly/ai-sdk": "^0.2.1"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "@opencode-ai/plugin": ">=1.0.0"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@opencode-ai/plugin": "^1.17.7",
60
- "@timefly/ai-sdk": "workspace:*",
60
+ "@timefly/ai-sdk": "0.2.1",
61
61
  "@types/bun": "^1.3.14",
62
62
  "@types/node": "^22.15.21",
63
63
  "typescript": "^5.9.3"