@prestyj/cli 4.2.77 → 4.3.34

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 (233) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.js +335 -85
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config.d.ts +10 -0
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/config.js +33 -0
  7. package/dist/config.js.map +1 -1
  8. package/dist/core/agent-session.d.ts.map +1 -1
  9. package/dist/core/agent-session.js +42 -26
  10. package/dist/core/agent-session.js.map +1 -1
  11. package/dist/core/auth-storage.d.ts.map +1 -1
  12. package/dist/core/auth-storage.js +17 -2
  13. package/dist/core/auth-storage.js.map +1 -1
  14. package/dist/core/auto-update.d.ts +16 -3
  15. package/dist/core/auto-update.d.ts.map +1 -1
  16. package/dist/core/auto-update.js +126 -56
  17. package/dist/core/auto-update.js.map +1 -1
  18. package/dist/core/auto-update.test.d.ts +2 -0
  19. package/dist/core/auto-update.test.d.ts.map +1 -0
  20. package/dist/core/auto-update.test.js +205 -0
  21. package/dist/core/auto-update.test.js.map +1 -0
  22. package/dist/core/compaction/compactor.d.ts +18 -1
  23. package/dist/core/compaction/compactor.d.ts.map +1 -1
  24. package/dist/core/compaction/compactor.js +48 -7
  25. package/dist/core/compaction/compactor.js.map +1 -1
  26. package/dist/core/compaction/compactor.test.js +39 -6
  27. package/dist/core/compaction/compactor.test.js.map +1 -1
  28. package/dist/core/compaction/token-estimator.d.ts.map +1 -1
  29. package/dist/core/compaction/token-estimator.js +4 -0
  30. package/dist/core/compaction/token-estimator.js.map +1 -1
  31. package/dist/core/compaction/token-estimator.test.js +1 -1
  32. package/dist/core/index.d.ts +1 -1
  33. package/dist/core/index.d.ts.map +1 -1
  34. package/dist/core/index.js +1 -1
  35. package/dist/core/index.js.map +1 -1
  36. package/dist/core/mcp/client.d.ts.map +1 -1
  37. package/dist/core/mcp/client.js +15 -2
  38. package/dist/core/mcp/client.js.map +1 -1
  39. package/dist/core/model-registry.d.ts.map +1 -1
  40. package/dist/core/model-registry.js +74 -5
  41. package/dist/core/model-registry.js.map +1 -1
  42. package/dist/core/oauth/types.d.ts +1 -0
  43. package/dist/core/oauth/types.d.ts.map +1 -1
  44. package/dist/core/prompt-commands.d.ts.map +1 -1
  45. package/dist/core/prompt-commands.js +134 -0
  46. package/dist/core/prompt-commands.js.map +1 -1
  47. package/dist/core/settings-manager.d.ts +5 -0
  48. package/dist/core/settings-manager.d.ts.map +1 -1
  49. package/dist/core/settings-manager.js +14 -2
  50. package/dist/core/settings-manager.js.map +1 -1
  51. package/dist/interactive.js +1 -1
  52. package/dist/interactive.js.map +1 -1
  53. package/dist/modes/agent-home-mode.d.ts +21 -0
  54. package/dist/modes/agent-home-mode.d.ts.map +1 -0
  55. package/dist/modes/agent-home-mode.js +553 -0
  56. package/dist/modes/agent-home-mode.js.map +1 -0
  57. package/dist/system-prompt.d.ts.map +1 -1
  58. package/dist/system-prompt.js +30 -14
  59. package/dist/system-prompt.js.map +1 -1
  60. package/dist/tools/bash.d.ts.map +1 -1
  61. package/dist/tools/bash.js +9 -2
  62. package/dist/tools/bash.js.map +1 -1
  63. package/dist/tools/edit.d.ts +4 -2
  64. package/dist/tools/edit.d.ts.map +1 -1
  65. package/dist/tools/edit.js +48 -33
  66. package/dist/tools/edit.js.map +1 -1
  67. package/dist/tools/edit.test.js +80 -20
  68. package/dist/tools/edit.test.js.map +1 -1
  69. package/dist/tools/grep.d.ts.map +1 -1
  70. package/dist/tools/grep.js +12 -0
  71. package/dist/tools/grep.js.map +1 -1
  72. package/dist/tools/index.d.ts +1 -0
  73. package/dist/tools/index.d.ts.map +1 -1
  74. package/dist/tools/index.js +6 -0
  75. package/dist/tools/index.js.map +1 -1
  76. package/dist/tools/overflow.js +1 -1
  77. package/dist/tools/overflow.js.map +1 -1
  78. package/dist/tools/plan-mode.test.js +2 -2
  79. package/dist/tools/plan-mode.test.js.map +1 -1
  80. package/dist/tools/read.d.ts.map +1 -1
  81. package/dist/tools/read.js +14 -1
  82. package/dist/tools/read.js.map +1 -1
  83. package/dist/tools/subagent.d.ts.map +1 -1
  84. package/dist/tools/subagent.js +20 -3
  85. package/dist/tools/subagent.js.map +1 -1
  86. package/dist/tools/web-search.d.ts +9 -0
  87. package/dist/tools/web-search.d.ts.map +1 -0
  88. package/dist/tools/web-search.js +241 -0
  89. package/dist/tools/web-search.js.map +1 -0
  90. package/dist/tools/write.js +2 -2
  91. package/dist/tools/write.js.map +1 -1
  92. package/dist/tools/write.test.js +7 -9
  93. package/dist/tools/write.test.js.map +1 -1
  94. package/dist/ui/App.d.ts +4 -0
  95. package/dist/ui/App.d.ts.map +1 -1
  96. package/dist/ui/App.js +324 -161
  97. package/dist/ui/App.js.map +1 -1
  98. package/dist/ui/activity-phrases.d.ts.map +1 -1
  99. package/dist/ui/activity-phrases.js +74 -129
  100. package/dist/ui/activity-phrases.js.map +1 -1
  101. package/dist/ui/components/ActivityIndicator.d.ts +3 -1
  102. package/dist/ui/components/ActivityIndicator.d.ts.map +1 -1
  103. package/dist/ui/components/ActivityIndicator.js +5 -2
  104. package/dist/ui/components/ActivityIndicator.js.map +1 -1
  105. package/dist/ui/components/AssistantMessage.d.ts +1 -0
  106. package/dist/ui/components/AssistantMessage.d.ts.map +1 -1
  107. package/dist/ui/components/AssistantMessage.js +2 -2
  108. package/dist/ui/components/AssistantMessage.js.map +1 -1
  109. package/dist/ui/components/DiffFrame.d.ts +10 -0
  110. package/dist/ui/components/DiffFrame.d.ts.map +1 -0
  111. package/dist/ui/components/DiffFrame.js +13 -0
  112. package/dist/ui/components/DiffFrame.js.map +1 -0
  113. package/dist/ui/components/Footer.d.ts +2 -3
  114. package/dist/ui/components/Footer.d.ts.map +1 -1
  115. package/dist/ui/components/Footer.js +6 -5
  116. package/dist/ui/components/Footer.js.map +1 -1
  117. package/dist/ui/components/InputArea.d.ts.map +1 -1
  118. package/dist/ui/components/InputArea.js +297 -8
  119. package/dist/ui/components/InputArea.js.map +1 -1
  120. package/dist/ui/components/Markdown.d.ts +1 -1
  121. package/dist/ui/components/Markdown.d.ts.map +1 -1
  122. package/dist/ui/components/Markdown.js +25 -213
  123. package/dist/ui/components/Markdown.js.map +1 -1
  124. package/dist/ui/components/ModelSelector.d.ts.map +1 -1
  125. package/dist/ui/components/ModelSelector.js +10 -2
  126. package/dist/ui/components/ModelSelector.js.map +1 -1
  127. package/dist/ui/components/Ratchet.d.ts.map +1 -1
  128. package/dist/ui/components/Ratchet.js +10 -3
  129. package/dist/ui/components/Ratchet.js.map +1 -1
  130. package/dist/ui/components/StreamingArea.d.ts.map +1 -1
  131. package/dist/ui/components/StreamingArea.js +2 -2
  132. package/dist/ui/components/StreamingArea.js.map +1 -1
  133. package/dist/ui/components/SubAgentPanel.d.ts.map +1 -1
  134. package/dist/ui/components/SubAgentPanel.js +5 -1
  135. package/dist/ui/components/SubAgentPanel.js.map +1 -1
  136. package/dist/ui/components/ThemeSelector.d.ts +9 -0
  137. package/dist/ui/components/ThemeSelector.d.ts.map +1 -0
  138. package/dist/ui/components/ThemeSelector.js +25 -0
  139. package/dist/ui/components/ThemeSelector.js.map +1 -0
  140. package/dist/ui/components/ToolExecution.d.ts +2 -0
  141. package/dist/ui/components/ToolExecution.d.ts.map +1 -1
  142. package/dist/ui/components/ToolExecution.js +95 -30
  143. package/dist/ui/components/ToolExecution.js.map +1 -1
  144. package/dist/ui/components/ToolGroupExecution.d.ts +3 -2
  145. package/dist/ui/components/ToolGroupExecution.d.ts.map +1 -1
  146. package/dist/ui/components/ToolGroupExecution.js +68 -51
  147. package/dist/ui/components/ToolGroupExecution.js.map +1 -1
  148. package/dist/ui/hooks/stall-retry-duplicate.test.d.ts +2 -0
  149. package/dist/ui/hooks/stall-retry-duplicate.test.d.ts.map +1 -0
  150. package/dist/ui/hooks/stall-retry-duplicate.test.js +94 -0
  151. package/dist/ui/hooks/stall-retry-duplicate.test.js.map +1 -0
  152. package/dist/ui/hooks/useAgentLoop.d.ts +10 -3
  153. package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
  154. package/dist/ui/hooks/useAgentLoop.js +131 -16
  155. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  156. package/dist/ui/hooks/useDoublePress.d.ts +8 -0
  157. package/dist/ui/hooks/useDoublePress.d.ts.map +1 -0
  158. package/dist/ui/hooks/useDoublePress.js +44 -0
  159. package/dist/ui/hooks/useDoublePress.js.map +1 -0
  160. package/dist/ui/hooks/useTerminalProgress.d.ts +1 -1
  161. package/dist/ui/hooks/useTerminalProgress.d.ts.map +1 -1
  162. package/dist/ui/hooks/useTerminalProgress.js +18 -15
  163. package/dist/ui/hooks/useTerminalProgress.js.map +1 -1
  164. package/dist/ui/hooks/useTerminalTitle.d.ts +3 -6
  165. package/dist/ui/hooks/useTerminalTitle.d.ts.map +1 -1
  166. package/dist/ui/hooks/useTerminalTitle.js +10 -19
  167. package/dist/ui/hooks/useTerminalTitle.js.map +1 -1
  168. package/dist/ui/live-item-flush.d.ts +4 -0
  169. package/dist/ui/live-item-flush.d.ts.map +1 -1
  170. package/dist/ui/live-item-flush.js +36 -0
  171. package/dist/ui/live-item-flush.js.map +1 -1
  172. package/dist/ui/login.d.ts.map +1 -1
  173. package/dist/ui/login.js +3 -0
  174. package/dist/ui/login.js.map +1 -1
  175. package/dist/ui/render.d.ts +3 -1
  176. package/dist/ui/render.d.ts.map +1 -1
  177. package/dist/ui/render.js +9 -3
  178. package/dist/ui/render.js.map +1 -1
  179. package/dist/ui/stores/create-store.d.ts +15 -0
  180. package/dist/ui/stores/create-store.d.ts.map +1 -0
  181. package/dist/ui/stores/create-store.js +30 -0
  182. package/dist/ui/stores/create-store.js.map +1 -0
  183. package/dist/ui/stores/index.d.ts +3 -0
  184. package/dist/ui/stores/index.d.ts.map +1 -0
  185. package/dist/ui/stores/index.js +3 -0
  186. package/dist/ui/stores/index.js.map +1 -0
  187. package/dist/ui/stores/taskbar-store.d.ts +19 -0
  188. package/dist/ui/stores/taskbar-store.d.ts.map +1 -0
  189. package/dist/ui/stores/taskbar-store.js +59 -0
  190. package/dist/ui/stores/taskbar-store.js.map +1 -0
  191. package/dist/ui/theme/dark-ansi.json +48 -0
  192. package/dist/ui/theme/dark-daltonized.json +48 -0
  193. package/dist/ui/theme/dark.json +22 -1
  194. package/dist/ui/theme/detect-theme.d.ts +12 -8
  195. package/dist/ui/theme/detect-theme.d.ts.map +1 -1
  196. package/dist/ui/theme/detect-theme.js +58 -39
  197. package/dist/ui/theme/detect-theme.js.map +1 -1
  198. package/dist/ui/theme/light-ansi.json +48 -0
  199. package/dist/ui/theme/light-daltonized.json +48 -0
  200. package/dist/ui/theme/light.json +44 -23
  201. package/dist/ui/theme/theme.d.ts +27 -1
  202. package/dist/ui/theme/theme.d.ts.map +1 -1
  203. package/dist/ui/theme/theme.js +24 -1
  204. package/dist/ui/theme/theme.js.map +1 -1
  205. package/dist/ui/utils/highlight.d.ts +1 -1
  206. package/dist/ui/utils/highlight.d.ts.map +1 -1
  207. package/dist/ui/utils/highlight.js +24 -4
  208. package/dist/ui/utils/highlight.js.map +1 -1
  209. package/dist/ui/utils/hyperlink.d.ts +11 -0
  210. package/dist/ui/utils/hyperlink.d.ts.map +1 -0
  211. package/dist/ui/utils/hyperlink.js +27 -0
  212. package/dist/ui/utils/hyperlink.js.map +1 -0
  213. package/dist/ui/utils/markdown-cache.d.ts +5 -6
  214. package/dist/ui/utils/markdown-cache.d.ts.map +1 -1
  215. package/dist/ui/utils/markdown-cache.js +5 -5
  216. package/dist/ui/utils/markdown-cache.js.map +1 -1
  217. package/dist/ui/utils/signal.d.ts +21 -0
  218. package/dist/ui/utils/signal.d.ts.map +1 -0
  219. package/dist/ui/utils/signal.js +30 -0
  220. package/dist/ui/utils/signal.js.map +1 -0
  221. package/dist/ui/utils/supports-hyperlinks.d.ts +6 -0
  222. package/dist/ui/utils/supports-hyperlinks.d.ts.map +1 -0
  223. package/dist/ui/utils/supports-hyperlinks.js +52 -0
  224. package/dist/ui/utils/supports-hyperlinks.js.map +1 -0
  225. package/dist/ui/utils/token-to-ansi.d.ts +9 -0
  226. package/dist/ui/utils/token-to-ansi.d.ts.map +1 -0
  227. package/dist/ui/utils/token-to-ansi.js +305 -0
  228. package/dist/ui/utils/token-to-ansi.js.map +1 -0
  229. package/dist/utils/session-title.d.ts +19 -0
  230. package/dist/utils/session-title.d.ts.map +1 -0
  231. package/dist/utils/session-title.js +82 -0
  232. package/dist/utils/session-title.js.map +1 -0
  233. package/package.json +7 -5
