@prestyj/cli 4.10.3 → 4.12.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 (178) hide show
  1. package/README.md +4 -4
  2. package/dist/app-sidecar.d.ts +2 -0
  3. package/dist/app-sidecar.d.ts.map +1 -0
  4. package/dist/app-sidecar.js +1720 -0
  5. package/dist/app-sidecar.js.map +1 -0
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +10 -25
  8. package/dist/cli.js.map +1 -1
  9. package/dist/config.d.ts +5 -0
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +3 -0
  12. package/dist/config.js.map +1 -1
  13. package/dist/core/agent-session.d.ts +162 -0
  14. package/dist/core/agent-session.d.ts.map +1 -1
  15. package/dist/core/agent-session.js +544 -23
  16. package/dist/core/agent-session.js.map +1 -1
  17. package/dist/core/api-benchmark.d.ts +64 -0
  18. package/dist/core/api-benchmark.d.ts.map +1 -0
  19. package/dist/core/api-benchmark.js +381 -0
  20. package/dist/core/api-benchmark.js.map +1 -0
  21. package/dist/core/auth-providers.d.ts +18 -0
  22. package/dist/core/auth-providers.d.ts.map +1 -0
  23. package/dist/core/auth-providers.js +71 -0
  24. package/dist/core/auth-providers.js.map +1 -0
  25. package/dist/core/event-bus.d.ts +6 -0
  26. package/dist/core/event-bus.d.ts.map +1 -1
  27. package/dist/core/event-bus.js +1 -0
  28. package/dist/core/event-bus.js.map +1 -1
  29. package/dist/core/mcp/client.d.ts +21 -0
  30. package/dist/core/mcp/client.d.ts.map +1 -1
  31. package/dist/core/mcp/client.js +167 -5
  32. package/dist/core/mcp/client.js.map +1 -1
  33. package/dist/core/mcp/index.d.ts +3 -1
  34. package/dist/core/mcp/index.d.ts.map +1 -1
  35. package/dist/core/mcp/index.js +2 -0
  36. package/dist/core/mcp/index.js.map +1 -1
  37. package/dist/core/mcp/oauth-provider.d.ts +51 -0
  38. package/dist/core/mcp/oauth-provider.d.ts.map +1 -0
  39. package/dist/core/mcp/oauth-provider.js +95 -0
  40. package/dist/core/mcp/oauth-provider.js.map +1 -0
  41. package/dist/core/mcp/oauth-store.d.ts +39 -0
  42. package/dist/core/mcp/oauth-store.d.ts.map +1 -0
  43. package/dist/core/mcp/oauth-store.js +63 -0
  44. package/dist/core/mcp/oauth-store.js.map +1 -0
  45. package/dist/core/mcp/oauth-store.test.d.ts +2 -0
  46. package/dist/core/mcp/oauth-store.test.d.ts.map +1 -0
  47. package/dist/core/mcp/oauth-store.test.js +94 -0
  48. package/dist/core/mcp/oauth-store.test.js.map +1 -0
  49. package/dist/core/process-manager.d.ts.map +1 -1
  50. package/dist/core/process-manager.js +5 -1
  51. package/dist/core/process-manager.js.map +1 -1
  52. package/dist/core/project-discovery.d.ts +41 -0
  53. package/dist/core/project-discovery.d.ts.map +1 -0
  54. package/dist/core/project-discovery.js +441 -0
  55. package/dist/core/project-discovery.js.map +1 -0
  56. package/dist/core/prompt-commands.d.ts.map +1 -1
  57. package/dist/core/prompt-commands.js +203 -0
  58. package/dist/core/prompt-commands.js.map +1 -1
  59. package/dist/core/prompt-commands.test.js +39 -0
  60. package/dist/core/prompt-commands.test.js.map +1 -1
  61. package/dist/core/radio.d.ts +44 -0
  62. package/dist/core/radio.d.ts.map +1 -0
  63. package/dist/core/radio.js +250 -0
  64. package/dist/core/radio.js.map +1 -0
  65. package/dist/core/resolve-start.d.ts +29 -0
  66. package/dist/core/resolve-start.d.ts.map +1 -0
  67. package/dist/core/resolve-start.js +35 -0
  68. package/dist/core/resolve-start.js.map +1 -0
  69. package/dist/core/resolve-start.test.d.ts +2 -0
  70. package/dist/core/resolve-start.test.d.ts.map +1 -0
  71. package/dist/core/resolve-start.test.js +66 -0
  72. package/dist/core/resolve-start.test.js.map +1 -0
  73. package/dist/core/settings-manager.d.ts +5 -1
  74. package/dist/core/settings-manager.d.ts.map +1 -1
  75. package/dist/core/settings-manager.js +5 -0
  76. package/dist/core/settings-manager.js.map +1 -1
  77. package/dist/core/shell-path.d.ts +12 -0
  78. package/dist/core/shell-path.d.ts.map +1 -0
  79. package/dist/core/shell-path.js +166 -0
  80. package/dist/core/shell-path.js.map +1 -0
  81. package/dist/core/shell.d.ts +51 -0
  82. package/dist/core/shell.d.ts.map +1 -0
  83. package/dist/core/shell.js +82 -0
  84. package/dist/core/shell.js.map +1 -0
  85. package/dist/core/shell.test.d.ts +2 -0
  86. package/dist/core/shell.test.d.ts.map +1 -0
  87. package/dist/core/shell.test.js +87 -0
  88. package/dist/core/shell.test.js.map +1 -0
  89. package/dist/core/slash-commands.d.ts.map +1 -1
  90. package/dist/core/slash-commands.js +10 -0
  91. package/dist/core/slash-commands.js.map +1 -1
  92. package/dist/core/speed-benchmark.d.ts +133 -0
  93. package/dist/core/speed-benchmark.d.ts.map +1 -0
  94. package/dist/core/speed-benchmark.js +410 -0
  95. package/dist/core/speed-benchmark.js.map +1 -0
  96. package/dist/core/speed-benchmark.test.d.ts +2 -0
  97. package/dist/core/speed-benchmark.test.d.ts.map +1 -0
  98. package/dist/core/speed-benchmark.test.js +97 -0
  99. package/dist/core/speed-benchmark.test.js.map +1 -0
  100. package/dist/core/telegram-config.d.ts +21 -0
  101. package/dist/core/telegram-config.d.ts.map +1 -0
  102. package/dist/core/telegram-config.js +52 -0
  103. package/dist/core/telegram-config.js.map +1 -0
  104. package/dist/index.d.ts +1 -0
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js +2 -0
  107. package/dist/index.js.map +1 -1
  108. package/dist/interactive.d.ts.map +1 -1
  109. package/dist/interactive.js +4 -3
  110. package/dist/interactive.js.map +1 -1
  111. package/dist/modes/serve-mode.d.ts +24 -0
  112. package/dist/modes/serve-mode.d.ts.map +1 -1
  113. package/dist/modes/serve-mode.js +54 -19
  114. package/dist/modes/serve-mode.js.map +1 -1
  115. package/dist/tools/bash.d.ts.map +1 -1
  116. package/dist/tools/bash.js +17 -1
  117. package/dist/tools/bash.js.map +1 -1
  118. package/dist/tools/generate-image.d.ts +39 -0
  119. package/dist/tools/generate-image.d.ts.map +1 -0
  120. package/dist/tools/generate-image.js +301 -0
  121. package/dist/tools/generate-image.js.map +1 -0
  122. package/dist/tools/generate-image.test.d.ts +2 -0
  123. package/dist/tools/generate-image.test.d.ts.map +1 -0
  124. package/dist/tools/generate-image.test.js +223 -0
  125. package/dist/tools/generate-image.test.js.map +1 -0
  126. package/dist/tools/goal-mode.test.js +1 -1
  127. package/dist/tools/goal-mode.test.js.map +1 -1
  128. package/dist/tools/goals.d.ts +7 -7
  129. package/dist/tools/index.d.ts +15 -1
  130. package/dist/tools/index.d.ts.map +1 -1
  131. package/dist/tools/index.js +17 -2
  132. package/dist/tools/index.js.map +1 -1
  133. package/dist/tools/plan-mode.test.js +5 -5
  134. package/dist/tools/plan-mode.test.js.map +1 -1
  135. package/dist/tools/prompt-hints.d.ts.map +1 -1
  136. package/dist/tools/prompt-hints.js +2 -0
  137. package/dist/tools/prompt-hints.js.map +1 -1
  138. package/dist/tools/safe-env.d.ts.map +1 -1
  139. package/dist/tools/safe-env.js +27 -0
  140. package/dist/tools/safe-env.js.map +1 -1
  141. package/dist/tools/subagent.d.ts +2 -4
  142. package/dist/tools/subagent.d.ts.map +1 -1
  143. package/dist/tools/subagent.js +24 -23
  144. package/dist/tools/subagent.js.map +1 -1
  145. package/dist/tools/tasks.d.ts +1 -7
  146. package/dist/tools/tasks.d.ts.map +1 -1
  147. package/dist/tools/tasks.js +4 -31
  148. package/dist/tools/tasks.js.map +1 -1
  149. package/dist/ui/App.d.ts +1 -1
  150. package/dist/ui/App.d.ts.map +1 -1
  151. package/dist/ui/hooks/usePixelFixFlow.d.ts +1 -1
  152. package/dist/ui/hooks/usePixelFixFlow.d.ts.map +1 -1
  153. package/dist/ui/hooks/usePixelFixFlow.js +1 -1
  154. package/dist/ui/hooks/usePixelFixFlow.js.map +1 -1
  155. package/dist/ui/hooks/useTranscriptHistory.d.ts +2 -0
  156. package/dist/ui/hooks/useTranscriptHistory.d.ts.map +1 -1
  157. package/dist/ui/hooks/useTranscriptHistory.js +3 -2
  158. package/dist/ui/hooks/useTranscriptHistory.js.map +1 -1
  159. package/dist/ui/long-prompt-regression-harness.test.js +8 -8
  160. package/dist/ui/render.d.ts +1 -1
  161. package/dist/ui/render.d.ts.map +1 -1
  162. package/dist/ui/render.js +60 -8
  163. package/dist/ui/render.js.map +1 -1
  164. package/dist/ui/startup-banner-order.test.js +1 -1
  165. package/dist/ui/startup-banner-order.test.js.map +1 -1
  166. package/dist/ui/terminal-history-repaint.test.d.ts +2 -0
  167. package/dist/ui/terminal-history-repaint.test.d.ts.map +1 -0
  168. package/dist/ui/terminal-history-repaint.test.js +73 -0
  169. package/dist/ui/terminal-history-repaint.test.js.map +1 -0
  170. package/dist/ui/terminal-history.d.ts +1 -0
  171. package/dist/ui/terminal-history.d.ts.map +1 -1
  172. package/dist/ui/terminal-history.js +31 -2
  173. package/dist/ui/terminal-history.js.map +1 -1
  174. package/dist/utils/git.d.ts +6 -0
  175. package/dist/utils/git.d.ts.map +1 -1
  176. package/dist/utils/git.js +12 -0
  177. package/dist/utils/git.js.map +1 -1
  178. package/package.json +6 -6
