sidekick-shared 0.21.1 → 0.21.3

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.
Files changed (77) hide show
  1. package/README.md +45 -45
  2. package/dist/accountManager.js +9 -7
  3. package/dist/accountRegistry.js +18 -14
  4. package/dist/accountStatus.js +4 -1
  5. package/dist/accounts.js +60 -25
  6. package/dist/aggregation/EventAggregator.d.ts +14 -11
  7. package/dist/aggregation/EventAggregator.js +68 -31
  8. package/dist/aggregation/PatternExtractor.js +5 -4
  9. package/dist/autoSwitch.js +6 -12
  10. package/dist/claudeProfiles.js +3 -3
  11. package/dist/codexProfiles.js +45 -22
  12. package/dist/codexQuota.js +44 -16
  13. package/dist/codexQuotaWatcher.js +10 -3
  14. package/dist/context/composer.js +8 -6
  15. package/dist/context/sessionContext.js +12 -9
  16. package/dist/credentialIO.js +10 -8
  17. package/dist/extractors/sessionAssets.js +9 -1
  18. package/dist/extractors/sources/claudeAssets.js +7 -1
  19. package/dist/extractors/sources/codexAssets.js +14 -3
  20. package/dist/extractors/toolCall.js +1 -4
  21. package/dist/formatters/eventHighlighter.js +148 -28
  22. package/dist/formatters/noiseClassifier.js +3 -11
  23. package/dist/formatters/sessionDump.js +22 -13
  24. package/dist/formatters/toolSummary.js +1 -3
  25. package/dist/index.d.ts +25 -23
  26. package/dist/index.js +12 -3
  27. package/dist/modelContext.js +10 -8
  28. package/dist/modelInfo.js +11 -6
  29. package/dist/multiProviderQuotaService.js +10 -7
  30. package/dist/parsers/codexParser.js +170 -103
  31. package/dist/parsers/debugLogParser.js +5 -4
  32. package/dist/parsers/openCodeParser.js +66 -48
  33. package/dist/parsers/planExtractor.js +3 -3
  34. package/dist/parsers/sessionActivityDetector.js +10 -6
  35. package/dist/parsers/sessionPathResolver.js +29 -34
  36. package/dist/parsers/subagentScanner.js +21 -20
  37. package/dist/parsers/subagentTraceParser.js +7 -4
  38. package/dist/phrases.js +11 -11
  39. package/dist/providerStatus.js +3 -4
  40. package/dist/providers/claudeCode.js +3 -1
  41. package/dist/providers/codex.js +35 -14
  42. package/dist/providers/codexDatabase.js +14 -5
  43. package/dist/providers/detect.js +10 -6
  44. package/dist/providers/openCode.d.ts +4 -4
  45. package/dist/providers/openCode.js +96 -68
  46. package/dist/providers/openCodeDatabase.js +44 -17
  47. package/dist/quota.d.ts +19 -0
  48. package/dist/quota.js +50 -8
  49. package/dist/quotaHistory.js +10 -3
  50. package/dist/quotaSnapshots.d.ts +1 -1
  51. package/dist/quotaSnapshots.js +2 -2
  52. package/dist/readers/decisions.js +1 -1
  53. package/dist/readers/notes.js +5 -3
  54. package/dist/readers/plans.js +43 -23
  55. package/dist/readers/tasks.js +1 -1
  56. package/dist/report/htmlHelpers.js +135 -16
  57. package/dist/report/htmlReportGenerator.js +38 -12
  58. package/dist/report/transcriptParser.js +8 -2
  59. package/dist/schemas/index.d.ts +4 -4
  60. package/dist/schemas/quota.js +2 -10
  61. package/dist/schemas/sessionEvent.js +20 -10
  62. package/dist/search/advancedFilter.js +3 -3
  63. package/dist/search/sessionSearch.js +10 -4
  64. package/dist/terminalSync.js +4 -2
  65. package/dist/turns/assistantTurn.js +5 -1
  66. package/dist/watchers/eventBridge.js +56 -26
  67. package/dist/watchers/factory.js +5 -2
  68. package/dist/watchers/jsonlTail.js +3 -1
  69. package/dist/watchers/jsonlWatcher.js +128 -52
  70. package/dist/watchers/providerReaderWatcher.js +3 -1
  71. package/dist/watchers/sqliteWatcher.js +52 -22
  72. package/dist/zaiQuota.d.ts +6 -6
  73. package/dist/zaiQuota.js +6 -10
  74. package/dist/zaiQuotaApi.d.ts +33 -0
  75. package/dist/zaiQuotaApi.js +279 -0
  76. package/dist/zaiQuotaWatcher.js +10 -9
  77. package/package.json +5 -2
