saeeol 1.2.0 → 1.2.2

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 (193) hide show
  1. package/package.json +14 -14
  2. package/src/cli/cmd/tui/component/dialog/dialog-agent.tsx +32 -0
  3. package/src/cli/cmd/tui/component/dialog/dialog-command.tsx +190 -0
  4. package/src/cli/cmd/tui/component/dialog/dialog-console-org.tsx +103 -0
  5. package/src/cli/cmd/tui/component/dialog/dialog-go-upsell.tsx +159 -0
  6. package/src/cli/cmd/tui/component/dialog/dialog-mcp.tsx +86 -0
  7. package/src/cli/cmd/tui/component/dialog/dialog-model.tsx +238 -0
  8. package/src/cli/cmd/tui/component/dialog/dialog-provider.tsx +343 -0
  9. package/src/cli/cmd/tui/component/dialog/dialog-session-delete-failed.tsx +103 -0
  10. package/src/cli/cmd/tui/component/dialog/dialog-session-list.tsx +301 -0
  11. package/src/cli/cmd/tui/component/dialog/dialog-session-rename.tsx +35 -0
  12. package/src/cli/cmd/tui/component/dialog/dialog-skill.tsx +37 -0
  13. package/src/cli/cmd/tui/component/dialog/dialog-stash.tsx +87 -0
  14. package/src/cli/cmd/tui/component/dialog/dialog-status.tsx +190 -0
  15. package/src/cli/cmd/tui/component/dialog/dialog-tag.tsx +44 -0
  16. package/src/cli/cmd/tui/component/dialog/dialog-theme-list.tsx +50 -0
  17. package/src/cli/cmd/tui/component/dialog/dialog-variant.tsx +39 -0
  18. package/src/cli/cmd/tui/component/dialog/dialog-workspace-create.tsx +200 -0
  19. package/src/cli/cmd/tui/component/dialog/dialog-workspace-unavailable.tsx +81 -0
  20. package/src/cli/cmd/tui/component/dialog-agent.tsx +1 -32
  21. package/src/cli/cmd/tui/component/dialog-command.tsx +1 -190
  22. package/src/cli/cmd/tui/component/dialog-console-org.tsx +1 -103
  23. package/src/cli/cmd/tui/component/dialog-go-upsell.tsx +1 -159
  24. package/src/cli/cmd/tui/component/dialog-mcp.tsx +1 -86
  25. package/src/cli/cmd/tui/component/dialog-model.tsx +1 -238
  26. package/src/cli/cmd/tui/component/dialog-provider.tsx +1 -343
  27. package/src/cli/cmd/tui/component/dialog-session-delete-failed.tsx +1 -103
  28. package/src/cli/cmd/tui/component/dialog-session-list.tsx +1 -301
  29. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +1 -35
  30. package/src/cli/cmd/tui/component/dialog-skill.tsx +1 -37
  31. package/src/cli/cmd/tui/component/dialog-stash.tsx +1 -87
  32. package/src/cli/cmd/tui/component/dialog-status.tsx +1 -190
  33. package/src/cli/cmd/tui/component/dialog-tag.tsx +1 -44
  34. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +1 -50
  35. package/src/cli/cmd/tui/component/dialog-variant.tsx +1 -39
  36. package/src/cli/cmd/tui/component/dialog-workspace-create.tsx +1 -200
  37. package/src/cli/cmd/tui/component/dialog-workspace-unavailable.tsx +1 -81
  38. package/src/session/compaction-helpers.ts +1 -169
  39. package/src/session/compaction.ts +1 -712
  40. package/src/session/core/compaction/compaction-helpers.ts +169 -0
  41. package/src/session/core/compaction/compaction.ts +712 -0
  42. package/src/session/core/compaction/overflow.ts +28 -0
  43. package/src/session/core/instruction.ts +234 -0
  44. package/src/session/core/llm.ts +504 -0
  45. package/src/session/core/network.ts +392 -0
  46. package/src/session/core/processor.ts +731 -0
  47. package/src/session/core/projectors.ts +139 -0
  48. package/src/session/core/resolve-tools.ts +241 -0
  49. package/src/session/core/retry.ts +149 -0
  50. package/src/session/core/revert.ts +173 -0
  51. package/src/session/core/run-state.ts +110 -0
  52. package/src/session/core/schema.ts +35 -0
  53. package/src/session/core/session-types.ts +160 -0
  54. package/src/session/core/session.sql.ts +124 -0
  55. package/src/session/core/session.ts +948 -0
  56. package/src/session/core/shell-exec.ts +205 -0
  57. package/src/session/core/status.ts +100 -0
  58. package/src/session/core/subtask.ts +268 -0
  59. package/src/session/core/summary.ts +173 -0
  60. package/src/session/core/system.ts +114 -0
  61. package/src/session/core/todo.ts +86 -0
  62. package/src/session/core/user-part.ts +293 -0
  63. package/src/session/instruction.ts +1 -234
  64. package/src/session/llm.ts +1 -504
  65. package/src/session/message/message-errors.ts +83 -0
  66. package/src/session/message/message-parts.ts +89 -0
  67. package/src/session/message/message-query.ts +107 -0
  68. package/src/session/message/message-transform.ts +156 -0
  69. package/src/session/message/message-types.ts +68 -0
  70. package/src/session/message/message-v2.ts +73 -0
  71. package/src/session/message/message.ts +192 -0
  72. package/src/session/message-errors.ts +1 -83
  73. package/src/session/message-parts.ts +1 -89
  74. package/src/session/message-query.ts +1 -107
  75. package/src/session/message-transform.ts +1 -156
  76. package/src/session/message-types.ts +1 -68
  77. package/src/session/message-v2.ts +1 -73
  78. package/src/session/message.ts +1 -192
  79. package/src/session/network.ts +1 -392
  80. package/src/session/overflow.ts +1 -28
  81. package/src/session/processor.ts +1 -731
  82. package/src/session/projectors.ts +2 -139
  83. package/src/session/prompt/prompt-command.ts +93 -0
  84. package/src/session/prompt/prompt-loop.ts +299 -0
  85. package/src/session/prompt/prompt-model.ts +44 -0
  86. package/src/session/prompt/prompt-reminders.ts +120 -0
  87. package/src/session/prompt/prompt-resolve.ts +42 -0
  88. package/src/session/prompt/prompt-schemas.ts +128 -0
  89. package/src/session/prompt/prompt-title.ts +55 -0
  90. package/src/session/prompt/prompt-types.ts +47 -0
  91. package/src/session/prompt/prompt-user-msg.ts +80 -0
  92. package/src/session/prompt/prompt.ts +211 -0
  93. package/src/session/prompt-command.ts +1 -93
  94. package/src/session/prompt-loop.ts +1 -299
  95. package/src/session/prompt-model.ts +1 -44
  96. package/src/session/prompt-reminders.ts +1 -120
  97. package/src/session/prompt-resolve.ts +1 -42
  98. package/src/session/prompt-schemas.ts +1 -128
  99. package/src/session/prompt-title.ts +1 -55
  100. package/src/session/prompt-types.ts +1 -47
  101. package/src/session/prompt-user-msg.ts +1 -80
  102. package/src/session/prompt.ts +1 -211
  103. package/src/session/resolve-tools.ts +1 -241
  104. package/src/session/retry.ts +1 -149
  105. package/src/session/revert.ts +1 -173
  106. package/src/session/run-state.ts +1 -110
  107. package/src/session/schema.ts +1 -35
  108. package/src/session/session-types.ts +1 -160
  109. package/src/session/session.sql.ts +1 -124
  110. package/src/session/session.ts +1 -948
  111. package/src/session/shell-exec.ts +1 -205
  112. package/src/session/status.ts +1 -100
  113. package/src/session/subtask.ts +1 -268
  114. package/src/session/summary.ts +1 -173
  115. package/src/session/system.ts +1 -114
  116. package/src/session/todo.ts +1 -86
  117. package/src/session/user-part.ts +1 -293
  118. package/src/tool/apply_patch.ts +1 -334
  119. package/src/tool/bash.ts +1 -656
  120. package/src/tool/core/external-directory.ts +55 -0
  121. package/src/tool/core/invalid.ts +21 -0
  122. package/src/tool/core/recall.ts +164 -0
  123. package/src/tool/core/recall.txt +12 -0
  124. package/src/tool/core/schema.ts +16 -0
  125. package/src/tool/core/tool.ts +162 -0
  126. package/src/tool/core/truncate.ts +160 -0
  127. package/src/tool/core/truncation-dir.ts +4 -0
  128. package/src/tool/diagnostics.ts +1 -20
  129. package/src/tool/edit-replacers.ts +1 -288
  130. package/src/tool/edit-utils.ts +1 -86
  131. package/src/tool/edit.ts +1 -262
  132. package/src/tool/external-directory.ts +1 -55
  133. package/src/tool/file/apply_patch.ts +334 -0
  134. package/src/tool/file/apply_patch.txt +33 -0
  135. package/src/tool/file/bash.ts +656 -0
  136. package/src/tool/file/bash.txt +119 -0
  137. package/src/tool/file/edit-replacers.ts +288 -0
  138. package/src/tool/file/edit-utils.ts +86 -0
  139. package/src/tool/file/edit.ts +262 -0
  140. package/src/tool/file/edit.txt +10 -0
  141. package/src/tool/file/read.ts +389 -0
  142. package/src/tool/file/read.txt +14 -0
  143. package/src/tool/file/write.ts +114 -0
  144. package/src/tool/file/write.txt +8 -0
  145. package/src/tool/glob.ts +1 -115
  146. package/src/tool/grep.ts +1 -151
  147. package/src/tool/integration/diagnostics.ts +20 -0
  148. package/src/tool/integration/lsp.ts +113 -0
  149. package/src/tool/integration/lsp.txt +24 -0
  150. package/src/tool/integration/mcp-exa.ts +73 -0
  151. package/src/tool/integration/package.ts +168 -0
  152. package/src/tool/integration/registry.ts +375 -0
  153. package/src/tool/invalid.ts +1 -21
  154. package/src/tool/lsp.ts +1 -113
  155. package/src/tool/mcp-exa.ts +1 -73
  156. package/src/tool/package.ts +1 -168
  157. package/src/tool/plan.ts +1 -30
  158. package/src/tool/question.ts +1 -52
  159. package/src/tool/read.ts +1 -389
  160. package/src/tool/recall.ts +1 -164
  161. package/src/tool/registry.ts +1 -375
  162. package/src/tool/schema.ts +1 -16
  163. package/src/tool/search/glob.ts +115 -0
  164. package/src/tool/search/glob.txt +6 -0
  165. package/src/tool/search/grep.ts +151 -0
  166. package/src/tool/search/grep.txt +8 -0
  167. package/src/tool/search/warpgrep.ts +107 -0
  168. package/src/tool/search/warpgrep.txt +10 -0
  169. package/src/tool/search/webfetch.ts +202 -0
  170. package/src/tool/search/webfetch.txt +13 -0
  171. package/src/tool/search/websearch.ts +71 -0
  172. package/src/tool/search/websearch.txt +14 -0
  173. package/src/tool/skill.ts +1 -91
  174. package/src/tool/task.ts +1 -197
  175. package/src/tool/todo.ts +1 -62
  176. package/src/tool/tool.ts +1 -162
  177. package/src/tool/truncate.ts +1 -160
  178. package/src/tool/truncation-dir.ts +1 -4
  179. package/src/tool/warpgrep.ts +1 -107
  180. package/src/tool/webfetch.ts +1 -202
  181. package/src/tool/websearch.ts +1 -71
  182. package/src/tool/workflow/plan-enter.txt +14 -0
  183. package/src/tool/workflow/plan-exit.txt +13 -0
  184. package/src/tool/workflow/plan.ts +30 -0
  185. package/src/tool/workflow/question.ts +52 -0
  186. package/src/tool/workflow/question.txt +11 -0
  187. package/src/tool/workflow/skill.ts +91 -0
  188. package/src/tool/workflow/skill.txt +5 -0
  189. package/src/tool/workflow/task.ts +197 -0
  190. package/src/tool/workflow/task.txt +57 -0
  191. package/src/tool/workflow/todo.ts +62 -0
  192. package/src/tool/workflow/todowrite.txt +167 -0
  193. package/src/tool/write.ts +1 -114
