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
@@ -1,44 +1 @@
1
- import { createMemo, createResource } from "solid-js"
2
- import { DialogSelect } from "@tui/ui/dialog-select"
3
- import { useDialog } from "@tui/ui/dialog"
4
- import { useSDK } from "@tui/context/sdk"
5
- import { createStore } from "solid-js/store"
6
-
7
- export function DialogTag(props: { onSelect?: (value: string) => void }) {
8
- const sdk = useSDK()
9
- const dialog = useDialog()
10
-
11
- const [store] = createStore({
12
- filter: "",
13
- })
14
-
15
- const [files] = createResource(
16
- () => [store.filter],
17
- async () => {
18
- const result = await sdk.client.find.files({
19
- query: store.filter,
20
- })
21
- if (result.error) return []
22
- const sliced = (result.data ?? []).slice(0, 5)
23
- return sliced
24
- },
25
- )
26
-
27
- const options = createMemo(() =>
28
- (files() ?? []).map((file) => ({
29
- value: file,
30
- title: file,
31
- })),
32
- )
33
-
34
- return (
35
- <DialogSelect
36
- title="Autocomplete"
37
- options={options()}
38
- onSelect={(option) => {
39
- props.onSelect?.(option.value)
40
- dialog.clear()
41
- }}
42
- />
43
- )
44
- }
1
+ export * from "./dialog/dialog-tag"
@@ -1,50 +1 @@
1
- import { DialogSelect, type DialogSelectRef } from "../ui/dialog-select"
2
- import { useTheme } from "../context/theme"
3
- import { useDialog } from "../ui/dialog"
4
- import { onCleanup } from "solid-js"
5
-
6
- export function DialogThemeList() {
7
- const theme = useTheme()
8
- const options = Object.keys(theme.all())
9
- .sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" }))
10
- .map((value) => ({
11
- title: value,
12
- value: value,
13
- }))
14
- const dialog = useDialog()
15
- let confirmed = false
16
- let ref: DialogSelectRef<string>
17
- const initial = theme.selected
18
-
19
- onCleanup(() => {
20
- if (!confirmed) theme.set(initial)
21
- })
22
-
23
- return (
24
- <DialogSelect
25
- title="Themes"
26
- options={options}
27
- current={initial}
28
- onMove={(opt) => {
29
- theme.set(opt.value)
30
- }}
31
- onSelect={(opt) => {
32
- theme.set(opt.value)
33
- confirmed = true
34
- dialog.clear()
35
- }}
36
- ref={(r) => {
37
- ref = r
38
- }}
39
- onFilter={(query) => {
40
- if (query.length === 0) {
41
- theme.set(initial)
42
- return
43
- }
44
-
45
- const first = ref.filtered[0]
46
- if (first) theme.set(first.value)
47
- }}
48
- />
49
- )
50
- }
1
+ export * from "./dialog/dialog-theme-list"
@@ -1,39 +1 @@
1
- import { createMemo } from "solid-js"
2
- import { useLocal } from "@tui/context/local"
3
- import { DialogSelect } from "@tui/ui/dialog-select"
4
- import { useDialog } from "@tui/ui/dialog"
5
-
6
- export function DialogVariant() {
7
- const local = useLocal()
8
- const dialog = useDialog()
9
-
10
- const options = createMemo(() => {
11
- return [
12
- {
13
- value: "default",
14
- title: "Default",
15
- onSelect: () => {
16
- dialog.clear()
17
- local.model.variant.set(undefined)
18
- },
19
- },
20
- ...local.model.variant.list().map((variant) => ({
21
- value: variant,
22
- title: variant,
23
- onSelect: () => {
24
- dialog.clear()
25
- local.model.variant.set(variant)
26
- },
27
- })),
28
- ]
29
- })
30
-
31
- return (
32
- <DialogSelect<string>
33
- options={options()}
34
- title={"Select variant"}
35
- current={local.model.variant.selected()}
36
- flat={true}
37
- />
38
- )
39
- }
1
+ export * from "./dialog/dialog-variant"
@@ -1,200 +1 @@
1
- import { createSaeeolClient } from "@saeeol/sdk/v2"
2
- import { useDialog } from "@tui/ui/dialog"
3
- import { DialogSelect } from "@tui/ui/dialog-select"
4
- import { useRoute } from "@tui/context/route"
5
- import { useSync } from "@tui/context/sync"
6
- import { useProject } from "@tui/context/project"
7
- import { createMemo, createSignal, onMount } from "solid-js"
8
- import { setTimeout as sleep } from "node:timers/promises"
9
- import { errorMessage } from "@/util/error"
10
- import { useSDK } from "../context/sdk"
11
- import { useToast } from "../ui/toast"
12
-
13
- type Adapter = {
14
- type: string
15
- name: string
16
- description: string
17
- }
18
-
19
- function scoped(sdk: ReturnType<typeof useSDK>, sync: ReturnType<typeof useSync>, workspaceID: string) {
20
- return createSaeeolClient({
21
- baseUrl: sdk.url,
22
- fetch: sdk.fetch,
23
- directory: sync.path.directory || sdk.directory,
24
- experimental_workspaceID: workspaceID,
25
- })
26
- }
27
-
28
- export async function openWorkspaceSession(input: {
29
- dialog: ReturnType<typeof useDialog>
30
- route: ReturnType<typeof useRoute>
31
- sdk: ReturnType<typeof useSDK>
32
- sync: ReturnType<typeof useSync>
33
- toast: ReturnType<typeof useToast>
34
- workspaceID: string
35
- }) {
36
- const client = scoped(input.sdk, input.sync, input.workspaceID)
37
-
38
- while (true) {
39
- const result = await client.session.create({ workspace: input.workspaceID }).catch(() => undefined)
40
- if (!result) {
41
- input.toast.show({
42
- message: "Failed to create workspace session",
43
- variant: "error",
44
- })
45
- return
46
- }
47
- if (result.response?.status && result.response.status >= 500 && result.response.status < 600) {
48
- await sleep(1000)
49
- continue
50
- }
51
- if (!result.data) {
52
- input.toast.show({
53
- message: "Failed to create workspace session",
54
- variant: "error",
55
- })
56
- return
57
- }
58
-
59
- input.route.navigate({
60
- type: "session",
61
- sessionID: result.data.id,
62
- })
63
- input.dialog.clear()
64
- return
65
- }
66
- }
67
-
68
- export async function restoreWorkspaceSession(input: {
69
- dialog: ReturnType<typeof useDialog>
70
- sdk: ReturnType<typeof useSDK>
71
- sync: ReturnType<typeof useSync>
72
- project: ReturnType<typeof useProject>
73
- toast: ReturnType<typeof useToast>
74
- workspaceID: string
75
- sessionID: string
76
- done?: () => void
77
- }) {
78
- const result = await input.sdk.client.experimental.workspace
79
- .sessionRestore({ id: input.workspaceID, sessionID: input.sessionID })
80
- .catch(() => undefined)
81
- if (!result?.data) {
82
- input.toast.show({
83
- message: `Failed to restore session: ${errorMessage(result?.error ?? "no response")}`,
84
- variant: "error",
85
- })
86
- return
87
- }
88
-
89
- input.project.workspace.set(input.workspaceID)
90
-
91
- await input.sync.bootstrap({ fatal: false }).catch(() => undefined)
92
-
93
- await Promise.all([input.project.workspace.sync(), input.sync.session.sync(input.sessionID)])
94
-
95
- input.toast.show({
96
- message: "Session restored into the new workspace",
97
- variant: "success",
98
- })
99
- input.done?.()
100
- if (input.done) return
101
- input.dialog.clear()
102
- }
103
-
104
- export function DialogWorkspaceCreate(props: { onSelect: (workspaceID: string) => Promise<void> | void }) {
105
- const dialog = useDialog()
106
- const sync = useSync()
107
- const project = useProject()
108
- const sdk = useSDK()
109
- const toast = useToast()
110
- const [creating, setCreating] = createSignal<string>()
111
- const [adapters, setAdapters] = createSignal<Adapter[]>()
112
-
113
- onMount(() => {
114
- dialog.setSize("medium")
115
- void (async () => {
116
- const dir = sync.path.directory || sdk.directory
117
- const url = new URL("/experimental/workspace/adapter", sdk.url)
118
- if (dir) url.searchParams.set("directory", dir)
119
- const res = await sdk
120
- .fetch(url)
121
- .then((x) => x.json() as Promise<Adapter[]>)
122
- .catch(() => undefined)
123
- if (!res) {
124
- toast.show({
125
- message: "Failed to load workspace adapters",
126
- variant: "error",
127
- })
128
- return
129
- }
130
- setAdapters(res)
131
- })()
132
- })
133
-
134
- const options = createMemo(() => {
135
- const type = creating()
136
- if (type) {
137
- return [
138
- {
139
- title: `Creating ${type} workspace...`,
140
- value: "creating" as const,
141
- description: "This can take a while for remote environments",
142
- },
143
- ]
144
- }
145
- const list = adapters()
146
- if (!list) {
147
- return [
148
- {
149
- title: "Loading workspaces...",
150
- value: "loading" as const,
151
- description: "Fetching available workspace adapters",
152
- },
153
- ]
154
- }
155
- return list.map((item) => ({
156
- title: item.name,
157
- value: item.type,
158
- description: item.description,
159
- }))
160
- })
161
-
162
- const create = async (type: string) => {
163
- if (creating()) return
164
- setCreating(type)
165
-
166
- const result = await sdk.client.experimental.workspace.create({ type, branch: null }).catch(() => {
167
- toast.show({
168
- message: "Creating workspace failed",
169
- variant: "error",
170
- })
171
- return undefined
172
- })
173
-
174
- const workspace = result?.data
175
- if (!workspace) {
176
- setCreating(undefined)
177
- toast.show({
178
- message: `Failed to create workspace: ${errorMessage(result?.error ?? "no response")}`,
179
- variant: "error",
180
- })
181
- return
182
- }
183
-
184
- await project.workspace.sync()
185
- await props.onSelect(workspace.id)
186
- setCreating(undefined)
187
- }
188
-
189
- return (
190
- <DialogSelect
191
- title={creating() ? "Creating Workspace" : "New Workspace"}
192
- skipFilter={true}
193
- options={options()}
194
- onSelect={(option) => {
195
- if (option.value === "creating" || option.value === "loading") return
196
- void create(option.value)
197
- }}
198
- />
199
- )
200
- }
1
+ export * from "./dialog/dialog-workspace-create"
@@ -1,81 +1 @@
1
- import { TextAttributes } from "@opentui/core"
2
- import { useKeyboard } from "@opentui/solid"
3
- import { createStore } from "solid-js/store"
4
- import { For } from "solid-js"
5
- import { useTheme } from "../context/theme"
6
- import { useDialog } from "../ui/dialog"
7
-
8
- export function DialogWorkspaceUnavailable(props: { onRestore?: () => boolean | void | Promise<boolean | void> }) {
9
- const dialog = useDialog()
10
- const { theme } = useTheme()
11
- const [store, setStore] = createStore({
12
- active: "restore" as "cancel" | "restore",
13
- })
14
-
15
- const options = ["cancel", "restore"] as const
16
-
17
- async function confirm() {
18
- if (store.active === "cancel") {
19
- dialog.clear()
20
- return
21
- }
22
- const result = await props.onRestore?.()
23
- if (result === false) return
24
- }
25
-
26
- useKeyboard((evt) => {
27
- if (evt.name === "return") {
28
- evt.preventDefault()
29
- evt.stopPropagation()
30
- void confirm()
31
- return
32
- }
33
- if (evt.name === "left") {
34
- evt.preventDefault()
35
- evt.stopPropagation()
36
- setStore("active", "cancel")
37
- return
38
- }
39
- if (evt.name === "right") {
40
- evt.preventDefault()
41
- evt.stopPropagation()
42
- setStore("active", "restore")
43
- }
44
- })
45
-
46
- return (
47
- <box paddingLeft={2} paddingRight={2} gap={1}>
48
- <box flexDirection="row" justifyContent="space-between">
49
- <text attributes={TextAttributes.BOLD} fg={theme.text}>
50
- Workspace Unavailable
51
- </text>
52
- <text fg={theme.textMuted} onMouseUp={() => dialog.clear()}>
53
- esc
54
- </text>
55
- </box>
56
- <text fg={theme.textMuted} wrapMode="word">
57
- This session is attached to a workspace that is no longer available.
58
- </text>
59
- <text fg={theme.textMuted} wrapMode="word">
60
- Would you like to restore this session into a new workspace?
61
- </text>
62
- <box flexDirection="row" justifyContent="flex-end" paddingBottom={1} gap={1}>
63
- <For each={options}>
64
- {(item) => (
65
- <box
66
- paddingLeft={2}
67
- paddingRight={2}
68
- backgroundColor={item === store.active ? theme.primary : undefined}
69
- onMouseUp={() => {
70
- setStore("active", item)
71
- void confirm()
72
- }}
73
- >
74
- <text fg={item === store.active ? theme.selectedListItemText : theme.textMuted}>{item}</text>
75
- </box>
76
- )}
77
- </For>
78
- </box>
79
- </box>
80
- )
81
- }
1
+ export * from "./dialog/dialog-workspace-unavailable"
@@ -1,169 +1 @@
1
- import { clamp } from "@saeeol/boxes/clamp"
2
- import { BusEvent } from "@/bus/bus-event"
3
- import { SessionID, MessageID } from "./schema"
4
- import { Provider } from "@/provider/provider"
5
- import { MessageV2 } from "./message-v2"
6
- import { Schema, Effect } from "effect"
7
- import { Config } from "@/config/config"
8
- import { usable } from "./overflow"
9
-
10
- export const PRUNE_MINIMUM = 20_000
11
- export const PRUNE_PROTECT = 40_000
12
- export const TOOL_OUTPUT_MAX_CHARS = 2_000
13
- export const PRUNE_PROTECTED_TOOLS = ["skill"]
14
- export const DEFAULT_TAIL_TURNS = 2
15
- export const MIN_PRESERVE_RECENT_TOKENS = 2_000
16
- export const MAX_PRESERVE_RECENT_TOKENS = 8_000
17
- export const SUMMARY_TEMPLATE = `Output exactly the Markdown structure shown inside <template> and keep the section order unchanged. Do not include the <template> tags in your response.
18
- <template>
19
- ## Goal
20
- - [single-sentence task summary]
21
-
22
- ## Constraints & Preferences
23
- - [user constraints, preferences, specs, or "(none)"]
24
-
25
- ## Progress
26
- ### Done
27
- - [completed work or "(none)"]
28
-
29
- ### In Progress
30
- - [current work or "(none)"]
31
-
32
- ### Blocked
33
- - [blockers or "(none)"]
34
-
35
- ## Key Decisions
36
- - [decision and why, or "(none)"]
37
-
38
- ## Next Steps
39
- - [ordered next actions or "(none)"]
40
-
41
- ## Critical Context
42
- - [important technical facts, errors, open questions, or "(none)"]
43
-
44
- ## Relevant Files
45
- - [file or directory path: why it matters, or "(none)"]
46
- </template>
47
-
48
- Rules:
49
- - Keep every section, even when empty.
50
- - Use terse bullets, not prose paragraphs.
51
- - Preserve exact file paths, commands, error strings, and identifiers when known.
52
- - Do not mention the summary process or that context was compacted.`
53
- export type Turn = {
54
- start: number
55
- end: number
56
- id: MessageID
57
- }
58
-
59
- export type Tail = {
60
- start: number
61
- id: MessageID
62
- }
63
-
64
- export type CompletedCompaction = {
65
- userIndex: number
66
- assistantIndex: number
67
- summary: string | undefined
68
- }
69
- export type PruneReason = "normal" | "post-compaction" | "payload-limit"
70
-
71
- export const Event = {
72
- Compacted: BusEvent.define(
73
- "session.compacted",
74
- Schema.Struct({
75
- sessionID: SessionID,
76
- }),
77
- ),
78
- }
79
-
80
- export function summaryText(message: MessageV2.WithParts) {
81
- const text = message.parts
82
- .filter((part): part is MessageV2.TextPart => part.type === "text")
83
- .map((part) => part.text.trim())
84
- .filter(Boolean)
85
- .join("\n\n")
86
- .trim()
87
- return text || undefined
88
- }
89
-
90
- export function completedCompactions(messages: MessageV2.WithParts[]) {
91
- const users = new Map<MessageID, number>()
92
- for (let i = 0; i < messages.length; i++) {
93
- const msg = messages[i]
94
- if (msg.info.role !== "user") continue
95
- if (!msg.parts.some((part) => part.type === "compaction")) continue
96
- users.set(msg.info.id, i)
97
- }
98
-
99
- return messages.flatMap((msg, assistantIndex): CompletedCompaction[] => {
100
- if (msg.info.role !== "assistant") return []
101
- if (!msg.info.summary || !msg.info.finish || msg.info.error) return []
102
- const userIndex = users.get(msg.info.parentID)
103
- if (userIndex === undefined) return []
104
- return [{ userIndex, assistantIndex, summary: summaryText(msg) }]
105
- })
106
- }
107
-
108
- export function buildPrompt(input: { previousSummary?: string; context: string[] }) {
109
- const anchor = input.previousSummary
110
- ? [
111
- "Update the anchored summary below using the conversation history above.",
112
- "Preserve still-true details, remove stale details, and merge in the new facts.",
113
- "<previous-summary>",
114
- input.previousSummary,
115
- "</previous-summary>",
116
- ].join("\n")
117
- : "Create a new anchored summary from the conversation history above."
118
- return [anchor, SUMMARY_TEMPLATE, ...input.context].join("\n\n")
119
- }
120
-
121
- export function preserveRecentBudget(input: { cfg: Config.Info; model: Provider.Model }) {
122
- return (
123
- input.cfg.compaction?.preserve_recent_tokens ??
124
- clamp(Math.floor(usable(input) * 0.25), MIN_PRESERVE_RECENT_TOKENS, MAX_PRESERVE_RECENT_TOKENS)
125
- )
126
- }
127
-
128
- export function turns(messages: MessageV2.WithParts[]) {
129
- const result: Turn[] = []
130
- for (let i = 0; i < messages.length; i++) {
131
- const msg = messages[i]
132
- if (msg.info.role !== "user") continue
133
- if (msg.parts.some((part) => part.type === "compaction")) continue
134
- result.push({
135
- start: i,
136
- end: messages.length,
137
- id: msg.info.id,
138
- })
139
- }
140
- for (let i = 0; i < result.length - 1; i++) {
141
- result[i].end = result[i + 1].start
142
- }
143
- return result
144
- }
145
-
146
- export function splitTurn(input: {
147
- messages: MessageV2.WithParts[]
148
- turn: Turn
149
- model: Provider.Model
150
- budget: number
151
- estimate: (input: { messages: MessageV2.WithParts[]; model: Provider.Model }) => Effect.Effect<number>
152
- }) {
153
- return Effect.gen(function* () {
154
- if (input.budget <= 0) return undefined
155
- if (input.turn.end - input.turn.start <= 1) return undefined
156
- for (let start = input.turn.start + 1; start < input.turn.end; start++) {
157
- const size = yield* input.estimate({
158
- messages: input.messages.slice(start, input.turn.end),
159
- model: input.model,
160
- })
161
- if (size > input.budget) continue
162
- return {
163
- start,
164
- id: input.messages[start]!.info.id,
165
- } satisfies Tail
166
- }
167
- return undefined
168
- })
169
- }
1
+ export * from "./core/compaction/compaction-helpers"