@@ -25,16 +25,8 @@ exports.quotaFailureKindSchema = zod_1.z.enum([
25
25
  'server',
26
26
  'unknown',
27
27
  ]);
28
- exports.quotaProviderIdSchema = zod_1.z.enum([
29
- 'claude-code',
30
- 'codex',
31
- 'zai',
32
- ]);
33
- exports.quotaSourceSchema = zod_1.z.enum([
34
- 'api',
35
- 'session',
36
- 'cache',
37
- ]);
28
+ exports.quotaProviderIdSchema = zod_1.z.enum(['claude-code', 'codex', 'zai']);
29
+ exports.quotaSourceSchema = zod_1.z.enum(['api', 'session', 'cache']);
38
30
  exports.quotaStateSchema = zod_1.z.object({
39
31
  fiveHour: exports.quotaWindowSchema,
40
32
  sevenDay: exports.quotaWindowSchema,
@@ -43,27 +43,37 @@ exports.sessionEventSchema = zod_1.z.object({
43
43
  timestamp: zod_1.z.string(),
44
44
  isSidechain: zod_1.z.boolean().optional(),
45
45
  permissionMode: exports.permissionModeSchema.optional(),
46
- rateLimits: zod_1.z.object({
47
- primary: zod_1.z.object({
46
+ rateLimits: zod_1.z
47
+ .object({
48
+ primary: zod_1.z
49
+ .object({
48
50
  usedPercent: zod_1.z.number(),
49
51
  windowMinutes: zod_1.z.number(),
50
52
  resetsAt: zod_1.z.number(),
51
- }).optional(),
52
- secondary: zod_1.z.object({
53
+ })
54
+ .optional(),
55
+ secondary: zod_1.z
56
+ .object({
53
57
  usedPercent: zod_1.z.number(),
54
58
  windowMinutes: zod_1.z.number(),
55
59
  resetsAt: zod_1.z.number(),
56
- }).optional(),
57
- }).optional(),
58
- tool: zod_1.z.object({
60
+ })
61
+ .optional(),
62
+ })
63
+ .optional(),
64
+ tool: zod_1.z
65
+ .object({
59
66
  name: zod_1.z.string(),
60
67
  input: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()),
61
- }).optional(),
62
- result: zod_1.z.object({
68
+ })
69
+ .optional(),
70
+ result: zod_1.z
71
+ .object({
63
72
  tool_use_id: zod_1.z.string(),
64
73
  output: zod_1.z.unknown().optional(),
65
74
  is_error: zod_1.z.boolean().optional(),
66
- }).optional(),
75
+ })
76
+ .optional(),
67
77
  });
68
78
  // ── Progress unwrapping ──
69
79
  /** Recursion bound for nested progress envelopes; real data nests 1 level. */
@@ -114,7 +114,7 @@ class FilterEngine {
114
114
  }
115
115
  matchFuzzy(text) {
116
116
  const lower = text.toLowerCase();
117
- return this.fuzzyWords.every(w => lower.includes(w));
117
+ return this.fuzzyWords.every((w) => lower.includes(w));
118
118
  }
119
119
  matchRegex(text) {
120
120
  if (!this.compiledRegex)
@@ -139,7 +139,7 @@ class FilterEngine {
139
139
  if (this.fuzzyWords.length === 0)
140
140
  return text;
141
141
  // Highlight each fuzzy word independently
142
- const pattern = this.fuzzyWords.map(w => this.escapeRegex(w)).join('|');
142
+ const pattern = this.fuzzyWords.map((w) => this.escapeRegex(w)).join('|');
143
143
  return this.wrapMatches(text, pattern, format, 'gi');
144
144
  }
145
145
  highlightRegex(text, format) {
@@ -190,7 +190,7 @@ class FilterEngine {
190
190
  this.fuzzyWords = this.query
191
191
  .toLowerCase()
192
192
  .split(/\s+/)
193
- .filter(w => w.length > 0);
193
+ .filter((w) => w.length > 0);
194
194
  break;
195
195
  }
196
196
  }
@@ -59,11 +59,15 @@ async function searchSessions(provider, query, opts) {
59
59
  try {
60
60
  const dir = folder.dir;
61
61
  if (fs.existsSync(dir)) {
62
- const entries = fs.readdirSync(dir).filter(f => f.endsWith('.jsonl') || f.endsWith('.json'));
63
- sessionFiles = entries.map(f => path.join(dir, f));
62
+ const entries = fs
63
+ .readdirSync(dir)
64
+ .filter((f) => f.endsWith('.jsonl') || f.endsWith('.json'));
65
+ sessionFiles = entries.map((f) => path.join(dir, f));
64
66
  }
65
67
  }
66
- catch { /* skip */ }
68
+ catch {
69
+ /* skip */
70
+ }
67
71
  // If no files found from dir scan, try findAllSessions with the folder name
68
72
  if (sessionFiles.length === 0) {
69
73
  // Use provider.findSessionFiles with the folder path as workspace
@@ -88,6 +92,8 @@ async function searchSessions(provider, query, opts) {
88
92
  }
89
93
  }
90
94
  }
91
- catch { /* skip */ }
95
+ catch {
96
+ /* skip */
97
+ }
92
98
  return results.slice(0, maxResults);
93
99
  }
@@ -73,7 +73,9 @@ function atomicWriteFile(filePath, content, mode = 0o600) {
73
73
  try {
74
74
  fs.rmSync(tmp, { force: true });
75
75
  }
76
- catch { /* nothing to clean up */ }
76
+ catch {
77
+ /* nothing to clean up */
78
+ }
77
79
  throw err;
78
80
  }
79
81
  }