@@ -1,11 +1,11 @@
1
- import { execSync, spawnSync } from "node:child_process";
1
+ import { spawn } from "node:child_process";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import os from "node:os";
5
5
  const PACKAGE_NAME = "@prestyj/cli";
6
6
  const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
7
- const CHECK_INTERVAL_MS = 4 * 60 * 60 * 1000; // 4 hours
8
- const FETCH_TIMEOUT_MS = 3000;
7
+ const CHECK_INTERVAL_MS = 1 * 60 * 60 * 1000; // 1 hour
8
+ const FETCH_TIMEOUT_MS = 10_000; // 10s — npm can be slow
9
9
  var PackageManager;
10
10
  (function (PackageManager) {
11
11
  PackageManager["NPM"] = "npm";
@@ -35,12 +35,6 @@ function writeState(state) {
35
35
  // Non-fatal
36
36
  }
37
37
  }
38
- function shouldCheck() {
39
- const state = readState();
40
- if (!state)
41
- return true;
42
- return Date.now() - state.lastCheckedAt > CHECK_INTERVAL_MS;
43
- }
44
38
  function compareVersions(a, b) {
45
39
  const pa = a.split(".").map(Number);
46
40
  const pb = b.split(".").map(Number);
@@ -77,76 +71,152 @@ function detectInstallInfo() {
77
71
  updateCommand: `npm install -g ${PACKAGE_NAME}@latest`,
78
72
  };
79
73
  }
80
- function fetchLatestVersionSync() {
81
- // Use a child process to fetch from npm registry with timeout
82
- // We use node -e to avoid needing fetch in the parent process synchronously
74
+ /**
75
+ * Fetch latest version from npm registry asynchronously.
76
+ */
77
+ async function fetchLatestVersion() {
83
78
  try {
84
- const script = `
85
- const c = new AbortController();
86
- const t = setTimeout(() => c.abort(), ${FETCH_TIMEOUT_MS});
87
- fetch("${REGISTRY_URL}", { signal: c.signal })
88
- .then(r => r.json())
89
- .then(d => { clearTimeout(t); process.stdout.write(d.version || ""); })
90
- .catch(() => { clearTimeout(t); process.exit(1); });
91
- `;
92
- const result = spawnSync(process.execPath, ["--input-type=module", "-e", script], {
93
- encoding: "utf-8",
94
- timeout: FETCH_TIMEOUT_MS + 1000,
95
- stdio: ["pipe", "pipe", "pipe"],
96
- });
97
- const version = result.stdout?.trim();
79
+ const controller = new AbortController();
80
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
81
+ const response = await fetch(REGISTRY_URL, { signal: controller.signal });
82
+ clearTimeout(timeout);
83
+ const data = (await response.json());
84
+ const version = data.version?.trim();
98
85
  return version && /^\d+\.\d+\.\d+/.test(version) ? version : null;
99
86
  }
100
87
  catch {
101
88
  return null;
102
89
  }
103
90
  }
104
- function performUpdate(command) {
91
+ /**
92
+ * Perform the update in a detached background process so it doesn't block
93
+ * or interfere with the current CLI session. The update takes effect on
94
+ * the next launch.
95
+ */
96
+ function performUpdateInBackground(command) {
105
97
  try {
106
- execSync(command, {
107
- stdio: "pipe",
108
- timeout: 60_000,
98
+ const parts = command.split(" ");
99
+ const child = spawn(parts[0], parts.slice(1), {
100
+ detached: true,
101
+ stdio: "ignore",
109
102
  env: { ...process.env, npm_config_loglevel: "silent" },
110
103
  });
111
- return true;
104
+ child.unref();
112
105
  }
113
106
  catch {
114
- return false;
107
+ // Non-fatal — will retry next startup
115
108
  }
116
109
  }
117
110
  /**
118
- * Check for updates and silently auto-update if a newer version is available.
119
- * Called at CLI startup. Non-blocking on failure — the CLI always proceeds.
111
+ * Check for updates at CLI startup. Two-phase approach:
112
+ *
113
+ * Phase 1 (instant): If a previous background check found a newer version,
114
+ * kick off a background update now. Non-blocking.
120
115
  *
121
- * Returns a message to display if an update happened, or null.
116
+ * Phase 2 (async): Fire off a background version check for the *next* startup.
117
+ *
118
+ * Returns a message to display, or null.
122
119
  */
123
120
  export function checkAndAutoUpdate(currentVersion) {
124
121
  try {
125
- if (!shouldCheck())
126
- return null;
127
- const latestVersion = fetchLatestVersionSync();
128
- // Always record that we checked
129
- writeState({
130
- lastCheckedAt: Date.now(),
131
- lastSeenVersion: latestVersion ?? undefined,
132
- });
133
- if (!latestVersion)
134
- return null;
135
- if (compareVersions(latestVersion, currentVersion) <= 0)
136
- return null;
137
- const info = detectInstallInfo();
138
- if (!info.updateCommand)
139
- return null;
140
- const success = performUpdate(info.updateCommand);
141
- if (success) {
142
- return `Updated ${PACKAGE_NAME} ${currentVersion} \u2192 ${latestVersion}`;
122
+ const state = readState();
123
+ let message = null;
124
+ // Phase 1: Apply pending update from previous check
125
+ if (state?.updatePending && state.latestVersion) {
126
+ if (compareVersions(state.latestVersion, currentVersion) > 0) {
127
+ const info = detectInstallInfo();
128
+ if (info.updateCommand) {
129
+ // Run update in background — takes effect next launch
130
+ performUpdateInBackground(info.updateCommand);
131
+ message = `Updating ${PACKAGE_NAME} ${currentVersion} → ${state.latestVersion} (takes effect next launch)`;
132
+ writeState({
133
+ ...state,
134
+ lastCheckedAt: Date.now(),
135
+ updatePending: false,
136
+ lastUpdateAttempt: Date.now(),
137
+ });
138
+ }
139
+ }
140
+ else {
141
+ // Already on latest (user may have updated manually)
142
+ writeState({ ...state, updatePending: false });
143
+ }
143
144
  }
144
- // Update failed show manual instructions
145
- return `Update available: ${currentVersion} \u2192 ${latestVersion}\nRun: ${info.updateCommand}`;
145
+ // Phase 2: Schedule background check for next startup
146
+ const shouldCheck = !state || Date.now() - state.lastCheckedAt > CHECK_INTERVAL_MS;
147
+ if (shouldCheck) {
148
+ scheduleBackgroundCheck(currentVersion);
149
+ }
150
+ return message;
146
151
  }
147
152
  catch {
148
- // Never block CLI startup
149
153
  return null;
150
154
  }
151
155
  }
156
+ /**
157
+ * Fire-and-forget async version check. Updates the state file so the
158
+ * next startup knows whether an update is available.
159
+ */
160
+ function scheduleBackgroundCheck(currentVersion) {
161
+ fetchLatestVersion()
162
+ .then((latestVersion) => {
163
+ const newState = {
164
+ lastCheckedAt: Date.now(),
165
+ latestVersion: latestVersion ?? undefined,
166
+ updatePending: false,
167
+ };
168
+ if (latestVersion && compareVersions(latestVersion, currentVersion) > 0) {
169
+ newState.updatePending = true;
170
+ }
171
+ writeState(newState);
172
+ })
173
+ .catch(() => {
174
+ // Non-fatal — will retry next time
175
+ });
176
+ }
177
+ // ── In-session periodic check ──────────────────────────────────────────
178
+ let periodicTimer = null;
179
+ /**
180
+ * Start periodic update checks during a long-running session.
181
+ * Checks every hour. If an update is found, calls `onUpdate` with a message.
182
+ */
183
+ export function startPeriodicUpdateCheck(currentVersion, onUpdate) {
184
+ if (periodicTimer)
185
+ return; // Already running
186
+ periodicTimer = setInterval(() => {
187
+ fetchLatestVersion()
188
+ .then((latestVersion) => {
189
+ if (!latestVersion)
190
+ return;
191
+ if (compareVersions(latestVersion, currentVersion) <= 0)
192
+ return;
193
+ const info = detectInstallInfo();
194
+ if (!info.updateCommand)
195
+ return;
196
+ // Mark pending for next startup
197
+ writeState({
198
+ lastCheckedAt: Date.now(),
199
+ latestVersion,
200
+ updatePending: true,
201
+ });
202
+ onUpdate(`Update available: ${currentVersion} → ${latestVersion} (will update on next launch, or run: ${info.updateCommand})`);
203
+ // Stop checking once we've notified
204
+ stopPeriodicUpdateCheck();
205
+ })
206
+ .catch(() => {
207
+ // Non-fatal
208
+ });
209
+ }, CHECK_INTERVAL_MS);
210
+ // Don't keep the process alive just for update checks
211
+ periodicTimer.unref();
212
+ }
213
+ /**
214
+ * Stop periodic update checks.
215
+ */
216
+ export function stopPeriodicUpdateCheck() {
217
+ if (periodicTimer) {
218
+ clearInterval(periodicTimer);
219
+ periodicTimer = null;
220
+ }
221
+ }
152
222
  //# sourceMappingURL=auto-update.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auto-update.js","sourceRoot":"","sources":["../../src/core/auto-update.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auto-update.js","sourceRoot":"","sources":["../../src/core/auto-update.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=auto-update.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-update.test.d.ts","sourceRoot":"","sources":["../../src/core/auto-update.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,205 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { spawn } from "node:child_process";
6
+ import { checkAndAutoUpdate, startPeriodicUpdateCheck, stopPeriodicUpdateCheck, } from "./auto-update.js";
7
+ // Use a temp directory for state file instead of the real ~/.gg
8
+ const tmpDir = path.join(os.tmpdir(), `gg-update-test-${process.pid}`);
9
+ // Mock the state file path
10
+ vi.mock("node:os", async () => {
11
+ const actual = await vi.importActual("node:os");
12
+ return {
13
+ ...actual,
14
+ default: {
15
+ ...actual,
16
+ homedir: () => tmpDir,
17
+ },
18
+ };
19
+ });
20
+ // Mock spawn so we never actually run install commands
21
+ vi.mock("node:child_process", () => ({
22
+ spawn: vi.fn(() => ({ unref: vi.fn() })),
23
+ }));
24
+ function writeStateFile(state) {
25
+ fs.mkdirSync(path.join(tmpDir, ".ezcoder"), { recursive: true });
26
+ fs.writeFileSync(path.join(tmpDir, ".ezcoder", "update-state.json"), JSON.stringify(state));
27
+ }
28
+ function readStateFile() {
29
+ try {
30
+ return JSON.parse(fs.readFileSync(path.join(tmpDir, ".ezcoder", "update-state.json"), "utf-8"));
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ beforeEach(() => {
37
+ fs.mkdirSync(path.join(tmpDir, ".ezcoder"), { recursive: true });
38
+ // Clear any existing state
39
+ try {
40
+ fs.unlinkSync(path.join(tmpDir, ".ezcoder", "update-state.json"));
41
+ }
42
+ catch {
43
+ // fine
44
+ }
45
+ vi.restoreAllMocks();
46
+ });
47
+ afterEach(() => {
48
+ stopPeriodicUpdateCheck();
49
+ vi.restoreAllMocks();
50
+ try {
51
+ fs.rmSync(tmpDir, { recursive: true, force: true });
52
+ }
53
+ catch {
54
+ // fine
55
+ }
56
+ });
57
+ describe("checkAndAutoUpdate", () => {
58
+ it("returns null on first run with no state file", () => {
59
+ // Mock fetch to return a version (but the background check is async, so
60
+ // on first call it just schedules a check and returns null)
61
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
62
+ json: () => Promise.resolve({ version: "99.0.0" }),
63
+ }));
64
+ const result = checkAndAutoUpdate("1.0.0");
65
+ expect(result).toBeNull();
66
+ });
67
+ it("returns null when already on latest version", () => {
68
+ writeStateFile({
69
+ lastCheckedAt: Date.now(),
70
+ latestVersion: "1.0.0",
71
+ updatePending: true,
72
+ });
73
+ const result = checkAndAutoUpdate("1.0.0");
74
+ expect(result).toBeNull();
75
+ // Should clear the pending flag
76
+ const state = readStateFile();
77
+ expect(state?.updatePending).toBe(false);
78
+ });
79
+ it("returns null when on a newer version than registry (manual install)", () => {
80
+ writeStateFile({
81
+ lastCheckedAt: Date.now(),
82
+ latestVersion: "1.0.0",
83
+ updatePending: true,
84
+ });
85
+ const result = checkAndAutoUpdate("2.0.0");
86
+ expect(result).toBeNull();
87
+ const state = readStateFile();
88
+ expect(state?.updatePending).toBe(false);
89
+ });
90
+ it("triggers background update when pending update exists", () => {
91
+ writeStateFile({
92
+ lastCheckedAt: Date.now(),
93
+ latestVersion: "2.0.0",
94
+ updatePending: true,
95
+ });
96
+ const result = checkAndAutoUpdate("1.0.0");
97
+ expect(result).toContain("Updating");
98
+ expect(result).toContain("1.0.0");
99
+ expect(result).toContain("2.0.0");
100
+ expect(vi.mocked(spawn)).toHaveBeenCalled();
101
+ // Should clear the pending flag
102
+ const state = readStateFile();
103
+ expect(state?.updatePending).toBe(false);
104
+ expect(state?.lastUpdateAttempt).toBeDefined();
105
+ });
106
+ it("schedules background check when last check was long ago", async () => {
107
+ const fetchMock = vi.fn().mockResolvedValue({
108
+ json: () => Promise.resolve({ version: "5.0.0" }),
109
+ });
110
+ vi.stubGlobal("fetch", fetchMock);
111
+ writeStateFile({
112
+ lastCheckedAt: 0, // very old
113
+ latestVersion: "1.0.0",
114
+ updatePending: false,
115
+ });
116
+ checkAndAutoUpdate("1.0.0");
117
+ // Give the async check time to complete
118
+ await vi.waitFor(() => {
119
+ expect(fetchMock).toHaveBeenCalled();
120
+ });
121
+ // Wait for the state to be written
122
+ await vi.waitFor(() => {
123
+ const state = readStateFile();
124
+ expect(state?.latestVersion).toBe("5.0.0");
125
+ expect(state?.updatePending).toBe(true);
126
+ });
127
+ });
128
+ it("does not schedule check when recently checked", () => {
129
+ const fetchMock = vi.fn().mockResolvedValue({
130
+ json: () => Promise.resolve({ version: "5.0.0" }),
131
+ });
132
+ vi.stubGlobal("fetch", fetchMock);
133
+ writeStateFile({
134
+ lastCheckedAt: Date.now(), // just now
135
+ latestVersion: "1.0.0",
136
+ updatePending: false,
137
+ });
138
+ checkAndAutoUpdate("1.0.0");
139
+ // fetch should NOT be called since we just checked
140
+ expect(fetchMock).not.toHaveBeenCalled();
141
+ });
142
+ it("handles fetch failure gracefully", async () => {
143
+ const fetchMock = vi.fn().mockRejectedValue(new Error("network error"));
144
+ vi.stubGlobal("fetch", fetchMock);
145
+ writeStateFile({
146
+ lastCheckedAt: 0,
147
+ updatePending: false,
148
+ });
149
+ // Should not throw
150
+ const result = checkAndAutoUpdate("1.0.0");
151
+ expect(result).toBeNull();
152
+ });
153
+ it("handles corrupt state file gracefully", () => {
154
+ fs.writeFileSync(path.join(tmpDir, ".ezcoder", "update-state.json"), "not json{{{");
155
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
156
+ json: () => Promise.resolve({ version: "2.0.0" }),
157
+ }));
158
+ // Should not throw — treats corrupt file like no file
159
+ const result = checkAndAutoUpdate("1.0.0");
160
+ expect(result).toBeNull();
161
+ });
162
+ });
163
+ describe("startPeriodicUpdateCheck", () => {
164
+ it("calls onUpdate when a newer version is found", async () => {
165
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
166
+ json: () => Promise.resolve({ version: "99.0.0" }),
167
+ }));
168
+ const onUpdate = vi.fn();
169
+ vi.useFakeTimers();
170
+ startPeriodicUpdateCheck("1.0.0", onUpdate);
171
+ // Advance past one check interval
172
+ await vi.advanceTimersByTimeAsync(1 * 60 * 60 * 1000 + 100);
173
+ expect(onUpdate).toHaveBeenCalledWith(expect.stringContaining("99.0.0"));
174
+ vi.useRealTimers();
175
+ });
176
+ it("does not call onUpdate when already on latest", async () => {
177
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
178
+ json: () => Promise.resolve({ version: "1.0.0" }),
179
+ }));
180
+ const onUpdate = vi.fn();
181
+ vi.useFakeTimers();
182
+ startPeriodicUpdateCheck("1.0.0", onUpdate);
183
+ await vi.advanceTimersByTimeAsync(1 * 60 * 60 * 1000 + 100);
184
+ expect(onUpdate).not.toHaveBeenCalled();
185
+ vi.useRealTimers();
186
+ });
187
+ it("stops checking after first notification", async () => {
188
+ const fetchMock = vi.fn().mockResolvedValue({
189
+ json: () => Promise.resolve({ version: "99.0.0" }),
190
+ });
191
+ vi.stubGlobal("fetch", fetchMock);
192
+ const onUpdate = vi.fn();
193
+ vi.useFakeTimers();
194
+ startPeriodicUpdateCheck("1.0.0", onUpdate);
195
+ // First interval
196
+ await vi.advanceTimersByTimeAsync(1 * 60 * 60 * 1000 + 100);
197
+ expect(onUpdate).toHaveBeenCalledTimes(1);
198
+ // Second interval — should NOT fire again (timer stopped)
199
+ fetchMock.mockClear();
200
+ await vi.advanceTimersByTimeAsync(1 * 60 * 60 * 1000 + 100);
201
+ expect(fetchMock).not.toHaveBeenCalled();
202
+ vi.useRealTimers();
203
+ });
204
+ });
205
+ //# sourceMappingURL=auto-update.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-update.test.js","sourceRoot":"","sources":["../../src/core/auto-update.test.ts"],"names":[],"mappings":"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"}
@@ -1,16 +1,33 @@
1
1
  import { type Message, type Provider, type ContentPart } from "@prestyj/ai";
