kaizenai 0.3.0 → 0.5.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.
Files changed (74) hide show
  1. package/README.md +0 -4
  2. package/bin/kaizen +16 -5
  3. package/dist/client/app-icon-180.png +0 -0
  4. package/dist/client/app-icon-192.png +0 -0
  5. package/dist/client/app-icon-512.png +0 -0
  6. package/dist/client/app-icon-96.png +0 -0
  7. package/dist/client/apple-touch-icon.png +0 -0
  8. package/dist/client/assets/index-C4Zm475L.js +638 -0
  9. package/dist/client/assets/index-CxKis6wK.css +32 -0
  10. package/dist/client/favicon.png +0 -0
  11. package/dist/client/index.html +17 -10
  12. package/dist/client/manifest-dark.webmanifest +3 -3
  13. package/dist/client/manifest.webmanifest +3 -3
  14. package/dist/client/pwa-192.png +0 -0
  15. package/dist/client/pwa-512.png +0 -0
  16. package/dist/server/cli-supervisor.js +306 -0
  17. package/dist/server/cli.js +12636 -0
  18. package/package.json +7 -10
  19. package/dist/client/assets/index-BBs80KD-.js +0 -623
  20. package/dist/client/assets/index-CkCgyLNq.css +0 -32
  21. package/src/server/acp-shared.ts +0 -315
  22. package/src/server/agent.ts +0 -1159
  23. package/src/server/attachments.ts +0 -133
  24. package/src/server/backgrounds.ts +0 -74
  25. package/src/server/cli-runtime.ts +0 -375
  26. package/src/server/cli-supervisor.ts +0 -97
  27. package/src/server/cli.ts +0 -68
  28. package/src/server/codex-app-server-protocol.ts +0 -453
  29. package/src/server/codex-app-server.ts +0 -1350
  30. package/src/server/cursor-acp.ts +0 -819
  31. package/src/server/discovery.ts +0 -322
  32. package/src/server/event-store.ts +0 -1470
  33. package/src/server/events.ts +0 -252
  34. package/src/server/external-open.ts +0 -272
  35. package/src/server/gemini-acp.ts +0 -844
  36. package/src/server/gemini-cli.ts +0 -525
  37. package/src/server/generate-title.ts +0 -36
  38. package/src/server/git-manager.ts +0 -79
  39. package/src/server/git-repository.ts +0 -101
  40. package/src/server/harness-types.ts +0 -20
  41. package/src/server/keybindings.ts +0 -177
  42. package/src/server/machine-name.ts +0 -22
  43. package/src/server/paths.ts +0 -112
  44. package/src/server/process-utils.ts +0 -22
  45. package/src/server/project-icon.ts +0 -352
  46. package/src/server/project-metadata.ts +0 -10
  47. package/src/server/provider-catalog.ts +0 -85
  48. package/src/server/provider-settings.ts +0 -155
  49. package/src/server/quick-response.ts +0 -153
  50. package/src/server/read-models.ts +0 -275
  51. package/src/server/recovery.ts +0 -507
  52. package/src/server/restart.ts +0 -56
  53. package/src/server/server.ts +0 -244
  54. package/src/server/terminal-manager.ts +0 -350
  55. package/src/server/theme-settings.ts +0 -179
  56. package/src/server/update-manager.ts +0 -230
  57. package/src/server/usage/base-provider-usage.ts +0 -57
  58. package/src/server/usage/claude-usage.ts +0 -558
  59. package/src/server/usage/codex-usage.ts +0 -144
  60. package/src/server/usage/cursor-browser.ts +0 -120
  61. package/src/server/usage/cursor-cookies.ts +0 -390
  62. package/src/server/usage/cursor-usage.ts +0 -490
  63. package/src/server/usage/gemini-usage.ts +0 -24
  64. package/src/server/usage/provider-usage.ts +0 -61
  65. package/src/server/usage/test-helpers.ts +0 -9
  66. package/src/server/usage/types.ts +0 -54
  67. package/src/server/usage/utils.ts +0 -325
  68. package/src/server/ws-router.ts +0 -742
  69. package/src/shared/branding.ts +0 -83
  70. package/src/shared/dev-ports.ts +0 -43
  71. package/src/shared/ports.ts +0 -2
  72. package/src/shared/protocol.ts +0 -156
  73. package/src/shared/tools.ts +0 -251
  74. package/src/shared/types.ts +0 -1040
