@techdivision/opencode-time-tracking 0.1.9 → 0.2.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techdivision/opencode-time-tracking",
3
- "version": "0.1.9",
3
+ "version": "0.2.0",
4
4
  "description": "Automatic time tracking plugin for OpenCode - tracks session duration and tool usage to CSV",
5
5
  "main": "src/Plugin.ts",
6
6
  "types": "src/Plugin.ts",
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Event hook for session lifecycle and token tracking.
3
3
  */
4
4
 
5
- import type { Event } from "@opencode-ai/sdk"
5
+ import type { AssistantMessage, Event, Message } from "@opencode-ai/sdk"
6
6
 
7
7
  import type { CsvWriter } from "../services/CsvWriter"
8
8
  import type { SessionManager } from "../services/SessionManager"
@@ -12,6 +12,13 @@ import type { OpencodeClient } from "../types/OpencodeClient"
12
12
 
13
13
  import { DescriptionGenerator } from "../utils/DescriptionGenerator"
14
14
 
15
+ /**
16
+ * Properties for message.updated events.
17
+ */
18
+ interface MessageUpdatedProperties {
19
+ info: Message
20
+ }
21
+
15
22
  /**
16
23
  * Extracts the summary title from the last user message.
17
24
  *
@@ -60,10 +67,11 @@ async function extractSummaryTitle(
60
67
  * @returns The event hook function
61
68
  *
62
69
  * @remarks
63
- * Handles two types of events:
70
+ * Handles three types of events:
64
71
  *
65
- * 1. **message.part.updated** - Tracks token usage from step-finish parts
66
- * 2. **session.idle** - Finalizes and exports the session
72
+ * 1. **message.updated** - Tracks model from assistant messages
73
+ * 2. **message.part.updated** - Tracks token usage from step-finish parts
74
+ * 3. **session.idle** - Finalizes and exports the session
67
75
  *
68
76
  * @example
69
77
  * ```typescript
@@ -78,6 +86,25 @@ export function createEventHook(
78
86
  client: OpencodeClient
79
87
  ) {
80
88
  return async ({ event }: { event: Event }): Promise<void> => {
89
+ // Track model from assistant messages
90
+ if (event.type === "message.updated") {
91
+ const props = event.properties as MessageUpdatedProperties
92
+ const message = props.info
93
+
94
+ if (message.role === "assistant") {
95
+ const assistantMsg = message as AssistantMessage
96
+
97
+ if (assistantMsg.modelID && assistantMsg.providerID) {
98
+ sessionManager.setModel(assistantMsg.sessionID, {
99
+ modelID: assistantMsg.modelID,
100
+ providerID: assistantMsg.providerID,
101
+ })
102
+ }
103
+ }
104
+
105
+ return
106
+ }
107
+
81
108
  // Track token usage from step-finish events
82
109
  if (event.type === "message.part.updated") {
83
110
  const props = event.properties as MessagePartUpdatedProperties
@@ -129,6 +156,11 @@ export function createEventHook(
129
156
  session.tokenUsage.output +
130
157
  session.tokenUsage.reasoning
131
158
 
159
+ // Format model as providerID/modelID
160
+ const modelString = session.model
161
+ ? `${session.model.providerID}/${session.model.modelID}`
162
+ : null
163
+
132
164
  try {
133
165
  await csvWriter.write({
134
166
  ticket: session.ticket,
@@ -138,6 +170,7 @@ export function createEventHook(
138
170
  description,
139
171
  notes: `Auto-tracked: ${toolSummary}`,
140
172
  tokenUsage: session.tokenUsage,
173
+ model: modelString,
141
174
  })
142
175
 
143
176
  const minutes = Math.round(durationSeconds / 60)
@@ -18,7 +18,7 @@ import "../types/Bun"
18
18
  * Compatible with Jira/Tempo time tracking import.
19
19
  */
20
20
  const CSV_HEADER =
21
- "id,start_date,end_date,user,ticket_name,issue_key,account_key,start_time,end_time,duration_seconds,tokens_used,tokens_remaining,story_points,description,notes"
21
+ "id,start_date,end_date,user,ticket_name,issue_key,account_key,start_time,end_time,duration_seconds,tokens_used,tokens_remaining,story_points,description,notes,model"
22
22
 
23
23
  /**
24
24
  * Writes time tracking entries to a CSV file.
@@ -122,6 +122,7 @@ export class CsvWriter {
122
122
  "",
123
123
  CsvFormatter.escape(data.description),
124
124
  CsvFormatter.escape(data.notes),
125
+ data.model ?? "",
125
126
  ]
126
127
 
127
128
  const csvLine = fields.map((f) => `"${f}"`).join(",")
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import type { ActivityData } from "../types/ActivityData"
6
+ import type { ModelInfo } from "../types/ModelInfo"
6
7
  import type { SessionData } from "../types/SessionData"
7
8
  import type { TokenUsage } from "../types/TokenUsage"
8
9
 
@@ -61,6 +62,7 @@ export class SessionManager {
61
62
  cacheRead: 0,
62
63
  cacheWrite: 0,
63
64
  },
65
+ model: null,
64
66
  }
65
67
 
66
68
  this.sessions.set(sessionID, session)
@@ -126,4 +128,22 @@ export class SessionManager {
126
128
  session.ticket = ticket
127
129
  }
128
130
  }
131
+
132
+ /**
133
+ * Sets the model for a session.
134
+ *
135
+ * @param sessionID - The OpenCode session identifier
136
+ * @param model - The model information
137
+ *
138
+ * @remarks
139
+ * Only sets the model if it hasn't been set yet.
140
+ * The first model detected in a session is used.
141
+ */
142
+ setModel(sessionID: string, model: ModelInfo): void {
143
+ const session = this.sessions.get(sessionID)
144
+
145
+ if (session && !session.model) {
146
+ session.model = model
147
+ }
148
+ }
129
149
  }
@@ -28,4 +28,12 @@ export interface CsvEntryData {
28
28
 
29
29
  /** Token consumption statistics */
30
30
  tokenUsage: TokenUsage
31
+
32
+ /**
33
+ * Model identifier in format `providerID/modelID`.
34
+ *
35
+ * @remarks
36
+ * Examples: `anthropic/claude-opus-4`, `openai/gpt-5`
37
+ */
38
+ model: string | null
31
39
  }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @fileoverview Model information type for tracking which LLM was used.
3
+ */
4
+
5
+ /**
6
+ * Information about the model used in a session.
7
+ *
8
+ * @remarks
9
+ * Extracted from AssistantMessage events in the OpenCode SDK.
10
+ * Used to calculate token costs per model.
11
+ */
12
+ export interface ModelInfo {
13
+ /**
14
+ * Model identifier (e.g., "claude-opus-4", "gpt-5").
15
+ *
16
+ * @remarks
17
+ * This is the model name as reported by the provider.
18
+ */
19
+ modelID: string
20
+
21
+ /**
22
+ * Provider identifier (e.g., "anthropic", "openai").
23
+ *
24
+ * @remarks
25
+ * Combined with modelID to form the full model reference: `providerID/modelID`
26
+ */
27
+ providerID: string
28
+ }
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import type { ActivityData } from "./ActivityData"
6
+ import type { ModelInfo } from "./ModelInfo"
6
7
  import type { TokenUsage } from "./TokenUsage"
7
8
 
8
9
  /**
@@ -20,4 +21,7 @@ export interface SessionData {
20
21
 
21
22
  /** Cumulative token usage for the session */
22
23
  tokenUsage: TokenUsage
24
+
25
+ /** Model used in this session, or `null` if not detected */
26
+ model: ModelInfo | null
23
27
  }