@@ -136,7 +138,7 @@ function uninstallShellHook() {
136
138
  }
137
139
  }
138
140
  function isShellHookInstalled() {
139
- return getShellRcPaths().some(rcPath => {
141
+ return getShellRcPaths().some((rcPath) => {
140
142
  try {
141
143
  const content = fs.readFileSync(rcPath, 'utf8');
142
144
  return content.includes(HOOK_START) && content.includes(HOOK_END);
@@ -262,7 +262,11 @@ function capProcessSteps(steps, maxRetainedSteps) {
262
262
  }
263
263
  const omitted = steps.length - kept.length;
264
264
  const omittedMarker = `... ${omitted} earlier process step${omitted === 1 ? '' : 's'} omitted`;
265
- return { steps: [{ kind: 'narration', text: omittedMarker }, ...kept], retainedIndexes, omittedMarker };
265
+ return {
266
+ steps: [{ kind: 'narration', text: omittedMarker }, ...kept],
267
+ retainedIndexes,
268
+ omittedMarker,
269
+ };
266
270
  }
267
271
  function capReasoningBlocks(blocks, maxRetainedBlocks) {
268
272
  const retainedIndexes = new Set();
@@ -38,12 +38,13 @@ function toFollowEvents(event, providerId) {
38
38
  if (Array.isArray(content)) {
39
39
  for (const block of content) {
40
40
  if (block.type === 'tool_result' && typeof block.tool_use_id === 'string') {
41
- const resultText = typeof block.content === 'string'
42
- ? truncate(block.content, 120)
43
- : '';
41
+ const resultText = typeof block.content === 'string' ? truncate(block.content, 120) : '';
44
42
  events.push({
45
- providerId, type: 'tool_result', timestamp: ts,
46
- summary: resultText || '(tool result)', raw: block,
43
+ providerId,
44
+ type: 'tool_result',
45
+ timestamp: ts,
46
+ summary: resultText || '(tool result)',
47
+ raw: block,
47
48
  });
48
49
  }
49
50
  }
@@ -51,8 +52,12 @@ function toFollowEvents(event, providerId) {
51
52
  const text = extractTextContent(content);
52
53
  if (text || events.length === 0) {
53
54
  events.push({
54
- providerId, type: 'user', timestamp: ts,
55
- summary: text || '(user message)', model, raw: event,
55
+ providerId,
56
+ type: 'user',
57
+ timestamp: ts,
58
+ summary: text || '(user message)',
59
+ model,
60
+ raw: event,
56
61
  });
57
62
  }
58
63
  break;
@@ -67,9 +72,14 @@ function toFollowEvents(event, providerId) {
67
72
  ? summarizeToolInput(block.name, block.input)
68
73
  : '';
69
74
  events.push({
70
- providerId, type: 'tool_use', timestamp: ts,
75
+ providerId,
76
+ type: 'tool_use',
77
+ timestamp: ts,
71
78
  summary: input ? `${block.name} ${input}` : block.name,
72
- toolName: block.name, toolInput: input, model, raw: block,
79
+ toolName: block.name,
80
+ toolInput: input,
81
+ model,
82
+ raw: block,
73
83
  });
74
84
  }
75
85
  }
@@ -78,8 +88,15 @@ function toFollowEvents(event, providerId) {
78
88
  const text = extractTextContent(content);
79
89
  if (text || events.length === 0) {
80
90
  events.push({
81
- providerId, type: 'assistant', timestamp: ts,
82
- summary: text || '(thinking...)', tokens, cacheTokens, cost, model, raw: event,
91
+ providerId,
92
+ type: 'assistant',
93
+ timestamp: ts,
94
+ summary: text || '(thinking...)',
95
+ tokens,
96
+ cacheTokens,
97
+ cost,
98
+ model,
99
+ raw: event,
83
100
  });
84
101
  }
85
102
  else if (tokens) {
@@ -93,13 +110,16 @@ function toFollowEvents(event, providerId) {
93
110
  }
94
111
  case 'tool_use': {
95
112
  const name = event.tool?.name || 'unknown';
96
- const input = event.tool?.input
97
- ? summarizeToolInput(name, event.tool.input)
98
- : '';
113
+ const input = event.tool?.input ? summarizeToolInput(name, event.tool.input) : '';
99
114
  events.push({
100
- providerId, type: 'tool_use', timestamp: ts,
115
+ providerId,
116
+ type: 'tool_use',
117
+ timestamp: ts,
101
118
  summary: input ? `${name} ${input}` : name,
102
- toolName: name, toolInput: input, model, raw: event,
119
+ toolName: name,
120
+ toolInput: input,
121
+ model,
122
+ raw: event,
103
123
  });
104
124
  break;
105
125
  }
@@ -107,26 +127,33 @@ function toFollowEvents(event, providerId) {
107
127
  const output = event.result?.output;
108
128
  const text = typeof output === 'string' ? truncate(output, 120) : '';
109
129
  events.push({
110
- providerId, type: 'tool_result', timestamp: ts,
111
- summary: text || '(tool result)', raw: event,
130
+ providerId,
131
+ type: 'tool_result',
132
+ timestamp: ts,
133
+ summary: text || '(tool result)',
134
+ raw: event,
112
135
  });
113
136
  break;
114
137
  }
115
138
  case 'summary': {
116
139
  events.push({
117
- providerId, type: 'summary', timestamp: ts,
118
- summary: 'Context compacted', raw: event,
140
+ providerId,
141
+ type: 'summary',
142
+ timestamp: ts,
143
+ summary: 'Context compacted',
144
+ raw: event,
119
145
  });
120
146
  break;
121
147
  }
122
148
  case 'system': {
123
149
  const text = extractTextContent(event.message?.content);
124
150
  const label = event.message?.sourceLabel || event.message?.role || 'system';
125
- const summary = text || (usage
126
- ? `Tokens: ${usage.input_tokens || 0} in / ${usage.output_tokens || 0} out`
127
- : label);
151
+ const summary = text ||
152
+ (usage ? `Tokens: ${usage.input_tokens || 0} in / ${usage.output_tokens || 0} out` : label);
128
153
  events.push({
129
- providerId, type: 'system', timestamp: ts,
154
+ providerId,
155
+ type: 'system',
156
+ timestamp: ts,
130
157
  summary: label && text ? `${label}: ${text}` : summary,
131
158
  tokens,
132
159
  cacheTokens,
@@ -142,8 +169,11 @@ function toFollowEvents(event, providerId) {
142
169
  const evtType = event.type;
143
170
  if (evtType === 'result') {
144
171
  events.push({
145
- providerId, type: 'system', timestamp: ts,
146
- summary: 'Session ended', raw: event,
172
+ providerId,
173
+ type: 'system',
174
+ timestamp: ts,
175
+ summary: 'Session ended',
176
+ raw: event,
147
177
  });
148
178
  }
149
179
  break;
@@ -56,9 +56,12 @@ function createWatcher(options) {
56
56
  }
57
57
  let sessionPath;
58
58
  if (sessionId) {
59
- const match = sessions.find(s => s.includes(sessionId));
59
+ const match = sessions.find((s) => s.includes(sessionId));
60
60
  if (!match) {
61
- throw new Error(`Session ${sessionId} not found. Available: ${sessions.slice(0, 5).map(s => path.basename(s)).join(', ')}`);
61
+ throw new Error(`Session ${sessionId} not found. Available: ${sessions
62
+ .slice(0, 5)
63
+ .map((s) => path.basename(s))
64
+ .join(', ')}`);
62
65
  }
63
66
  sessionPath = match;
64
67
  }
@@ -140,7 +140,9 @@ class JsonlTailReader {
140
140
  try {
141
141
  fs.closeSync(fd);
142
142
  }
143
- catch { /* ignore close errors */ }
143
+ catch {
144
+ /* ignore close errors */
145
+ }
144
146
  }
145
147
  this.options.onError?.(error instanceof Error ? error : new Error(String(error)));
146
148
  }
@@ -50,7 +50,9 @@ function normalizeClaudeCodeEvent(raw) {
50
50
  const ts = raw.timestamp || new Date().toISOString();
51
51
  const permissionMode = raw.permissionMode;
52
52
  const usage = raw.message?.usage;
53
- const tokens = usage ? { input: usage.input_tokens || 0, output: usage.output_tokens || 0 } : undefined;
53
+ const tokens = usage
54
+ ? { input: usage.input_tokens || 0, output: usage.output_tokens || 0 }
55
+ : undefined;
54
56
  const cacheTokens = usage && (usage.cache_read_input_tokens || usage.cache_creation_input_tokens)
55
57
  ? { read: usage.cache_read_input_tokens || 0, write: usage.cache_creation_input_tokens || 0 }
56
58
  : undefined;
@@ -62,12 +64,13 @@ function normalizeClaudeCodeEvent(raw) {
62
64
  if (Array.isArray(content)) {
63
65
  for (const block of content) {
64
66
  if (block.type === 'tool_result' && typeof block.tool_use_id === 'string') {
65
- const resultText = typeof block.content === 'string'
66
- ? truncate(block.content, 120)
67
- : '';
67
+ const resultText = typeof block.content === 'string' ? truncate(block.content, 120) : '';
68
68
  events.push({
69
- providerId: 'claude-code', type: 'tool_result', timestamp: ts,
70
- summary: resultText || '(tool result)', raw: block,
69
+ providerId: 'claude-code',
70
+ type: 'tool_result',
71
+ timestamp: ts,
72
+ summary: resultText || '(tool result)',
73
+ raw: block,
71
74
  });
72
75
  }
73
76
  }
@@ -75,8 +78,12 @@ function normalizeClaudeCodeEvent(raw) {
75
78
  const text = extractTextContent(content);
76
79
  if (text || events.length === 0) {
77
80
  events.push({
78
- providerId: 'claude-code', type: 'user', timestamp: ts,
79
- summary: text || '(user message)', model, raw,
81
+ providerId: 'claude-code',
82
+ type: 'user',
83
+ timestamp: ts,
84
+ summary: text || '(user message)',
85
+ model,
86
+ raw,
80
87
  });
81
88
  }
82
89
  }
@@ -86,11 +93,18 @@ function normalizeClaudeCodeEvent(raw) {
86
93
  if (Array.isArray(content)) {
87
94
  for (const block of content) {
88
95
  if (block.type === 'tool_use' && typeof block.name === 'string') {
89
- const input = block.input ? summarizeToolInput(block.name, block.input) : '';
96
+ const input = block.input
97
+ ? summarizeToolInput(block.name, block.input)
98
+ : '';
90
99
  events.push({
91
- providerId: 'claude-code', type: 'tool_use', timestamp: ts,
100
+ providerId: 'claude-code',
101
+ type: 'tool_use',
102
+ timestamp: ts,
92
103
  summary: input ? `${block.name} ${input}` : block.name,
93
- toolName: block.name, toolInput: input, model, raw: block,
104
+ toolName: block.name,
105
+ toolInput: input,
106
+ model,
107
+ raw: block,
94
108
  });
95
109
  }
96
110
  }
@@ -99,8 +113,15 @@ function normalizeClaudeCodeEvent(raw) {
99
113
  const text = extractTextContent(content);
100
114
  if (text || events.length === 0) {
101
115
  events.push({
102
- providerId: 'claude-code', type: 'assistant', timestamp: ts,
103
- summary: text || '(thinking...)', tokens, cacheTokens, cost, model, raw,
116
+ providerId: 'claude-code',
117
+ type: 'assistant',
118
+ timestamp: ts,
119
+ summary: text || '(thinking...)',
120
+ tokens,
121
+ cacheTokens,
122
+ cost,
123
+ model,
124
+ raw,
104
125
  });
105
126
  }
106
127
  else if (tokens) {
@@ -113,16 +134,22 @@ function normalizeClaudeCodeEvent(raw) {
113
134
  }
114
135
  else if (raw.type === 'summary') {
115
136
  events.push({
116
- providerId: 'claude-code', type: 'summary', timestamp: ts,
117
- summary: 'Context compacted', raw,
137
+ providerId: 'claude-code',
138
+ type: 'summary',
139
+ timestamp: ts,
140
+ summary: 'Context compacted',
141
+ raw,
118
142
  });
119
143
  }
120
144
  else {
121
145
  // system / result events
122
146
  if (raw.type === 'result') {
123
147
  events.push({
124
- providerId: 'claude-code', type: 'system', timestamp: ts,
125
- summary: 'Session ended', raw,
148
+ providerId: 'claude-code',
149
+ type: 'system',
150
+ timestamp: ts,
151
+ summary: 'Session ended',
152
+ raw,
126
153
  });
127
154
  }
128
155
  }
@@ -171,16 +198,20 @@ function extractRateLimits(rl) {
171
198
  if (!primary && !secondary)
172
199
  return undefined;
173
200
  return {
174
- primary: primary ? {
175
- usedPercent: primary.used_percent || 0,
176
- windowMinutes: primary.window_minutes || 0,
177
- resetsAt: primary.resets_at || 0,
178
- } : undefined,
179
- secondary: secondary ? {
180
- usedPercent: secondary.used_percent || 0,
181
- windowMinutes: secondary.window_minutes || 0,
182
- resetsAt: secondary.resets_at || 0,
183
- } : undefined,
201
+ primary: primary
202
+ ? {
203
+ usedPercent: primary.used_percent || 0,
204
+ windowMinutes: primary.window_minutes || 0,
205
+ resetsAt: primary.resets_at || 0,
206
+ }
207
+ : undefined,
208
+ secondary: secondary
209
+ ? {
210
+ usedPercent: secondary.used_percent || 0,
211
+ windowMinutes: secondary.window_minutes || 0,
212
+ resetsAt: secondary.resets_at || 0,
213
+ }
214
+ : undefined,
184
215
  };
185
216
  }
186
217
  function truncate(text, maxLen) {
@@ -208,7 +239,9 @@ class JsonlSessionWatcher {
208
239
  onError: (err) => callbacks.onError?.(err),
209
240
  });
210
241
  }
211
- get isActive() { return this._isActive; }
242
+ get isActive() {
243
+ return this._isActive;
244
+ }
212
245
  /** Seek to a byte offset. Must be called before start(). */
213
246
  seekTo(position) {
214
247
  this.filePosition = position;
@@ -311,7 +344,9 @@ class JsonlSessionWatcher {
311
344
  try {
312
345
  fs.closeSync(fd);
313
346
  }
314
- catch { /* ignore */ }
347
+ catch {
348
+ /* ignore */
349
+ }
315
350
  }
316
351
  this.callbacks.onError?.(err instanceof Error ? err : new Error(String(err)));
317
352
  }
@@ -332,16 +367,23 @@ class JsonlSessionWatcher {
332
367
  const type = raw.type;
333
368
  if (type === 'session_meta') {
334
369
  events.push({
335
- providerId: 'codex', type: 'system', timestamp: ts,
336
- summary: `Session started in ${raw.payload?.cwd || '?'}`, raw,
370
+ providerId: 'codex',
371
+ type: 'system',
372
+ timestamp: ts,
373
+ summary: `Session started in ${raw.payload?.cwd || '?'}`,
374
+ raw,
337
375
  });
338
376
  }
339
377
  else if (type === 'turn_context') {
340
378
  const payload = raw.payload;
341
379
  if (payload?.model) {
342
380
  events.push({
343
- providerId: 'codex', type: 'system', timestamp: ts,
344
- summary: `Model: ${payload.model}`, model: payload.model, raw,
381
+ providerId: 'codex',
382
+ type: 'system',
383
+ timestamp: ts,
384
+ summary: `Model: ${payload.model}`,
385
+ model: payload.model,
386
+ raw,
345
387
  });
346
388
  }
347
389
  }
@@ -352,15 +394,21 @@ class JsonlSessionWatcher {
352
394
  if (p.role === 'user') {
353
395
  const text = extractPayloadContent(p);
354
396
  events.push({
355
- providerId: 'codex', type: 'user', timestamp: ts,
356
- summary: text || '(user message)', raw,
397
+ providerId: 'codex',
398
+ type: 'user',
399
+ timestamp: ts,
400
+ summary: text || '(user message)',
401
+ raw,
357
402
  });
358
403
  }
359
404
  else if (p.role === 'assistant' || p.type === 'message') {
360
405
  const text = extractPayloadContent(p);
361
406
  events.push({
362
- providerId: 'codex', type: 'assistant', timestamp: ts,
363
- summary: text || '(thinking...)', raw,
407
+ providerId: 'codex',
408
+ type: 'assistant',
409
+ timestamp: ts,
410
+ summary: text || '(thinking...)',
411
+ raw,
364
412
  });
365
413
  }
366
414
  else if (p.type === 'function_call' || p.type === 'custom_tool_call') {
@@ -370,25 +418,39 @@ class JsonlSessionWatcher {
370
418
  try {
371
419
  parsedArgs = JSON.parse(p.arguments);
372
420
  }
373
- catch { /* keep empty */ }
421
+ catch {
422
+ /* keep empty */
423
+ }
374
424
  const args = typeof p.arguments === 'string' ? truncate(p.arguments, 80) : '';
375
425
  events.push({
376
- providerId: 'codex', type: 'tool_use', timestamp: ts,
426
+ providerId: 'codex',
427
+ type: 'tool_use',
428
+ timestamp: ts,
377
429
  summary: args ? `${name} ${args}` : name,
378
- toolName: name, toolInput: args, raw: { input: parsedArgs },
430
+ toolName: name,
431
+ toolInput: args,
432
+ raw: { input: parsedArgs },
379
433
  });
380
434
  }
381
435
  else if (p.type === 'local_shell_call') {
382
436
  const cmd = truncate(JSON.stringify(p.command ?? p.arguments ?? ''), 80);
383
437
  events.push({
384
- providerId: 'codex', type: 'tool_use', timestamp: ts,
385
- summary: `Bash ${cmd}`, toolName: 'Bash', toolInput: cmd, raw,
438
+ providerId: 'codex',
439
+ type: 'tool_use',
440
+ timestamp: ts,
441
+ summary: `Bash ${cmd}`,
442
+ toolName: 'Bash',
443
+ toolInput: cmd,
444
+ raw,
386
445
  });
387
446
  }
388
447
  else if (p.type === 'function_call_output') {
389
448
  events.push({
390
- providerId: 'codex', type: 'tool_result', timestamp: ts,
391
- summary: truncate(String(p.output ?? ''), 120), raw,
449
+ providerId: 'codex',
450
+ type: 'tool_result',
451
+ timestamp: ts,
452
+ summary: truncate(String(p.output ?? ''), 120),
453
+ raw,
392
454
  });
393
455
  }
394
456
  }
@@ -402,12 +464,17 @@ class JsonlSessionWatcher {
402
464
  const rateLimits = rl ? extractRateLimits(rl) : undefined;
403
465
  if (usage || rateLimits) {
404
466
  events.push({
405
- providerId: 'codex', type: 'system', timestamp: ts,
467
+ providerId: 'codex',
468
+ type: 'system',
469
+ timestamp: ts,
406
470
  summary: usage
407
471
  ? `Tokens: ${usage.input_tokens ?? 0} in / ${usage.output_tokens ?? 0} out`
408
472
  : 'Rate limits updated',
409
473
  tokens: usage
410
- ? { input: usage.input_tokens || 0, output: usage.output_tokens || 0 }
474
+ ? {
475
+ input: usage.input_tokens || 0,
476
+ output: usage.output_tokens || 0,
477
+ }
411
478
  : undefined,
412
479
  rateLimits,
413
480
  raw,
@@ -419,8 +486,11 @@ class JsonlSessionWatcher {
419
486
  if (collaboration === 'plan' && !this.codexInPlanMode) {
420
487
  this.codexInPlanMode = true;
421
488
  events.push({
422
- providerId: 'codex', type: 'tool_use', timestamp: ts,
423
- summary: 'EnterPlanMode', toolName: 'EnterPlanMode',
489
+ providerId: 'codex',
490
+ type: 'tool_use',
491
+ timestamp: ts,
492
+ summary: 'EnterPlanMode',
493
+ toolName: 'EnterPlanMode',
424
494
  raw: { input: { source: 'codex_task_started' } },
425
495
  });
426
496
  }
@@ -429,8 +499,11 @@ class JsonlSessionWatcher {
429
499
  if (this.codexInPlanMode) {
430
500
  this.codexInPlanMode = false;
431
501
  events.push({
432
- providerId: 'codex', type: 'tool_use', timestamp: ts,
433
- summary: 'ExitPlanMode', toolName: 'ExitPlanMode',
502
+ providerId: 'codex',
503
+ type: 'tool_use',
504
+ timestamp: ts,
505
+ summary: 'ExitPlanMode',
506
+ toolName: 'ExitPlanMode',
434
507
  raw: { input: { source: 'codex_task_complete' } },
435
508
  });
436
509
  }
@@ -438,8 +511,11 @@ class JsonlSessionWatcher {
438
511
  }
439
512
  else if (type === 'compacted') {
440
513
  events.push({
441
- providerId: 'codex', type: 'summary', timestamp: ts,
442
- summary: 'Context compacted', raw,
514
+ providerId: 'codex',
515
+ type: 'summary',
516
+ timestamp: ts,
517
+ summary: 'Context compacted',
518
+ raw,
443
519
  });
444
520
  }
445
521
  return events;
@@ -59,7 +59,9 @@ class ProviderReaderSessionWatcher {
59
59
  this.callbacks = callbacks;
60
60
  this.reader = provider.createReader(sessionPath);
61
61
  }
62
- get isActive() { return this._isActive; }
62
+ get isActive() {
63
+ return this._isActive;
64
+ }
63
65
  seekTo(position) {
64
66
  this.reader.seekTo(position);
65
67
  }