@@ -0,0 +1,410 @@
1
+ /**
2
+ * Speed benchmark harness — measures the latency impact of speedProfile
3
+ * optimizations (1-h cache TTL + pre-warming) against the current baseline
4
+ * (5-min cache TTL, no pre-warm).
5
+ *
6
+ * Uses a mock streaming provider that simulates realistic provider timing:
7
+ * - Cold prefill (cache miss): proportional to uncached input tokens
8
+ * - Warm prefill (cache hit): 10× faster (cache reads cost 0.1×)
9
+ * - Output streaming: at a configurable token rate
10
+ * - Cache TTL: configurable (5 min baseline vs 1 h optimized)
11
+ *
12
+ * No real API calls are made — the mock uses real `setTimeout` for prefill and
13
+ * output-token latency (so wall-clock measurements are meaningful) but a
14
+ * virtual clock for inter-turn delays (so multi-minute gaps are instant).
15
+ *
16
+ * Run via vitest: npx vitest run src/core/speed-benchmark.test.ts
17
+ * Or as a script: npx tsx src/core/speed-benchmark.ts
18
+ */
19
+ import { StreamResult, } from "@prestyj/ai";
20
+ import { z } from "zod";
21
+ export const REALISTIC_TIMING = {
22
+ coldPrefillMsPerToken: 0.15, // 10k tokens = 1.5s prefill
23
+ warmPrefillMsPerToken: 0.015, // 10k tokens cached = 0.15s prefill (10× faster)
24
+ outputMsPerToken: 12, // ~83 tok/s output
25
+ cacheTtlMs: 5 * 60 * 1000, // 5 min (baseline)
26
+ networkOverheadMs: 200, // 200ms fixed network overhead
27
+ defaultOutputTokens: 150,
28
+ };
29
+ // ── Mock Provider Implementation ────────────────────────────
30
+ /**
31
+ * A mock streaming provider that simulates LLM timing with cache semantics.
32
+ * Registered as provider "benchmark-mock" so the real agent loop code path
33
+ * is exercised end-to-end.
34
+ */
35
+ export class MockBenchmarkProvider {
36
+ cache = new Map();
37
+ config;
38
+ /** Virtual clock — lets the benchmark simulate multi-minute gaps between
39
+ * turns without actually sleeping. The cache TTL check uses this, not
40
+ * Date.now(). Only prefill/output latency uses real setTimeout. */
41
+ virtualNow = 0;
42
+ stats = {
43
+ cacheHits: 0,
44
+ cacheMisses: 0,
45
+ cacheWrites: 0,
46
+ cacheEvictions: 0,
47
+ totalPrefillMs: 0,
48
+ totalOutputMs: 0,
49
+ totalNetworkMs: 0,
50
+ turns: 0,
51
+ };
52
+ constructor(config = {}) {
53
+ this.config = { ...REALISTIC_TIMING, ...config };
54
+ }
55
+ /** Update config (e.g., switch TTL for baseline vs optimized run). */
56
+ setConfig(config) {
57
+ this.config = { ...this.config, ...config };
58
+ }
59
+ /** Advance the virtual clock (for cache TTL simulation without real sleeping). */
60
+ advanceClock(ms) {
61
+ this.virtualNow += ms;
62
+ }
63
+ /** Clear all cache state and stats (between benchmark runs). */
64
+ reset() {
65
+ this.cache.clear();
66
+ this.virtualNow = 0;
67
+ this.stats.cacheHits = 0;
68
+ this.stats.cacheMisses = 0;
69
+ this.stats.cacheWrites = 0;
70
+ this.stats.cacheEvictions = 0;
71
+ this.stats.totalPrefillMs = 0;
72
+ this.stats.totalOutputMs = 0;
73
+ this.stats.totalNetworkMs = 0;
74
+ this.stats.turns = 0;
75
+ }
76
+ /** Number of entries currently in the mock cache. */
77
+ getCacheSize() {
78
+ return this.cache.size;
79
+ }
80
+ /** Force a cache write (simulates pre-warming). */
81
+ prewarm(cacheKey, tokenCount) {
82
+ this.cache.set(cacheKey, { createdAt: this.virtualNow, tokenCount });
83
+ this.stats.cacheWrites++;
84
+ }
85
+ stream(options) {
86
+ const generator = this.runStream(options);
87
+ return new StreamResult(generator, options.signal);
88
+ }
89
+ async *runStream(options) {
90
+ const cfg = this.config;
91
+ this.stats.turns++;
92
+ // Estimate input tokens from message content (rough: 4 chars per token).
93
+ let inputChars = 0;
94
+ for (const msg of options.messages) {
95
+ if (typeof msg.content === "string") {
96
+ inputChars += msg.content.length;
97
+ }
98
+ else if (Array.isArray(msg.content)) {
99
+ for (const part of msg.content) {
100
+ if ("text" in part && typeof part.text === "string")
101
+ inputChars += part.text.length;
102
+ if ("content" in part && typeof part.content === "string")
103
+ inputChars += part.content.length;
104
+ }
105
+ }
106
+ }
107
+ // Add tool schema overhead (~500 tokens for a typical tool set).
108
+ if (options.tools?.length) {
109
+ inputChars += options.tools.length * 2000;
110
+ }
111
+ const inputTokens = Math.ceil(inputChars / 4);
112
+ // Compute cache key — hash the system prompt + tools (the stable prefix).
113
+ const cacheKey = this.computeCacheKey(options);
114
+ // Check cache state (using virtual clock, not real time).
115
+ const now = this.virtualNow;
116
+ const cached = this.cache.get(cacheKey);
117
+ let isCacheHit = false;
118
+ let prefillTokens = inputTokens;
119
+ if (cached && now - cached.createdAt < cfg.cacheTtlMs) {
120
+ // Cache hit — only the diff needs prefill.
121
+ isCacheHit = true;
122
+ prefillTokens = Math.max(0, inputTokens - cached.tokenCount);
123
+ this.stats.cacheHits++;
124
+ }
125
+ else {
126
+ // Cache miss or expired.
127
+ if (cached && now - cached.createdAt >= cfg.cacheTtlMs) {
128
+ this.stats.cacheEvictions++;
129
+ }
130
+ this.stats.cacheMisses++;
131
+ }
132
+ // Simulate prefill latency.
133
+ const prefillRate = isCacheHit ? cfg.warmPrefillMsPerToken : cfg.coldPrefillMsPerToken;
134
+ const prefillMs = prefillTokens * prefillRate;
135
+ this.stats.totalPrefillMs += prefillMs;
136
+ // Simulate network overhead.
137
+ this.stats.totalNetworkMs += cfg.networkOverheadMs;
138
+ // Wait for TTFT (network + prefill).
139
+ const ttft = cfg.networkOverheadMs + prefillMs;
140
+ await sleep(ttft);
141
+ // Update cache after the request is processed.
142
+ // On a miss: write a fresh entry. On a hit: refresh the TTL and update
143
+ // the token count (mirrors Anthropic — reading from cache extends its TTL
144
+ // and the prefix grows as new messages accumulate).
145
+ this.cache.set(cacheKey, { createdAt: this.virtualNow, tokenCount: inputTokens });
146
+ if (!isCacheHit) {
147
+ this.stats.cacheWrites++;
148
+ }
149
+ // Determine output tokens.
150
+ const outputTokens = cfg.defaultOutputTokens;
151
+ // Stream output tokens.
152
+ const outputMs = outputTokens * cfg.outputMsPerToken;
153
+ this.stats.totalOutputMs += outputMs;
154
+ let textAccum = "";
155
+ for (let i = 0; i < outputTokens; i++) {
156
+ await sleep(cfg.outputMsPerToken);
157
+ const chunk = "x";
158
+ textAccum += chunk;
159
+ yield { type: "text_delta", text: chunk };
160
+ }
161
+ yield { type: "done", stopReason: "end_turn" };
162
+ const response = {
163
+ message: { role: "assistant", content: textAccum },
164
+ stopReason: "end_turn",
165
+ usage: {
166
+ inputTokens: isCacheHit ? prefillTokens : inputTokens,
167
+ outputTokens,
168
+ ...(isCacheHit ? { cacheRead: inputTokens - prefillTokens } : {}),
169
+ ...(!isCacheHit ? { cacheWrite: inputTokens } : {}),
170
+ },
171
+ };
172
+ return response;
173
+ }
174
+ /** Compute a stable cache key from the system prompt + tool names. */
175
+ computeCacheKey(options) {
176
+ const systemMsg = options.messages.find((m) => m.role === "system");
177
+ const systemText = typeof systemMsg?.content === "string" ? systemMsg.content : "";
178
+ const toolNames = (options.tools ?? []).map((t) => t.name).join(",");
179
+ return `${systemText.length}:${toolNames}`;
180
+ }
181
+ }
182
+ // ── Benchmark Runner ────────────────────────────────────────
183
+ /** Run a workload against the mock provider and collect per-turn metrics. */
184
+ export async function runBenchmark(workload, config, options = {}) {
185
+ const provider = new MockBenchmarkProvider(config);
186
+ // Build mock tools.
187
+ const tools = workload.toolNames.map((name) => ({
188
+ name,
189
+ description: `Mock tool: ${name}`,
190
+ parameters: z.object({}),
191
+ }));
192
+ // Build messages.
193
+ const messages = [{ role: "system", content: workload.systemPrompt }];
194
+ // Pre-warm if requested.
195
+ if (options.prewarm) {
196
+ const cacheKey = `${workload.systemPrompt.length}:${workload.toolNames.join(",")}`;
197
+ const inputChars = workload.systemPrompt.length + workload.toolNames.length * 2000;
198
+ provider.prewarm(cacheKey, Math.ceil(inputChars / 4));
199
+ }
200
+ const turnMetrics = [];
201
+ for (let i = 0; i < workload.turns.length; i++) {
202
+ const turn = workload.turns[i];
203
+ // Simulate user delay before this turn — advance the virtual clock
204
+ // (not real sleep) so multi-minute gaps are instant.
205
+ if (turn.delayMs && turn.delayMs > 0) {
206
+ provider.advanceClock(turn.delayMs);
207
+ }
208
+ if (turn.outputTokens) {
209
+ provider.setConfig({ defaultOutputTokens: turn.outputTokens });
210
+ }
211
+ // Add user message.
212
+ messages.push({ role: "user", content: turn.prompt });
213
+ const turnStart = Date.now();
214
+ // Measure TTFT.
215
+ let ttftMs = 0;
216
+ let firstEvent = true;
217
+ const streamOptions = {
218
+ provider: "benchmark-mock",
219
+ model: "mock-model",
220
+ messages: [...messages],
221
+ tools,
222
+ maxTokens: 4096,
223
+ };
224
+ const result = provider.stream(streamOptions);
225
+ for await (const _event of result) {
226
+ if (firstEvent) {
227
+ ttftMs = Date.now() - turnStart;
228
+ firstEvent = false;
229
+ }
230
+ }
231
+ const response = await result.response;
232
+ const cacheHit = (response.usage.cacheRead ?? 0) > 0;
233
+ const inputTokens = response.usage.inputTokens + (response.usage.cacheRead ?? 0);
234
+ const outputTokens = response.usage.outputTokens;
235
+ const wallClockMs = Date.now() - turnStart;
236
+ turnMetrics.push({
237
+ turnNumber: i + 1,
238
+ promptPreview: turn.prompt.slice(0, 50),
239
+ delayBeforeTurnMs: turn.delayMs ?? 0,
240
+ ttftMs,
241
+ cacheHit,
242
+ inputTokens,
243
+ outputTokens,
244
+ wallClockMs,
245
+ });
246
+ // Add assistant message for next turn.
247
+ messages.push({
248
+ role: "assistant",
249
+ content: typeof response.message.content === "string" ? response.message.content : "",
250
+ });
251
+ }
252
+ const cacheHits = turnMetrics.filter((t) => t.cacheHit).length;
253
+ const cacheMisses = turnMetrics.length - cacheHits;
254
+ return {
255
+ name: options.name ?? workload.name,
256
+ config,
257
+ prewarmed: !!options.prewarm,
258
+ turns: turnMetrics,
259
+ totalWallClockMs: turnMetrics.reduce((sum, t) => sum + t.wallClockMs, 0),
260
+ totalTtftMs: turnMetrics.reduce((sum, t) => sum + t.ttftMs, 0),
261
+ cacheHits,
262
+ cacheMisses,
263
+ cacheHitRate: turnMetrics.length > 0 ? cacheHits / turnMetrics.length : 0,
264
+ };
265
+ }
266
+ export function compareResults(baseline, optimized) {
267
+ const wallClockImprovement = baseline.totalWallClockMs > 0
268
+ ? ((baseline.totalWallClockMs - optimized.totalWallClockMs) / baseline.totalWallClockMs) * 100
269
+ : 0;
270
+ const ttftImprovement = baseline.totalTtftMs > 0
271
+ ? ((baseline.totalTtftMs - optimized.totalTtftMs) / baseline.totalTtftMs) * 100
272
+ : 0;
273
+ const cacheHitRateImprovement = (optimized.cacheHitRate - baseline.cacheHitRate) * 100;
274
+ return {
275
+ baseline,
276
+ optimized,
277
+ wallClockImprovement,
278
+ ttftImprovement,
279
+ cacheHitRateImprovement,
280
+ };
281
+ }
282
+ /** Format a benchmark result as a readable table. */
283
+ export function formatResultTable(result) {
284
+ const lines = [];
285
+ lines.push(`┌─ ${result.name} ${"─".repeat(Math.max(0, 52 - result.name.length))}┐`);
286
+ lines.push(`│ Cache TTL: ${formatDuration(result.config.cacheTtlMs)} | Prewarmed: ${result.prewarmed ? "YES" : "no"}${" ".repeat(18)}│`);
287
+ lines.push("│ Turn │ Delay │ TTFT │ Cache │ Wall │ Input tok │");
288
+ lines.push("│──────┼──────────┼──────────┼───────┼──────────┼───────────│");
289
+ for (const turn of result.turns) {
290
+ lines.push(`│ ${String(turn.turnNumber).padStart(4)} │ ${formatDuration(turn.delayBeforeTurnMs).padStart(8)} │ ${formatDuration(turn.ttftMs).padStart(8)} │ ${turn.cacheHit ? " HIT " : "MISS "} │ ${formatDuration(turn.wallClockMs).padStart(8)} │ ${String(turn.inputTokens).padStart(9)} │`);
291
+ }
292
+ lines.push("└──────┴──────────┴──────────┴───────┴──────────┴───────────┘");
293
+ lines.push(`│ Total: ${formatDuration(result.totalWallClockMs).padStart(8)} | TTFT: ${formatDuration(result.totalTtftMs).padStart(8)} | Hit rate: ${(result.cacheHitRate * 100).toFixed(0)}%${" ".repeat(6)}│`);
294
+ return lines.join("\n");
295
+ }
296
+ /** Format a side-by-side comparison. */
297
+ export function formatComparison(comparison) {
298
+ const lines = [];
299
+ lines.push("");
300
+ lines.push("╔══════════════════════════════════════════════════════════════╗");
301
+ lines.push("║ SPEED BENCHMARK: BASELINE vs OPTIMIZED ║");
302
+ lines.push("╠══════════════════════════════════════════════════════════════╣");
303
+ lines.push("");
304
+ lines.push(formatResultTable(comparison.baseline));
305
+ lines.push("");
306
+ lines.push(formatResultTable(comparison.optimized));
307
+ lines.push("");
308
+ lines.push("┌─────────────────────────┬──────────────┬──────────────┬───────────┐");
309
+ lines.push("│ Metric │ Baseline │ Optimized │ Delta │");
310
+ lines.push("├─────────────────────────┼──────────────┼──────────────┼───────────┤");
311
+ lines.push(`│ Total wall-clock │ ${formatDuration(comparison.baseline.totalWallClockMs).padStart(11)} │ ${formatDuration(comparison.optimized.totalWallClockMs).padStart(11)} │ ${comparison.wallClockImprovement >= 0 ? "-" : "+"}${Math.abs(comparison.wallClockImprovement).toFixed(1).padStart(5)}% │`);
312
+ lines.push(`│ Total TTFT │ ${formatDuration(comparison.baseline.totalTtftMs).padStart(11)} │ ${formatDuration(comparison.optimized.totalTtftMs).padStart(11)} │ ${comparison.ttftImprovement >= 0 ? "-" : "+"}${Math.abs(comparison.ttftImprovement).toFixed(1).padStart(5)}% │`);
313
+ lines.push(`│ Cache hit rate │ ${(comparison.baseline.cacheHitRate * 100).toFixed(0).padStart(10)}% │ ${(comparison.optimized.cacheHitRate * 100).toFixed(0).padStart(11)}% │ +${comparison.cacheHitRateImprovement.toFixed(0).padStart(4)}pp │`);
314
+ lines.push(`│ Cache hits / misses │ ${String(comparison.baseline.cacheHits).padStart(5)} / ${String(comparison.baseline.cacheMisses).padStart(3)} │ ${String(comparison.optimized.cacheHits).padStart(11)} / ${String(comparison.optimized.cacheMisses).padStart(3)} │ │`);
315
+ lines.push("└─────────────────────────┴──────────────┴──────────────┴───────────┘");
316
+ lines.push("");
317
+ return lines.join("\n");
318
+ }
319
+ // ── Default Workload ────────────────────────────────────────
320
+ /** A realistic multi-turn coding workload with time gaps that expose the
321
+ * 5-min vs 1-h TTL difference. */
322
+ export function createDefaultWorkload() {
323
+ const systemPrompt = [
324
+ "You are EZ Coder — a coding agent that works directly in the user's codebase.",
325
+ "You explore, understand, change, and verify code.",
326
+ "",
327
+ "## Tools",
328
+ "- read: Read file contents",
329
+ "- write: Write file contents",
330
+ "- edit: Replace text in a file",
331
+ "- bash: Execute shell commands",
332
+ "- grep: Search file contents",
333
+ "- find: Find files matching a pattern",
334
+ "",
335
+ "## Environment",
336
+ `- Working directory: /home/user/project`,
337
+ `- Platform: linux`,
338
+ ].join("\n");
339
+ return {
340
+ name: "Multi-turn coding session",
341
+ systemPrompt,
342
+ toolNames: ["read", "write", "edit", "bash", "grep", "find", "ls", "web_fetch", "subagent"],
343
+ turns: [
344
+ {
345
+ prompt: "Can you look at the auth module and tell me how it works?",
346
+ delayMs: 0, // First turn — cold cache
347
+ outputTokens: 200,
348
+ },
349
+ {
350
+ prompt: "Great, now add a rate limiter to the login endpoint.",
351
+ delayMs: 3 * 60 * 1000, // 3 min — within both TTLs
352
+ outputTokens: 150,
353
+ },
354
+ {
355
+ prompt: "Also add logging for failed login attempts.",
356
+ delayMs: 4 * 60 * 1000, // 4 min — within both TTLs
357
+ outputTokens: 120,
358
+ },
359
+ {
360
+ prompt: "Now update the tests to cover the new rate limiter.",
361
+ delayMs: 7 * 60 * 1000, // 7 min — BEYOND 5min TTL, within 1h
362
+ outputTokens: 180,
363
+ },
364
+ {
365
+ prompt: "Run the test suite to make sure everything passes.",
366
+ delayMs: 2 * 60 * 1000, // 2 min — within both TTLs
367
+ outputTokens: 100,
368
+ },
369
+ ],
370
+ };
371
+ }
372
+ // ── Helpers ─────────────────────────────────────────────────
373
+ function sleep(ms) {
374
+ if (ms <= 0)
375
+ return Promise.resolve();
376
+ return new Promise((resolve) => setTimeout(resolve, ms));
377
+ }
378
+ function formatDuration(ms) {
379
+ if (ms < 1000)
380
+ return `${Math.round(ms)}ms`;
381
+ if (ms < 60_000)
382
+ return `${(ms / 1000).toFixed(1)}s`;
383
+ const min = Math.floor(ms / 60_000);
384
+ const sec = Math.round((ms % 60_000) / 1000);
385
+ return `${min}m${sec}s`;
386
+ }
387
+ // ── Full Benchmark Runner ───────────────────────────────────
388
+ /** Run baseline vs optimized and return the comparison. Uses scaled-down
389
+ * timing (10× faster than real) so the benchmark completes in seconds. */
390
+ export async function runFullBenchmark() {
391
+ const workload = createDefaultWorkload();
392
+ // Scale timing down 10× for fast test runs — ratios are preserved.
393
+ const scale = 0.1;
394
+ const baseConfig = {
395
+ coldPrefillMsPerToken: 0.15 * scale,
396
+ warmPrefillMsPerToken: 0.015 * scale,
397
+ outputMsPerToken: 12 * scale,
398
+ networkOverheadMs: 200 * scale,
399
+ defaultOutputTokens: 150,
400
+ cacheTtlMs: 0, // set per-run below
401
+ };
402
+ // Baseline: 5-min TTL, no pre-warm.
403
+ const baseline = await runBenchmark(workload, { ...baseConfig, cacheTtlMs: 5 * 60 * 1000 }, {
404
+ name: "BASELINE (5min TTL, no prewarm)",
405
+ });
406
+ // Optimized: 1-h TTL, with pre-warm.
407
+ const optimized = await runBenchmark(workload, { ...baseConfig, cacheTtlMs: 60 * 60 * 1000 }, { name: "OPTIMIZED (1h TTL + prewarm)", prewarm: true });
408
+ return compareResults(baseline, optimized);
409
+ }
410
+ //# sourceMappingURL=speed-benchmark.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speed-benchmark.js","sourceRoot":"","sources":["../../src/core/speed-benchmark.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=speed-benchmark.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speed-benchmark.test.d.ts","sourceRoot":"","sources":["../../src/core/speed-benchmark.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,97 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { runFullBenchmark, runBenchmark, createDefaultWorkload, formatComparison, MockBenchmarkProvider, REALISTIC_TIMING, } from "./speed-benchmark.js";
3
+ describe("Speed Benchmark", () => {
4
+ it("optimized should be faster than baseline on multi-turn workload", async () => {
5
+ const comparison = await runFullBenchmark();
6
+ // Print the full comparison table for visibility.
7
+ const report = formatComparison(comparison);
8
+ console.log(report);
9
+ // The optimized run should have a higher cache hit rate — the 7-min gap
10
+ // turn (turn 4) misses the 5-min cache but hits the 1-h cache.
11
+ expect(comparison.optimized.cacheHitRate).toBeGreaterThan(comparison.baseline.cacheHitRate);
12
+ // The optimized run should have lower total TTFT.
13
+ expect(comparison.optimized.totalTtftMs).toBeLessThan(comparison.baseline.totalTtftMs);
14
+ // The optimized run should have lower total wall-clock.
15
+ expect(comparison.optimized.totalWallClockMs).toBeLessThan(comparison.baseline.totalWallClockMs);
16
+ // TTFT improvement should be meaningful (> 5%).
17
+ expect(comparison.ttftImprovement).toBeGreaterThan(5);
18
+ }, 30_000);
19
+ it("baseline misses cache on turn after 5-min gap", async () => {
20
+ const workload = createDefaultWorkload();
21
+ const baselineConfig = {
22
+ ...REALISTIC_TIMING,
23
+ coldPrefillMsPerToken: 0.015, // scaled for speed
24
+ warmPrefillMsPerToken: 0.0015,
25
+ outputMsPerToken: 1.2,
26
+ networkOverheadMs: 20,
27
+ cacheTtlMs: 5 * 60 * 1000, // 5 min
28
+ };
29
+ const result = await runBenchmark(workload, baselineConfig, {
30
+ name: "5min TTL",
31
+ });
32
+ // Turn 4 has a 7-min delay — should be a cache miss with 5-min TTL.
33
+ const turn4 = result.turns[3];
34
+ expect(turn4).toBeDefined();
35
+ expect(turn4.cacheHit).toBe(false);
36
+ // Turns 2 and 3 (3-min and 4-min delays) should be cache hits.
37
+ expect(result.turns[1].cacheHit).toBe(true);
38
+ expect(result.turns[2].cacheHit).toBe(true);
39
+ });
40
+ it("optimized hits cache on turn after 5-min gap (1-h TTL)", async () => {
41
+ const workload = createDefaultWorkload();
42
+ const optimizedConfig = {
43
+ ...REALISTIC_TIMING,
44
+ coldPrefillMsPerToken: 0.015,
45
+ warmPrefillMsPerToken: 0.0015,
46
+ outputMsPerToken: 1.2,
47
+ networkOverheadMs: 20,
48
+ cacheTtlMs: 60 * 60 * 1000, // 1 hour
49
+ };
50
+ const result = await runBenchmark(workload, optimizedConfig, {
51
+ name: "1h TTL",
52
+ prewarm: true,
53
+ });
54
+ // Turn 4 has a 7-min delay — should be a cache HIT with 1-h TTL.
55
+ const turn4 = result.turns[3];
56
+ expect(turn4).toBeDefined();
57
+ expect(turn4.cacheHit).toBe(true);
58
+ // Turn 1 should be a cache hit too (pre-warmed).
59
+ expect(result.turns[0].cacheHit).toBe(true);
60
+ });
61
+ it("pre-warming makes first turn a cache hit", async () => {
62
+ const workload = createDefaultWorkload();
63
+ const config = {
64
+ ...REALISTIC_TIMING,
65
+ coldPrefillMsPerToken: 0.015,
66
+ warmPrefillMsPerToken: 0.0015,
67
+ outputMsPerToken: 1.2,
68
+ networkOverheadMs: 20,
69
+ cacheTtlMs: 60 * 60 * 1000,
70
+ };
71
+ // Without pre-warm.
72
+ const cold = await runBenchmark(workload, config, { name: "No prewarm" });
73
+ expect(cold.turns[0].cacheHit).toBe(false);
74
+ // With pre-warm.
75
+ const warm = await runBenchmark(workload, config, {
76
+ name: "With prewarm",
77
+ prewarm: true,
78
+ });
79
+ expect(warm.turns[0].cacheHit).toBe(true);
80
+ // First-turn TTFT should be lower with pre-warm.
81
+ expect(warm.turns[0].ttftMs).toBeLessThan(cold.turns[0].ttftMs);
82
+ });
83
+ it("mock provider tracks stats correctly", () => {
84
+ const provider = new MockBenchmarkProvider({
85
+ ...REALISTIC_TIMING,
86
+ cacheTtlMs: 60 * 60 * 1000,
87
+ });
88
+ // Pre-warm.
89
+ provider.prewarm("test-key", 1000);
90
+ expect(provider.stats.cacheWrites).toBe(1);
91
+ // Reset.
92
+ provider.reset();
93
+ expect(provider.stats.cacheWrites).toBe(0);
94
+ expect(provider.getCacheSize()).toBe(0);
95
+ });
96
+ });
97
+ //# sourceMappingURL=speed-benchmark.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speed-benchmark.test.js","sourceRoot":"","sources":["../../src/core/speed-benchmark.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ export interface TelegramConfig {
2
+ botToken: string;
3
+ userId: number;
4
+ }
5
+ /** Read the saved Telegram config, or null when unset/incomplete. */
6
+ export declare function loadTelegramConfig(): Promise<TelegramConfig | null>;
7
+ /** Persist the Telegram config with owner-only permissions (0600). */
8
+ export declare function saveTelegramConfig(config: TelegramConfig): Promise<void>;
9
+ /** Rough bot-token shape check (`123456789:ABCdef...`). */
10
+ export declare function isValidBotTokenFormat(token: string): boolean;
11
+ export interface BotVerification {
12
+ ok: boolean;
13
+ username?: string;
14
+ firstName?: string;
15
+ }
16
+ /**
17
+ * Verify a bot token against Telegram's getMe. Returns `{ ok: false }` when the
18
+ * token is malformed or Telegram rejects it (never throws on a bad token).
19
+ */
20
+ export declare function verifyBotToken(botToken: string): Promise<BotVerification>;
21
+ //# sourceMappingURL=telegram-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram-config.d.ts","sourceRoot":"","sources":["../../src/core/telegram-config.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qEAAqE;AACrE,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CASzE;AAED,sEAAsE;AACtE,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAM9E;AAED,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAa/E"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Shared Telegram bot configuration: the `~/.ezcoder/telegram.json` store (bot token
3
+ * + authorized user id) plus bot-token verification. Used by the CLI
4
+ * (`ezcoder telegram` / `ezcoder serve`) and the desktop app sidecar so both
5
+ * read/write the same file with the same validation.
6
+ */
7
+ import fs from "node:fs/promises";
8
+ import { ensureAppDirs, getAppPaths } from "../config.js";
9
+ /** Read the saved Telegram config, or null when unset/incomplete. */
10
+ export async function loadTelegramConfig() {
11
+ try {
12
+ const raw = await fs.readFile(getAppPaths().telegramFile, "utf-8");
13
+ const data = JSON.parse(raw);
14
+ if (data.botToken && data.userId)
15
+ return data;
16
+ return null;
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ /** Persist the Telegram config with owner-only permissions (0600). */
23
+ export async function saveTelegramConfig(config) {
24
+ const paths = await ensureAppDirs();
25
+ await fs.writeFile(paths.telegramFile, JSON.stringify(config, null, 2), {
26
+ encoding: "utf-8",
27
+ mode: 0o600,
28
+ });
29
+ }
30
+ /** Rough bot-token shape check (`123456789:ABCdef...`). */
31
+ export function isValidBotTokenFormat(token) {
32
+ return /^\d+:[A-Za-z0-9_-]+$/.test(token);
33
+ }
34
+ /**
35
+ * Verify a bot token against Telegram's getMe. Returns `{ ok: false }` when the
36
+ * token is malformed or Telegram rejects it (never throws on a bad token).
37
+ */
38
+ export async function verifyBotToken(botToken) {
39
+ if (!isValidBotTokenFormat(botToken))
40
+ return { ok: false };
41
+ try {
42
+ const res = await fetch(`https://api.telegram.org/bot${botToken}/getMe`, { method: "POST" });
43
+ const data = (await res.json());
44
+ if (!data.ok || !data.result)
45
+ return { ok: false };
46
+ return { ok: true, username: data.result.username, firstName: data.result.first_name };
47
+ }
48
+ catch {
49
+ return { ok: false };
50
+ }
51
+ }
52
+ //# sourceMappingURL=telegram-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram-config.js","sourceRoot":"","sources":["../../src/core/telegram-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAO1D,qEAAqE;AACrE,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;QAC/C,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAC9C,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,sEAAsE;AACtE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAsB;IAC7D,MAAM,KAAK,GAAG,MAAM,aAAa,EAAE,CAAC;IACpC,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;QACtE,QAAQ,EAAE,OAAO;QACjB,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;AACL,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,qBAAqB,CAAC,KAAa;IACjD,OAAO,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC5C,CAAC;AAQD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAAgB;IACnD,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,+BAA+B,QAAQ,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7F,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAG7B,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;QACnD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;IACzF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;IACvB,CAAC;AACH,CAAC"}
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export { EventBus, AgentSession, SessionManager, SettingsManager, AuthStorage, S
5
5
  export { runPrintMode } from "./modes/index.js";
6
6
  export { renderApp } from "./ui/render.js";
7
7
  export { APP_NAME, VERSION, getAppPaths, ensureAppDirs } from "./config.js";
8
+ export { discoverProjects, listRecentSessions, type DiscoveredProject, type ProjectSource, type RecentSession, } from "./core/project-discovery.js";
8
9
  export type { CliConfig, SessionHeader as LegacySessionHeader, SessionMessageEntry, SessionEntry as LegacySessionEntry, SessionInfo as LegacySessionInfo, } from "./types.js";
9
10
  export type { AgentSessionOptions, AgentSessionState, BusEventMap, ContextWindowOptions, ModelInfo, Settings, SlashCommand, SlashCommandContext, Skill, Extension, ExtensionContext, CompactionResult, SessionEntry, SessionInfo, SessionHeader, } from "./core/index.js";
10
11
  //# sourceMappingURL=index.d.ts.map