@@ -0,0 +1,28 @@
1
+ import type { Config } from "@/config/config"
2
+ import type { Provider } from "@/provider/provider"
3
+ import { ProviderTransform } from "@/provider/transform"
4
+ import type { MessageV2 } from "../../message/message-v2"
5
+ import { SaeeolSessionOverflow } from "@/saeeol/session/overflow"
6
+
7
+ const COMPACTION_BUFFER = 20_000
8
+
9
+ export function usable(input: { cfg: Config.Info; model: Provider.Model }) {
10
+ const context = input.model.limit.context
11
+ if (context === 0) return 0
12
+
13
+ const reserved =
14
+ input.cfg.compaction?.reserved ?? Math.min(COMPACTION_BUFFER, ProviderTransform.maxOutputTokens(input.model))
15
+ return input.model.limit.input
16
+ ? Math.max(0, input.model.limit.input - reserved)
17
+ : Math.max(0, context - ProviderTransform.maxOutputTokens(input.model))
18
+ }
19
+
20
+ export function isOverflow(input: { cfg: Config.Info; tokens: MessageV2.Assistant["tokens"]; model: Provider.Model }) {
21
+ if (input.cfg.compaction?.auto === false) return false
22
+ if (input.model.limit.context === 0) return false
23
+
24
+ const count =
25
+ input.tokens.total || input.tokens.input + input.tokens.output + input.tokens.cache.read + input.tokens.cache.write
26
+ const cap = SaeeolSessionOverflow.limit({ cfg: input.cfg, model: input.model, usable: usable(input) })
27
+ return count >= cap
28
+ }
@@ -0,0 +1,234 @@
1
+ import path from "path"
2
+ import { Effect, Layer, Context } from "effect"
3
+ import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http"
4
+ import { Config } from "@/config/config"
5
+ import { InstanceState } from "@/effect/instance-state"
6
+ import { Flag } from "@saeeol/core/flag/flag"
7
+ import { AppFileSystem } from "@saeeol/core/filesystem"
8
+ import { withTransientReadRetry } from "@/util/effect-http-client"
9
+ import { Global } from "@saeeol/core/global"
10
+ import { SaeeolInstruction } from "@/saeeol/session/instruction"
11
+ import type { MessageV2 } from "../message/message-v2"
12
+ import type { MessageID } from "./schema"
13
+
14
+ const FILES = [
15
+ "AGENTS.md",
16
+ ...(Flag.SAEEOL_DISABLE_CLAUDE_CODE_PROMPT ? [] : ["CLAUDE.md"]),
17
+ "CONTEXT.md", // deprecated
18
+ ]
19
+
20
+ function extract(messages: MessageV2.WithParts[]) {
21
+ const paths = new Set<string>()
22
+ for (const msg of messages) {
23
+ for (const part of msg.parts) {
24
+ if (part.type === "tool" && part.tool === "read" && part.state.status === "completed") {
25
+ if (part.state.time.compacted) continue
26
+ const loaded = part.state.metadata?.loaded
27
+ if (!loaded || !Array.isArray(loaded)) continue
28
+ for (const p of loaded) {
29
+ if (typeof p === "string") paths.add(p)
30
+ }
31
+ }
32
+ }
33
+ }
34
+ return paths
35
+ }
36
+
37
+ export interface Interface {
38
+ readonly clear: (messageID: MessageID) => Effect.Effect<void>
39
+ readonly systemPaths: () => Effect.Effect<Set<string>, AppFileSystem.Error>
40
+ readonly system: () => Effect.Effect<string[], AppFileSystem.Error>
41
+ readonly find: (dir: string) => Effect.Effect<string | undefined, AppFileSystem.Error>
42
+ readonly resolve: (
43
+ messages: MessageV2.WithParts[],
44
+ filepath: string,
45
+ messageID: MessageID,
46
+ ) => Effect.Effect<{ filepath: string; content: string }[], AppFileSystem.Error>
47
+ }
48
+
49
+ export class Service extends Context.Service<Service, Interface>()("@saeeol/Instruction") {}
50
+
51
+ export const layer: Layer.Layer<
52
+ Service,
53
+ never,
54
+ AppFileSystem.Service | Config.Service | Global.Service | HttpClient.HttpClient
55
+ > = Layer.effect(
56
+ Service,
57
+ Effect.gen(function* () {
58
+ const cfg = yield* Config.Service
59
+ const fs = yield* AppFileSystem.Service
60
+ const global = yield* Global.Service
61
+ const http = HttpClient.filterStatusOk(withTransientReadRetry(yield* HttpClient.HttpClient))
62
+ const globalFiles = [
63
+ ...(Flag.SAEEOL_CONFIG_DIR ? [path.join(Flag.SAEEOL_CONFIG_DIR, "AGENTS.md")] : []),
64
+ path.join(global.config, "AGENTS.md"),
65
+ ...(!Flag.SAEEOL_DISABLE_CLAUDE_CODE_PROMPT ? [path.join(global.home, ".claude", "CLAUDE.md")] : []),
66
+ ]
67
+
68
+ const state = yield* InstanceState.make(
69
+ Effect.fn("Instruction.state")(() =>
70
+ Effect.succeed({
71
+ // Track which instruction files have already been attached for a given assistant message.
72
+ claims: new Map<MessageID, Set<string>>(),
73
+ }),
74
+ ),
75
+ )
76
+
77
+ const relative = Effect.fnUntraced(function* (instruction: string) {
78
+ const ctx = yield* InstanceState.context
79
+ if (!Flag.SAEEOL_DISABLE_PROJECT_CONFIG) {
80
+ return yield* fs
81
+ .globUp(instruction, ctx.directory, ctx.worktree)
82
+ .pipe(Effect.catch(() => Effect.succeed([] as string[])))
83
+ }
84
+ const root = Flag.SAEEOL_CONFIG_DIR ?? global.config
85
+ return yield* fs.globUp(instruction, root, root).pipe(Effect.catch(() => Effect.succeed([] as string[])))
86
+ })
87
+
88
+ const read = Effect.fnUntraced(function* (filepath: string) {
89
+ const content = yield* fs.readFileString(filepath).pipe(Effect.catch(() => Effect.succeed("")))
90
+ return yield* Effect.promise(() => SaeeolInstruction.content(content, filepath))
91
+ })
92
+
93
+ const fetch = Effect.fnUntraced(function* (url: string) {
94
+ const res = yield* http.execute(HttpClientRequest.get(url)).pipe(
95
+ Effect.timeout(5000),
96
+ Effect.catch(() => Effect.succeed(null)),
97
+ )
98
+ if (!res) return ""
99
+ const body = yield* res.arrayBuffer.pipe(Effect.catch(() => Effect.succeed(new ArrayBuffer(0))))
100
+ return new TextDecoder().decode(body)
101
+ })
102
+
103
+ const clear = Effect.fn("Instruction.clear")(function* (messageID: MessageID) {
104
+ const s = yield* InstanceState.get(state)
105
+ s.claims.delete(messageID)
106
+ })
107
+
108
+ const systemPaths = Effect.fn("Instruction.systemPaths")(function* () {
109
+ const config = yield* cfg.get()
110
+ const ctx = yield* InstanceState.context
111
+ const paths = new Set<string>()
112
+
113
+ for (const file of globalFiles) {
114
+ if (yield* fs.existsSafe(file)) {
115
+ paths.add(path.resolve(file))
116
+ break
117
+ }
118
+ }
119
+
120
+ // The first project-level match wins so we don't stack AGENTS.md/CLAUDE.md from every ancestor.
121
+ if (!Flag.SAEEOL_DISABLE_PROJECT_CONFIG) {
122
+ for (const file of FILES) {
123
+ const matches = yield* fs.findUp(file, ctx.directory, ctx.worktree)
124
+ if (matches.length > 0) {
125
+ matches.forEach((item) => paths.add(path.resolve(item)))
126
+ break
127
+ }
128
+ }
129
+ }
130
+
131
+ if (config.instructions) {
132
+ for (const raw of config.instructions) {
133
+ if (raw.startsWith("https://") || raw.startsWith("http://")) continue
134
+ const instruction = raw.startsWith("~/") ? path.join(global.home, raw.slice(2)) : raw
135
+ const matches = yield* (
136
+ path.isAbsolute(instruction)
137
+ ? fs.glob(path.basename(instruction), {
138
+ cwd: path.dirname(instruction),
139
+ absolute: true,
140
+ include: "file",
141
+ })
142
+ : relative(instruction)
143
+ ).pipe(Effect.catch(() => Effect.succeed([] as string[])))
144
+ matches.forEach((item) => paths.add(path.resolve(item)))
145
+ }
146
+ }
147
+
148
+ return paths
149
+ })
150
+
151
+ const system = Effect.fn("Instruction.system")(function* () {
152
+ const config = yield* cfg.get()
153
+ const paths = yield* systemPaths()
154
+ const urls = (config.instructions ?? []).filter(
155
+ (item) => item.startsWith("https://") || item.startsWith("http://"),
156
+ )
157
+
158
+ const files = yield* Effect.forEach(Array.from(paths), read, { concurrency: 8 })
159
+ const remote = yield* Effect.forEach(urls, fetch, { concurrency: 4 })
160
+
161
+ return [
162
+ ...Array.from(paths).flatMap((item, i) => (files[i] ? [`Instructions from: ${item}\n${files[i]}`] : [])),
163
+ ...urls.flatMap((item, i) => (remote[i] ? [`Instructions from: ${item}\n${remote[i]}`] : [])),
164
+ ]
165
+ })
166
+
167
+ const find = Effect.fn("Instruction.find")(function* (dir: string) {
168
+ for (const file of FILES) {
169
+ const filepath = path.resolve(path.join(dir, file))
170
+ if (yield* fs.existsSafe(filepath)) return filepath
171
+ }
172
+ return undefined
173
+ })
174
+
175
+ const resolve = Effect.fn("Instruction.resolve")(function* (
176
+ messages: MessageV2.WithParts[],
177
+ filepath: string,
178
+ messageID: MessageID,
179
+ ) {
180
+ const sys = yield* systemPaths()
181
+ const already = extract(messages)
182
+ const results: { filepath: string; content: string }[] = []
183
+ const s = yield* InstanceState.get(state)
184
+ const root = path.resolve(yield* InstanceState.directory)
185
+
186
+ const target = path.resolve(filepath)
187
+ let current = path.dirname(target)
188
+
189
+ // Walk upward from the file being read and attach nearby instruction files once per message.
190
+ while (current.startsWith(root) && current !== root) {
191
+ const found = yield* find(current)
192
+ if (!found || found === target || sys.has(found) || already.has(found)) {
193
+ current = path.dirname(current)
194
+ continue
195
+ }
196
+
197
+ let set = s.claims.get(messageID)
198
+ if (!set) {
199
+ set = new Set()
200
+ s.claims.set(messageID, set)
201
+ }
202
+ if (set.has(found)) {
203
+ current = path.dirname(current)
204
+ continue
205
+ }
206
+
207
+ set.add(found)
208
+ const content = yield* read(found)
209
+ if (content) {
210
+ results.push({ filepath: found, content: `Instructions from: ${found}\n${content}` })
211
+ }
212
+
213
+ current = path.dirname(current)
214
+ }
215
+
216
+ return results
217
+ })
218
+
219
+ return Service.of({ clear, systemPaths, system, find, resolve })
220
+ }),
221
+ )
222
+
223
+ export const defaultLayer = layer.pipe(
224
+ Layer.provide(Config.defaultLayer),
225
+ Layer.provide(Global.layer),
226
+ Layer.provide(AppFileSystem.defaultLayer),
227
+ Layer.provide(FetchHttpClient.layer),
228
+ )
229
+
230
+ export function loaded(messages: MessageV2.WithParts[]) {
231
+ return extract(messages)
232
+ }
233
+
234
+ export * as Instruction from "./instruction"