2
2
  export interface CompactionResult {
3
+ /** Whether messages were actually reduced. */
4
+ compacted: boolean;
5
+ /** Why compaction was skipped (only set when compacted is false). */
6
+ reason?: string;
3
7
  originalCount: number;
4
8
  newCount: number;
5
9
  tokensBeforeEstimate: number;
6
10
  tokensAfterEstimate: number;
7
11
  }
12
+ /**
13
+ * Default token reserve for compaction.
14
+ * Leaves headroom for the model's next response + system overhead.
15
+ * Matches the widely-used Pi / Grok-CLI default of 16 384 tokens.
16
+ */
17
+ export declare const COMPACTION_RESERVE_TOKENS = 16384;
8
18
  /**
9
19
  * Check if compaction should be triggered.
20
+ *
21
+ * Uses the reserve-based approach (contextWindow − reserveTokens) used by
22
+ * Pi, Grok-CLI, OpenClaw, BrowserOS, and most real-world agent frameworks.
23
+ * A percentage-based threshold is still supported: when both are supplied the
24
+ * more conservative (lower) limit wins.
10
25
  */
11
26
  export declare function shouldCompact(messages: Message[], contextWindow: number, threshold?: number,
12
27
  /** Actual API-reported token count — preferred over char-based estimate when available. */
13
- actualTokens?: number): boolean;
28
+ actualTokens?: number,
29
+ /** Fixed token reserve subtracted from contextWindow. Defaults to 16 384. */
30
+ reserveTokens?: number): boolean;
14
31
  /**
15
32
  * Find the index where recent messages should start, given a token budget.
16
33
  * Walks backward from the end, accumulating token estimates, and returns the
@@ -1 +1 @@
1
- {"version":3,"file":"compactor.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/compactor.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,WAAW,EAEjB,MAAM,aAAa,CAAC;AAwCrB,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,OAAO,EAAE,EACnB,aAAa,EAAE,MAAM,EACrB,SAAS,SAAM;AACf,2FAA2F;AAC3F,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAMT;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CA4CnF;AAiED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CA6CpE;AAqID;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,EAAE,CAkBtF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,cAAc,EAAE,OAAO,EAAE,EACzB,OAAO,EAAE;IAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,GACpD,MAAM,CAgCR;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,GAAG,MAAM,CAM1E;AAKD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,OAAO,CAC3B,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE;IACP,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GACA,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,CAAC,CAiO5D"}
1
+ {"version":3,"file":"compactor.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/compactor.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,WAAW,EAEjB,MAAM,aAAa,CAAC;AAwCrB,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,SAAS,EAAE,OAAO,CAAC;IACnB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAKhD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,OAAO,EAAE,EACnB,aAAa,EAAE,MAAM,EACrB,SAAS,SAAM;AACf,2FAA2F;AAC3F,YAAY,CAAC,EAAE,MAAM;AACrB,6EAA6E;AAC7E,aAAa,SAA4B,GACxC,OAAO,CAoBT;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CA4CnF;AAiED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CA6CpE;AAqID;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,EAAE,CAkBtF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,cAAc,EAAE,OAAO,EAAE,EACzB,OAAO,EAAE;IAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,GACpD,MAAM,CAgCR;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,GAAG,MAAM,CAM1E;AAKD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,OAAO,CAC3B,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE;IACP,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GACA,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,CAAC,CAiP5D"}
@@ -30,14 +30,42 @@ const COMPACTION_SYSTEM_PROMPT = "You are a conversation compaction assistant. Y
30
30
  "Focus on technical precision. Include specific identifiers (file paths, function names, etc.) " +
31
31
  "that would be essential for continuation. Write in third person and maintain an objective, technical tone.";
32
32
  const COMPACTION_USER_PROMPT = "Summarize the conversation above into a concise summary following the instructions. Output only the summary, nothing else.";
33
+ /**
34
+ * Default token reserve for compaction.
35
+ * Leaves headroom for the model's next response + system overhead.
36
+ * Matches the widely-used Pi / Grok-CLI default of 16 384 tokens.
37
+ */
38
+ export const COMPACTION_RESERVE_TOKENS = 16_384;
39
+ /** Minimum messages before compaction is attempted (Mysti uses 4). */
40
+ const COMPACTION_MIN_MESSAGES = 4;
33
41
  /**
34
42
  * Check if compaction should be triggered.
43
+ *
44
+ * Uses the reserve-based approach (contextWindow − reserveTokens) used by
45
+ * Pi, Grok-CLI, OpenClaw, BrowserOS, and most real-world agent frameworks.
46
+ * A percentage-based threshold is still supported: when both are supplied the
47
+ * more conservative (lower) limit wins.
35
48
  */
