kaizenai 0.1.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/LICENSE +22 -0
- package/README.md +246 -0
- package/bin/kaizen +15 -0
- package/dist/client/apple-touch-icon.png +0 -0
- package/dist/client/assets/index-D-ORCGrq.js +603 -0
- package/dist/client/assets/index-r28mcHqz.css +32 -0
- package/dist/client/favicon.png +0 -0
- package/dist/client/fonts/body-medium.woff2 +0 -0
- package/dist/client/fonts/body-regular-italic.woff2 +0 -0
- package/dist/client/fonts/body-regular.woff2 +0 -0
- package/dist/client/fonts/body-semibold.woff2 +0 -0
- package/dist/client/index.html +22 -0
- package/dist/client/manifest-dark.webmanifest +24 -0
- package/dist/client/manifest.webmanifest +24 -0
- package/dist/client/pwa-192.png +0 -0
- package/dist/client/pwa-512.png +0 -0
- package/dist/client/pwa-icon.svg +15 -0
- package/dist/client/pwa-splash.png +0 -0
- package/dist/client/pwa-splash.svg +15 -0
- package/package.json +103 -0
- package/src/server/acp-shared.ts +315 -0
- package/src/server/agent.ts +1120 -0
- package/src/server/attachments.ts +133 -0
- package/src/server/backgrounds.ts +74 -0
- package/src/server/cli-runtime.ts +333 -0
- package/src/server/cli-supervisor.ts +81 -0
- package/src/server/cli.ts +68 -0
- package/src/server/codex-app-server-protocol.ts +453 -0
- package/src/server/codex-app-server.ts +1350 -0
- package/src/server/cursor-acp.ts +819 -0
- package/src/server/discovery.ts +322 -0
- package/src/server/event-store.ts +1369 -0
- package/src/server/events.ts +244 -0
- package/src/server/external-open.ts +272 -0
- package/src/server/gemini-acp.ts +844 -0
- package/src/server/gemini-cli.ts +525 -0
- package/src/server/generate-title.ts +36 -0
- package/src/server/git-manager.ts +79 -0
- package/src/server/git-repository.ts +101 -0
- package/src/server/harness-types.ts +20 -0
- package/src/server/keybindings.ts +177 -0
- package/src/server/machine-name.ts +22 -0
- package/src/server/paths.ts +112 -0
- package/src/server/process-utils.ts +22 -0
- package/src/server/project-icon.ts +344 -0
- package/src/server/project-metadata.ts +10 -0
- package/src/server/provider-catalog.ts +85 -0
- package/src/server/provider-settings.ts +155 -0
- package/src/server/quick-response.ts +153 -0
- package/src/server/read-models.ts +275 -0
- package/src/server/recovery.ts +507 -0
- package/src/server/restart.ts +30 -0
- package/src/server/server.ts +244 -0
- package/src/server/terminal-manager.ts +350 -0
- package/src/server/theme-settings.ts +179 -0
- package/src/server/update-manager.ts +230 -0
- package/src/server/usage/base-provider-usage.ts +57 -0
- package/src/server/usage/claude-usage.ts +558 -0
- package/src/server/usage/codex-usage.ts +144 -0
- package/src/server/usage/cursor-browser.ts +120 -0
- package/src/server/usage/cursor-cookies.ts +390 -0
- package/src/server/usage/cursor-usage.ts +490 -0
- package/src/server/usage/gemini-usage.ts +24 -0
- package/src/server/usage/provider-usage.ts +61 -0
- package/src/server/usage/test-helpers.ts +9 -0
- package/src/server/usage/types.ts +54 -0
- package/src/server/usage/utils.ts +325 -0
- package/src/server/ws-router.ts +717 -0
- package/src/shared/branding.ts +83 -0
- package/src/shared/dev-ports.ts +43 -0
- package/src/shared/ports.ts +2 -0
- package/src/shared/protocol.ts +152 -0
- package/src/shared/tools.ts +251 -0
- package/src/shared/types.ts +1028 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentProvider,
|
|
3
|
+
ChatUsageSnapshot,
|
|
4
|
+
ChatUsageWarning,
|
|
5
|
+
ProviderUsageAvailability,
|
|
6
|
+
ProviderUsageEntry,
|
|
7
|
+
TranscriptEntry,
|
|
8
|
+
} from "../../shared/types"
|
|
9
|
+
import type { NumericUsage, ProviderRateLimitSnapshot } from "./types"
|
|
10
|
+
|
|
11
|
+
export const WARNING_THRESHOLD = 75
|
|
12
|
+
export const CRITICAL_THRESHOLD = 90
|
|
13
|
+
export const STALE_AFTER_MS = 5 * 60 * 1000
|
|
14
|
+
|
|
15
|
+
export function asRecord(value: unknown): Record<string, unknown> | null {
|
|
16
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null
|
|
17
|
+
return value as Record<string, unknown>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function parseJsonLine(line: string): Record<string, unknown> | null {
|
|
21
|
+
try {
|
|
22
|
+
return asRecord(JSON.parse(line))
|
|
23
|
+
} catch {
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function toNumber(value: unknown): number | null {
|
|
29
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function toPercent(value: number | null) {
|
|
33
|
+
if (value === null || !Number.isFinite(value)) return null
|
|
34
|
+
return Math.max(0, Math.min(100, value))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function usageWarnings(args: {
|
|
38
|
+
contextUsedPercent: number | null
|
|
39
|
+
sessionLimitUsedPercent: number | null
|
|
40
|
+
updatedAt: number | null
|
|
41
|
+
}): ChatUsageWarning[] {
|
|
42
|
+
const warnings: ChatUsageWarning[] = []
|
|
43
|
+
|
|
44
|
+
if (args.contextUsedPercent !== null) {
|
|
45
|
+
if (args.contextUsedPercent >= CRITICAL_THRESHOLD) {
|
|
46
|
+
warnings.push("context_critical")
|
|
47
|
+
} else if (args.contextUsedPercent >= WARNING_THRESHOLD) {
|
|
48
|
+
warnings.push("context_warning")
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (args.sessionLimitUsedPercent !== null) {
|
|
53
|
+
if (args.sessionLimitUsedPercent >= CRITICAL_THRESHOLD) {
|
|
54
|
+
warnings.push("rate_critical")
|
|
55
|
+
} else if (args.sessionLimitUsedPercent >= WARNING_THRESHOLD) {
|
|
56
|
+
warnings.push("rate_warning")
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (args.updatedAt !== null && Date.now() - args.updatedAt > STALE_AFTER_MS) {
|
|
61
|
+
warnings.push("stale")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return warnings
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function relevantMessagesForCurrentContext(messages: TranscriptEntry[]) {
|
|
68
|
+
let lastContextClearedIndex = -1
|
|
69
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
70
|
+
if (messages[index]?.kind === "context_cleared") {
|
|
71
|
+
lastContextClearedIndex = index
|
|
72
|
+
break
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return lastContextClearedIndex >= 0 ? messages.slice(lastContextClearedIndex + 1) : messages
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function estimateTokenCountFromText(text: string | undefined | null) {
|
|
80
|
+
if (!text) return 0
|
|
81
|
+
const trimmed = text.trim()
|
|
82
|
+
if (!trimmed) return 0
|
|
83
|
+
return Math.ceil(trimmed.length / 4)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function estimateTokenCountFromUnknown(value: unknown) {
|
|
87
|
+
if (typeof value === "string") {
|
|
88
|
+
return estimateTokenCountFromText(value)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
return estimateTokenCountFromText(JSON.stringify(value))
|
|
93
|
+
} catch {
|
|
94
|
+
return 0
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function estimateCurrentThreadTokens(messages: TranscriptEntry[]) {
|
|
99
|
+
let total = 0
|
|
100
|
+
|
|
101
|
+
for (const entry of relevantMessagesForCurrentContext(messages)) {
|
|
102
|
+
switch (entry.kind) {
|
|
103
|
+
case "user_prompt":
|
|
104
|
+
total += estimateTokenCountFromText(entry.content)
|
|
105
|
+
if (entry.attachments?.length) {
|
|
106
|
+
total += entry.attachments.length * 256
|
|
107
|
+
}
|
|
108
|
+
break
|
|
109
|
+
case "assistant_text":
|
|
110
|
+
total += estimateTokenCountFromText(entry.text)
|
|
111
|
+
break
|
|
112
|
+
case "compact_summary":
|
|
113
|
+
total += estimateTokenCountFromText(entry.summary)
|
|
114
|
+
break
|
|
115
|
+
case "result":
|
|
116
|
+
total += estimateTokenCountFromText(entry.result)
|
|
117
|
+
break
|
|
118
|
+
case "tool_call":
|
|
119
|
+
total += estimateTokenCountFromText(entry.tool.toolName)
|
|
120
|
+
total += estimateTokenCountFromUnknown(entry.tool.input)
|
|
121
|
+
break
|
|
122
|
+
case "tool_result":
|
|
123
|
+
total += estimateTokenCountFromUnknown(entry.content)
|
|
124
|
+
break
|
|
125
|
+
case "status":
|
|
126
|
+
total += estimateTokenCountFromText(entry.status)
|
|
127
|
+
break
|
|
128
|
+
default:
|
|
129
|
+
break
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return total
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function buildSnapshot(args: {
|
|
137
|
+
provider: AgentProvider
|
|
138
|
+
threadTokens: number | null
|
|
139
|
+
contextWindowTokens: number | null
|
|
140
|
+
lastTurnTokens: number | null
|
|
141
|
+
inputTokens: number | null
|
|
142
|
+
outputTokens: number | null
|
|
143
|
+
cachedInputTokens: number | null
|
|
144
|
+
reasoningOutputTokens?: number | null
|
|
145
|
+
sessionLimitUsedPercent: number | null
|
|
146
|
+
rateLimitResetAt: number | null
|
|
147
|
+
source: ChatUsageSnapshot["source"]
|
|
148
|
+
updatedAt: number | null
|
|
149
|
+
}): ChatUsageSnapshot | null {
|
|
150
|
+
const contextUsedPercent = args.threadTokens !== null && args.contextWindowTokens && args.contextWindowTokens > 0
|
|
151
|
+
? toPercent((args.threadTokens / args.contextWindowTokens) * 100)
|
|
152
|
+
: null
|
|
153
|
+
|
|
154
|
+
const snapshot: ChatUsageSnapshot = {
|
|
155
|
+
provider: args.provider,
|
|
156
|
+
threadTokens: args.threadTokens,
|
|
157
|
+
contextWindowTokens: args.contextWindowTokens,
|
|
158
|
+
contextUsedPercent,
|
|
159
|
+
lastTurnTokens: args.lastTurnTokens,
|
|
160
|
+
inputTokens: args.inputTokens,
|
|
161
|
+
outputTokens: args.outputTokens,
|
|
162
|
+
cachedInputTokens: args.cachedInputTokens,
|
|
163
|
+
reasoningOutputTokens: args.reasoningOutputTokens ?? null,
|
|
164
|
+
sessionLimitUsedPercent: toPercent(args.sessionLimitUsedPercent),
|
|
165
|
+
rateLimitResetAt: args.rateLimitResetAt,
|
|
166
|
+
source: args.source,
|
|
167
|
+
updatedAt: args.updatedAt,
|
|
168
|
+
warnings: usageWarnings({
|
|
169
|
+
contextUsedPercent,
|
|
170
|
+
sessionLimitUsedPercent: toPercent(args.sessionLimitUsedPercent),
|
|
171
|
+
updatedAt: args.updatedAt,
|
|
172
|
+
}),
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const hasAnyData = [
|
|
176
|
+
snapshot.threadTokens,
|
|
177
|
+
snapshot.contextWindowTokens,
|
|
178
|
+
snapshot.lastTurnTokens,
|
|
179
|
+
snapshot.inputTokens,
|
|
180
|
+
snapshot.outputTokens,
|
|
181
|
+
snapshot.cachedInputTokens,
|
|
182
|
+
snapshot.reasoningOutputTokens ?? null,
|
|
183
|
+
snapshot.sessionLimitUsedPercent,
|
|
184
|
+
].some((value) => value !== null)
|
|
185
|
+
|
|
186
|
+
return hasAnyData ? snapshot : null
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function usageTotals(usage: Record<string, unknown>): NumericUsage & { totalTokens: number } {
|
|
190
|
+
const inputTokens = toNumber(usage.input_tokens) ?? 0
|
|
191
|
+
const outputTokens = toNumber(usage.output_tokens) ?? 0
|
|
192
|
+
const cachedInputTokens = (toNumber(usage.cache_read_input_tokens) ?? 0) + (toNumber(usage.cache_creation_input_tokens) ?? 0)
|
|
193
|
+
const reasoningOutputTokens = toNumber(usage.reasoning_output_tokens) ?? 0
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
inputTokens,
|
|
197
|
+
outputTokens,
|
|
198
|
+
cachedInputTokens,
|
|
199
|
+
reasoningOutputTokens,
|
|
200
|
+
totalTokens: inputTokens + outputTokens + cachedInputTokens + reasoningOutputTokens,
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function mergeUsageSnapshots(
|
|
205
|
+
reconstructed: ChatUsageSnapshot | null,
|
|
206
|
+
live: ChatUsageSnapshot | null
|
|
207
|
+
): ChatUsageSnapshot | null {
|
|
208
|
+
if (!reconstructed) return live
|
|
209
|
+
if (!live) return reconstructed
|
|
210
|
+
|
|
211
|
+
const merged: ChatUsageSnapshot = {
|
|
212
|
+
...reconstructed,
|
|
213
|
+
...live,
|
|
214
|
+
provider: live.provider,
|
|
215
|
+
threadTokens: live.threadTokens ?? reconstructed.threadTokens,
|
|
216
|
+
contextWindowTokens: live.contextWindowTokens ?? reconstructed.contextWindowTokens,
|
|
217
|
+
contextUsedPercent: live.contextUsedPercent ?? reconstructed.contextUsedPercent,
|
|
218
|
+
lastTurnTokens: live.lastTurnTokens ?? reconstructed.lastTurnTokens,
|
|
219
|
+
inputTokens: live.inputTokens ?? reconstructed.inputTokens,
|
|
220
|
+
outputTokens: live.outputTokens ?? reconstructed.outputTokens,
|
|
221
|
+
cachedInputTokens: live.cachedInputTokens ?? reconstructed.cachedInputTokens,
|
|
222
|
+
reasoningOutputTokens: live.reasoningOutputTokens ?? reconstructed.reasoningOutputTokens,
|
|
223
|
+
sessionLimitUsedPercent: live.sessionLimitUsedPercent ?? reconstructed.sessionLimitUsedPercent,
|
|
224
|
+
rateLimitResetAt: live.rateLimitResetAt ?? reconstructed.rateLimitResetAt,
|
|
225
|
+
source: live.source === "live" ? "live" : reconstructed.source,
|
|
226
|
+
updatedAt: live.updatedAt ?? reconstructed.updatedAt,
|
|
227
|
+
warnings: [],
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
merged.warnings = usageWarnings({
|
|
231
|
+
contextUsedPercent: merged.contextUsedPercent,
|
|
232
|
+
sessionLimitUsedPercent: merged.sessionLimitUsedPercent,
|
|
233
|
+
updatedAt: merged.updatedAt,
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
return merged
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function applyThreadEstimate(
|
|
240
|
+
snapshot: ChatUsageSnapshot | null,
|
|
241
|
+
messages: TranscriptEntry[]
|
|
242
|
+
): ChatUsageSnapshot | null {
|
|
243
|
+
const estimatedThreadTokens = estimateCurrentThreadTokens(messages)
|
|
244
|
+
if (!snapshot) return null
|
|
245
|
+
|
|
246
|
+
const contextUsedPercent = snapshot.contextWindowTokens && snapshot.contextWindowTokens > 0
|
|
247
|
+
? toPercent((estimatedThreadTokens / snapshot.contextWindowTokens) * 100)
|
|
248
|
+
: null
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
...snapshot,
|
|
252
|
+
threadTokens: estimatedThreadTokens,
|
|
253
|
+
contextUsedPercent,
|
|
254
|
+
warnings: usageWarnings({
|
|
255
|
+
contextUsedPercent,
|
|
256
|
+
sessionLimitUsedPercent: snapshot.sessionLimitUsedPercent,
|
|
257
|
+
updatedAt: snapshot.updatedAt,
|
|
258
|
+
}),
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export function deriveAvailability(updatedAt: number | null): ProviderUsageAvailability {
|
|
263
|
+
if (updatedAt === null) return "available"
|
|
264
|
+
if (updatedAt < Date.now() - STALE_AFTER_MS) return "stale"
|
|
265
|
+
if (Date.now() < updatedAt) return "stale"
|
|
266
|
+
return "available"
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function snapshotToEntry(provider: AgentProvider, snapshot: ChatUsageSnapshot | null): ProviderUsageEntry {
|
|
270
|
+
if (!snapshot) {
|
|
271
|
+
return {
|
|
272
|
+
provider,
|
|
273
|
+
sessionLimitUsedPercent: null,
|
|
274
|
+
apiLimitUsedPercent: null,
|
|
275
|
+
rateLimitResetAt: null,
|
|
276
|
+
rateLimitResetLabel: null,
|
|
277
|
+
weeklyLimitUsedPercent: null,
|
|
278
|
+
weeklyRateLimitResetAt: null,
|
|
279
|
+
weeklyRateLimitResetLabel: null,
|
|
280
|
+
statusDetail: null,
|
|
281
|
+
availability: "available",
|
|
282
|
+
updatedAt: null,
|
|
283
|
+
warnings: [],
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const availability = deriveAvailability(snapshot.updatedAt)
|
|
288
|
+
const warnings = usageWarnings({
|
|
289
|
+
contextUsedPercent: null,
|
|
290
|
+
sessionLimitUsedPercent: snapshot.sessionLimitUsedPercent,
|
|
291
|
+
updatedAt: snapshot.updatedAt,
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
provider,
|
|
296
|
+
sessionLimitUsedPercent: snapshot.sessionLimitUsedPercent,
|
|
297
|
+
apiLimitUsedPercent: null,
|
|
298
|
+
rateLimitResetAt: snapshot.rateLimitResetAt,
|
|
299
|
+
rateLimitResetLabel: (snapshot as ProviderRateLimitSnapshot).rateLimitResetLabel ?? null,
|
|
300
|
+
weeklyLimitUsedPercent: (snapshot as ProviderRateLimitSnapshot).weeklyLimitUsedPercent ?? null,
|
|
301
|
+
weeklyRateLimitResetAt: (snapshot as ProviderRateLimitSnapshot).weeklyRateLimitResetAt ?? null,
|
|
302
|
+
weeklyRateLimitResetLabel: (snapshot as ProviderRateLimitSnapshot).weeklyRateLimitResetLabel ?? null,
|
|
303
|
+
statusDetail: null,
|
|
304
|
+
availability,
|
|
305
|
+
updatedAt: snapshot.updatedAt,
|
|
306
|
+
warnings,
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export function unavailableEntry(provider: AgentProvider): ProviderUsageEntry {
|
|
311
|
+
return {
|
|
312
|
+
provider,
|
|
313
|
+
sessionLimitUsedPercent: null,
|
|
314
|
+
apiLimitUsedPercent: null,
|
|
315
|
+
rateLimitResetAt: null,
|
|
316
|
+
rateLimitResetLabel: null,
|
|
317
|
+
weeklyLimitUsedPercent: null,
|
|
318
|
+
weeklyRateLimitResetAt: null,
|
|
319
|
+
weeklyRateLimitResetLabel: null,
|
|
320
|
+
statusDetail: null,
|
|
321
|
+
availability: "unavailable",
|
|
322
|
+
updatedAt: null,
|
|
323
|
+
warnings: [],
|
|
324
|
+
}
|
|
325
|
+
}
|