@@ -1,83 +0,0 @@
1
- export const APP_NAME = "Kaizen"
2
- export const CLI_COMMAND = "kaizen"
3
- export const DATA_ROOT_NAME = ".kaizen"
4
- export const DEV_DATA_ROOT_NAME = ".kaizen-dev"
5
- export const PACKAGE_NAME = "kaizenai"
6
- export const PROJECT_METADATA_DIR_NAME = ".kaizen"
7
- export const RUNTIME_PROFILE_ENV_VAR = "KAIZEN_RUNTIME_PROFILE"
8
- export const DISABLE_SELF_UPDATE_ENV_VAR = "KAIZEN_DISABLE_SELF_UPDATE"
9
- export const DEV_ALLOWED_HOSTS_ENV_VAR = "KAIZEN_DEV_ALLOWED_HOSTS"
10
- export const DEV_BACKEND_TARGET_HOST_ENV_VAR = "KAIZEN_DEV_BACKEND_TARGET_HOST"
11
- export const DEV_BACKEND_PORT_ENV_VAR = "KAIZEN_DEV_BACKEND_PORT"
12
- // Read version from package.json — JSON import works in both Bun and Vite
13
- import pkg from "../../package.json"
14
- export const SDK_CLIENT_APP = `kaizen/${pkg.version}`
15
- export const LOG_PREFIX = "[kaizen]"
16
- export const DEFAULT_NEW_PROJECT_ROOT = `~/${APP_NAME}`
17
- export const BROWSER_STORAGE_PREFIX = "kaizen:"
18
-
19
- export type RuntimeProfile = "dev" | "prod"
20
-
21
- type RuntimeEnv = Record<string, string | undefined> | undefined
22
-
23
- function getRuntimeEnv(): RuntimeEnv {
24
- const candidate = globalThis as typeof globalThis & {
25
- process?: {
26
- env?: Record<string, string | undefined>
27
- }
28
- }
29
- return candidate.process?.env
30
- }
31
-
32
- export function getRuntimeProfile(env: RuntimeEnv = getRuntimeEnv()): RuntimeProfile {
33
- const profile = env?.[RUNTIME_PROFILE_ENV_VAR]
34
- return profile?.trim().toLowerCase() === "dev" ? "dev" : "prod"
35
- }
36
-
37
- export function getDataRootName(env: RuntimeEnv = getRuntimeEnv()) {
38
- return getRuntimeProfile(env) === "dev" ? DEV_DATA_ROOT_NAME : DATA_ROOT_NAME
39
- }
40
-
41
- export function getDataRootDir(homeDir: string, env: RuntimeEnv = getRuntimeEnv()) {
42
- return `${homeDir}/${getDataRootName(env)}`
43
- }
44
-
45
- export function getDataRootDirDisplay(env: RuntimeEnv = getRuntimeEnv()) {
46
- return `~/${getDataRootName(env)}`
47
- }
48
-
49
- export function getDataDir(homeDir: string, env: RuntimeEnv = getRuntimeEnv()) {
50
- return `${getDataRootDir(homeDir, env)}/data`
51
- }
52
-
53
- export function getDataDirDisplay(env: RuntimeEnv = getRuntimeEnv()) {
54
- return `${getDataRootDirDisplay(env)}/data`
55
- }
56
-
57
- export function getKeybindingsFilePath(homeDir: string, env: RuntimeEnv = getRuntimeEnv()) {
58
- return `${getDataRootDir(homeDir, env)}/keybindings.json`
59
- }
60
-
61
- export function getKeybindingsFilePathDisplay(env: RuntimeEnv = getRuntimeEnv()) {
62
- return `${getDataRootDirDisplay(env)}/keybindings.json`
63
- }
64
-
65
- export function getThemeSettingsFilePath(homeDir: string, env: RuntimeEnv = getRuntimeEnv()) {
66
- return `${getDataRootDir(homeDir, env)}/theme.json`
67
- }
68
-
69
- export function getThemeSettingsFilePathDisplay(env: RuntimeEnv = getRuntimeEnv()) {
70
- return `${getDataRootDirDisplay(env)}/theme.json`
71
- }
72
-
73
- export function getProviderSettingsFilePath(homeDir: string, env: RuntimeEnv = getRuntimeEnv()) {
74
- return `${getDataRootDir(homeDir, env)}/providers.json`
75
- }
76
-
77
- export function getProviderSettingsFilePathDisplay(env: RuntimeEnv = getRuntimeEnv()) {
78
- return `${getDataRootDirDisplay(env)}/providers.json`
79
- }
80
-
81
- export function getCliInvocation(arg?: string) {
82
- return arg ? `${CLI_COMMAND} ${arg}` : CLI_COMMAND
83
- }
@@ -1,43 +0,0 @@
1
- export const DEFAULT_DEV_CLIENT_PORT = 5174
2
-
3
- export function getDefaultDevServerPort(clientPort = DEFAULT_DEV_CLIENT_PORT) {
4
- return clientPort + 1
5
- }
6
-
7
- export function resolveDevPorts(args: string[]) {
8
- let clientPort = DEFAULT_DEV_CLIENT_PORT
9
-
10
- for (let index = 0; index < args.length; index += 1) {
11
- const arg = args[index]
12
- if (arg !== "--port") continue
13
-
14
- const next = args[index + 1]
15
- if (!next || next.startsWith("-")) {
16
- throw new Error("Missing value for --port")
17
- }
18
-
19
- clientPort = Number(next)
20
- index += 1
21
- }
22
-
23
- return {
24
- clientPort,
25
- serverPort: getDefaultDevServerPort(clientPort),
26
- }
27
- }
28
-
29
- export function stripPortArg(args: string[]) {
30
- const stripped: string[] = []
31
-
32
- for (let index = 0; index < args.length; index += 1) {
33
- const arg = args[index]
34
- if (arg === "--port") {
35
- index += 1
36
- continue
37
- }
38
-
39
- stripped.push(arg)
40
- }
41
-
42
- return stripped
43
- }
@@ -1,2 +0,0 @@
1
- export const PROD_SERVER_PORT = 3210
2
- export const DEV_CLIENT_PORT = 5174
@@ -1,156 +0,0 @@
1
- import type {
2
- DirectoryBrowserSnapshot,
3
- AgentProvider,
4
- ChatUserMessage,
5
- ChatSnapshot,
6
- FeatureBrowserState,
7
- FeatureStage,
8
- KeybindingsSnapshot,
9
- ProviderSettingsSnapshot,
10
- LocalProjectsSnapshot,
11
- ModelOptions,
12
- SidebarData,
13
- FeatureOverviewSnapshot,
14
- ThemeSettingsSnapshot,
15
- UpdateSnapshot,
16
- } from "./types"
17
-
18
- export type EditorPreset = "cursor" | "vscode" | "windsurf" | "custom"
19
-
20
- export interface EditorOpenSettings {
21
- preset: EditorPreset
22
- commandTemplate: string
23
- }
24
-
25
- export type SubscriptionTopic =
26
- | { type: "sidebar" }
27
- | { type: "local-projects" }
28
- | { type: "update" }
29
- | { type: "keybindings" }
30
- | { type: "theme-settings" }
31
- | { type: "provider-settings" }
32
- | { type: "chat"; chatId: string }
33
- | { type: "feature-overview"; featureId: string }
34
- | { type: "terminal"; terminalId: string }
35
-
36
- export interface TerminalSnapshot {
37
- terminalId: string
38
- title: string
39
- cwd: string
40
- shell: string
41
- cols: number
42
- rows: number
43
- scrollback: number
44
- serializedState: string
45
- status: "running" | "exited"
46
- exitCode: number | null
47
- signal?: number
48
- }
49
-
50
- export type TerminalEvent =
51
- | { type: "terminal.output"; terminalId: string; data: string }
52
- | { type: "terminal.exit"; terminalId: string; exitCode: number; signal?: number }
53
-
54
- export type ClientCommand =
55
- | { type: "project.open"; localPath: string }
56
- | { type: "project.create"; localPath: string; title: string }
57
- | { type: "project.remove"; projectId: string }
58
- | { type: "project.setBrowserState"; projectId: string; browserState: FeatureBrowserState }
59
- | { type: "project.setGeneralChatsBrowserState"; projectId: string; browserState: FeatureBrowserState }
60
- | { type: "project.hide"; localPath: string }
61
- | { type: "project.setProjectMetadataDirectoryCommitMode"; projectId?: string; localPath?: string; commitProjectMetadata: boolean }
62
- | { type: "system.listDirectory"; localPath?: string }
63
- | { type: "system.ping" }
64
- | { type: "system.openUrl"; url: string }
65
- | { type: "update.check"; force?: boolean }
66
- | { type: "update.install" }
67
- | { type: "settings.readKeybindings" }
68
- | { type: "settings.writeKeybindings"; bindings: KeybindingsSnapshot["bindings"] }
69
- | { type: "settings.writeThemeSettings"; settings: ThemeSettingsSnapshot["settings"] }
70
- | { type: "settings.writeProviderSettings"; settings: ProviderSettingsSnapshot["settings"] }
71
- | {
72
- type: "system.openExternal"
73
- localPath: string
74
- action: "open_finder" | "open_terminal" | "open_editor"
75
- line?: number
76
- column?: number
77
- editor?: EditorOpenSettings
78
- }
79
- | { type: "chat.create"; projectId: string; featureId?: string }
80
- | { type: "feature.create"; projectId: string; title: string; description?: string; generateOverview?: boolean }
81
- | { type: "feature.rename"; featureId: string; title: string }
82
- | { type: "feature.setBrowserState"; featureId: string; browserState: FeatureBrowserState }
83
- | { type: "feature.setStage"; featureId: string; stage: FeatureStage }
84
- | { type: "feature.reorder"; projectId: string; orderedFeatureIds: string[] }
85
- | { type: "feature.delete"; featureId: string }
86
- | { type: "feature.updateOverview"; featureId: string; content: string }
87
- | { type: "chat.setFeature"; chatId: string; featureId: string | null }
88
- | { type: "chat.rename"; chatId: string; title: string }
89
- | { type: "chat.delete"; chatId: string }
90
- | {
91
- type: "chat.send"
92
- chatId?: string
93
- projectId?: string
94
- provider?: AgentProvider
95
- message: ChatUserMessage
96
- model?: string
97
- modelOptions?: ModelOptions
98
- effort?: string
99
- planMode?: boolean
100
- }
101
- | { type: "chat.cancel"; chatId: string }
102
- | { type: "chat.respondTool"; chatId: string; toolUseId: string; result: unknown }
103
- | { type: "provider.refreshUsage"; provider?: AgentProvider }
104
- | { type: "provider.browserLogin"; provider: AgentProvider }
105
- | { type: "provider.importUsageCurl"; provider: AgentProvider; curlCommand: string }
106
- | { type: "terminal.create"; projectId: string; terminalId: string; cols: number; rows: number; scrollback: number }
107
- | { type: "terminal.input"; terminalId: string; data: string }
108
- | { type: "terminal.resize"; terminalId: string; cols: number; rows: number }
109
- | { type: "terminal.close"; terminalId: string }
110
- | { type: "git.getBranches"; projectId: string }
111
- | { type: "git.switchBranch"; projectId: string; branchName: string }
112
- | { type: "git.createBranch"; projectId: string; branchName: string; checkout: boolean }
113
-
114
- export type ClientEnvelope =
115
- | { v: 1; type: "subscribe"; id: string; topic: SubscriptionTopic }
116
- | { v: 1; type: "unsubscribe"; id: string }
117
- | { v: 1; type: "command"; id: string; command: ClientCommand }
118
-
119
- export type ServerSnapshot =
120
- | { type: "sidebar"; data: SidebarData }
121
- | { type: "local-projects"; data: LocalProjectsSnapshot }
122
- | { type: "update"; data: UpdateSnapshot }
123
- | { type: "keybindings"; data: KeybindingsSnapshot }
124
- | { type: "theme-settings"; data: ThemeSettingsSnapshot }
125
- | { type: "provider-settings"; data: ProviderSettingsSnapshot }
126
- | { type: "chat"; data: ChatSnapshot | null }
127
- | { type: "feature-overview"; data: FeatureOverviewSnapshot | null }
128
- | { type: "terminal"; data: TerminalSnapshot | null }
129
-
130
- export type ServerEnvelope =
131
- | { v: 1; type: "snapshot"; id: string; snapshot: ServerSnapshot }
132
- | { v: 1; type: "event"; id: string; event: TerminalEvent }
133
- | { v: 1; type: "ack"; id: string; result?: unknown }
134
- | { v: 1; type: "error"; id?: string; message: string }
135
-
136
- export interface GitBranchesResult {
137
- isRepo: boolean
138
- currentBranch: string | null
139
- branches: string[]
140
- }
141
-
142
- export interface GitSwitchBranchResult {
143
- currentBranch: string
144
- }
145
-
146
- export interface GitCreateBranchResult {
147
- currentBranch: string
148
- }
149
-
150
- export interface DirectoryListResult extends DirectoryBrowserSnapshot {}
151
-
152
- export function isClientEnvelope(value: unknown): value is ClientEnvelope {
153
- if (!value || typeof value !== "object") return false
154
- const candidate = value as Partial<ClientEnvelope>
155
- return candidate.v === 1 && typeof candidate.type === "string"
156
- }
@@ -1,251 +0,0 @@
1
- import type {
2
- AskUserQuestionItem,
3
- AskUserQuestionAnswerMap,
4
- AskUserQuestionToolResult,
5
- ExitPlanModeToolResult,
6
- HydratedToolCall,
7
- NormalizedToolCall,
8
- ReadFileToolResult,
9
- TodoItem,
10
- } from "./types"
11
-
12
- function asRecord(value: unknown): Record<string, unknown> | null {
13
- if (!value || typeof value !== "object" || Array.isArray(value)) return null
14
- return value as Record<string, unknown>
15
- }
16
-
17
- export function normalizeToolCall(args: {
18
- toolName: string
19
- toolId: string
20
- input: Record<string, unknown>
21
- }): NormalizedToolCall {
22
- const { toolName, toolId, input } = args
23
-
24
- switch (toolName) {
25
- case "AskUserQuestion":
26
- return {
27
- kind: "tool",
28
- toolKind: "ask_user_question",
29
- toolName,
30
- toolId,
31
- input: {
32
- questions: Array.isArray(input.questions) ? (input.questions as AskUserQuestionItem[]) : [],
33
- },
34
- rawInput: input,
35
- }
36
- case "ExitPlanMode":
37
- return {
38
- kind: "tool",
39
- toolKind: "exit_plan_mode",
40
- toolName,
41
- toolId,
42
- input: {
43
- plan: typeof input.plan === "string" ? input.plan : undefined,
44
- summary: typeof input.summary === "string" ? input.summary : undefined,
45
- },
46
- rawInput: input,
47
- }
48
- case "TodoWrite":
49
- return {
50
- kind: "tool",
51
- toolKind: "todo_write",
52
- toolName,
53
- toolId,
54
- input: {
55
- todos: Array.isArray(input.todos) ? (input.todos as TodoItem[]) : [],
56
- },
57
- rawInput: input,
58
- }
59
- case "Skill":
60
- return {
61
- kind: "tool",
62
- toolKind: "skill",
63
- toolName,
64
- toolId,
65
- input: {
66
- skill: typeof input.skill === "string" ? input.skill : "",
67
- },
68
- rawInput: input,
69
- }
70
- case "Glob":
71
- return {
72
- kind: "tool",
73
- toolKind: "glob",
74
- toolName,
75
- toolId,
76
- input: {
77
- pattern: typeof input.pattern === "string" ? input.pattern : "",
78
- },
79
- rawInput: input,
80
- }
81
- case "Grep":
82
- return {
83
- kind: "tool",
84
- toolKind: "grep",
85
- toolName,
86
- toolId,
87
- input: {
88
- pattern: typeof input.pattern === "string" ? input.pattern : "",
89
- outputMode: typeof input.output_mode === "string" ? input.output_mode : undefined,
90
- },
91
- rawInput: input,
92
- }
93
- case "Bash":
94
- return {
95
- kind: "tool",
96
- toolKind: "bash",
97
- toolName,
98
- toolId,
99
- input: {
100
- command: typeof input.command === "string" ? input.command : "",
101
- description: typeof input.description === "string" ? input.description : undefined,
102
- timeoutMs: typeof input.timeout === "number" ? input.timeout : undefined,
103
- runInBackground: Boolean(input.run_in_background),
104
- },
105
- rawInput: input,
106
- }
107
- case "WebSearch":
108
- return {
109
- kind: "tool",
110
- toolKind: "web_search",
111
- toolName,
112
- toolId,
113
- input: {
114
- query: typeof input.query === "string" ? input.query : "",
115
- },
116
- rawInput: input,
117
- }
118
- case "Read":
119
- return {
120
- kind: "tool",
121
- toolKind: "read_file",
122
- toolName,
123
- toolId,
124
- input: {
125
- filePath: typeof input.file_path === "string" ? input.file_path : "",
126
- },
127
- rawInput: input,
128
- }
129
- case "Write":
130
- return {
131
- kind: "tool",
132
- toolKind: "write_file",
133
- toolName,
134
- toolId,
135
- input: {
136
- filePath: typeof input.file_path === "string" ? input.file_path : "",
137
- content: typeof input.content === "string" ? input.content : "",
138
- },
139
- rawInput: input,
140
- }
141
- case "Edit":
142
- return {
143
- kind: "tool",
144
- toolKind: "edit_file",
145
- toolName,
146
- toolId,
147
- input: {
148
- filePath: typeof input.file_path === "string" ? input.file_path : "",
149
- oldString: typeof input.old_string === "string" ? input.old_string : "",
150
- newString: typeof input.new_string === "string" ? input.new_string : "",
151
- },
152
- rawInput: input,
153
- }
154
- }
155
-
156
- const mcpMatch = toolName.match(/^mcp__(.+?)__(.+)$/)
157
- if (mcpMatch) {
158
- return {
159
- kind: "tool",
160
- toolKind: "mcp_generic",
161
- toolName,
162
- toolId,
163
- input: {
164
- server: mcpMatch[1],
165
- tool: mcpMatch[2],
166
- payload: input,
167
- },
168
- rawInput: input,
169
- }
170
- }
171
-
172
- if (typeof input.subagent_type === "string") {
173
- return {
174
- kind: "tool",
175
- toolKind: "subagent_task",
176
- toolName,
177
- toolId,
178
- input: {
179
- subagentType: input.subagent_type,
180
- },
181
- rawInput: input,
182
- }
183
- }
184
-
185
- return {
186
- kind: "tool",
187
- toolKind: "unknown_tool",
188
- toolName,
189
- toolId,
190
- input: {
191
- payload: input,
192
- },
193
- rawInput: input,
194
- }
195
- }
196
-
197
- function parseJsonValue(value: unknown): unknown {
198
- if (typeof value !== "string") return value
199
- try {
200
- return JSON.parse(value)
201
- } catch {
202
- return value
203
- }
204
- }
205
-
206
- export function hydrateToolResult(tool: NormalizedToolCall, raw: unknown): HydratedToolCall["result"] {
207
- const parsed = parseJsonValue(raw)
208
-
209
- switch (tool.toolKind) {
210
- case "ask_user_question": {
211
- const record = asRecord(parsed)
212
- const answers = asRecord(record?.answers) ?? (record ? record : {})
213
- return {
214
- answers: Object.fromEntries(
215
- Object.entries(answers).map(([key, value]) => {
216
- if (Array.isArray(value)) {
217
- return [key, value.map((entry) => String(entry))]
218
- }
219
- if (value && typeof value === "object" && Array.isArray((value as { answers?: unknown }).answers)) {
220
- return [key, (value as { answers: unknown[] }).answers.map((entry) => String(entry))]
221
- }
222
- if (value == null || value === "") {
223
- return [key, []]
224
- }
225
- return [key, [String(value)]]
226
- })
227
- ) as AskUserQuestionAnswerMap,
228
- ...(record?.discarded === true ? { discarded: true } : {}),
229
- } satisfies AskUserQuestionToolResult
230
- }
231
- case "exit_plan_mode": {
232
- const record = asRecord(parsed)
233
- return {
234
- confirmed: typeof record?.confirmed === "boolean" ? record.confirmed : undefined,
235
- clearContext: typeof record?.clearContext === "boolean" ? record.clearContext : undefined,
236
- message: typeof record?.message === "string" ? record.message : undefined,
237
- ...(record?.discarded === true ? { discarded: true } : {}),
238
- } satisfies ExitPlanModeToolResult
239
- }
240
- case "read_file":
241
- if (typeof parsed === "string") {
242
- return parsed
243
- }
244
- const record = asRecord(parsed)
245
- return {
246
- content: typeof record?.content === "string" ? record.content : JSON.stringify(parsed, null, 2),
247
- } satisfies ReadFileToolResult
248
- default:
249
- return parsed
250
- }
251
- }