36
49
  export function shouldCompact(messages, contextWindow, threshold = 0.8,
37
50
  /** Actual API-reported token count — preferred over char-based estimate when available. */
38
- actualTokens) {
51
+ actualTokens,
52
+ /** Fixed token reserve subtracted from contextWindow. Defaults to 16 384. */
53
+ reserveTokens = COMPACTION_RESERVE_TOKENS) {
54
+ // Don't attempt compaction with too few messages — compact() would bail
55
+ // anyway (middleMessages <= 2), but this avoids the spinner + LLM auth dance.
56
+ // Skip the guard when actualTokens is provided (force-compact / overflow paths
57
+ // where the caller has precise token info regardless of message count).
58
+ if (actualTokens == null && messages.length < COMPACTION_MIN_MESSAGES) {
59
+ log("INFO", "compaction", `Context check: skipping — only ${messages.length} messages`);
60
+ return false;
61
+ }
39
62
  const estimated = actualTokens ?? estimateConversationTokens(messages);
40
- const limit = contextWindow * threshold;
63
+ const percentageLimit = contextWindow * threshold;
64
+ // Only apply the fixed reserve when the context window is large enough
65
+ // that the reserve is meaningful (< 50% of the window).
66
+ const limit = reserveTokens > 0 && reserveTokens < contextWindow * 0.5
67
+ ? Math.min(percentageLimit, contextWindow - reserveTokens)
68
+ : percentageLimit;
41
69
  const source = actualTokens != null ? "actual" : "estimated";
42
70
  log("INFO", "compaction", `Context check: ${estimated} ${source} tokens, threshold ${limit}`);
43
71
  return estimated > limit;
@@ -426,6 +454,8 @@ export async function compact(messages, options) {
426
454
  return {
427
455
  messages: [...messages],
428
456
  result: {
457
+ compacted: false,
458
+ reason: "too_few_messages",
429
459
  originalCount,
430
460
  newCount: messages.length,
431
461
  tokensBeforeEstimate,
@@ -543,13 +573,21 @@ export async function compact(messages, options) {
543
573
  role: "user",
544
574
  content: `[Previous conversation summary]\n\n${summaryText}${fileTrackingSection}`,
545
575
  };
576
+ // Skip the assistant ack when recentMessages starts with an assistant message
577
+ // to prevent consecutive assistant messages that the Anthropic API rejects.
578
+ // This happens when findRecentCutPoint backs up from a tool to an assistant.
579
+ const skipAck = recentMessages.length > 0 && recentMessages[0].role === "assistant";
546
580
  const newMessages = [
547
581
  systemMessage,
548
582
  summaryMessage,
549
- {
550
- role: "assistant",
551
- content: "Understood — I have the context from what was discussed earlier.",
552
- },
583
+ ...(skipAck
584
+ ? []
585
+ : [
586
+ {
587
+ role: "assistant",
588
+ content: "Understood — I have the context from what was discussed earlier.",
589
+ },
590
+ ]),
553
591
  ...recentMessages,
554
592
  ];
555
593
  // Repair tool_use / tool_result pairing in the final message array.
@@ -562,7 +600,9 @@ export async function compact(messages, options) {
562
600
  // But never pop below 3 messages (system + summary + ack) — removing the
563
601
  // compaction ack would leave only the summary, causing `ezcoder continue`
564
602
  // to restore just 1 message instead of the full session.
565
- while (newMessages.length > 3 && newMessages[newMessages.length - 1].role === "assistant") {
603
+ const minMessages = skipAck ? 2 : 3;
604
+ while (newMessages.length > minMessages &&
605
+ newMessages[newMessages.length - 1].role === "assistant") {
566
606
  newMessages.pop();
567
607
  }
568
608
  const tokensAfterEstimate = estimateConversationTokens(newMessages);
@@ -581,6 +621,7 @@ export async function compact(messages, options) {
581
621
  return {
582
622
  messages: newMessages,
583
623
  result: {
624
+ compacted: true,
584
625
  originalCount,
585
626
  newCount: newMessages.length,
586
627
  tokensBeforeEstimate,