@harperfast/agent 0.16.5 → 0.16.7
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.
- package/dist/{agent.js → chunk-LSVGOLBU.js} +515 -3411
- package/dist/cli.js +2919 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.js +6 -0
- package/package.json +11 -8
- /package/dist/{agent.d.ts → cli.d.ts} +0 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
1
|
// ink/emitters/listener.ts
|
|
4
2
|
import React, { useCallback, useEffect } from "react";
|
|
5
3
|
var listenersMap = {};
|
|
4
|
+
var emittedMap = {};
|
|
6
5
|
function useListener(name, listener, deps) {
|
|
7
6
|
const listenerRef = React.useRef(listener);
|
|
8
7
|
React.useEffect(() => {
|
|
9
8
|
listenerRef.current = listener;
|
|
10
9
|
}, [listener]);
|
|
11
|
-
const callback = useCallback((newValue, trigger) =>
|
|
10
|
+
const callback = useCallback((newValue, trigger) => {
|
|
11
|
+
return listenerRef.current(newValue, trigger);
|
|
12
|
+
}, [deps]);
|
|
12
13
|
useEffect(() => {
|
|
13
14
|
if (!listenersMap[name]) {
|
|
14
15
|
listenersMap[name] = [];
|
|
@@ -38,6 +39,7 @@ async function onceListener(name) {
|
|
|
38
39
|
});
|
|
39
40
|
}
|
|
40
41
|
function emitToListeners(name, value, trigger) {
|
|
42
|
+
emittedMap[name] = value;
|
|
41
43
|
const listeners = listenersMap[name];
|
|
42
44
|
if (listeners) {
|
|
43
45
|
const stableCopyOfListeners = listeners.slice();
|
|
@@ -46,6 +48,9 @@ function emitToListeners(name, value, trigger) {
|
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
}
|
|
51
|
+
function lastEmittedValue(name) {
|
|
52
|
+
return emittedMap[name];
|
|
53
|
+
}
|
|
49
54
|
function addListener(name, callback) {
|
|
50
55
|
if (!listenersMap[name]) {
|
|
51
56
|
listenersMap[name] = [];
|
|
@@ -62,151 +67,6 @@ function curryEmitToListeners(name, value, trigger) {
|
|
|
62
67
|
return (e) => emitToListeners(name, value, trigger ?? e);
|
|
63
68
|
}
|
|
64
69
|
|
|
65
|
-
// utils/sessions/rateLimits.ts
|
|
66
|
-
var RateLimitTracker = class {
|
|
67
|
-
status = {
|
|
68
|
-
limitRequests: null,
|
|
69
|
-
limitTokens: null,
|
|
70
|
-
remainingRequests: null,
|
|
71
|
-
remainingTokens: null,
|
|
72
|
-
resetRequests: null,
|
|
73
|
-
resetTokens: null,
|
|
74
|
-
retryAfter: null
|
|
75
|
-
};
|
|
76
|
-
updateFromHeaders(headers) {
|
|
77
|
-
const normalizedHeaders = {};
|
|
78
|
-
for (const key of Object.keys(headers)) {
|
|
79
|
-
normalizedHeaders[key.toLowerCase()] = headers[key];
|
|
80
|
-
}
|
|
81
|
-
const getHeader = (name) => {
|
|
82
|
-
const value = normalizedHeaders[name.toLowerCase()];
|
|
83
|
-
return Array.isArray(value) ? value[0] : value;
|
|
84
|
-
};
|
|
85
|
-
const limitRequests = getHeader("x-ratelimit-limit-requests") || getHeader("anthropic-ratelimit-requests-limit") || getHeader("x-ratelimit-limit") || getHeader("ratelimit-limit") || getHeader("x-request-limit");
|
|
86
|
-
const limitTokens = getHeader("x-ratelimit-limit-tokens") || getHeader("anthropic-ratelimit-tokens-limit") || getHeader("x-token-limit");
|
|
87
|
-
const remainingRequests = getHeader("x-ratelimit-remaining-requests") || getHeader("anthropic-ratelimit-requests-remaining") || getHeader("x-ratelimit-remaining") || getHeader("ratelimit-remaining") || getHeader("x-request-remaining");
|
|
88
|
-
const remainingTokens = getHeader("x-ratelimit-remaining-tokens") || getHeader("anthropic-ratelimit-tokens-remaining") || getHeader("x-token-remaining");
|
|
89
|
-
const resetRequests = getHeader("x-ratelimit-reset-requests") || getHeader("anthropic-ratelimit-requests-reset") || getHeader("x-ratelimit-reset") || getHeader("ratelimit-reset") || getHeader("x-request-reset");
|
|
90
|
-
const resetTokens = getHeader("x-ratelimit-reset-tokens") || getHeader("anthropic-ratelimit-tokens-reset") || getHeader("x-token-reset");
|
|
91
|
-
const retryAfter = getHeader("retry-after");
|
|
92
|
-
if (limitRequests) {
|
|
93
|
-
this.status.limitRequests = parseInt(limitRequests, 10);
|
|
94
|
-
}
|
|
95
|
-
if (limitTokens) {
|
|
96
|
-
this.status.limitTokens = parseInt(limitTokens, 10);
|
|
97
|
-
}
|
|
98
|
-
if (remainingRequests) {
|
|
99
|
-
this.status.remainingRequests = parseInt(remainingRequests, 10);
|
|
100
|
-
}
|
|
101
|
-
if (remainingTokens) {
|
|
102
|
-
this.status.remainingTokens = parseInt(remainingTokens, 10);
|
|
103
|
-
}
|
|
104
|
-
if (resetRequests) {
|
|
105
|
-
this.status.resetRequests = resetRequests;
|
|
106
|
-
}
|
|
107
|
-
if (resetTokens) {
|
|
108
|
-
this.status.resetTokens = resetTokens;
|
|
109
|
-
}
|
|
110
|
-
if (retryAfter) {
|
|
111
|
-
this.status.retryAfter = parseInt(retryAfter, 10);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
isApproachingLimit(threshold) {
|
|
115
|
-
const usage = this.getUsagePercentage();
|
|
116
|
-
return {
|
|
117
|
-
requests: usage.requests >= threshold,
|
|
118
|
-
tokens: usage.tokens >= threshold
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
getStatus() {
|
|
122
|
-
return { ...this.status };
|
|
123
|
-
}
|
|
124
|
-
getUsagePercentage() {
|
|
125
|
-
const requests = this.status.limitRequests && this.status.remainingRequests !== null ? 100 * (1 - this.status.remainingRequests / this.status.limitRequests) : 0;
|
|
126
|
-
const tokens = this.status.limitTokens && this.status.remainingTokens !== null ? 100 * (1 - this.status.remainingTokens / this.status.limitTokens) : 0;
|
|
127
|
-
return { requests, tokens };
|
|
128
|
-
}
|
|
129
|
-
reset() {
|
|
130
|
-
this.status = {
|
|
131
|
-
limitRequests: null,
|
|
132
|
-
limitTokens: null,
|
|
133
|
-
remainingRequests: null,
|
|
134
|
-
remainingTokens: null,
|
|
135
|
-
resetRequests: null,
|
|
136
|
-
resetTokens: null,
|
|
137
|
-
retryAfter: null
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
var rateLimitTracker = new RateLimitTracker();
|
|
142
|
-
|
|
143
|
-
// lifecycle/patchFetch.ts
|
|
144
|
-
var originalFetch = globalThis.fetch;
|
|
145
|
-
if (originalFetch) {
|
|
146
|
-
globalThis.fetch = async (...args) => {
|
|
147
|
-
const response = await originalFetch(...args);
|
|
148
|
-
const headers = {};
|
|
149
|
-
response.headers.forEach((value, key) => {
|
|
150
|
-
headers[key] = value;
|
|
151
|
-
});
|
|
152
|
-
const commonHeaders = [
|
|
153
|
-
"x-ratelimit-limit-requests",
|
|
154
|
-
"x-ratelimit-limit-tokens",
|
|
155
|
-
"x-ratelimit-remaining-requests",
|
|
156
|
-
"x-ratelimit-remaining-tokens",
|
|
157
|
-
"x-ratelimit-reset-requests",
|
|
158
|
-
"x-ratelimit-reset-tokens",
|
|
159
|
-
"anthropic-ratelimit-requests-limit",
|
|
160
|
-
"anthropic-ratelimit-requests-remaining",
|
|
161
|
-
"anthropic-ratelimit-requests-reset",
|
|
162
|
-
"anthropic-ratelimit-tokens-limit",
|
|
163
|
-
"anthropic-ratelimit-tokens-remaining",
|
|
164
|
-
"anthropic-ratelimit-tokens-reset",
|
|
165
|
-
"retry-after"
|
|
166
|
-
];
|
|
167
|
-
for (const key of commonHeaders) {
|
|
168
|
-
if (!headers[key]) {
|
|
169
|
-
const val = response.headers.get(key);
|
|
170
|
-
if (val) {
|
|
171
|
-
headers[key] = val;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
rateLimitTracker.updateFromHeaders(headers);
|
|
176
|
-
emitToListeners("SettingsUpdated", void 0);
|
|
177
|
-
const retryAfter = response.headers.get("retry-after");
|
|
178
|
-
if (retryAfter) {
|
|
179
|
-
const seconds = parseInt(retryAfter, 10);
|
|
180
|
-
if (!isNaN(seconds) && seconds > 0) {
|
|
181
|
-
emitToListeners("PushNewMessages", [{
|
|
182
|
-
type: "interrupted",
|
|
183
|
-
text: `Rate limit reached. Sleeping for ${seconds} seconds (Retry-After)...`,
|
|
184
|
-
version: 1
|
|
185
|
-
}]);
|
|
186
|
-
await new Promise((resolve2) => setTimeout(resolve2, seconds * 1e3));
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return response;
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// agent.ts
|
|
194
|
-
import chalk7 from "chalk";
|
|
195
|
-
|
|
196
|
-
// agent/AgentManager.ts
|
|
197
|
-
import { Agent as Agent3 } from "@openai/agents";
|
|
198
|
-
|
|
199
|
-
// ink/contexts/globalPlanContext.ts
|
|
200
|
-
var globalPlanContext = {
|
|
201
|
-
planDescription: "",
|
|
202
|
-
planItems: [],
|
|
203
|
-
progress: 0
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
// lifecycle/defaultInstructions.ts
|
|
207
|
-
import { existsSync as existsSync2 } from "fs";
|
|
208
|
-
import { join as join2 } from "path";
|
|
209
|
-
|
|
210
70
|
// utils/files/harperApp.ts
|
|
211
71
|
import { existsSync } from "fs";
|
|
212
72
|
import path, { dirname, join } from "path";
|
|
@@ -274,110 +134,6 @@ function bootstrapTrackedState() {
|
|
|
274
134
|
};
|
|
275
135
|
}
|
|
276
136
|
|
|
277
|
-
// lifecycle/defaultInstructions.ts
|
|
278
|
-
function defaultInstructions() {
|
|
279
|
-
const harperAppExists = existsSync2(join2(trackedState.cwd, "config.yaml"));
|
|
280
|
-
const vibing = harperAppExists ? "updating" : "creating";
|
|
281
|
-
return `You are working on ${vibing} a harper app with the user.`;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// lifecycle/getModel.ts
|
|
285
|
-
import { anthropic } from "@ai-sdk/anthropic";
|
|
286
|
-
import { google } from "@ai-sdk/google";
|
|
287
|
-
import { openai } from "@ai-sdk/openai";
|
|
288
|
-
import { aisdk } from "@openai/agents-extensions/ai-sdk";
|
|
289
|
-
import { createOllama, ollama } from "ollama-ai-provider-v2";
|
|
290
|
-
|
|
291
|
-
// agent/defaults.ts
|
|
292
|
-
var defaultModelToken = "default";
|
|
293
|
-
var defaultOpenAIModel = "gpt-5.2";
|
|
294
|
-
var defaultOpenAICompactionModel = "gpt-5-nano";
|
|
295
|
-
var defaultAnthropicModel = "claude-4-6-opus-latest";
|
|
296
|
-
var defaultAnthropicCompactionModel = "claude-4-5-haiku-latest";
|
|
297
|
-
var defaultGoogleModel = "gemini-3-pro";
|
|
298
|
-
var defaultGoogleCompactionModel = "gemini-2.5-flash-lite";
|
|
299
|
-
var defaultOllamaModel = "ollama-qwen3.5";
|
|
300
|
-
var defaultOllamaCompactionModel = "ollama-qwen3.5:2b";
|
|
301
|
-
var defaultModels = [
|
|
302
|
-
defaultOpenAIModel,
|
|
303
|
-
defaultAnthropicModel,
|
|
304
|
-
defaultGoogleModel,
|
|
305
|
-
defaultOllamaModel
|
|
306
|
-
];
|
|
307
|
-
var defaultCompactionModels = [
|
|
308
|
-
defaultOpenAICompactionModel,
|
|
309
|
-
defaultAnthropicCompactionModel,
|
|
310
|
-
defaultGoogleCompactionModel,
|
|
311
|
-
defaultOllamaCompactionModel
|
|
312
|
-
];
|
|
313
|
-
|
|
314
|
-
// utils/ollama/normalizeOllamaBaseUrl.ts
|
|
315
|
-
function normalizeOllamaBaseUrl(baseUrl) {
|
|
316
|
-
let url = baseUrl.trim();
|
|
317
|
-
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
|
318
|
-
url = `http://${url}`;
|
|
319
|
-
}
|
|
320
|
-
const urlObj = new URL(url);
|
|
321
|
-
if (!urlObj.port) {
|
|
322
|
-
urlObj.port = "11434";
|
|
323
|
-
}
|
|
324
|
-
let pathname = urlObj.pathname;
|
|
325
|
-
if (pathname.endsWith("/")) {
|
|
326
|
-
pathname = pathname.slice(0, -1);
|
|
327
|
-
}
|
|
328
|
-
if (!pathname.endsWith("/api")) {
|
|
329
|
-
pathname += "/api";
|
|
330
|
-
}
|
|
331
|
-
urlObj.pathname = pathname;
|
|
332
|
-
return urlObj.toString().replace(/\/$/, "");
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// lifecycle/getModel.ts
|
|
336
|
-
function isOpenAIModel(modelName) {
|
|
337
|
-
if (!modelName || modelName === defaultOpenAIModel) {
|
|
338
|
-
return true;
|
|
339
|
-
}
|
|
340
|
-
return !modelName.startsWith("claude-") && !modelName.startsWith("gemini-") && !modelName.startsWith("ollama-") && !modelName.includes(":");
|
|
341
|
-
}
|
|
342
|
-
function getProvider(modelName) {
|
|
343
|
-
if (modelName.startsWith("claude-")) {
|
|
344
|
-
return "Anthropic";
|
|
345
|
-
}
|
|
346
|
-
if (modelName.startsWith("gemini-")) {
|
|
347
|
-
return "Google";
|
|
348
|
-
}
|
|
349
|
-
if (modelName.startsWith("ollama-") || modelName.includes(":")) {
|
|
350
|
-
return "Ollama";
|
|
351
|
-
}
|
|
352
|
-
return "OpenAI";
|
|
353
|
-
}
|
|
354
|
-
function getModel(modelName) {
|
|
355
|
-
if (modelName.startsWith("claude-")) {
|
|
356
|
-
return aisdk(anthropic(modelName));
|
|
357
|
-
}
|
|
358
|
-
if (modelName.startsWith("gemini-")) {
|
|
359
|
-
return aisdk(google(modelName));
|
|
360
|
-
}
|
|
361
|
-
if (modelName.startsWith("ollama-") || modelName.includes(":")) {
|
|
362
|
-
const ollamaBaseUrl = process.env.OLLAMA_BASE_URL ? normalizeOllamaBaseUrl(process.env.OLLAMA_BASE_URL) : void 0;
|
|
363
|
-
const ollamaProvider = ollamaBaseUrl ? createOllama({ baseURL: ollamaBaseUrl, compatibility: "strict" }) : ollama;
|
|
364
|
-
return aisdk(ollamaProvider(getModelName(modelName)));
|
|
365
|
-
}
|
|
366
|
-
return aisdk(openai(modelName));
|
|
367
|
-
}
|
|
368
|
-
function getModelName(modelName) {
|
|
369
|
-
if (modelName.startsWith("claude-")) {
|
|
370
|
-
return modelName;
|
|
371
|
-
}
|
|
372
|
-
if (modelName.startsWith("gemini-")) {
|
|
373
|
-
return modelName;
|
|
374
|
-
}
|
|
375
|
-
if (modelName.startsWith("ollama-")) {
|
|
376
|
-
return modelName.slice(7);
|
|
377
|
-
}
|
|
378
|
-
return modelName;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
137
|
// lifecycle/handleExit.ts
|
|
382
138
|
import { getGlobalTraceProvider } from "@openai/agents";
|
|
383
139
|
|
|
@@ -385,7 +141,7 @@ import { getGlobalTraceProvider } from "@openai/agents";
|
|
|
385
141
|
import spawn from "cross-spawn";
|
|
386
142
|
import { execSync } from "child_process";
|
|
387
143
|
import { homedir } from "os";
|
|
388
|
-
import { join as
|
|
144
|
+
import { join as join2 } from "path";
|
|
389
145
|
var HarperProcess = class {
|
|
390
146
|
childProcess = null;
|
|
391
147
|
externalPid = null;
|
|
@@ -421,7 +177,7 @@ var HarperProcess = class {
|
|
|
421
177
|
this.stopTailingLogs();
|
|
422
178
|
}
|
|
423
179
|
startTailingLogs() {
|
|
424
|
-
const logPath =
|
|
180
|
+
const logPath = join2(homedir(), "hdb", "log", "hdb.log");
|
|
425
181
|
this.logTailProcess = spawn("tail", ["-f", logPath], {
|
|
426
182
|
stdio: ["ignore", "pipe", "pipe"]
|
|
427
183
|
});
|
|
@@ -501,38 +257,181 @@ async function handleExit() {
|
|
|
501
257
|
process.exit(0);
|
|
502
258
|
}
|
|
503
259
|
|
|
504
|
-
//
|
|
505
|
-
import { existsSync as
|
|
506
|
-
import {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
260
|
+
// utils/logger.ts
|
|
261
|
+
import { appendFileSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
262
|
+
import { homedir as homedir2 } from "os";
|
|
263
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
264
|
+
var ERROR_LOG_PATH = join3(homedir2(), ".harper", "harper-agent-errors");
|
|
265
|
+
function logError(error) {
|
|
266
|
+
const message = error instanceof Error ? error.stack || error.message : String(error);
|
|
267
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
268
|
+
const logEntry = `[${timestamp}] ${message}
|
|
511
269
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
270
|
+
`;
|
|
271
|
+
try {
|
|
272
|
+
const dir = dirname2(ERROR_LOG_PATH);
|
|
273
|
+
if (!existsSync2(dir)) {
|
|
274
|
+
mkdirSync(dir, { recursive: true });
|
|
275
|
+
}
|
|
276
|
+
appendFileSync(ERROR_LOG_PATH, logEntry, "utf8");
|
|
277
|
+
} catch (err) {
|
|
278
|
+
console.error("Failed to write to error log:", err);
|
|
279
|
+
console.error("Original error:", error);
|
|
518
280
|
}
|
|
519
281
|
}
|
|
282
|
+
function setupGlobalErrorHandlers() {
|
|
283
|
+
process.on("uncaughtException", (error) => {
|
|
284
|
+
logError(error);
|
|
285
|
+
console.error("Uncaught Exception:", error);
|
|
286
|
+
process.exit(1);
|
|
287
|
+
});
|
|
288
|
+
process.on("unhandledRejection", (reason) => {
|
|
289
|
+
logError(reason);
|
|
290
|
+
console.error("Unhandled Rejection:", reason);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
520
293
|
|
|
521
|
-
//
|
|
522
|
-
import {
|
|
523
|
-
import { z } from "zod";
|
|
294
|
+
// agent/AgentManager.ts
|
|
295
|
+
import { Agent as Agent3 } from "@openai/agents";
|
|
524
296
|
|
|
525
|
-
//
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
297
|
+
// lifecycle/defaultInstructions.ts
|
|
298
|
+
import { existsSync as existsSync3 } from "fs";
|
|
299
|
+
import { join as join4 } from "path";
|
|
300
|
+
function defaultInstructions() {
|
|
301
|
+
const harperAppExists = existsSync3(join4(trackedState.cwd, "config.yaml"));
|
|
302
|
+
const vibing = harperAppExists ? "updating" : "creating";
|
|
303
|
+
return `You are working on ${vibing} a harper app with the user.`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// lifecycle/getModel.ts
|
|
307
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
308
|
+
import { google } from "@ai-sdk/google";
|
|
309
|
+
import { openai } from "@ai-sdk/openai";
|
|
310
|
+
import { aisdk } from "@openai/agents-extensions/ai-sdk";
|
|
311
|
+
import { createOllama, ollama } from "ollama-ai-provider-v2";
|
|
312
|
+
|
|
313
|
+
// agent/defaults.ts
|
|
314
|
+
var defaultModelToken = "default";
|
|
315
|
+
var defaultOpenAIModel = "gpt-5.2";
|
|
316
|
+
var defaultOpenAICompactionModel = "gpt-5-nano";
|
|
317
|
+
var defaultAnthropicModel = "claude-4-6-opus-latest";
|
|
318
|
+
var defaultAnthropicCompactionModel = "claude-4-5-haiku-latest";
|
|
319
|
+
var defaultGoogleModel = "gemini-3-pro";
|
|
320
|
+
var defaultGoogleCompactionModel = "gemini-2.5-flash-lite";
|
|
321
|
+
var defaultOllamaModel = "ollama-qwen3.5";
|
|
322
|
+
var defaultOllamaCompactionModel = "ollama-qwen3.5:2b";
|
|
323
|
+
var defaultModels = [
|
|
324
|
+
defaultOpenAIModel,
|
|
325
|
+
defaultAnthropicModel,
|
|
326
|
+
defaultGoogleModel,
|
|
327
|
+
defaultOllamaModel
|
|
328
|
+
];
|
|
329
|
+
var defaultCompactionModels = [
|
|
330
|
+
defaultOpenAICompactionModel,
|
|
331
|
+
defaultAnthropicCompactionModel,
|
|
332
|
+
defaultGoogleCompactionModel,
|
|
333
|
+
defaultOllamaCompactionModel
|
|
334
|
+
];
|
|
335
|
+
|
|
336
|
+
// utils/ollama/normalizeOllamaBaseUrl.ts
|
|
337
|
+
function normalizeOllamaBaseUrl(baseUrl) {
|
|
338
|
+
let url = baseUrl.trim();
|
|
339
|
+
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
|
340
|
+
url = `http://${url}`;
|
|
341
|
+
}
|
|
342
|
+
const urlObj = new URL(url);
|
|
343
|
+
if (!urlObj.port) {
|
|
344
|
+
urlObj.port = "11434";
|
|
345
|
+
}
|
|
346
|
+
let pathname = urlObj.pathname;
|
|
347
|
+
if (pathname.endsWith("/")) {
|
|
348
|
+
pathname = pathname.slice(0, -1);
|
|
349
|
+
}
|
|
350
|
+
if (!pathname.endsWith("/api")) {
|
|
351
|
+
pathname += "/api";
|
|
352
|
+
}
|
|
353
|
+
urlObj.pathname = pathname;
|
|
354
|
+
return urlObj.toString().replace(/\/$/, "");
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// lifecycle/getModel.ts
|
|
358
|
+
function isOpenAIModel(modelName) {
|
|
359
|
+
if (!modelName || modelName === defaultOpenAIModel) {
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
return !modelName.startsWith("claude-") && !modelName.startsWith("gemini-") && !modelName.startsWith("ollama-") && !modelName.includes(":");
|
|
363
|
+
}
|
|
364
|
+
function getProvider(modelName) {
|
|
365
|
+
if (modelName.startsWith("claude-")) {
|
|
366
|
+
return "Anthropic";
|
|
367
|
+
}
|
|
368
|
+
if (modelName.startsWith("gemini-")) {
|
|
369
|
+
return "Google";
|
|
370
|
+
}
|
|
371
|
+
if (modelName.startsWith("ollama-") || modelName.includes(":")) {
|
|
372
|
+
return "Ollama";
|
|
373
|
+
}
|
|
374
|
+
return "OpenAI";
|
|
375
|
+
}
|
|
376
|
+
function getModel(modelName) {
|
|
377
|
+
if (modelName.startsWith("claude-")) {
|
|
378
|
+
return aisdk(anthropic(modelName));
|
|
379
|
+
}
|
|
380
|
+
if (modelName.startsWith("gemini-")) {
|
|
381
|
+
return aisdk(google(modelName));
|
|
382
|
+
}
|
|
383
|
+
if (modelName.startsWith("ollama-") || modelName.includes(":")) {
|
|
384
|
+
const ollamaBaseUrl = process.env.OLLAMA_BASE_URL ? normalizeOllamaBaseUrl(process.env.OLLAMA_BASE_URL) : void 0;
|
|
385
|
+
const ollamaProvider = ollamaBaseUrl ? createOllama({ baseURL: ollamaBaseUrl, compatibility: "strict" }) : ollama;
|
|
386
|
+
return aisdk(ollamaProvider(getModelName(modelName)));
|
|
387
|
+
}
|
|
388
|
+
return aisdk(openai(modelName));
|
|
389
|
+
}
|
|
390
|
+
function getModelName(modelName) {
|
|
391
|
+
if (modelName.startsWith("claude-")) {
|
|
392
|
+
return modelName;
|
|
393
|
+
}
|
|
394
|
+
if (modelName.startsWith("gemini-")) {
|
|
395
|
+
return modelName;
|
|
396
|
+
}
|
|
397
|
+
if (modelName.startsWith("ollama-")) {
|
|
398
|
+
return modelName.slice(7);
|
|
399
|
+
}
|
|
400
|
+
return modelName;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// lifecycle/readAgentSkillsRoot.ts
|
|
404
|
+
import { existsSync as existsSync4, readFileSync } from "fs";
|
|
405
|
+
import { join as join5 } from "path";
|
|
406
|
+
|
|
407
|
+
// lifecycle/agentsSkillReference.ts
|
|
408
|
+
var agentsSkillDir = ".agents/skills/harper-best-practices";
|
|
409
|
+
var agentsSkillReference = `${agentsSkillDir}/SKILL.md`;
|
|
410
|
+
|
|
411
|
+
// lifecycle/readAgentSkillsRoot.ts
|
|
412
|
+
function readAgentSkillsRoot() {
|
|
413
|
+
const agentsFile = join5(trackedState.cwd, agentsSkillReference);
|
|
414
|
+
const agentsMdExists = existsSync4(agentsFile);
|
|
415
|
+
if (agentsMdExists) {
|
|
416
|
+
return readFileSync(agentsFile, "utf8");
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// tools/browser/browserClickTool.ts
|
|
421
|
+
import { tool } from "@openai/agents";
|
|
422
|
+
import { z } from "zod";
|
|
423
|
+
|
|
424
|
+
// tools/browser/browserManager.ts
|
|
425
|
+
var browser = null;
|
|
426
|
+
var page = null;
|
|
427
|
+
var logs = [];
|
|
428
|
+
async function getBrowser() {
|
|
429
|
+
if (!browser) {
|
|
430
|
+
let puppeteer;
|
|
431
|
+
try {
|
|
432
|
+
puppeteer = await import("puppeteer");
|
|
433
|
+
} catch {
|
|
434
|
+
throw new Error(
|
|
536
435
|
"Puppeteer is not installed. Browser tools require puppeteer. Please install it with `npm install puppeteer`."
|
|
537
436
|
);
|
|
538
437
|
}
|
|
@@ -814,21 +713,21 @@ function getEnv(newKey, oldKey) {
|
|
|
814
713
|
import { tool as tool10 } from "@openai/agents";
|
|
815
714
|
import { readdirSync, readFileSync as readFileSync2 } from "fs";
|
|
816
715
|
import { createRequire } from "module";
|
|
817
|
-
import { dirname as
|
|
716
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
818
717
|
import { z as z10 } from "zod";
|
|
819
718
|
var require2 = createRequire(import.meta.url);
|
|
820
|
-
var harperSkillsModuleDir =
|
|
719
|
+
var harperSkillsModuleDir = dirname3(
|
|
821
720
|
require2.resolve("@harperfast/skills/package.json")
|
|
822
721
|
);
|
|
823
|
-
var harperBestPracticesDir =
|
|
722
|
+
var harperBestPracticesDir = join6(
|
|
824
723
|
harperSkillsModuleDir,
|
|
825
724
|
"harper-best-practices"
|
|
826
725
|
);
|
|
827
|
-
var skillRootFile =
|
|
726
|
+
var skillRootFile = join6(
|
|
828
727
|
harperBestPracticesDir,
|
|
829
728
|
"SKILL.md"
|
|
830
729
|
);
|
|
831
|
-
var rulesDir =
|
|
730
|
+
var rulesDir = join6(
|
|
832
731
|
harperBestPracticesDir,
|
|
833
732
|
"rules"
|
|
834
733
|
);
|
|
@@ -864,7 +763,7 @@ async function execute10({ skill }) {
|
|
|
864
763
|
return "No skill requested.";
|
|
865
764
|
}
|
|
866
765
|
try {
|
|
867
|
-
const filePath =
|
|
766
|
+
const filePath = join6(rulesDir, `${skill}.md`);
|
|
868
767
|
const content = readFileSync2(filePath, "utf8");
|
|
869
768
|
agentManager.session?.addSkillRead?.(skill);
|
|
870
769
|
return content;
|
|
@@ -899,42 +798,6 @@ import path3 from "path";
|
|
|
899
798
|
// utils/files/aiignore.ts
|
|
900
799
|
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
901
800
|
import path2 from "path";
|
|
902
|
-
|
|
903
|
-
// utils/logger.ts
|
|
904
|
-
import { appendFileSync, existsSync as existsSync4, mkdirSync } from "fs";
|
|
905
|
-
import { homedir as homedir2 } from "os";
|
|
906
|
-
import { dirname as dirname3, join as join6 } from "path";
|
|
907
|
-
var ERROR_LOG_PATH = join6(homedir2(), ".harper", "harper-agent-errors");
|
|
908
|
-
function logError(error) {
|
|
909
|
-
const message = error instanceof Error ? error.stack || error.message : String(error);
|
|
910
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
911
|
-
const logEntry = `[${timestamp}] ${message}
|
|
912
|
-
|
|
913
|
-
`;
|
|
914
|
-
try {
|
|
915
|
-
const dir = dirname3(ERROR_LOG_PATH);
|
|
916
|
-
if (!existsSync4(dir)) {
|
|
917
|
-
mkdirSync(dir, { recursive: true });
|
|
918
|
-
}
|
|
919
|
-
appendFileSync(ERROR_LOG_PATH, logEntry, "utf8");
|
|
920
|
-
} catch (err) {
|
|
921
|
-
console.error("Failed to write to error log:", err);
|
|
922
|
-
console.error("Original error:", error);
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
function setupGlobalErrorHandlers() {
|
|
926
|
-
process.on("uncaughtException", (error) => {
|
|
927
|
-
logError(error);
|
|
928
|
-
console.error("Uncaught Exception:", error);
|
|
929
|
-
process.exit(1);
|
|
930
|
-
});
|
|
931
|
-
process.on("unhandledRejection", (reason) => {
|
|
932
|
-
logError(reason);
|
|
933
|
-
console.error("Unhandled Rejection:", reason);
|
|
934
|
-
});
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
// utils/files/aiignore.ts
|
|
938
801
|
var ignorePatterns = [];
|
|
939
802
|
function loadAiIgnore() {
|
|
940
803
|
const ignorePath = path2.join(trackedState.cwd, ".aiignore");
|
|
@@ -1165,11 +1028,11 @@ function pickExistingSkill(candidates) {
|
|
|
1165
1028
|
}
|
|
1166
1029
|
return null;
|
|
1167
1030
|
}
|
|
1168
|
-
async function requiredSkillForOperation(
|
|
1031
|
+
async function requiredSkillForOperation(path9, type) {
|
|
1169
1032
|
if (type === "delete_file") {
|
|
1170
1033
|
return null;
|
|
1171
1034
|
}
|
|
1172
|
-
const p = normalizedPath(
|
|
1035
|
+
const p = normalizedPath(path9);
|
|
1173
1036
|
const read = await getSkillsRead();
|
|
1174
1037
|
if (p.includes("/resources/") || p.startsWith("resources/") || p.endsWith("resources.ts") || p.endsWith("resources.js")) {
|
|
1175
1038
|
if (!read.includes("automatic-apis")) {
|
|
@@ -1195,14 +1058,6 @@ async function needsApproval(runContext, operation, callId) {
|
|
|
1195
1058
|
}
|
|
1196
1059
|
const autoApproved = getEnv("HARPER_AGENT_AUTO_APPROVE_PATCHES", "APPLY_PATCH_AUTO_APPROVE") === "1";
|
|
1197
1060
|
if (autoApproved) {
|
|
1198
|
-
if (callId) {
|
|
1199
|
-
emitToListeners("RegisterToolInfo", {
|
|
1200
|
-
type: operation.type,
|
|
1201
|
-
path: operation.path,
|
|
1202
|
-
diff: operation.diff,
|
|
1203
|
-
callId
|
|
1204
|
-
});
|
|
1205
|
-
}
|
|
1206
1061
|
return false;
|
|
1207
1062
|
}
|
|
1208
1063
|
emitToListeners("OpenApprovalViewer", {
|
|
@@ -1290,9 +1145,9 @@ import { z as z12 } from "zod";
|
|
|
1290
1145
|
var ToolParameters11 = z12.object({
|
|
1291
1146
|
path: z12.string().describe("Directory to switch into. Can be absolute or relative to current workspace.")
|
|
1292
1147
|
});
|
|
1293
|
-
async function execute12({ path:
|
|
1148
|
+
async function execute12({ path: path9 }) {
|
|
1294
1149
|
try {
|
|
1295
|
-
const target = resolvePath(trackedState.cwd,
|
|
1150
|
+
const target = resolvePath(trackedState.cwd, path9);
|
|
1296
1151
|
const stat = statSync(target);
|
|
1297
1152
|
if (!stat.isDirectory()) {
|
|
1298
1153
|
return `Path is not a directory: ${target}`;
|
|
@@ -1485,13 +1340,6 @@ async function needsApproval2(runContext, parameters, callId) {
|
|
|
1485
1340
|
}
|
|
1486
1341
|
const autoApproved = getEnv("HARPER_AGENT_AUTO_APPROVE_CODE_INTERPRETER", "CODE_INTERPRETER_AUTO_APPROVE") === "1";
|
|
1487
1342
|
if (autoApproved) {
|
|
1488
|
-
if (callId) {
|
|
1489
|
-
emitToListeners("RegisterToolInfo", {
|
|
1490
|
-
type: "code_interpreter",
|
|
1491
|
-
code: parameters.code,
|
|
1492
|
-
callId
|
|
1493
|
-
});
|
|
1494
|
-
}
|
|
1495
1343
|
return false;
|
|
1496
1344
|
}
|
|
1497
1345
|
emitToListeners("OpenApprovalViewer", {
|
|
@@ -1740,15 +1588,8 @@ function isRiskyCommand(command) {
|
|
|
1740
1588
|
// utils/shell/LocalShell.ts
|
|
1741
1589
|
import { exec as exec3 } from "child_process";
|
|
1742
1590
|
import { promisify as promisify5 } from "util";
|
|
1743
|
-
|
|
1744
|
-
// ink/contexts/ShellContext.tsx
|
|
1745
|
-
import { createContext, useContext, useMemo, useState } from "react";
|
|
1746
|
-
import { jsx } from "react/jsx-runtime";
|
|
1747
|
-
var ShellContext = createContext(void 0);
|
|
1748
|
-
var commandId = 0;
|
|
1749
|
-
|
|
1750
|
-
// utils/shell/LocalShell.ts
|
|
1751
1591
|
var execAsync3 = promisify5(exec3);
|
|
1592
|
+
var commandId = 0;
|
|
1752
1593
|
var LocalShell = class {
|
|
1753
1594
|
defaultTimeoutMs;
|
|
1754
1595
|
constructor(options) {
|
|
@@ -1769,12 +1610,7 @@ var LocalShell = class {
|
|
|
1769
1610
|
const parts = command.split(" ");
|
|
1770
1611
|
const firstPart = parts[0];
|
|
1771
1612
|
const laterParts = parts.slice(1).join(" ") || "";
|
|
1772
|
-
|
|
1773
|
-
emitToListeners("AddShellCommand", {
|
|
1774
|
-
command: firstPart,
|
|
1775
|
-
args: laterParts,
|
|
1776
|
-
running: true
|
|
1777
|
-
});
|
|
1613
|
+
const myCommandId = ++commandId;
|
|
1778
1614
|
emitToListeners("AddActionItem", {
|
|
1779
1615
|
id: myCommandId,
|
|
1780
1616
|
kind: "shell",
|
|
@@ -1800,11 +1636,6 @@ var LocalShell = class {
|
|
|
1800
1636
|
stderr = error?.stderr ?? "";
|
|
1801
1637
|
outcome = error?.killed || error?.signal === "SIGTERM" ? { type: "timeout" } : { type: "exit", exitCode };
|
|
1802
1638
|
}
|
|
1803
|
-
emitToListeners("UpdateShellCommand", {
|
|
1804
|
-
id: myCommandId,
|
|
1805
|
-
running: false,
|
|
1806
|
-
exitCode
|
|
1807
|
-
});
|
|
1808
1639
|
emitToListeners("UpdateActionItem", {
|
|
1809
1640
|
id: myCommandId,
|
|
1810
1641
|
running: false,
|
|
@@ -1864,13 +1695,6 @@ TIMEOUT`;
|
|
|
1864
1695
|
const foundIgnoredInteraction = commands.find((command) => mentionsIgnoredPath(command));
|
|
1865
1696
|
const autoApproved = getEnv("HARPER_AGENT_AUTO_APPROVE_SHELL", "SHELL_AUTO_APPROVE") === "1" && !foundRiskyCommand && !foundIgnoredInteraction;
|
|
1866
1697
|
if (autoApproved) {
|
|
1867
|
-
if (callId) {
|
|
1868
|
-
emitToListeners("RegisterToolInfo", {
|
|
1869
|
-
type: "shell",
|
|
1870
|
-
commands,
|
|
1871
|
-
callId
|
|
1872
|
-
});
|
|
1873
|
-
}
|
|
1874
1698
|
return false;
|
|
1875
1699
|
}
|
|
1876
1700
|
emitToListeners("OpenApprovalViewer", {
|
|
@@ -2320,14 +2144,14 @@ var hitHarperAPITool = tool35({
|
|
|
2320
2144
|
}
|
|
2321
2145
|
return false;
|
|
2322
2146
|
},
|
|
2323
|
-
async execute({ path:
|
|
2147
|
+
async execute({ path: path9 = "/openapi", port, method = "GET", body }) {
|
|
2324
2148
|
try {
|
|
2325
2149
|
const effectivePort = port ?? (harperProcess.running ? harperProcess.httpPort : void 0);
|
|
2326
2150
|
if (!effectivePort) {
|
|
2327
2151
|
return `Error: No Harper application is currently running and no port was specified.`;
|
|
2328
2152
|
}
|
|
2329
2153
|
const response = await fetch(
|
|
2330
|
-
`http://localhost:${effectivePort}${
|
|
2154
|
+
`http://localhost:${effectivePort}${path9.startsWith("/") ? "" : "/"}${path9}`,
|
|
2331
2155
|
{
|
|
2332
2156
|
method,
|
|
2333
2157
|
headers: body ? { "Content-Type": "application/json" } : {},
|
|
@@ -2432,29 +2256,35 @@ var stopHarperTool = tool38({
|
|
|
2432
2256
|
}
|
|
2433
2257
|
});
|
|
2434
2258
|
|
|
2435
|
-
// tools/plan/
|
|
2259
|
+
// tools/plan/createAddPlanItemTool.ts
|
|
2436
2260
|
import { tool as tool39 } from "@openai/agents";
|
|
2437
2261
|
import { z as z39 } from "zod";
|
|
2438
2262
|
var AddPlanItemParameters = z39.object({
|
|
2439
2263
|
text: z39.string().describe("The description of the task or milestone to add to the plan.")
|
|
2440
2264
|
});
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2265
|
+
function createAddPlanItemTool(session) {
|
|
2266
|
+
return tool39({
|
|
2267
|
+
name: "add_plan_item",
|
|
2268
|
+
description: "Add a new item to the plan.",
|
|
2269
|
+
parameters: AddPlanItemParameters,
|
|
2270
|
+
async execute({ text }) {
|
|
2271
|
+
const planContext = await session.getPlanState();
|
|
2272
|
+
const existingPlanItems = planContext.planItems;
|
|
2273
|
+
const newItems = [
|
|
2274
|
+
...existingPlanItems,
|
|
2275
|
+
{
|
|
2276
|
+
id: existingPlanItems.length + 1,
|
|
2277
|
+
text,
|
|
2278
|
+
status: "todo"
|
|
2279
|
+
}
|
|
2280
|
+
];
|
|
2281
|
+
planContext.planItems = newItems;
|
|
2282
|
+
session.setPlanState(planContext);
|
|
2283
|
+
emitToListeners("SetPlanItems", newItems);
|
|
2284
|
+
return `Added plan item: ${text}`;
|
|
2285
|
+
}
|
|
2286
|
+
});
|
|
2287
|
+
}
|
|
2458
2288
|
|
|
2459
2289
|
// tools/plan/setPlanDescriptionTool.ts
|
|
2460
2290
|
import { tool as tool40 } from "@openai/agents";
|
|
@@ -2462,15 +2292,20 @@ import { z as z40 } from "zod";
|
|
|
2462
2292
|
var SetPlanDescriptionParameters = z40.object({
|
|
2463
2293
|
description: z40.string().describe("A high-level description of the overall plan and goals.")
|
|
2464
2294
|
});
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2295
|
+
function createSetPlanDescriptionTool(session) {
|
|
2296
|
+
return tool40({
|
|
2297
|
+
name: "set_plan_description",
|
|
2298
|
+
description: "Set the high-level description for the current plan.",
|
|
2299
|
+
parameters: SetPlanDescriptionParameters,
|
|
2300
|
+
async execute({ description }) {
|
|
2301
|
+
const planContext = await session.getPlanState();
|
|
2302
|
+
planContext.planDescription = description;
|
|
2303
|
+
session.setPlanState(planContext);
|
|
2304
|
+
emitToListeners("SetPlanDescription", description);
|
|
2305
|
+
return `Plan description updated to: ${description}`;
|
|
2306
|
+
}
|
|
2307
|
+
});
|
|
2308
|
+
}
|
|
2474
2309
|
|
|
2475
2310
|
// tools/plan/setPlanItemsTool.ts
|
|
2476
2311
|
import { tool as tool41 } from "@openai/agents";
|
|
@@ -2478,20 +2313,25 @@ import { z as z41 } from "zod";
|
|
|
2478
2313
|
var SetPlanItemsParameters = z41.object({
|
|
2479
2314
|
items: z41.array(z41.string()).describe("An array of task descriptions to set as the plan items.")
|
|
2480
2315
|
});
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
text,
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2316
|
+
function createSetPlanItemsTool(session) {
|
|
2317
|
+
return tool41({
|
|
2318
|
+
name: "set_plan_items",
|
|
2319
|
+
description: "Set multiple plan items at once, replacing any existing items.",
|
|
2320
|
+
parameters: SetPlanItemsParameters,
|
|
2321
|
+
async execute({ items }) {
|
|
2322
|
+
const planContext = await session.getPlanState();
|
|
2323
|
+
const newItems = items.map((text, index) => ({
|
|
2324
|
+
id: index + 1,
|
|
2325
|
+
text,
|
|
2326
|
+
status: "todo"
|
|
2327
|
+
}));
|
|
2328
|
+
planContext.planItems = newItems;
|
|
2329
|
+
session.setPlanState(planContext);
|
|
2330
|
+
emitToListeners("SetPlanItems", newItems);
|
|
2331
|
+
return `Set ${newItems.length} plan items.`;
|
|
2332
|
+
}
|
|
2333
|
+
});
|
|
2334
|
+
}
|
|
2495
2335
|
|
|
2496
2336
|
// tools/plan/updatePlanItemTool.ts
|
|
2497
2337
|
import { tool as tool42 } from "@openai/agents";
|
|
@@ -2503,32 +2343,51 @@ var UpdatePlanItemParameters = z42.object({
|
|
|
2503
2343
|
"The new status of the task."
|
|
2504
2344
|
)
|
|
2505
2345
|
});
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2346
|
+
function createUpdatePlanItemTool(session) {
|
|
2347
|
+
return tool42({
|
|
2348
|
+
name: "update_plan_item",
|
|
2349
|
+
description: "Update an existing plan item.",
|
|
2350
|
+
parameters: UpdatePlanItemParameters,
|
|
2351
|
+
async execute({ id, text, status }) {
|
|
2352
|
+
const planContext = await session.getPlanState();
|
|
2353
|
+
const planItems = planContext?.planItems ?? [];
|
|
2354
|
+
const itemExists = planItems.some((item) => item.id === id);
|
|
2355
|
+
if (!itemExists) {
|
|
2356
|
+
return `Error: Plan item with ID ${id} not found.`;
|
|
2357
|
+
}
|
|
2358
|
+
const newItems = planItems.map((item) => {
|
|
2359
|
+
if (item.id === id) {
|
|
2360
|
+
return {
|
|
2361
|
+
...item,
|
|
2362
|
+
text: text || item.text,
|
|
2363
|
+
status: status && status !== "unchanged" ? status : item.status
|
|
2364
|
+
};
|
|
2365
|
+
}
|
|
2366
|
+
return item;
|
|
2367
|
+
});
|
|
2368
|
+
planContext.planItems = newItems;
|
|
2369
|
+
session.setPlanState(planContext);
|
|
2370
|
+
emitToListeners("SetPlanItems", newItems);
|
|
2371
|
+
return `Updated plan item ${id}`;
|
|
2524
2372
|
}
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
}
|
|
2528
|
-
});
|
|
2373
|
+
});
|
|
2374
|
+
}
|
|
2529
2375
|
|
|
2530
2376
|
// tools/factory.ts
|
|
2531
|
-
function
|
|
2377
|
+
function createSharedTools(session) {
|
|
2378
|
+
return [
|
|
2379
|
+
collectFeedbackTool,
|
|
2380
|
+
createAddPlanItemTool(session),
|
|
2381
|
+
createSetPlanDescriptionTool(session),
|
|
2382
|
+
createSetPlanItemsTool(session),
|
|
2383
|
+
createUpdatePlanItemTool(session),
|
|
2384
|
+
getHarperConfigSchemaTool,
|
|
2385
|
+
getHarperResourceInterfaceTool,
|
|
2386
|
+
getHarperSchemaGraphQLTool,
|
|
2387
|
+
getHarperSkillTool
|
|
2388
|
+
];
|
|
2389
|
+
}
|
|
2390
|
+
function createCLITools(_session) {
|
|
2532
2391
|
return [
|
|
2533
2392
|
browserClickTool,
|
|
2534
2393
|
browserCloseTool,
|
|
@@ -2539,19 +2398,13 @@ function createTools() {
|
|
|
2539
2398
|
browserNavigateTool,
|
|
2540
2399
|
browserScreenshotTool,
|
|
2541
2400
|
browserTypeTool,
|
|
2542
|
-
addPlanItemTool,
|
|
2543
2401
|
changeCwdTool,
|
|
2544
2402
|
checkHarperStatusTool,
|
|
2545
|
-
collectFeedbackTool,
|
|
2546
2403
|
codeInterpreterTool,
|
|
2547
2404
|
createApplyPatchTool(),
|
|
2548
2405
|
createNewHarperApplicationTool,
|
|
2549
2406
|
egrepTool,
|
|
2550
2407
|
findTool,
|
|
2551
|
-
getHarperConfigSchemaTool,
|
|
2552
|
-
getHarperResourceInterfaceTool,
|
|
2553
|
-
getHarperSchemaGraphQLTool,
|
|
2554
|
-
getHarperSkillTool,
|
|
2555
2408
|
gitAddTool,
|
|
2556
2409
|
gitBranchTool,
|
|
2557
2410
|
gitCommitTool,
|
|
@@ -2565,13 +2418,10 @@ function createTools() {
|
|
|
2565
2418
|
readHarperLogsTool,
|
|
2566
2419
|
setInterpreterAutoApproveTool,
|
|
2567
2420
|
setPatchAutoApproveTool,
|
|
2568
|
-
setPlanDescriptionTool,
|
|
2569
|
-
setPlanItemsTool,
|
|
2570
2421
|
setShellAutoApproveTool,
|
|
2571
2422
|
shellTool,
|
|
2572
2423
|
startHarperTool,
|
|
2573
|
-
stopHarperTool
|
|
2574
|
-
updatePlanItemTool
|
|
2424
|
+
stopHarperTool
|
|
2575
2425
|
];
|
|
2576
2426
|
}
|
|
2577
2427
|
|
|
@@ -2868,7 +2718,11 @@ var DiskSession = class extends MemorySession {
|
|
|
2868
2718
|
await this.ready;
|
|
2869
2719
|
const sessionId = await this.getSessionId();
|
|
2870
2720
|
const storage = await this.loadStorage();
|
|
2871
|
-
return storage.plan?.[sessionId] ??
|
|
2721
|
+
return storage.plan?.[sessionId] ?? {
|
|
2722
|
+
planDescription: "",
|
|
2723
|
+
planItems: [],
|
|
2724
|
+
progress: 0
|
|
2725
|
+
};
|
|
2872
2726
|
}
|
|
2873
2727
|
async setPlanState(state) {
|
|
2874
2728
|
await this.ready;
|
|
@@ -3162,14 +3016,17 @@ var MemoryCompactionSession = class {
|
|
|
3162
3016
|
}
|
|
3163
3017
|
if (this.planStateLocal) {
|
|
3164
3018
|
const existing = base ?? { planDescription: "", planItems: [], progress: 0 };
|
|
3165
|
-
|
|
3019
|
+
return {
|
|
3166
3020
|
...existing,
|
|
3167
3021
|
...this.planStateLocal,
|
|
3168
3022
|
planItems: Array.isArray(this.planStateLocal.planItems) ? this.planStateLocal.planItems : existing.planItems
|
|
3169
3023
|
};
|
|
3170
|
-
return merged;
|
|
3171
3024
|
}
|
|
3172
|
-
return base
|
|
3025
|
+
return base || {
|
|
3026
|
+
planDescription: "",
|
|
3027
|
+
planItems: [],
|
|
3028
|
+
progress: 0
|
|
3029
|
+
};
|
|
3173
3030
|
}
|
|
3174
3031
|
async setPlanState(state) {
|
|
3175
3032
|
const u = this.underlyingSession;
|
|
@@ -3274,11 +3131,11 @@ function createSession(sessionPath = null) {
|
|
|
3274
3131
|
import { run as run2, system as system2 } from "@openai/agents";
|
|
3275
3132
|
|
|
3276
3133
|
// ink/contexts/ActionsContext.tsx
|
|
3277
|
-
import { createContext
|
|
3278
|
-
import { jsx
|
|
3279
|
-
var ActionsContext =
|
|
3134
|
+
import { createContext, useContext, useEffect as useEffect2, useMemo, useState } from "react";
|
|
3135
|
+
import { jsx } from "react/jsx-runtime";
|
|
3136
|
+
var ActionsContext = createContext(void 0);
|
|
3280
3137
|
var useActions = () => {
|
|
3281
|
-
const context =
|
|
3138
|
+
const context = useContext(ActionsContext);
|
|
3282
3139
|
if (!context) {
|
|
3283
3140
|
throw new Error("useActions must be used within an ActionsProvider");
|
|
3284
3141
|
}
|
|
@@ -3286,7 +3143,7 @@ var useActions = () => {
|
|
|
3286
3143
|
};
|
|
3287
3144
|
var actionId = 0;
|
|
3288
3145
|
var ActionsProvider = ({ children }) => {
|
|
3289
|
-
const [actions, setActions] =
|
|
3146
|
+
const [actions, setActions] = useState([]);
|
|
3290
3147
|
useListener("AddActionItem", (action) => {
|
|
3291
3148
|
setActions((prev) => {
|
|
3292
3149
|
const assignedId = action.id ?? actionId++;
|
|
@@ -3310,8 +3167,8 @@ var ActionsProvider = ({ children }) => {
|
|
|
3310
3167
|
}
|
|
3311
3168
|
}
|
|
3312
3169
|
}, []);
|
|
3313
|
-
const value =
|
|
3314
|
-
return /* @__PURE__ */
|
|
3170
|
+
const value = useMemo(() => ({ actions }), [actions]);
|
|
3171
|
+
return /* @__PURE__ */ jsx(ActionsContext.Provider, { value, children });
|
|
3315
3172
|
};
|
|
3316
3173
|
|
|
3317
3174
|
// utils/sessions/cost.ts
|
|
@@ -3493,31 +3350,109 @@ var CostTracker = class {
|
|
|
3493
3350
|
};
|
|
3494
3351
|
var costTracker = new CostTracker();
|
|
3495
3352
|
|
|
3496
|
-
// utils/
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3353
|
+
// utils/sessions/rateLimits.ts
|
|
3354
|
+
var RateLimitTracker = class {
|
|
3355
|
+
status = {
|
|
3356
|
+
limitRequests: null,
|
|
3357
|
+
limitTokens: null,
|
|
3358
|
+
remainingRequests: null,
|
|
3359
|
+
remainingTokens: null,
|
|
3360
|
+
resetRequests: null,
|
|
3361
|
+
resetTokens: null,
|
|
3362
|
+
retryAfter: null
|
|
3363
|
+
};
|
|
3364
|
+
updateFromHeaders(headers) {
|
|
3365
|
+
const normalizedHeaders = {};
|
|
3366
|
+
for (const key of Object.keys(headers)) {
|
|
3367
|
+
normalizedHeaders[key.toLowerCase()] = headers[key];
|
|
3368
|
+
}
|
|
3369
|
+
const getHeader = (name) => {
|
|
3370
|
+
const value = normalizedHeaders[name.toLowerCase()];
|
|
3371
|
+
return Array.isArray(value) ? value[0] : value;
|
|
3372
|
+
};
|
|
3373
|
+
const limitRequests = getHeader("x-ratelimit-limit-requests") || getHeader("anthropic-ratelimit-requests-limit") || getHeader("x-ratelimit-limit") || getHeader("ratelimit-limit") || getHeader("x-request-limit");
|
|
3374
|
+
const limitTokens = getHeader("x-ratelimit-limit-tokens") || getHeader("anthropic-ratelimit-tokens-limit") || getHeader("x-token-limit");
|
|
3375
|
+
const remainingRequests = getHeader("x-ratelimit-remaining-requests") || getHeader("anthropic-ratelimit-requests-remaining") || getHeader("x-ratelimit-remaining") || getHeader("ratelimit-remaining") || getHeader("x-request-remaining");
|
|
3376
|
+
const remainingTokens = getHeader("x-ratelimit-remaining-tokens") || getHeader("anthropic-ratelimit-tokens-remaining") || getHeader("x-token-remaining");
|
|
3377
|
+
const resetRequests = getHeader("x-ratelimit-reset-requests") || getHeader("anthropic-ratelimit-requests-reset") || getHeader("x-ratelimit-reset") || getHeader("ratelimit-reset") || getHeader("x-request-reset");
|
|
3378
|
+
const resetTokens = getHeader("x-ratelimit-reset-tokens") || getHeader("anthropic-ratelimit-tokens-reset") || getHeader("x-token-reset");
|
|
3379
|
+
const retryAfter = getHeader("retry-after");
|
|
3380
|
+
if (limitRequests) {
|
|
3381
|
+
this.status.limitRequests = parseInt(limitRequests, 10);
|
|
3382
|
+
}
|
|
3383
|
+
if (limitTokens) {
|
|
3384
|
+
this.status.limitTokens = parseInt(limitTokens, 10);
|
|
3385
|
+
}
|
|
3386
|
+
if (remainingRequests) {
|
|
3387
|
+
this.status.remainingRequests = parseInt(remainingRequests, 10);
|
|
3388
|
+
}
|
|
3389
|
+
if (remainingTokens) {
|
|
3390
|
+
this.status.remainingTokens = parseInt(remainingTokens, 10);
|
|
3391
|
+
}
|
|
3392
|
+
if (resetRequests) {
|
|
3393
|
+
this.status.resetRequests = resetRequests;
|
|
3394
|
+
}
|
|
3395
|
+
if (resetTokens) {
|
|
3396
|
+
this.status.resetTokens = resetTokens;
|
|
3397
|
+
}
|
|
3398
|
+
if (retryAfter) {
|
|
3399
|
+
this.status.retryAfter = parseInt(retryAfter, 10);
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
isApproachingLimit(threshold) {
|
|
3403
|
+
const usage = this.getUsagePercentage();
|
|
3404
|
+
return {
|
|
3405
|
+
requests: usage.requests >= threshold,
|
|
3406
|
+
tokens: usage.tokens >= threshold
|
|
3407
|
+
};
|
|
3408
|
+
}
|
|
3409
|
+
getStatus() {
|
|
3410
|
+
return { ...this.status };
|
|
3411
|
+
}
|
|
3412
|
+
getUsagePercentage() {
|
|
3413
|
+
const requests = this.status.limitRequests && this.status.remainingRequests !== null ? 100 * (1 - this.status.remainingRequests / this.status.limitRequests) : 0;
|
|
3414
|
+
const tokens = this.status.limitTokens && this.status.remainingTokens !== null ? 100 * (1 - this.status.remainingTokens / this.status.limitTokens) : 0;
|
|
3415
|
+
return { requests, tokens };
|
|
3416
|
+
}
|
|
3417
|
+
reset() {
|
|
3418
|
+
this.status = {
|
|
3419
|
+
limitRequests: null,
|
|
3420
|
+
limitTokens: null,
|
|
3421
|
+
remainingRequests: null,
|
|
3422
|
+
remainingTokens: null,
|
|
3423
|
+
resetRequests: null,
|
|
3424
|
+
resetTokens: null,
|
|
3425
|
+
retryAfter: null
|
|
3426
|
+
};
|
|
3427
|
+
}
|
|
3428
|
+
};
|
|
3429
|
+
var rateLimitTracker = new RateLimitTracker();
|
|
3430
|
+
|
|
3431
|
+
// utils/strings/isTrue.ts
|
|
3432
|
+
function isTrue(v) {
|
|
3433
|
+
if (v === void 0) {
|
|
3434
|
+
return false;
|
|
3435
|
+
}
|
|
3436
|
+
const val = v.trim().toLowerCase();
|
|
3437
|
+
return val === "true" || val === "y" || val === "1" || val === "yes" || val === "on" || val === "approved" || val === "approve";
|
|
3438
|
+
}
|
|
3439
|
+
|
|
3440
|
+
// agent/showErrorToUser.ts
|
|
3441
|
+
function showErrorToUser(error, lastToolCallInfo) {
|
|
3442
|
+
logError(error);
|
|
3443
|
+
const err = error ?? {};
|
|
3444
|
+
const name = err.name || "Error";
|
|
3445
|
+
const message = err.message || String(err);
|
|
3446
|
+
const code = err.code ? ` code=${err.code}` : "";
|
|
3447
|
+
const status = err.status || err.statusCode || err.response?.status;
|
|
3448
|
+
const statusStr = status ? ` status=${status}` : "";
|
|
3449
|
+
const callIdMatch = message.match(/function call\s+(call_[A-Za-z0-9_-]+)/i);
|
|
3450
|
+
const callId = callIdMatch?.[1];
|
|
3451
|
+
const isNoToolOutput = /No tool output found for function call/i.test(message || "");
|
|
3452
|
+
const hint = isNoToolOutput ? `
|
|
3453
|
+
Hint: A tool likely threw or returned no result. Ensure tools always return a structured object (e.g., { status, output }) and never throw. If this followed a tool call${callId ? ` (${callId})` : ""}${lastToolCallInfo ? `: ${lastToolCallInfo}` : ""}, review that tool's implementation and logs.` : "";
|
|
3454
|
+
let responseDataSnippet = "";
|
|
3455
|
+
const data = err.response?.data ?? err.data;
|
|
3521
3456
|
if (data) {
|
|
3522
3457
|
try {
|
|
3523
3458
|
const s = typeof data === "string" ? data : JSON.stringify(data);
|
|
@@ -3543,14 +3478,11 @@ Last tool call: ${lastToolCallInfo}` : "";
|
|
|
3543
3478
|
// agent/runAgentForOnePass.ts
|
|
3544
3479
|
async function runAgentForOnePass(agent, session, input, controller, isPrompt) {
|
|
3545
3480
|
let lastToolCallInfo = null;
|
|
3546
|
-
const toolInfoMap = /* @__PURE__ */ new Map();
|
|
3547
|
-
const removeToolListener = addListener("RegisterToolInfo", (info) => {
|
|
3548
|
-
toolInfoMap.set(info.callId, info);
|
|
3549
|
-
});
|
|
3550
3481
|
try {
|
|
3551
3482
|
let hasStartedResponse = false;
|
|
3483
|
+
const planContext = await session.getPlanState();
|
|
3552
3484
|
let adjustedInput = input;
|
|
3553
|
-
const noPlanYet =
|
|
3485
|
+
const noPlanYet = planContext.planItems.length === 0 && (!planContext?.planDescription || planContext?.planDescription.trim().length === 0);
|
|
3554
3486
|
if (noPlanYet && typeof input === "string") {
|
|
3555
3487
|
const planningInstruction = [
|
|
3556
3488
|
isPrompt ? "The following request is from a non-interactive prompt. You MUST establish a comprehensive plan first, then execute it autonomously until completion." : "If there is no current plan, first establish one and keep it updated:",
|
|
@@ -3872,7 +3804,6 @@ async function runAgentForOnePass(agent, session, input, controller, isPrompt) {
|
|
|
3872
3804
|
}
|
|
3873
3805
|
emitToListeners("SetThinking", true);
|
|
3874
3806
|
setTimeout(curryEmitToListeners("SetInputMode", "waiting"), 1e3);
|
|
3875
|
-
removeToolListener();
|
|
3876
3807
|
return stream.state;
|
|
3877
3808
|
} else {
|
|
3878
3809
|
costTracker.recordTurn(
|
|
@@ -3882,7 +3813,6 @@ async function runAgentForOnePass(agent, session, input, controller, isPrompt) {
|
|
|
3882
3813
|
);
|
|
3883
3814
|
emitToListeners("UpdateCost", costTracker.getSessionStats());
|
|
3884
3815
|
}
|
|
3885
|
-
removeToolListener();
|
|
3886
3816
|
return null;
|
|
3887
3817
|
} catch (error) {
|
|
3888
3818
|
if (error?.name?.includes("MaxTurnsExceededError")) {
|
|
@@ -3906,6 +3836,65 @@ async function runAgentForOnePass(agent, session, input, controller, isPrompt) {
|
|
|
3906
3836
|
}
|
|
3907
3837
|
}
|
|
3908
3838
|
|
|
3839
|
+
// agent/translateSessionItemsToMessages.ts
|
|
3840
|
+
function translateSessionItemsToMessages(items) {
|
|
3841
|
+
const messages = [];
|
|
3842
|
+
if (items.length > 0) {
|
|
3843
|
+
let id = 0;
|
|
3844
|
+
for (const item of items) {
|
|
3845
|
+
if (item.type === "message" && item.role === "user") {
|
|
3846
|
+
messages.push({
|
|
3847
|
+
id: id++,
|
|
3848
|
+
type: "user",
|
|
3849
|
+
text: item.content,
|
|
3850
|
+
version: 1
|
|
3851
|
+
});
|
|
3852
|
+
} else if (item.type === "message" && item.role === "assistant") {
|
|
3853
|
+
if (typeof item.content === "string") {
|
|
3854
|
+
messages.push({
|
|
3855
|
+
id: id++,
|
|
3856
|
+
type: "agent",
|
|
3857
|
+
text: item.content,
|
|
3858
|
+
version: 1
|
|
3859
|
+
});
|
|
3860
|
+
} else if (Array.isArray(item.content)) {
|
|
3861
|
+
for (const part of item.content) {
|
|
3862
|
+
if (part.type === "text" || part.type === "output_text") {
|
|
3863
|
+
messages.push({
|
|
3864
|
+
id: id++,
|
|
3865
|
+
type: "agent",
|
|
3866
|
+
text: part.text,
|
|
3867
|
+
version: 1
|
|
3868
|
+
});
|
|
3869
|
+
} else if (part.type === "tool_call" || part.type === "function_call") {
|
|
3870
|
+
const args = typeof part.arguments === "string" ? part.arguments : part.arguments ? JSON.stringify(part.arguments) : "";
|
|
3871
|
+
const displayedArgs = args ? `(${args})` : "()";
|
|
3872
|
+
messages.push({
|
|
3873
|
+
id: id++,
|
|
3874
|
+
type: "tool",
|
|
3875
|
+
text: part.name,
|
|
3876
|
+
args: displayedArgs,
|
|
3877
|
+
version: 1
|
|
3878
|
+
});
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
} else if (item.type === "function_call") {
|
|
3883
|
+
const args = typeof item.arguments === "string" ? item.arguments : item.arguments ? JSON.stringify(item.arguments) : "";
|
|
3884
|
+
const displayedArgs = args ? `(${args})` : "()";
|
|
3885
|
+
messages.push({
|
|
3886
|
+
id: id++,
|
|
3887
|
+
type: "tool",
|
|
3888
|
+
text: item.name,
|
|
3889
|
+
args: displayedArgs,
|
|
3890
|
+
version: 1
|
|
3891
|
+
});
|
|
3892
|
+
}
|
|
3893
|
+
}
|
|
3894
|
+
}
|
|
3895
|
+
return messages;
|
|
3896
|
+
}
|
|
3897
|
+
|
|
3909
3898
|
// agent/AgentManager.ts
|
|
3910
3899
|
var AgentManager = class _AgentManager {
|
|
3911
3900
|
isInitialized = false;
|
|
@@ -3915,121 +3904,39 @@ var AgentManager = class _AgentManager {
|
|
|
3915
3904
|
agent = null;
|
|
3916
3905
|
session = null;
|
|
3917
3906
|
initialMessages = [];
|
|
3918
|
-
static instantiateAgent(tools) {
|
|
3907
|
+
static instantiateAgent(tools, instructions) {
|
|
3919
3908
|
return new Agent3({
|
|
3920
3909
|
name: "Harper Agent",
|
|
3921
3910
|
model: isOpenAIModel(trackedState.model) ? trackedState.model : getModel(trackedState.model),
|
|
3922
3911
|
modelSettings: getModelSettings(trackedState.model),
|
|
3923
|
-
instructions: readAgentSkillsRoot() || defaultInstructions(),
|
|
3912
|
+
instructions: instructions || readAgentSkillsRoot() || defaultInstructions(),
|
|
3924
3913
|
tools
|
|
3925
3914
|
});
|
|
3926
3915
|
}
|
|
3927
|
-
async initialize() {
|
|
3916
|
+
async initialize(agent, session) {
|
|
3928
3917
|
if (this.isInitialized) {
|
|
3929
3918
|
return;
|
|
3930
3919
|
}
|
|
3931
|
-
this.
|
|
3932
|
-
this.
|
|
3920
|
+
this.session = session || createSession(trackedState.sessionPath);
|
|
3921
|
+
this.agent = agent || _AgentManager.instantiateAgent([
|
|
3922
|
+
...createSharedTools(this.session),
|
|
3923
|
+
...createCLITools(this.session)
|
|
3924
|
+
]);
|
|
3933
3925
|
try {
|
|
3934
3926
|
const plan = await this.session?.getPlanState?.();
|
|
3935
3927
|
if (plan && typeof plan === "object") {
|
|
3936
3928
|
if (typeof plan.planDescription === "string") {
|
|
3937
|
-
globalPlanContext.planDescription = plan.planDescription;
|
|
3938
3929
|
emitToListeners("SetPlanDescription", plan.planDescription);
|
|
3939
3930
|
}
|
|
3940
3931
|
if (Array.isArray(plan.planItems)) {
|
|
3941
|
-
globalPlanContext.planItems = plan.planItems;
|
|
3942
|
-
const completedCount = plan.planItems.filter(
|
|
3943
|
-
(it) => it?.status === "done" || it?.status === "not-needed"
|
|
3944
|
-
).length;
|
|
3945
|
-
const progress = plan.planItems.length === 0 ? 0 : Math.round(completedCount / plan.planItems.length * 100);
|
|
3946
|
-
globalPlanContext.progress = progress;
|
|
3947
3932
|
emitToListeners("SetPlanItems", plan.planItems);
|
|
3948
3933
|
}
|
|
3949
3934
|
}
|
|
3950
3935
|
} catch {
|
|
3951
3936
|
}
|
|
3952
|
-
try {
|
|
3953
|
-
addListener("SetPlanDescription", async (desc) => {
|
|
3954
|
-
try {
|
|
3955
|
-
await this.session?.setPlanState?.({ planDescription: desc });
|
|
3956
|
-
} catch {
|
|
3957
|
-
}
|
|
3958
|
-
});
|
|
3959
|
-
addListener("SetPlanItems", async (items) => {
|
|
3960
|
-
if (Array.isArray(items)) {
|
|
3961
|
-
globalPlanContext.planItems = items;
|
|
3962
|
-
const completedCount = items.filter((it) => it?.status === "done" || it?.status === "not-needed").length;
|
|
3963
|
-
globalPlanContext.progress = items.length > 0 ? Math.round(completedCount / items.length * 100) : 0;
|
|
3964
|
-
}
|
|
3965
|
-
try {
|
|
3966
|
-
const completedCount = Array.isArray(items) ? items.filter((it) => it?.status === "done" || it?.status === "not-needed").length : 0;
|
|
3967
|
-
const progress = Array.isArray(items) && items.length > 0 ? Math.round(completedCount / items.length * 100) : 0;
|
|
3968
|
-
await this.session?.setPlanState?.({ planItems: items, progress });
|
|
3969
|
-
} catch {
|
|
3970
|
-
}
|
|
3971
|
-
});
|
|
3972
|
-
} catch {
|
|
3973
|
-
}
|
|
3974
3937
|
if (trackedState.sessionPath) {
|
|
3975
3938
|
const items = await this.session.getItems();
|
|
3976
|
-
|
|
3977
|
-
const messages = [];
|
|
3978
|
-
let id = 0;
|
|
3979
|
-
for (const item of items) {
|
|
3980
|
-
if (item.type === "message" && item.role === "user") {
|
|
3981
|
-
messages.push({
|
|
3982
|
-
id: id++,
|
|
3983
|
-
type: "user",
|
|
3984
|
-
text: item.content,
|
|
3985
|
-
version: 1
|
|
3986
|
-
});
|
|
3987
|
-
} else if (item.type === "message" && item.role === "assistant") {
|
|
3988
|
-
if (typeof item.content === "string") {
|
|
3989
|
-
messages.push({
|
|
3990
|
-
id: id++,
|
|
3991
|
-
type: "agent",
|
|
3992
|
-
text: item.content,
|
|
3993
|
-
version: 1
|
|
3994
|
-
});
|
|
3995
|
-
} else if (Array.isArray(item.content)) {
|
|
3996
|
-
for (const part of item.content) {
|
|
3997
|
-
if (part.type === "text" || part.type === "output_text") {
|
|
3998
|
-
messages.push({
|
|
3999
|
-
id: id++,
|
|
4000
|
-
type: "agent",
|
|
4001
|
-
text: part.text,
|
|
4002
|
-
version: 1
|
|
4003
|
-
});
|
|
4004
|
-
} else if (part.type === "tool_call" || part.type === "function_call") {
|
|
4005
|
-
const args = typeof part.arguments === "string" ? part.arguments : part.arguments ? JSON.stringify(part.arguments) : "";
|
|
4006
|
-
const displayedArgs = args ? `(${args})` : "()";
|
|
4007
|
-
messages.push({
|
|
4008
|
-
id: id++,
|
|
4009
|
-
type: "tool",
|
|
4010
|
-
text: part.name,
|
|
4011
|
-
args: displayedArgs,
|
|
4012
|
-
version: 1
|
|
4013
|
-
});
|
|
4014
|
-
}
|
|
4015
|
-
}
|
|
4016
|
-
}
|
|
4017
|
-
} else if (item.type === "function_call") {
|
|
4018
|
-
const args = typeof item.arguments === "string" ? item.arguments : item.arguments ? JSON.stringify(item.arguments) : "";
|
|
4019
|
-
const displayedArgs = args ? `(${args})` : "()";
|
|
4020
|
-
messages.push({
|
|
4021
|
-
id: id++,
|
|
4022
|
-
type: "tool",
|
|
4023
|
-
text: item.name,
|
|
4024
|
-
args: displayedArgs,
|
|
4025
|
-
version: 1
|
|
4026
|
-
});
|
|
4027
|
-
}
|
|
4028
|
-
}
|
|
4029
|
-
if (messages.length > 0) {
|
|
4030
|
-
this.initialMessages = messages;
|
|
4031
|
-
}
|
|
4032
|
-
}
|
|
3939
|
+
this.initialMessages = translateSessionItemsToMessages(items);
|
|
4033
3940
|
}
|
|
4034
3941
|
this.isInitialized = true;
|
|
4035
3942
|
if (trackedState.prompt?.trim?.()?.length) {
|
|
@@ -4098,7 +4005,8 @@ var AgentManager = class _AgentManager {
|
|
|
4098
4005
|
}
|
|
4099
4006
|
}
|
|
4100
4007
|
if (trackedState.autonomous && !this.resumeState) {
|
|
4101
|
-
const
|
|
4008
|
+
const planState = await this.session.getPlanState();
|
|
4009
|
+
const planItems = planState.planItems;
|
|
4102
4010
|
const hasPlan = planItems.length > 0;
|
|
4103
4011
|
const allDone = hasPlan && planItems.every((item) => item.status === "done" || item.status === "not-needed");
|
|
4104
4012
|
if (allDone) {
|
|
@@ -4150,2836 +4058,32 @@ var AgentManager = class _AgentManager {
|
|
|
4150
4058
|
};
|
|
4151
4059
|
var agentManager = new AgentManager();
|
|
4152
4060
|
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
id: 0,
|
|
4182
|
-
type: "agent",
|
|
4183
|
-
text: 'What shall we build today? (Type "/clear" to reset, "/skills" to add skills, or "/exit" to quit)',
|
|
4184
|
-
version: 1
|
|
4185
|
-
}];
|
|
4186
|
-
}
|
|
4187
|
-
return initialMessages;
|
|
4188
|
-
}
|
|
4189
|
-
var ChatProvider = ({
|
|
4190
|
-
children
|
|
4191
|
-
}) => {
|
|
4192
|
-
const [messages, setMessages] = useState3(getInitialMessages());
|
|
4193
|
-
const [userInputMode, setUserInputMode] = useState3("waiting");
|
|
4194
|
-
const [isThinking, setIsThinking] = useState3(false);
|
|
4195
|
-
const [isCompacting, setIsCompacting] = useState3(false);
|
|
4196
|
-
const [pullingState, setPullingState] = useState3(null);
|
|
4197
|
-
const [focusedArea, setFocusedArea] = useState3("input");
|
|
4198
|
-
useListener("PushNewMessages", (messages2) => {
|
|
4199
|
-
setMessages((prev) => {
|
|
4200
|
-
return prev.concat(
|
|
4201
|
-
messages2.map((message, index) => ({ ...message, id: prev.length + index, version: 1 }))
|
|
4202
|
-
);
|
|
4203
|
-
});
|
|
4204
|
-
}, []);
|
|
4205
|
-
useListener("SetInputMode", (newInputMode) => {
|
|
4206
|
-
setUserInputMode(newInputMode);
|
|
4207
|
-
}, []);
|
|
4208
|
-
useListener("SetThinking", (value2) => {
|
|
4209
|
-
setIsThinking(Boolean(value2));
|
|
4210
|
-
}, []);
|
|
4211
|
-
useListener("SetCompacting", (value2) => {
|
|
4212
|
-
setIsCompacting(Boolean(value2));
|
|
4213
|
-
}, []);
|
|
4214
|
-
useListener("SetPulling", (value2) => {
|
|
4215
|
-
setPullingState(value2);
|
|
4216
|
-
}, []);
|
|
4217
|
-
useListener("InterruptThought", () => {
|
|
4218
|
-
setIsThinking(false);
|
|
4219
|
-
}, []);
|
|
4220
|
-
useListener("UpdateLastMessageText", (text) => {
|
|
4221
|
-
setMessages((prev) => {
|
|
4222
|
-
if (prev.length === 0) {
|
|
4223
|
-
return prev;
|
|
4224
|
-
}
|
|
4225
|
-
const lastIndex = [...prev].reverse().findIndex((m) => m.type === "agent");
|
|
4226
|
-
if (lastIndex === -1) {
|
|
4227
|
-
return prev;
|
|
4228
|
-
}
|
|
4229
|
-
const actualIndex = prev.length - 1 - lastIndex;
|
|
4230
|
-
const last = prev[actualIndex];
|
|
4231
|
-
const updated = [...prev];
|
|
4232
|
-
updated[actualIndex] = {
|
|
4233
|
-
...last,
|
|
4234
|
-
text: last.text + text,
|
|
4235
|
-
version: last.version + 1
|
|
4236
|
-
};
|
|
4237
|
-
return updated;
|
|
4238
|
-
});
|
|
4239
|
-
}, []);
|
|
4240
|
-
useListener("ClearChatHistory", () => {
|
|
4241
|
-
agentManager.session?.clearSession();
|
|
4242
|
-
setMessages([{
|
|
4243
|
-
id: 0,
|
|
4244
|
-
type: "agent",
|
|
4245
|
-
text: "Chat cleared. What shall we build today?",
|
|
4246
|
-
version: 1
|
|
4247
|
-
}]);
|
|
4248
|
-
}, []);
|
|
4249
|
-
const value = useMemo3(() => ({
|
|
4250
|
-
messages,
|
|
4251
|
-
userInputMode,
|
|
4252
|
-
isThinking,
|
|
4253
|
-
isCompacting,
|
|
4254
|
-
pullingState,
|
|
4255
|
-
focusedArea,
|
|
4256
|
-
setFocusedArea
|
|
4257
|
-
}), [messages, userInputMode, isThinking, isCompacting, pullingState, focusedArea]);
|
|
4258
|
-
return /* @__PURE__ */ jsx3(ChatContext.Provider, { value, children });
|
|
4259
|
-
};
|
|
4260
|
-
|
|
4261
|
-
// ink/bindings/useMessageListener.ts
|
|
4262
|
-
var hasShownInterruptHint = false;
|
|
4263
|
-
function useMessageListener() {
|
|
4264
|
-
const { userInputMode, isThinking } = useChat();
|
|
4265
|
-
useListener("InterruptThought", () => {
|
|
4266
|
-
agentManager.interrupt();
|
|
4267
|
-
emitToListeners("PushNewMessages", [
|
|
4268
|
-
{
|
|
4269
|
-
type: "interrupted",
|
|
4270
|
-
text: "- thought interrupted -",
|
|
4271
|
-
version: 1
|
|
4272
|
-
}
|
|
4273
|
-
]);
|
|
4274
|
-
}, []);
|
|
4275
|
-
useListener("PushNewMessages", async (messages) => {
|
|
4276
|
-
for (const message of messages) {
|
|
4277
|
-
if ((message.type === "user" || message.type === "prompt") && message.text) {
|
|
4278
|
-
const lowerText = message.text.toLowerCase();
|
|
4279
|
-
if (lowerText === "exit" || lowerText === "bye") {
|
|
4280
|
-
await handleExit();
|
|
4281
|
-
}
|
|
4282
|
-
if (isThinking) {
|
|
4283
|
-
if (!hasShownInterruptHint) {
|
|
4284
|
-
hasShownInterruptHint = true;
|
|
4285
|
-
emitToListeners("PushNewMessages", [{
|
|
4286
|
-
type: "interrupted",
|
|
4287
|
-
text: "- to interrupt the current thinking, press escape -",
|
|
4288
|
-
version: 1
|
|
4289
|
-
}]);
|
|
4290
|
-
}
|
|
4291
|
-
agentManager.enqueueUserInput(message.text);
|
|
4292
|
-
message.handled = true;
|
|
4293
|
-
} else if (!message.handled) {
|
|
4294
|
-
void agentManager.runTask(message.text, message.type === "prompt");
|
|
4295
|
-
}
|
|
4296
|
-
}
|
|
4297
|
-
}
|
|
4298
|
-
}, [userInputMode, isThinking]);
|
|
4299
|
-
}
|
|
4300
|
-
|
|
4301
|
-
// ink/constants/footerHeight.ts
|
|
4302
|
-
var footerHeight = 2;
|
|
4303
|
-
|
|
4304
|
-
// ink/contexts/ApprovalContext.tsx
|
|
4305
|
-
import { createContext as createContext4, useCallback as useCallback2, useContext as useContext4, useMemo as useMemo4, useState as useState4 } from "react";
|
|
4306
|
-
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
4307
|
-
var ApprovalContext = createContext4(void 0);
|
|
4308
|
-
var useApproval = () => {
|
|
4309
|
-
const context = useContext4(ApprovalContext);
|
|
4310
|
-
if (!context) {
|
|
4311
|
-
throw new Error("useApproval must be used within an ApprovalProvider");
|
|
4312
|
-
}
|
|
4313
|
-
return context;
|
|
4314
|
-
};
|
|
4315
|
-
var ApprovalProvider = ({ children }) => {
|
|
4316
|
-
const [payload, setPayload] = useState4(null);
|
|
4317
|
-
const [toolInfos] = useState4(/* @__PURE__ */ new Map());
|
|
4318
|
-
const registerToolInfo = useCallback2(
|
|
4319
|
-
(info) => {
|
|
4320
|
-
toolInfos.set(info.callId, info);
|
|
4321
|
-
},
|
|
4322
|
-
[toolInfos]
|
|
4323
|
-
);
|
|
4324
|
-
useListener("OpenApprovalViewer", (p) => {
|
|
4325
|
-
let finalPayload = { ...p, openedAt: Date.now() };
|
|
4326
|
-
if (p.mode === "info" && p.callId) {
|
|
4327
|
-
const info = toolInfos.get(p.callId);
|
|
4328
|
-
if (info) {
|
|
4329
|
-
finalPayload = { ...finalPayload, ...info };
|
|
4330
|
-
}
|
|
4331
|
-
} else if (p.callId) {
|
|
4332
|
-
registerToolInfo({
|
|
4333
|
-
callId: p.callId,
|
|
4334
|
-
type: p.type,
|
|
4335
|
-
path: p.path,
|
|
4336
|
-
diff: p.diff,
|
|
4337
|
-
code: p.code,
|
|
4338
|
-
commands: p.commands
|
|
4339
|
-
});
|
|
4340
|
-
}
|
|
4341
|
-
setPayload(finalPayload);
|
|
4342
|
-
}, [registerToolInfo, toolInfos]);
|
|
4343
|
-
useListener("RegisterToolInfo", (info) => {
|
|
4344
|
-
registerToolInfo(info);
|
|
4345
|
-
}, [registerToolInfo]);
|
|
4346
|
-
useListener("CloseApprovalViewer", () => {
|
|
4347
|
-
setPayload(null);
|
|
4348
|
-
}, []);
|
|
4349
|
-
const value = useMemo4(() => ({
|
|
4350
|
-
payload,
|
|
4351
|
-
setPayload,
|
|
4352
|
-
registerToolInfo
|
|
4353
|
-
}), [payload, registerToolInfo]);
|
|
4354
|
-
return /* @__PURE__ */ jsx4(ApprovalContext.Provider, { value, children });
|
|
4355
|
-
};
|
|
4356
|
-
|
|
4357
|
-
// ink/contexts/PlanContext.tsx
|
|
4358
|
-
import { createContext as createContext5, useContext as useContext5, useMemo as useMemo5, useState as useState5 } from "react";
|
|
4359
|
-
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
4360
|
-
var PlanContext = createContext5(globalPlanContext);
|
|
4361
|
-
var usePlan = () => {
|
|
4362
|
-
const context = useContext5(PlanContext);
|
|
4363
|
-
if (!context) {
|
|
4364
|
-
throw new Error("usePlan must be used within a PlanProvider");
|
|
4365
|
-
}
|
|
4366
|
-
return context;
|
|
4367
|
-
};
|
|
4368
|
-
var PlanProvider = ({
|
|
4369
|
-
children
|
|
4370
|
-
}) => {
|
|
4371
|
-
const [planDescription, setPlanDescription] = useState5(globalPlanContext.planDescription);
|
|
4372
|
-
const [planItems, setPlanItems] = useState5(globalPlanContext.planItems);
|
|
4373
|
-
const [progress, setProgress] = useState5(globalPlanContext.progress);
|
|
4374
|
-
useListener("SetPlanDescription", (newGoal) => {
|
|
4375
|
-
globalPlanContext.planDescription = newGoal;
|
|
4376
|
-
setPlanDescription(newGoal);
|
|
4377
|
-
}, []);
|
|
4378
|
-
useListener("SetPlanItems", (planItems2) => {
|
|
4379
|
-
globalPlanContext.planItems = planItems2;
|
|
4380
|
-
const completedCount = planItems2.filter((item) => item.status === "done" || item.status === "not-needed").length;
|
|
4381
|
-
const progress2 = planItems2.length === 0 ? 0 : Math.round(completedCount / planItems2.length * 100);
|
|
4382
|
-
globalPlanContext.progress = progress2;
|
|
4383
|
-
setPlanItems(planItems2);
|
|
4384
|
-
setProgress(progress2);
|
|
4385
|
-
}, []);
|
|
4386
|
-
const value = useMemo5(() => ({
|
|
4387
|
-
progress,
|
|
4388
|
-
planDescription,
|
|
4389
|
-
planItems
|
|
4390
|
-
}), [progress, planDescription, planItems]);
|
|
4391
|
-
return /* @__PURE__ */ jsx5(PlanContext.Provider, { value, children });
|
|
4061
|
+
export {
|
|
4062
|
+
useListener,
|
|
4063
|
+
emitToListeners,
|
|
4064
|
+
lastEmittedValue,
|
|
4065
|
+
curryEmitToListeners,
|
|
4066
|
+
rateLimitTracker,
|
|
4067
|
+
trackedState,
|
|
4068
|
+
resetTrackedState,
|
|
4069
|
+
defaultModelToken,
|
|
4070
|
+
defaultOpenAIModel,
|
|
4071
|
+
defaultOpenAICompactionModel,
|
|
4072
|
+
defaultAnthropicModel,
|
|
4073
|
+
defaultAnthropicCompactionModel,
|
|
4074
|
+
defaultGoogleModel,
|
|
4075
|
+
defaultGoogleCompactionModel,
|
|
4076
|
+
defaultOllamaModel,
|
|
4077
|
+
defaultOllamaCompactionModel,
|
|
4078
|
+
isOpenAIModel,
|
|
4079
|
+
handleExit,
|
|
4080
|
+
setupGlobalErrorHandlers,
|
|
4081
|
+
updateEnv,
|
|
4082
|
+
fetchOllamaModels,
|
|
4083
|
+
excludeFalsy,
|
|
4084
|
+
useActions,
|
|
4085
|
+
ActionsProvider,
|
|
4086
|
+
isTrue,
|
|
4087
|
+
AgentManager,
|
|
4088
|
+
agentManager
|
|
4392
4089
|
};
|
|
4393
|
-
|
|
4394
|
-
// ink/library/useTerminalSize.ts
|
|
4395
|
-
import { useStdout } from "ink";
|
|
4396
|
-
import { useEffect as useEffect3, useState as useState6 } from "react";
|
|
4397
|
-
function useTerminalSize() {
|
|
4398
|
-
const { stdout } = useStdout();
|
|
4399
|
-
const [size, setSize] = useState6({
|
|
4400
|
-
rows: stdout?.rows ?? 24,
|
|
4401
|
-
columns: stdout?.columns ?? 80
|
|
4402
|
-
});
|
|
4403
|
-
useEffect3(() => {
|
|
4404
|
-
if (!stdout || !stdout.isTTY) {
|
|
4405
|
-
return;
|
|
4406
|
-
}
|
|
4407
|
-
const handleResize = () => {
|
|
4408
|
-
setSize({
|
|
4409
|
-
rows: stdout.rows ?? 24,
|
|
4410
|
-
columns: stdout.columns ?? 80
|
|
4411
|
-
});
|
|
4412
|
-
};
|
|
4413
|
-
stdout.on("resize", handleResize);
|
|
4414
|
-
return () => {
|
|
4415
|
-
stdout.off("resize", handleResize);
|
|
4416
|
-
};
|
|
4417
|
-
}, [stdout]);
|
|
4418
|
-
return size;
|
|
4419
|
-
}
|
|
4420
|
-
|
|
4421
|
-
// ink/library/wrapText.ts
|
|
4422
|
-
function wrapText(text, width) {
|
|
4423
|
-
if (width <= 1) {
|
|
4424
|
-
return text ? [text] : [""];
|
|
4425
|
-
}
|
|
4426
|
-
const hardLines = text.split("\n");
|
|
4427
|
-
const allWrappedLines = [];
|
|
4428
|
-
for (const hardLine of hardLines) {
|
|
4429
|
-
if (hardLine.length === 0) {
|
|
4430
|
-
allWrappedLines.push("");
|
|
4431
|
-
continue;
|
|
4432
|
-
}
|
|
4433
|
-
const words = hardLine.split(/(\s+)/);
|
|
4434
|
-
let currentLine = "";
|
|
4435
|
-
for (const token of words) {
|
|
4436
|
-
if (token.length === 0) {
|
|
4437
|
-
continue;
|
|
4438
|
-
}
|
|
4439
|
-
if (currentLine.length + token.length <= width) {
|
|
4440
|
-
currentLine += token;
|
|
4441
|
-
} else {
|
|
4442
|
-
if (currentLine) {
|
|
4443
|
-
allWrappedLines.push(currentLine.trimEnd());
|
|
4444
|
-
}
|
|
4445
|
-
if (token.trim().length === 0) {
|
|
4446
|
-
currentLine = "";
|
|
4447
|
-
} else if (token.length > width) {
|
|
4448
|
-
for (let i = 0; i < token.length; i += width) {
|
|
4449
|
-
const chunk = token.slice(i, i + width);
|
|
4450
|
-
if (chunk.length === width) {
|
|
4451
|
-
allWrappedLines.push(chunk);
|
|
4452
|
-
} else {
|
|
4453
|
-
currentLine = chunk;
|
|
4454
|
-
}
|
|
4455
|
-
}
|
|
4456
|
-
} else {
|
|
4457
|
-
currentLine = token;
|
|
4458
|
-
}
|
|
4459
|
-
}
|
|
4460
|
-
}
|
|
4461
|
-
if (currentLine) {
|
|
4462
|
-
allWrappedLines.push(currentLine.trimEnd());
|
|
4463
|
-
}
|
|
4464
|
-
}
|
|
4465
|
-
return allWrappedLines.length ? allWrappedLines : [""];
|
|
4466
|
-
}
|
|
4467
|
-
|
|
4468
|
-
// ink/components/ActionsView.tsx
|
|
4469
|
-
import { Box as Box3, Text as Text3, useInput } from "ink";
|
|
4470
|
-
import { useCallback as useCallback3, useEffect as useEffect5, useState as useState8 } from "react";
|
|
4471
|
-
|
|
4472
|
-
// ink/components/ActionItemRow.tsx
|
|
4473
|
-
import { Spinner } from "@inkjs/ui";
|
|
4474
|
-
import { Box, Text } from "ink";
|
|
4475
|
-
import { memo } from "react";
|
|
4476
|
-
import { jsx as jsx6, jsxs } from "react/jsx-runtime";
|
|
4477
|
-
var ActionItemRow = memo(
|
|
4478
|
-
({ item, isSelected, isFocused, width }) => {
|
|
4479
|
-
const statusColor = item.running ? "yellow" : item.status === "approved" || item.status === "succeeded" || typeof item.exitCode === "number" && item.exitCode === 0 ? "green" : item.status === "denied" || item.status === "failed" || typeof item.exitCode === "number" && item.exitCode !== 0 ? "red" : "cyan";
|
|
4480
|
-
const selectionColor = isFocused ? "cyan" : "gray";
|
|
4481
|
-
const pipe = /* @__PURE__ */ jsx6(Text, { color: isSelected ? selectionColor : "gray", bold: isSelected, children: isSelected ? "\u2503 " : "\u2502 " });
|
|
4482
|
-
const statusLabel = item.running ? " " : item.kind === "approval" ? item.status === "approved" ? " approved" : item.status === "denied" ? " denied" : "" : typeof item.exitCode === "number" ? ` ${item.exitCode}` : "";
|
|
4483
|
-
const statusLabelWidth = item.running ? 2 : statusLabel.length;
|
|
4484
|
-
const pipeWidth = 3;
|
|
4485
|
-
const nameWithSpace = `${item.title} `;
|
|
4486
|
-
const availableForDetail = width - pipeWidth - nameWithSpace.length - statusLabelWidth;
|
|
4487
|
-
const fullDetail = item.detail || "";
|
|
4488
|
-
let displayedDetail = fullDetail;
|
|
4489
|
-
if (availableForDetail < fullDetail.length) {
|
|
4490
|
-
if (availableForDetail > 3) {
|
|
4491
|
-
displayedDetail = fullDetail.slice(0, availableForDetail - 3) + "...";
|
|
4492
|
-
} else {
|
|
4493
|
-
displayedDetail = fullDetail.slice(0, Math.max(0, availableForDetail));
|
|
4494
|
-
}
|
|
4495
|
-
}
|
|
4496
|
-
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
4497
|
-
pipe,
|
|
4498
|
-
/* @__PURE__ */ jsxs(Box, { flexGrow: 1, children: [
|
|
4499
|
-
/* @__PURE__ */ jsx6(Text, { color: statusColor, bold: true, children: item.title }),
|
|
4500
|
-
/* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
4501
|
-
" ",
|
|
4502
|
-
displayedDetail
|
|
4503
|
-
] })
|
|
4504
|
-
] }),
|
|
4505
|
-
/* @__PURE__ */ jsx6(Box, { children: item.running ? /* @__PURE__ */ jsxs(Box, { children: [
|
|
4506
|
-
/* @__PURE__ */ jsx6(Text, { color: "yellow" }),
|
|
4507
|
-
/* @__PURE__ */ jsx6(Spinner, { type: "dots" })
|
|
4508
|
-
] }) : /* @__PURE__ */ jsx6(Text, { color: statusColor, bold: true, children: statusLabel }) })
|
|
4509
|
-
] });
|
|
4510
|
-
}
|
|
4511
|
-
);
|
|
4512
|
-
|
|
4513
|
-
// ink/components/VirtualList.tsx
|
|
4514
|
-
import { Box as Box2, Text as Text2 } from "ink";
|
|
4515
|
-
import { forwardRef, useEffect as useEffect4, useImperativeHandle, useMemo as useMemo6, useRef, useState as useState7 } from "react";
|
|
4516
|
-
import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
4517
|
-
function getDefaultKey(item, index) {
|
|
4518
|
-
if (item && typeof item === "object") {
|
|
4519
|
-
if ("id" in item && (typeof item.id === "string" || typeof item.id === "number")) {
|
|
4520
|
-
return String(item.id);
|
|
4521
|
-
}
|
|
4522
|
-
if ("key" in item && (typeof item.key === "string" || typeof item.key === "number")) {
|
|
4523
|
-
return String(item.key);
|
|
4524
|
-
}
|
|
4525
|
-
}
|
|
4526
|
-
return String(index);
|
|
4527
|
-
}
|
|
4528
|
-
var VirtualListInner = (props, ref) => {
|
|
4529
|
-
const {
|
|
4530
|
-
items,
|
|
4531
|
-
renderItem,
|
|
4532
|
-
getItemHeight: getItemHeightProp,
|
|
4533
|
-
itemHeight: fixedItemHeight,
|
|
4534
|
-
selectedIndex = 0,
|
|
4535
|
-
keyExtractor,
|
|
4536
|
-
height: propHeight,
|
|
4537
|
-
reservedLines = 0,
|
|
4538
|
-
showOverflowIndicators = true,
|
|
4539
|
-
overflowIndicatorThreshold = 1,
|
|
4540
|
-
renderOverflowTop,
|
|
4541
|
-
renderOverflowBottom,
|
|
4542
|
-
renderFiller,
|
|
4543
|
-
onViewportChange
|
|
4544
|
-
} = props;
|
|
4545
|
-
const { rows: terminalRows } = useTerminalSize();
|
|
4546
|
-
const resolvedHeight = useMemo6(() => {
|
|
4547
|
-
if (typeof propHeight === "number") {
|
|
4548
|
-
return propHeight;
|
|
4549
|
-
}
|
|
4550
|
-
if (typeof propHeight === "string" && propHeight.endsWith("%")) {
|
|
4551
|
-
const percent = parseFloat(propHeight) / 100;
|
|
4552
|
-
return Math.floor(terminalRows * percent);
|
|
4553
|
-
}
|
|
4554
|
-
return Math.max(1, terminalRows - reservedLines);
|
|
4555
|
-
}, [propHeight, terminalRows, reservedLines]);
|
|
4556
|
-
const getItemHeight = useMemo6(() => {
|
|
4557
|
-
if (getItemHeightProp) {
|
|
4558
|
-
return getItemHeightProp;
|
|
4559
|
-
}
|
|
4560
|
-
if (fixedItemHeight) {
|
|
4561
|
-
return () => fixedItemHeight;
|
|
4562
|
-
}
|
|
4563
|
-
return () => 1;
|
|
4564
|
-
}, [getItemHeightProp, fixedItemHeight]);
|
|
4565
|
-
const indicatorLines = showOverflowIndicators ? 2 : 0;
|
|
4566
|
-
const availableHeight = Math.max(0, resolvedHeight - indicatorLines);
|
|
4567
|
-
const [viewportOffset, setViewportOffset] = useState7(0);
|
|
4568
|
-
const lastItemsLength = useRef(items.length);
|
|
4569
|
-
const viewportOffsetRef = useRef(viewportOffset);
|
|
4570
|
-
useEffect4(() => {
|
|
4571
|
-
viewportOffsetRef.current = viewportOffset;
|
|
4572
|
-
}, [viewportOffset]);
|
|
4573
|
-
const clampedSelectedIndex = Math.max(0, Math.min(selectedIndex, items.length - 1));
|
|
4574
|
-
const { visibleCount, visibleItems } = useMemo6(() => {
|
|
4575
|
-
if (availableHeight <= 0) {
|
|
4576
|
-
return { visibleCount: 0, visibleItems: [] };
|
|
4577
|
-
}
|
|
4578
|
-
let currentHeight = 0;
|
|
4579
|
-
let count = 0;
|
|
4580
|
-
const visibleItems2 = [];
|
|
4581
|
-
for (let i = viewportOffset; i < items.length; i++) {
|
|
4582
|
-
const h = getItemHeight(items[i], i);
|
|
4583
|
-
if (currentHeight + h > availableHeight && count > 0) {
|
|
4584
|
-
break;
|
|
4585
|
-
}
|
|
4586
|
-
currentHeight += h;
|
|
4587
|
-
count++;
|
|
4588
|
-
visibleItems2.push(items[i]);
|
|
4589
|
-
}
|
|
4590
|
-
return { visibleCount: count, visibleItems: visibleItems2 };
|
|
4591
|
-
}, [items, viewportOffset, availableHeight, getItemHeight]);
|
|
4592
|
-
useEffect4(() => {
|
|
4593
|
-
if (items.length === 0 || availableHeight <= 0) {
|
|
4594
|
-
lastItemsLength.current = items.length;
|
|
4595
|
-
return;
|
|
4596
|
-
}
|
|
4597
|
-
const currentViewportOffset = viewportOffsetRef.current;
|
|
4598
|
-
const isSelectingLast = clampedSelectedIndex === items.length - 1;
|
|
4599
|
-
const wasAtBottom = lastItemsLength.current > 0 && currentViewportOffset + visibleCount >= lastItemsLength.current - 1;
|
|
4600
|
-
lastItemsLength.current = items.length;
|
|
4601
|
-
if (clampedSelectedIndex < currentViewportOffset) {
|
|
4602
|
-
setViewportOffset(clampedSelectedIndex);
|
|
4603
|
-
return;
|
|
4604
|
-
}
|
|
4605
|
-
let heightToSelected = 0;
|
|
4606
|
-
let isVisible = false;
|
|
4607
|
-
for (let i = currentViewportOffset; i <= clampedSelectedIndex; i++) {
|
|
4608
|
-
const h = getItemHeight(items[i], i);
|
|
4609
|
-
if (heightToSelected + h > availableHeight) {
|
|
4610
|
-
isVisible = false;
|
|
4611
|
-
break;
|
|
4612
|
-
}
|
|
4613
|
-
heightToSelected += h;
|
|
4614
|
-
if (i === clampedSelectedIndex) {
|
|
4615
|
-
isVisible = true;
|
|
4616
|
-
}
|
|
4617
|
-
}
|
|
4618
|
-
if (!isVisible || isSelectingLast || wasAtBottom) {
|
|
4619
|
-
let tempOffset = clampedSelectedIndex;
|
|
4620
|
-
let hSum2 = 0;
|
|
4621
|
-
while (tempOffset >= 0) {
|
|
4622
|
-
const h = getItemHeight(items[tempOffset], tempOffset);
|
|
4623
|
-
if (hSum2 + h > availableHeight) {
|
|
4624
|
-
tempOffset++;
|
|
4625
|
-
break;
|
|
4626
|
-
}
|
|
4627
|
-
hSum2 += h;
|
|
4628
|
-
if (tempOffset === 0) {
|
|
4629
|
-
break;
|
|
4630
|
-
}
|
|
4631
|
-
tempOffset--;
|
|
4632
|
-
}
|
|
4633
|
-
const finalOffset = Math.max(0, tempOffset);
|
|
4634
|
-
if (finalOffset !== currentViewportOffset) {
|
|
4635
|
-
setViewportOffset(finalOffset);
|
|
4636
|
-
}
|
|
4637
|
-
}
|
|
4638
|
-
}, [clampedSelectedIndex, items, availableHeight, getItemHeight, visibleCount]);
|
|
4639
|
-
const viewport = useMemo6(
|
|
4640
|
-
() => ({
|
|
4641
|
-
offset: viewportOffset,
|
|
4642
|
-
visibleCount,
|
|
4643
|
-
totalCount: items.length
|
|
4644
|
-
}),
|
|
4645
|
-
[viewportOffset, visibleCount, items.length]
|
|
4646
|
-
);
|
|
4647
|
-
const isInitialMount = useRef(true);
|
|
4648
|
-
useEffect4(() => {
|
|
4649
|
-
if (isInitialMount.current) {
|
|
4650
|
-
if (items.length > 0 && availableHeight > 0) {
|
|
4651
|
-
isInitialMount.current = false;
|
|
4652
|
-
let tempOffset = items.length - 1;
|
|
4653
|
-
let hSum2 = 0;
|
|
4654
|
-
while (tempOffset >= 0) {
|
|
4655
|
-
const h = getItemHeight(items[tempOffset], tempOffset);
|
|
4656
|
-
if (hSum2 + h > availableHeight) {
|
|
4657
|
-
tempOffset++;
|
|
4658
|
-
break;
|
|
4659
|
-
}
|
|
4660
|
-
hSum2 += h;
|
|
4661
|
-
if (tempOffset === 0) {
|
|
4662
|
-
break;
|
|
4663
|
-
}
|
|
4664
|
-
tempOffset--;
|
|
4665
|
-
}
|
|
4666
|
-
setViewportOffset(Math.max(0, tempOffset));
|
|
4667
|
-
}
|
|
4668
|
-
}
|
|
4669
|
-
}, [availableHeight, items.length, getItemHeight]);
|
|
4670
|
-
useEffect4(() => {
|
|
4671
|
-
onViewportChange?.(viewport);
|
|
4672
|
-
}, [viewport, onViewportChange]);
|
|
4673
|
-
useImperativeHandle(
|
|
4674
|
-
ref,
|
|
4675
|
-
() => ({
|
|
4676
|
-
scrollToIndex: (index, alignment = "auto") => {
|
|
4677
|
-
const clampedIndex = Math.max(0, Math.min(index, items.length - 1));
|
|
4678
|
-
let newOffset = viewportOffset;
|
|
4679
|
-
switch (alignment) {
|
|
4680
|
-
case "top":
|
|
4681
|
-
newOffset = clampedIndex;
|
|
4682
|
-
break;
|
|
4683
|
-
case "center": {
|
|
4684
|
-
let hSum2 = 0;
|
|
4685
|
-
let tempOffset = clampedIndex;
|
|
4686
|
-
while (tempOffset >= 0 && hSum2 < availableHeight / 2) {
|
|
4687
|
-
hSum2 += getItemHeight(items[tempOffset], tempOffset);
|
|
4688
|
-
tempOffset--;
|
|
4689
|
-
}
|
|
4690
|
-
newOffset = tempOffset + 1;
|
|
4691
|
-
break;
|
|
4692
|
-
}
|
|
4693
|
-
case "bottom": {
|
|
4694
|
-
let hSum2 = 0;
|
|
4695
|
-
let tempOffset = clampedIndex;
|
|
4696
|
-
while (tempOffset >= 0 && hSum2 < availableHeight) {
|
|
4697
|
-
const h = getItemHeight(items[tempOffset], tempOffset);
|
|
4698
|
-
if (hSum2 + h > availableHeight) {
|
|
4699
|
-
tempOffset++;
|
|
4700
|
-
break;
|
|
4701
|
-
}
|
|
4702
|
-
hSum2 += h;
|
|
4703
|
-
if (tempOffset === 0) {
|
|
4704
|
-
break;
|
|
4705
|
-
}
|
|
4706
|
-
tempOffset--;
|
|
4707
|
-
}
|
|
4708
|
-
newOffset = tempOffset;
|
|
4709
|
-
break;
|
|
4710
|
-
}
|
|
4711
|
-
case "auto":
|
|
4712
|
-
default: {
|
|
4713
|
-
if (clampedIndex < viewportOffset) {
|
|
4714
|
-
newOffset = clampedIndex;
|
|
4715
|
-
} else {
|
|
4716
|
-
let heightToSelected = 0;
|
|
4717
|
-
let isVisible = false;
|
|
4718
|
-
for (let i = viewportOffset; i <= clampedIndex; i++) {
|
|
4719
|
-
const h = getItemHeight(items[i], i);
|
|
4720
|
-
if (heightToSelected + h > availableHeight) {
|
|
4721
|
-
isVisible = false;
|
|
4722
|
-
break;
|
|
4723
|
-
}
|
|
4724
|
-
heightToSelected += h;
|
|
4725
|
-
if (i === clampedIndex) {
|
|
4726
|
-
isVisible = true;
|
|
4727
|
-
}
|
|
4728
|
-
}
|
|
4729
|
-
if (!isVisible) {
|
|
4730
|
-
let hSum2 = 0;
|
|
4731
|
-
let tempOffset = clampedIndex;
|
|
4732
|
-
while (tempOffset >= 0) {
|
|
4733
|
-
const h = getItemHeight(items[tempOffset], tempOffset);
|
|
4734
|
-
if (hSum2 + h > availableHeight) {
|
|
4735
|
-
tempOffset++;
|
|
4736
|
-
break;
|
|
4737
|
-
}
|
|
4738
|
-
hSum2 += h;
|
|
4739
|
-
if (tempOffset === 0) {
|
|
4740
|
-
break;
|
|
4741
|
-
}
|
|
4742
|
-
tempOffset--;
|
|
4743
|
-
}
|
|
4744
|
-
newOffset = tempOffset;
|
|
4745
|
-
}
|
|
4746
|
-
}
|
|
4747
|
-
}
|
|
4748
|
-
}
|
|
4749
|
-
setViewportOffset(Math.max(0, newOffset));
|
|
4750
|
-
},
|
|
4751
|
-
getViewport: () => viewport
|
|
4752
|
-
}),
|
|
4753
|
-
[items, viewportOffset, availableHeight, getItemHeight, viewport]
|
|
4754
|
-
);
|
|
4755
|
-
const overflowTop = viewportOffset;
|
|
4756
|
-
let itemsFittingBelow = 0;
|
|
4757
|
-
let hSum = 0;
|
|
4758
|
-
for (let i = viewportOffset; i < items.length; i++) {
|
|
4759
|
-
const h = getItemHeight(items[i], i);
|
|
4760
|
-
if (hSum + h > availableHeight) {
|
|
4761
|
-
break;
|
|
4762
|
-
}
|
|
4763
|
-
hSum += h;
|
|
4764
|
-
itemsFittingBelow++;
|
|
4765
|
-
}
|
|
4766
|
-
const overflowBottom = Math.max(0, items.length - viewportOffset - itemsFittingBelow);
|
|
4767
|
-
const defaultOverflowTop = (count) => {
|
|
4768
|
-
if (count < overflowIndicatorThreshold) {
|
|
4769
|
-
return null;
|
|
4770
|
-
}
|
|
4771
|
-
return /* @__PURE__ */ jsx7(Box2, { paddingLeft: 2, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
4772
|
-
"\u25B2 ",
|
|
4773
|
-
count,
|
|
4774
|
-
" more"
|
|
4775
|
-
] }) });
|
|
4776
|
-
};
|
|
4777
|
-
const defaultOverflowBottom = (count) => {
|
|
4778
|
-
if (count < overflowIndicatorThreshold) {
|
|
4779
|
-
return null;
|
|
4780
|
-
}
|
|
4781
|
-
return /* @__PURE__ */ jsx7(Box2, { paddingLeft: 2, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
4782
|
-
"\u25BC ",
|
|
4783
|
-
count,
|
|
4784
|
-
" more"
|
|
4785
|
-
] }) });
|
|
4786
|
-
};
|
|
4787
|
-
const topIndicator = renderOverflowTop ?? defaultOverflowTop;
|
|
4788
|
-
const bottomIndicator = renderOverflowBottom ?? defaultOverflowBottom;
|
|
4789
|
-
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: resolvedHeight, children: [
|
|
4790
|
-
showOverflowIndicators && /* @__PURE__ */ jsx7(Box2, { height: 1, children: topIndicator(overflowTop) }),
|
|
4791
|
-
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", flexGrow: 1, children: [
|
|
4792
|
-
visibleItems.map((item, idx) => {
|
|
4793
|
-
const actualIndex = viewportOffset + idx;
|
|
4794
|
-
const key = keyExtractor ? keyExtractor(item, actualIndex) : getDefaultKey(item, actualIndex);
|
|
4795
|
-
const itemProps = {
|
|
4796
|
-
item,
|
|
4797
|
-
index: actualIndex,
|
|
4798
|
-
isSelected: actualIndex === clampedSelectedIndex
|
|
4799
|
-
};
|
|
4800
|
-
return /* @__PURE__ */ jsx7(Box2, { height: getItemHeight(item, actualIndex), overflow: "hidden", children: renderItem(itemProps) }, key);
|
|
4801
|
-
}),
|
|
4802
|
-
renderFiller && hSum < availableHeight && /* @__PURE__ */ jsx7(Box2, { flexDirection: "column", flexGrow: 1, children: renderFiller(availableHeight - hSum) })
|
|
4803
|
-
] }),
|
|
4804
|
-
showOverflowIndicators && /* @__PURE__ */ jsx7(Box2, { height: 1, children: bottomIndicator(overflowBottom) })
|
|
4805
|
-
] });
|
|
4806
|
-
};
|
|
4807
|
-
var VirtualList = forwardRef(VirtualListInner);
|
|
4808
|
-
|
|
4809
|
-
// ink/components/ActionsView.tsx
|
|
4810
|
-
import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
4811
|
-
function ActionsView({ height, isFocused }) {
|
|
4812
|
-
const { actions } = useActions();
|
|
4813
|
-
const [selectedIndex, setSelectedIndex] = useState8(0);
|
|
4814
|
-
const size = useTerminalSize();
|
|
4815
|
-
const timelineWidth = Math.floor(size.columns * 0.65);
|
|
4816
|
-
const statusWidth = size.columns - timelineWidth;
|
|
4817
|
-
useEffect5(() => {
|
|
4818
|
-
if (actions.length > 0) {
|
|
4819
|
-
setSelectedIndex(actions.length - 1);
|
|
4820
|
-
}
|
|
4821
|
-
}, [actions.length]);
|
|
4822
|
-
useInput((_, key) => {
|
|
4823
|
-
if (!isFocused) {
|
|
4824
|
-
return;
|
|
4825
|
-
}
|
|
4826
|
-
if (key.upArrow) {
|
|
4827
|
-
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
4828
|
-
}
|
|
4829
|
-
if (key.downArrow) {
|
|
4830
|
-
setSelectedIndex((prev) => Math.min(actions.length - 1, prev + 1));
|
|
4831
|
-
}
|
|
4832
|
-
if (key.return) {
|
|
4833
|
-
const selected = actions[selectedIndex];
|
|
4834
|
-
if (selected && (selected.kind === "apply_patch" || selected.kind === "approval" || selected.title === "shell" || selected.title === "code_interpreter")) {
|
|
4835
|
-
if (selected.callId) {
|
|
4836
|
-
emitToListeners("OpenApprovalViewer", {
|
|
4837
|
-
type: selected.kind === "apply_patch" ? "update_file" : selected.title,
|
|
4838
|
-
mode: "info",
|
|
4839
|
-
callId: selected.callId,
|
|
4840
|
-
actionId: selected.id
|
|
4841
|
-
});
|
|
4842
|
-
}
|
|
4843
|
-
}
|
|
4844
|
-
}
|
|
4845
|
-
});
|
|
4846
|
-
const renderOverflowTop = useCallback3((count) => /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
4847
|
-
/* @__PURE__ */ jsx8(Text3, { color: "gray", dimColor: true, children: "\u2502" }),
|
|
4848
|
-
count > 0 && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
4849
|
-
" ",
|
|
4850
|
-
"\u25B2 ",
|
|
4851
|
-
count,
|
|
4852
|
-
" more"
|
|
4853
|
-
] })
|
|
4854
|
-
] }), []);
|
|
4855
|
-
const renderOverflowBottom = useCallback3((count) => /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
4856
|
-
/* @__PURE__ */ jsx8(Text3, { color: "gray", dimColor: true, children: "\u2502" }),
|
|
4857
|
-
count > 0 && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
4858
|
-
" ",
|
|
4859
|
-
"\u25BC ",
|
|
4860
|
-
count,
|
|
4861
|
-
" more"
|
|
4862
|
-
] })
|
|
4863
|
-
] }), []);
|
|
4864
|
-
const renderFiller = useCallback3((h) => /* @__PURE__ */ jsx8(Box3, { flexDirection: "column", children: Array.from({ length: h }).map((_, i) => /* @__PURE__ */ jsx8(Box3, { children: /* @__PURE__ */ jsx8(Text3, { color: "gray", dimColor: true, children: "\u2502" }) }, i)) }), []);
|
|
4865
|
-
const getItemHeight = useCallback3((_item) => 1, []);
|
|
4866
|
-
if (actions.length === 0) {
|
|
4867
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", flexGrow: 1, height, children: [
|
|
4868
|
-
/* @__PURE__ */ jsx8(Box3, { children: /* @__PURE__ */ jsx8(Text3, { italic: true, color: "gray", children: "No actions yet." }) }),
|
|
4869
|
-
Array.from({ length: height - 1 }).map((_, i) => /* @__PURE__ */ jsx8(Box3, { children: /* @__PURE__ */ jsx8(Text3, {}) }, i))
|
|
4870
|
-
] });
|
|
4871
|
-
}
|
|
4872
|
-
return /* @__PURE__ */ jsx8(Box3, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx8(
|
|
4873
|
-
VirtualList,
|
|
4874
|
-
{
|
|
4875
|
-
items: actions,
|
|
4876
|
-
getItemHeight,
|
|
4877
|
-
height,
|
|
4878
|
-
selectedIndex,
|
|
4879
|
-
renderOverflowTop,
|
|
4880
|
-
renderOverflowBottom,
|
|
4881
|
-
renderFiller,
|
|
4882
|
-
renderItem: ({ item, isSelected }) => /* @__PURE__ */ jsx8(ActionItemRow, { item, isSelected, isFocused, width: statusWidth - 2 })
|
|
4883
|
-
}
|
|
4884
|
-
) });
|
|
4885
|
-
}
|
|
4886
|
-
|
|
4887
|
-
// ink/components/CostView.tsx
|
|
4888
|
-
import { Box as Box4, Text as Text4 } from "ink";
|
|
4889
|
-
import "react";
|
|
4890
|
-
|
|
4891
|
-
// ink/contexts/CostContext.tsx
|
|
4892
|
-
import { createContext as createContext6, useContext as useContext6, useMemo as useMemo7, useState as useState9 } from "react";
|
|
4893
|
-
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
4894
|
-
var CostContext = createContext6(void 0);
|
|
4895
|
-
var useCost = () => {
|
|
4896
|
-
const context = useContext6(CostContext);
|
|
4897
|
-
if (!context) {
|
|
4898
|
-
throw new Error("useCost must be used within a CostProvider");
|
|
4899
|
-
}
|
|
4900
|
-
return context;
|
|
4901
|
-
};
|
|
4902
|
-
var CostProvider = ({
|
|
4903
|
-
children
|
|
4904
|
-
}) => {
|
|
4905
|
-
const [cost, setCost] = useState9({
|
|
4906
|
-
totalCost: 0,
|
|
4907
|
-
inputTokens: 0,
|
|
4908
|
-
outputTokens: 0,
|
|
4909
|
-
cachedInputTokens: 0,
|
|
4910
|
-
hasUnknownPrices: false
|
|
4911
|
-
});
|
|
4912
|
-
useListener("UpdateCost", (newCost) => {
|
|
4913
|
-
setCost(newCost);
|
|
4914
|
-
}, []);
|
|
4915
|
-
const value = useMemo7(() => ({
|
|
4916
|
-
cost
|
|
4917
|
-
}), [cost]);
|
|
4918
|
-
return /* @__PURE__ */ jsx9(CostContext.Provider, { value, children });
|
|
4919
|
-
};
|
|
4920
|
-
|
|
4921
|
-
// ink/components/CostView.tsx
|
|
4922
|
-
import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
4923
|
-
function CostView({ isDense = false }) {
|
|
4924
|
-
const { cost } = useCost();
|
|
4925
|
-
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingLeft: 1, paddingTop: isDense ? 0 : 1, children: [
|
|
4926
|
-
/* @__PURE__ */ jsx10(Box4, { marginBottom: isDense ? 0 : 1, children: /* @__PURE__ */ jsx10(Text4, { bold: true, underline: true, color: "cyan", children: "Session Cost Breakdown" }) }),
|
|
4927
|
-
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
4928
|
-
/* @__PURE__ */ jsx10(Box4, { width: 20, children: /* @__PURE__ */ jsx10(Text4, { children: "Total Cost:" }) }),
|
|
4929
|
-
/* @__PURE__ */ jsxs4(Text4, { color: "green", children: [
|
|
4930
|
-
"$",
|
|
4931
|
-
cost.totalCost.toFixed(4)
|
|
4932
|
-
] }),
|
|
4933
|
-
cost.hasUnknownPrices && /* @__PURE__ */ jsx10(Text4, { color: "yellow", children: "(est.)" })
|
|
4934
|
-
] }),
|
|
4935
|
-
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
4936
|
-
/* @__PURE__ */ jsx10(Box4, { width: 20, children: /* @__PURE__ */ jsx10(Text4, { children: "Input Tokens:" }) }),
|
|
4937
|
-
/* @__PURE__ */ jsx10(Text4, { children: cost.inputTokens.toLocaleString() })
|
|
4938
|
-
] }),
|
|
4939
|
-
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
4940
|
-
/* @__PURE__ */ jsx10(Box4, { width: 20, children: /* @__PURE__ */ jsx10(Text4, { children: "Cached Tokens:" }) }),
|
|
4941
|
-
/* @__PURE__ */ jsx10(Text4, { color: "gray", children: cost.cachedInputTokens.toLocaleString() })
|
|
4942
|
-
] }),
|
|
4943
|
-
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
4944
|
-
/* @__PURE__ */ jsx10(Box4, { width: 20, children: /* @__PURE__ */ jsx10(Text4, { children: "Output Tokens:" }) }),
|
|
4945
|
-
/* @__PURE__ */ jsx10(Text4, { children: cost.outputTokens.toLocaleString() })
|
|
4946
|
-
] })
|
|
4947
|
-
] });
|
|
4948
|
-
}
|
|
4949
|
-
|
|
4950
|
-
// ink/components/MessageLineItem.tsx
|
|
4951
|
-
import { Box as Box5, Text as Text5 } from "ink";
|
|
4952
|
-
import { memo as memo2 } from "react";
|
|
4953
|
-
import { Fragment, jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
4954
|
-
var MessageLineItem = memo2(
|
|
4955
|
-
({
|
|
4956
|
-
item,
|
|
4957
|
-
isSelected,
|
|
4958
|
-
isFocused,
|
|
4959
|
-
indent = 0
|
|
4960
|
-
}) => {
|
|
4961
|
-
const color = messageTypeToColor(item.type);
|
|
4962
|
-
const label = item.type === "interrupted" ? "" : item.type.toUpperCase();
|
|
4963
|
-
const selectionColor = isFocused ? "cyan" : "gray";
|
|
4964
|
-
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
4965
|
-
/* @__PURE__ */ jsx11(Text5, { color: isSelected ? selectionColor : "gray", dimColor: !isSelected, bold: isSelected, children: isSelected ? "\u2503 " : "\u2502 " }),
|
|
4966
|
-
/* @__PURE__ */ jsx11(Box5, { paddingLeft: indent, children: item.isFirstLine ? /* @__PURE__ */ jsxs5(Text5, { children: [
|
|
4967
|
-
label && /* @__PURE__ */ jsxs5(Text5, { bold: true, color, children: [
|
|
4968
|
-
label,
|
|
4969
|
-
item.type === "tool" ? ": " : item.isArgsLine ? " args: " : ": "
|
|
4970
|
-
] }),
|
|
4971
|
-
item.type === "tool" && item.toolName ? /* @__PURE__ */ jsx11(Text5, { children: item.text.startsWith(item.toolName) ? /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
4972
|
-
/* @__PURE__ */ jsx11(Text5, { color: "white", bold: true, children: item.toolName }),
|
|
4973
|
-
/* @__PURE__ */ jsx11(Text5, { dimColor: true, italic: true, children: item.text.slice(item.toolName.length) })
|
|
4974
|
-
] }) : /* @__PURE__ */ jsx11(Text5, { dimColor: true, italic: true, children: item.text.startsWith(" ") ? item.text : ` ${item.text}` }) }) : /* @__PURE__ */ jsx11(Text5, { dimColor: !!item.isArgsLine, italic: !!item.isArgsLine, children: item.text })
|
|
4975
|
-
] }) : /* @__PURE__ */ jsx11(Text5, { children: item.type === "tool" && item.toolName ? /* @__PURE__ */ jsx11(Text5, { dimColor: true, italic: true, children: item.text }) : /* @__PURE__ */ jsx11(Text5, { dimColor: !!item.isArgsLine, italic: !!item.isArgsLine, children: item.text }) }) })
|
|
4976
|
-
] });
|
|
4977
|
-
}
|
|
4978
|
-
);
|
|
4979
|
-
function messageTypeToColor(type) {
|
|
4980
|
-
switch (type) {
|
|
4981
|
-
case "prompt":
|
|
4982
|
-
case "user":
|
|
4983
|
-
return "green";
|
|
4984
|
-
case "agent":
|
|
4985
|
-
return "cyan";
|
|
4986
|
-
case "interrupted":
|
|
4987
|
-
return "gray";
|
|
4988
|
-
default:
|
|
4989
|
-
return "magenta";
|
|
4990
|
-
}
|
|
4991
|
-
}
|
|
4992
|
-
|
|
4993
|
-
// ink/components/PlanView.tsx
|
|
4994
|
-
import { ProgressBar } from "@inkjs/ui";
|
|
4995
|
-
import { Box as Box6, Text as Text6 } from "ink";
|
|
4996
|
-
import "react";
|
|
4997
|
-
import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
4998
|
-
function PlanView() {
|
|
4999
|
-
const { planDescription, planItems, progress } = usePlan();
|
|
5000
|
-
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", flexGrow: 1, children: [
|
|
5001
|
-
/* @__PURE__ */ jsx12(Box6, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx12(Text6, { italic: true, children: planDescription }) }),
|
|
5002
|
-
/* @__PURE__ */ jsx12(Box6, { flexDirection: "column", flexGrow: 1, children: planItems.map((planItem) => /* @__PURE__ */ jsx12(Box6, { children: /* @__PURE__ */ jsxs6(
|
|
5003
|
-
Text6,
|
|
5004
|
-
{
|
|
5005
|
-
color: planItem.status === "done" ? "green" : planItem.status === "in-progress" ? "yellow" : planItem.status === "not-needed" ? "gray" : "white",
|
|
5006
|
-
dimColor: planItem.status === "not-needed",
|
|
5007
|
-
children: [
|
|
5008
|
-
planItem.status === "done" ? " \u25CF " : planItem.status === "in-progress" ? " \u25B6 " : planItem.status === "not-needed" ? " \u25CC " : " \u25CB ",
|
|
5009
|
-
planItem.text
|
|
5010
|
-
]
|
|
5011
|
-
}
|
|
5012
|
-
) }, planItem.id)) }),
|
|
5013
|
-
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, children: [
|
|
5014
|
-
/* @__PURE__ */ jsxs6(Text6, { bold: true, children: [
|
|
5015
|
-
"PROGRESS: ",
|
|
5016
|
-
progress,
|
|
5017
|
-
"%"
|
|
5018
|
-
] }),
|
|
5019
|
-
/* @__PURE__ */ jsx12(ProgressBar, { value: progress })
|
|
5020
|
-
] })
|
|
5021
|
-
] });
|
|
5022
|
-
}
|
|
5023
|
-
|
|
5024
|
-
// ink/components/SettingsView.tsx
|
|
5025
|
-
import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
|
|
5026
|
-
import path9 from "path";
|
|
5027
|
-
import { useEffect as useEffect6, useMemo as useMemo9, useState as useState11 } from "react";
|
|
5028
|
-
|
|
5029
|
-
// ink/contexts/SettingsContext.tsx
|
|
5030
|
-
import { createContext as createContext7, useContext as useContext7, useMemo as useMemo8, useState as useState10 } from "react";
|
|
5031
|
-
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
5032
|
-
var SettingsContext = createContext7(void 0);
|
|
5033
|
-
var useSettings = () => {
|
|
5034
|
-
const context = useContext7(SettingsContext);
|
|
5035
|
-
if (!context) {
|
|
5036
|
-
throw new Error("useSettings must be used within a SettingsProvider");
|
|
5037
|
-
}
|
|
5038
|
-
return context;
|
|
5039
|
-
};
|
|
5040
|
-
var SettingsProvider = ({
|
|
5041
|
-
children
|
|
5042
|
-
}) => {
|
|
5043
|
-
const [version, setVersion] = useState10(0);
|
|
5044
|
-
useListener("SettingsUpdated", () => {
|
|
5045
|
-
setVersion((v) => v + 1);
|
|
5046
|
-
}, []);
|
|
5047
|
-
const value = useMemo8(() => ({
|
|
5048
|
-
version,
|
|
5049
|
-
model: trackedState.model,
|
|
5050
|
-
compactionModel: trackedState.compactionModel,
|
|
5051
|
-
sessionPath: trackedState.sessionPath,
|
|
5052
|
-
cwd: trackedState.cwd,
|
|
5053
|
-
useFlexTier: trackedState.useFlexTier,
|
|
5054
|
-
currentTurn: trackedState.currentTurn,
|
|
5055
|
-
maxTurns: trackedState.maxTurns,
|
|
5056
|
-
maxCost: trackedState.maxCost,
|
|
5057
|
-
autoApproveCodeInterpreter: trackedState.autoApproveCodeInterpreter,
|
|
5058
|
-
autoApprovePatches: trackedState.autoApprovePatches,
|
|
5059
|
-
autoApproveShell: trackedState.autoApproveShell,
|
|
5060
|
-
monitorRateLimits: trackedState.monitorRateLimits,
|
|
5061
|
-
rateLimitThreshold: trackedState.rateLimitThreshold,
|
|
5062
|
-
rateLimitStatus: rateLimitTracker.getStatus()
|
|
5063
|
-
}), [version]);
|
|
5064
|
-
return /* @__PURE__ */ jsx13(SettingsContext.Provider, { value, children });
|
|
5065
|
-
};
|
|
5066
|
-
|
|
5067
|
-
// ink/components/SettingsView.tsx
|
|
5068
|
-
import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
5069
|
-
function SettingsView({ isDense = false }) {
|
|
5070
|
-
const {
|
|
5071
|
-
model,
|
|
5072
|
-
compactionModel,
|
|
5073
|
-
sessionPath,
|
|
5074
|
-
cwd,
|
|
5075
|
-
useFlexTier,
|
|
5076
|
-
currentTurn,
|
|
5077
|
-
maxTurns,
|
|
5078
|
-
maxCost,
|
|
5079
|
-
autoApproveCodeInterpreter: initialAutoApproveCodeInterpreter,
|
|
5080
|
-
autoApprovePatches: initialAutoApprovePatches,
|
|
5081
|
-
autoApproveShell: initialAutoApproveShell,
|
|
5082
|
-
monitorRateLimits: initialMonitorRateLimits,
|
|
5083
|
-
rateLimitThreshold: initialRateLimitThreshold,
|
|
5084
|
-
rateLimitStatus
|
|
5085
|
-
} = useSettings();
|
|
5086
|
-
const { focusedArea } = useChat();
|
|
5087
|
-
const [autoApproveCodeInterpreter, setAutoApproveCodeInterpreter] = useState11(initialAutoApproveCodeInterpreter);
|
|
5088
|
-
const [autoApprovePatches, setAutoApprovePatches] = useState11(initialAutoApprovePatches);
|
|
5089
|
-
const [autoApproveShell, setAutoApproveShell] = useState11(initialAutoApproveShell);
|
|
5090
|
-
const [monitorRateLimits, setMonitorRateLimits] = useState11(initialMonitorRateLimits);
|
|
5091
|
-
useEffect6(() => {
|
|
5092
|
-
setAutoApproveCodeInterpreter(initialAutoApproveCodeInterpreter);
|
|
5093
|
-
}, [initialAutoApproveCodeInterpreter]);
|
|
5094
|
-
useEffect6(() => {
|
|
5095
|
-
setAutoApprovePatches(initialAutoApprovePatches);
|
|
5096
|
-
}, [initialAutoApprovePatches]);
|
|
5097
|
-
useEffect6(() => {
|
|
5098
|
-
setAutoApproveShell(initialAutoApproveShell);
|
|
5099
|
-
}, [initialAutoApproveShell]);
|
|
5100
|
-
useEffect6(() => {
|
|
5101
|
-
setMonitorRateLimits(initialMonitorRateLimits);
|
|
5102
|
-
}, [initialMonitorRateLimits]);
|
|
5103
|
-
const [selectedIndex, setSelectedIndex] = useState11(0);
|
|
5104
|
-
const selectableOptions = useMemo9(() => [
|
|
5105
|
-
{
|
|
5106
|
-
label: "Code Interpreter",
|
|
5107
|
-
value: autoApproveCodeInterpreter,
|
|
5108
|
-
envKey: "HARPER_AGENT_AUTO_APPROVE_CODE_INTERPRETER",
|
|
5109
|
-
setter: setAutoApproveCodeInterpreter
|
|
5110
|
-
},
|
|
5111
|
-
{
|
|
5112
|
-
label: "File Patches",
|
|
5113
|
-
value: autoApprovePatches,
|
|
5114
|
-
envKey: "HARPER_AGENT_AUTO_APPROVE_PATCHES",
|
|
5115
|
-
setter: setAutoApprovePatches
|
|
5116
|
-
},
|
|
5117
|
-
{
|
|
5118
|
-
label: "Shell Commands",
|
|
5119
|
-
value: autoApproveShell,
|
|
5120
|
-
envKey: "HARPER_AGENT_AUTO_APPROVE_SHELL",
|
|
5121
|
-
setter: setAutoApproveShell
|
|
5122
|
-
},
|
|
5123
|
-
{
|
|
5124
|
-
label: "Monitor Rate Limits",
|
|
5125
|
-
value: monitorRateLimits,
|
|
5126
|
-
envKey: "HARPER_AGENT_MONITOR_RATE_LIMITS",
|
|
5127
|
-
setter: (val) => {
|
|
5128
|
-
setMonitorRateLimits(val);
|
|
5129
|
-
updateEnv("HARPER_AGENT_MONITOR_RATE_LIMITS", val ? "true" : "false");
|
|
5130
|
-
emitToListeners("SettingsUpdated", void 0);
|
|
5131
|
-
}
|
|
5132
|
-
},
|
|
5133
|
-
{
|
|
5134
|
-
label: "<edit settings>",
|
|
5135
|
-
isAction: true,
|
|
5136
|
-
action: bootstrapConfig
|
|
5137
|
-
}
|
|
5138
|
-
], [autoApproveCodeInterpreter, autoApprovePatches, autoApproveShell, monitorRateLimits]);
|
|
5139
|
-
useInput2((_input, key) => {
|
|
5140
|
-
if (focusedArea !== "status") {
|
|
5141
|
-
return;
|
|
5142
|
-
}
|
|
5143
|
-
if (key.upArrow) {
|
|
5144
|
-
setSelectedIndex((prev) => prev > 0 ? prev - 1 : selectableOptions.length - 1);
|
|
5145
|
-
}
|
|
5146
|
-
if (key.downArrow) {
|
|
5147
|
-
setSelectedIndex((prev) => prev < selectableOptions.length - 1 ? prev + 1 : 0);
|
|
5148
|
-
}
|
|
5149
|
-
if (key.return || _input === " ") {
|
|
5150
|
-
const selected = selectableOptions[selectedIndex];
|
|
5151
|
-
if (!selected) {
|
|
5152
|
-
return;
|
|
5153
|
-
}
|
|
5154
|
-
if ("isAction" in selected && selected.isAction) {
|
|
5155
|
-
selected.action();
|
|
5156
|
-
} else if ("setter" in selected) {
|
|
5157
|
-
const newValue = !selected.value;
|
|
5158
|
-
selected.setter(newValue);
|
|
5159
|
-
updateEnv(selected.envKey, newValue ? "1" : "0");
|
|
5160
|
-
emitToListeners("SettingsUpdated", void 0);
|
|
5161
|
-
}
|
|
5162
|
-
}
|
|
5163
|
-
});
|
|
5164
|
-
const displayPath = useMemo9(() => {
|
|
5165
|
-
if (!sessionPath) {
|
|
5166
|
-
return null;
|
|
5167
|
-
}
|
|
5168
|
-
const relative = path9.relative(cwd, sessionPath);
|
|
5169
|
-
if (!relative.startsWith("..") && !path9.isAbsolute(relative)) {
|
|
5170
|
-
return `./${relative}`;
|
|
5171
|
-
}
|
|
5172
|
-
return sessionPath;
|
|
5173
|
-
}, [cwd, sessionPath]);
|
|
5174
|
-
const marginBottom = isDense ? 0 : 1;
|
|
5175
|
-
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", flexGrow: 1, paddingLeft: 1, children: [
|
|
5176
|
-
/* @__PURE__ */ jsx14(Box7, { marginBottom, children: /* @__PURE__ */ jsx14(Text7, { bold: true, underline: true, color: "cyan", children: "Configuration" }) }),
|
|
5177
|
-
/* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
5178
|
-
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "Model:" }) }),
|
|
5179
|
-
/* @__PURE__ */ jsx14(Text7, { children: model })
|
|
5180
|
-
] }),
|
|
5181
|
-
/* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
5182
|
-
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "Compaction Model:" }) }),
|
|
5183
|
-
/* @__PURE__ */ jsx14(Text7, { children: compactionModel })
|
|
5184
|
-
] }),
|
|
5185
|
-
displayPath && /* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
5186
|
-
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "Session Path:" }) }),
|
|
5187
|
-
/* @__PURE__ */ jsx14(Text7, { children: displayPath })
|
|
5188
|
-
] }),
|
|
5189
|
-
/* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
5190
|
-
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "Flex Tier:" }) }),
|
|
5191
|
-
/* @__PURE__ */ jsx14(Text7, { children: useFlexTier ? "Yes" : "No" })
|
|
5192
|
-
] }),
|
|
5193
|
-
/* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
5194
|
-
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "Current Turn:" }) }),
|
|
5195
|
-
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
5196
|
-
currentTurn,
|
|
5197
|
-
" / ",
|
|
5198
|
-
maxTurns
|
|
5199
|
-
] })
|
|
5200
|
-
] }),
|
|
5201
|
-
/* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
5202
|
-
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "Rate Limit @:" }) }),
|
|
5203
|
-
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
5204
|
-
initialRateLimitThreshold,
|
|
5205
|
-
"%"
|
|
5206
|
-
] })
|
|
5207
|
-
] }),
|
|
5208
|
-
rateLimitStatus && /* @__PURE__ */ jsxs7(Box7, { marginBottom, flexDirection: "column", children: [
|
|
5209
|
-
rateLimitStatus.retryAfter !== null && /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
5210
|
-
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { color: "red", children: "Retry After:" }) }),
|
|
5211
|
-
/* @__PURE__ */ jsxs7(Text7, { color: "red", children: [
|
|
5212
|
-
rateLimitStatus.retryAfter,
|
|
5213
|
-
"s"
|
|
5214
|
-
] })
|
|
5215
|
-
] }),
|
|
5216
|
-
(rateLimitStatus.limitRequests !== null || rateLimitStatus.remainingRequests !== null) && /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
5217
|
-
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "RPM Limit:" }) }),
|
|
5218
|
-
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
5219
|
-
rateLimitStatus.remainingRequests ?? "?",
|
|
5220
|
-
" / ",
|
|
5221
|
-
rateLimitStatus.limitRequests ?? "?",
|
|
5222
|
-
" (Reset:",
|
|
5223
|
-
" ",
|
|
5224
|
-
rateLimitStatus.resetRequests ?? "?",
|
|
5225
|
-
")"
|
|
5226
|
-
] })
|
|
5227
|
-
] }),
|
|
5228
|
-
rateLimitStatus.remainingTokens !== null && /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
5229
|
-
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "TPM Limit:" }) }),
|
|
5230
|
-
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
5231
|
-
rateLimitStatus.remainingTokens ?? "?",
|
|
5232
|
-
" / ",
|
|
5233
|
-
rateLimitStatus.limitTokens ?? "?",
|
|
5234
|
-
" (Reset:",
|
|
5235
|
-
" ",
|
|
5236
|
-
rateLimitStatus.resetTokens ?? "?",
|
|
5237
|
-
")"
|
|
5238
|
-
] })
|
|
5239
|
-
] })
|
|
5240
|
-
] }),
|
|
5241
|
-
maxCost !== null && /* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
5242
|
-
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "Max Cost:" }) }),
|
|
5243
|
-
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
5244
|
-
"$",
|
|
5245
|
-
maxCost.toFixed(2)
|
|
5246
|
-
] })
|
|
5247
|
-
] }),
|
|
5248
|
-
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
5249
|
-
/* @__PURE__ */ jsx14(Text7, { bold: true, children: "Auto-approvals (up/down & space to toggle):" }),
|
|
5250
|
-
selectableOptions.map((option, index) => {
|
|
5251
|
-
const isSelected = index === selectedIndex && focusedArea === "status";
|
|
5252
|
-
return /* @__PURE__ */ jsx14(Box7, { children: /* @__PURE__ */ jsxs7(Text7, { color: isSelected ? "cyan" : "white", children: [
|
|
5253
|
-
isSelected ? "> " : " ",
|
|
5254
|
-
option.label,
|
|
5255
|
-
"isAction" in option ? "" : `: ${option.value ? "ON" : "OFF"}`
|
|
5256
|
-
] }) }, option.label);
|
|
5257
|
-
})
|
|
5258
|
-
] })
|
|
5259
|
-
] });
|
|
5260
|
-
}
|
|
5261
|
-
|
|
5262
|
-
// ink/components/UserInput.tsx
|
|
5263
|
-
import { Box as Box9, Text as Text9 } from "ink";
|
|
5264
|
-
import { exec as exec4 } from "child_process";
|
|
5265
|
-
import { promisify as promisify13 } from "util";
|
|
5266
|
-
import { useCallback as useCallback4, useState as useState13 } from "react";
|
|
5267
|
-
|
|
5268
|
-
// ink/components/BlinkingTextInput.tsx
|
|
5269
|
-
import chalk3 from "chalk";
|
|
5270
|
-
import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
|
|
5271
|
-
import { useEffect as useEffect7, useMemo as useMemo10, useReducer, useState as useState12 } from "react";
|
|
5272
|
-
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
5273
|
-
var reducer = (state, action) => {
|
|
5274
|
-
switch (action.type) {
|
|
5275
|
-
case "move-cursor-left":
|
|
5276
|
-
return {
|
|
5277
|
-
...state,
|
|
5278
|
-
cursorOffset: Math.max(0, state.cursorOffset - 1)
|
|
5279
|
-
};
|
|
5280
|
-
case "move-cursor-right":
|
|
5281
|
-
return {
|
|
5282
|
-
...state,
|
|
5283
|
-
cursorOffset: Math.min(state.value.length, state.cursorOffset + 1)
|
|
5284
|
-
};
|
|
5285
|
-
case "insert":
|
|
5286
|
-
return {
|
|
5287
|
-
...state,
|
|
5288
|
-
previousValue: state.value,
|
|
5289
|
-
value: state.value.slice(0, state.cursorOffset) + action.text + state.value.slice(state.cursorOffset),
|
|
5290
|
-
cursorOffset: state.cursorOffset + action.text.length
|
|
5291
|
-
};
|
|
5292
|
-
case "newline":
|
|
5293
|
-
return {
|
|
5294
|
-
...state,
|
|
5295
|
-
previousValue: state.value,
|
|
5296
|
-
value: state.value.slice(0, state.cursorOffset) + "\n" + state.value.slice(state.cursorOffset),
|
|
5297
|
-
cursorOffset: state.cursorOffset + 1
|
|
5298
|
-
};
|
|
5299
|
-
case "delete": {
|
|
5300
|
-
const newCursorOffset = Math.max(0, state.cursorOffset - 1);
|
|
5301
|
-
return {
|
|
5302
|
-
...state,
|
|
5303
|
-
previousValue: state.value,
|
|
5304
|
-
value: state.value.slice(0, newCursorOffset) + state.value.slice(state.cursorOffset),
|
|
5305
|
-
cursorOffset: newCursorOffset
|
|
5306
|
-
};
|
|
5307
|
-
}
|
|
5308
|
-
default:
|
|
5309
|
-
return state;
|
|
5310
|
-
}
|
|
5311
|
-
};
|
|
5312
|
-
function BlinkingTextInput({
|
|
5313
|
-
isDisabled = false,
|
|
5314
|
-
defaultValue = "",
|
|
5315
|
-
placeholder = "",
|
|
5316
|
-
suggestions = [],
|
|
5317
|
-
onChange,
|
|
5318
|
-
onSubmit
|
|
5319
|
-
}) {
|
|
5320
|
-
const [state, dispatch] = useReducer(reducer, {
|
|
5321
|
-
value: defaultValue,
|
|
5322
|
-
previousValue: defaultValue,
|
|
5323
|
-
cursorOffset: defaultValue.length
|
|
5324
|
-
});
|
|
5325
|
-
const [isCursorVisible, setIsCursorVisible] = useState12(true);
|
|
5326
|
-
useEffect7(() => {
|
|
5327
|
-
if (isDisabled) {
|
|
5328
|
-
setIsCursorVisible(false);
|
|
5329
|
-
return;
|
|
5330
|
-
}
|
|
5331
|
-
const interval = setInterval(() => {
|
|
5332
|
-
setIsCursorVisible((prev) => !prev);
|
|
5333
|
-
}, 500);
|
|
5334
|
-
return () => clearInterval(interval);
|
|
5335
|
-
}, [isDisabled]);
|
|
5336
|
-
useEffect7(() => {
|
|
5337
|
-
if (state.value !== state.previousValue) {
|
|
5338
|
-
onChange?.(state.value);
|
|
5339
|
-
}
|
|
5340
|
-
}, [state.value, state.previousValue, onChange]);
|
|
5341
|
-
const suggestion = useMemo10(() => {
|
|
5342
|
-
if (state.value.length === 0) {
|
|
5343
|
-
return "";
|
|
5344
|
-
}
|
|
5345
|
-
return suggestions.find((s) => s.startsWith(state.value))?.slice(state.value.length) || "";
|
|
5346
|
-
}, [state.value, suggestions]);
|
|
5347
|
-
useInput3(
|
|
5348
|
-
(input, key) => {
|
|
5349
|
-
if (key.upArrow || key.downArrow || key.ctrl && input === "c" || key.tab || key.shift && key.tab) {
|
|
5350
|
-
return;
|
|
5351
|
-
}
|
|
5352
|
-
setIsCursorVisible(true);
|
|
5353
|
-
if (key.return) {
|
|
5354
|
-
if (key.meta) {
|
|
5355
|
-
dispatch({ type: "newline" });
|
|
5356
|
-
return;
|
|
5357
|
-
}
|
|
5358
|
-
if (suggestion) {
|
|
5359
|
-
onSubmit?.(state.value + suggestion);
|
|
5360
|
-
} else {
|
|
5361
|
-
onSubmit?.(state.value);
|
|
5362
|
-
}
|
|
5363
|
-
return;
|
|
5364
|
-
}
|
|
5365
|
-
if (key.leftArrow) {
|
|
5366
|
-
dispatch({ type: "move-cursor-left" });
|
|
5367
|
-
} else if (key.rightArrow) {
|
|
5368
|
-
dispatch({ type: "move-cursor-right" });
|
|
5369
|
-
} else if (key.backspace || key.delete) {
|
|
5370
|
-
dispatch({ type: "delete" });
|
|
5371
|
-
} else if (input) {
|
|
5372
|
-
dispatch({ type: "insert", text: input });
|
|
5373
|
-
}
|
|
5374
|
-
},
|
|
5375
|
-
{ isActive: !isDisabled }
|
|
5376
|
-
);
|
|
5377
|
-
const renderedValue = useMemo10(() => {
|
|
5378
|
-
if (isDisabled) {
|
|
5379
|
-
const lines2 = (state.value || (placeholder ? chalk3.dim(placeholder) : "")).split("\n");
|
|
5380
|
-
return /* @__PURE__ */ jsx15(Box8, { flexDirection: "column", children: lines2.map((line, i) => /* @__PURE__ */ jsx15(Box8, { children: /* @__PURE__ */ jsx15(Text8, { children: line }) }, i)) });
|
|
5381
|
-
}
|
|
5382
|
-
if (state.value.length === 0) {
|
|
5383
|
-
let displayContent = "";
|
|
5384
|
-
if (placeholder && placeholder.length > 0) {
|
|
5385
|
-
displayContent = (isCursorVisible ? chalk3.inverse(placeholder[0]) : placeholder[0]) + chalk3.dim(placeholder.slice(1));
|
|
5386
|
-
} else {
|
|
5387
|
-
displayContent = isCursorVisible ? chalk3.inverse(" ") : " ";
|
|
5388
|
-
}
|
|
5389
|
-
return /* @__PURE__ */ jsx15(Box8, { flexGrow: 1, minWidth: 1, children: /* @__PURE__ */ jsx15(Text8, { wrap: "end", children: displayContent }) });
|
|
5390
|
-
}
|
|
5391
|
-
const lines = state.value.split("\n");
|
|
5392
|
-
const result = [];
|
|
5393
|
-
let totalOffset = 0;
|
|
5394
|
-
lines.forEach((line, lineIndex) => {
|
|
5395
|
-
let lineContent = "";
|
|
5396
|
-
for (let i = 0; i < line.length; i++) {
|
|
5397
|
-
const char = line[i];
|
|
5398
|
-
if (totalOffset === state.cursorOffset) {
|
|
5399
|
-
lineContent += isCursorVisible ? chalk3.inverse(char) : char;
|
|
5400
|
-
} else {
|
|
5401
|
-
lineContent += char;
|
|
5402
|
-
}
|
|
5403
|
-
totalOffset++;
|
|
5404
|
-
}
|
|
5405
|
-
if (totalOffset === state.cursorOffset) {
|
|
5406
|
-
if (lineIndex === lines.length - 1 && suggestion) {
|
|
5407
|
-
lineContent += (isCursorVisible ? chalk3.inverse(suggestion[0]) : suggestion[0]) + chalk3.dim(suggestion.slice(1));
|
|
5408
|
-
} else {
|
|
5409
|
-
lineContent += isCursorVisible ? chalk3.inverse(" ") : " ";
|
|
5410
|
-
}
|
|
5411
|
-
} else if (lineIndex === lines.length - 1 && suggestion) {
|
|
5412
|
-
lineContent += chalk3.dim(suggestion);
|
|
5413
|
-
}
|
|
5414
|
-
result.push(
|
|
5415
|
-
/* @__PURE__ */ jsx15(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx15(Text8, { wrap: "end", children: lineContent }) }, lineIndex)
|
|
5416
|
-
);
|
|
5417
|
-
if (lineIndex < lines.length - 1) {
|
|
5418
|
-
totalOffset++;
|
|
5419
|
-
}
|
|
5420
|
-
});
|
|
5421
|
-
return /* @__PURE__ */ jsx15(Box8, { flexDirection: "column", flexGrow: 1, children: result });
|
|
5422
|
-
}, [state.value, state.cursorOffset, suggestion, isCursorVisible, isDisabled, placeholder]);
|
|
5423
|
-
return /* @__PURE__ */ jsx15(Box8, { flexGrow: 1, minWidth: 1, children: renderedValue });
|
|
5424
|
-
}
|
|
5425
|
-
|
|
5426
|
-
// ink/components/UserInput.tsx
|
|
5427
|
-
import { jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
5428
|
-
var execAsync4 = promisify13(exec4);
|
|
5429
|
-
var modeSuggestion = {
|
|
5430
|
-
approved: ["/clear", "/skills", "/exit"],
|
|
5431
|
-
approving: ["yes", "approve", "no", "deny"],
|
|
5432
|
-
denied: [],
|
|
5433
|
-
waiting: ["/clear", "/skills", "/exit"]
|
|
5434
|
-
};
|
|
5435
|
-
function UserInput() {
|
|
5436
|
-
const { userInputMode, focusedArea } = useChat();
|
|
5437
|
-
const [resetKey, setResetKey] = useState13(0);
|
|
5438
|
-
const [, setBlankLines] = useState13(0);
|
|
5439
|
-
useListener("ClearUserInput", () => {
|
|
5440
|
-
setResetKey((prev) => prev + 1);
|
|
5441
|
-
}, []);
|
|
5442
|
-
const borderColor = focusedArea === "input" ? "cyan" : "gray";
|
|
5443
|
-
const placeholder = calculatePlaceholder(userInputMode);
|
|
5444
|
-
const onSubmitResetKey = useCallback4(async (value) => {
|
|
5445
|
-
if (value.length) {
|
|
5446
|
-
const trimmedValue = value.trim();
|
|
5447
|
-
if (trimmedValue === "/clear") {
|
|
5448
|
-
setResetKey((prev) => prev + 1);
|
|
5449
|
-
emitToListeners("ClearChatHistory", void 0);
|
|
5450
|
-
return;
|
|
5451
|
-
}
|
|
5452
|
-
if (trimmedValue === "/exit" || trimmedValue === "exit") {
|
|
5453
|
-
await handleExit();
|
|
5454
|
-
return;
|
|
5455
|
-
}
|
|
5456
|
-
if (trimmedValue === "/skills") {
|
|
5457
|
-
setResetKey((prev) => prev + 1);
|
|
5458
|
-
emitToListeners("PushNewMessages", [{
|
|
5459
|
-
type: "user",
|
|
5460
|
-
text: "/skills",
|
|
5461
|
-
version: 1
|
|
5462
|
-
}, {
|
|
5463
|
-
type: "agent",
|
|
5464
|
-
text: "Installing skills...",
|
|
5465
|
-
version: 1
|
|
5466
|
-
}]);
|
|
5467
|
-
try {
|
|
5468
|
-
const { stdout, stderr } = await execAsync4("npx skills add harperfast/skills");
|
|
5469
|
-
emitToListeners(
|
|
5470
|
-
"UpdateLastMessageText",
|
|
5471
|
-
`
|
|
5472
|
-
|
|
5473
|
-
Skills installation result:
|
|
5474
|
-
${stdout}${stderr ? `
|
|
5475
|
-
Errors:
|
|
5476
|
-
${stderr}` : ""}`
|
|
5477
|
-
);
|
|
5478
|
-
} catch (error) {
|
|
5479
|
-
emitToListeners("UpdateLastMessageText", `
|
|
5480
|
-
|
|
5481
|
-
Failed to install skills: ${error.message}`);
|
|
5482
|
-
}
|
|
5483
|
-
return;
|
|
5484
|
-
}
|
|
5485
|
-
setResetKey((prev) => prev + 1);
|
|
5486
|
-
emitToListeners("PushNewMessages", [{ type: "user", text: trimmedValue, version: 1 }]);
|
|
5487
|
-
setBlankLines(0);
|
|
5488
|
-
} else {
|
|
5489
|
-
setBlankLines((value2) => {
|
|
5490
|
-
value2 += 1;
|
|
5491
|
-
if (value2 === 2) {
|
|
5492
|
-
void handleExit();
|
|
5493
|
-
}
|
|
5494
|
-
return value2;
|
|
5495
|
-
});
|
|
5496
|
-
}
|
|
5497
|
-
}, []);
|
|
5498
|
-
return /* @__PURE__ */ jsxs8(
|
|
5499
|
-
Box9,
|
|
5500
|
-
{
|
|
5501
|
-
minHeight: footerHeight,
|
|
5502
|
-
borderStyle: "bold",
|
|
5503
|
-
borderTop: false,
|
|
5504
|
-
borderColor,
|
|
5505
|
-
flexDirection: "row",
|
|
5506
|
-
alignItems: "flex-start",
|
|
5507
|
-
gap: 1,
|
|
5508
|
-
children: [
|
|
5509
|
-
/* @__PURE__ */ jsx16(Box9, { marginLeft: 1, height: 1, children: /* @__PURE__ */ jsx16(Text9, { bold: true, color: borderColor, children: "\u276F" }) }),
|
|
5510
|
-
/* @__PURE__ */ jsx16(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx16(
|
|
5511
|
-
BlinkingTextInput,
|
|
5512
|
-
{
|
|
5513
|
-
placeholder,
|
|
5514
|
-
onSubmit: onSubmitResetKey,
|
|
5515
|
-
suggestions: modeSuggestion[userInputMode],
|
|
5516
|
-
isDisabled: focusedArea !== "input"
|
|
5517
|
-
},
|
|
5518
|
-
resetKey
|
|
5519
|
-
) })
|
|
5520
|
-
]
|
|
5521
|
-
}
|
|
5522
|
-
);
|
|
5523
|
-
}
|
|
5524
|
-
function calculatePlaceholder(mode) {
|
|
5525
|
-
const prefix = " ";
|
|
5526
|
-
switch (mode) {
|
|
5527
|
-
case "denied":
|
|
5528
|
-
return prefix + "OK! Let's find another way.";
|
|
5529
|
-
case "approving":
|
|
5530
|
-
return prefix + "Do you approve? yes / no";
|
|
5531
|
-
default:
|
|
5532
|
-
case "approved":
|
|
5533
|
-
case "waiting":
|
|
5534
|
-
return "";
|
|
5535
|
-
}
|
|
5536
|
-
}
|
|
5537
|
-
|
|
5538
|
-
// ink/components/ChatContent.tsx
|
|
5539
|
-
import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
5540
|
-
function ChatContent() {
|
|
5541
|
-
const { messages, isThinking, isCompacting, pullingState, focusedArea, setFocusedArea } = useChat();
|
|
5542
|
-
const { payload } = useApproval();
|
|
5543
|
-
const size = useTerminalSize();
|
|
5544
|
-
useMessageListener();
|
|
5545
|
-
const [activeTab, setActiveTab] = useState14("settings");
|
|
5546
|
-
const [userHasSwitchedTab, setUserHasSwitchedTab] = useState14(false);
|
|
5547
|
-
const { planDescription, planItems } = usePlan();
|
|
5548
|
-
useEffect8(() => {
|
|
5549
|
-
if (!userHasSwitchedTab && activeTab === "settings") {
|
|
5550
|
-
const hasPlan = planDescription.trim().length > 0 || planItems.length > 0;
|
|
5551
|
-
if (hasPlan) {
|
|
5552
|
-
setActiveTab("planDescription");
|
|
5553
|
-
}
|
|
5554
|
-
}
|
|
5555
|
-
}, [planDescription, planItems, userHasSwitchedTab, activeTab]);
|
|
5556
|
-
const [selectedIndex, setSelectedIndex] = useState14(0);
|
|
5557
|
-
const wrapCache = useRef2(
|
|
5558
|
-
/* @__PURE__ */ new Map()
|
|
5559
|
-
);
|
|
5560
|
-
useInput4((input, key) => {
|
|
5561
|
-
if (key.tab) {
|
|
5562
|
-
const focusOrder = ["input", "timeline", "status"];
|
|
5563
|
-
const currentIndex = focusOrder.indexOf(focusedArea);
|
|
5564
|
-
if (key.shift || input === "\x1B[Z") {
|
|
5565
|
-
const nextIndex = (currentIndex - 1 + focusOrder.length) % focusOrder.length;
|
|
5566
|
-
setFocusedArea(focusOrder[nextIndex]);
|
|
5567
|
-
} else {
|
|
5568
|
-
const nextIndex = (currentIndex + 1) % focusOrder.length;
|
|
5569
|
-
setFocusedArea(focusOrder[nextIndex]);
|
|
5570
|
-
}
|
|
5571
|
-
return;
|
|
5572
|
-
}
|
|
5573
|
-
if (focusedArea === "timeline") {
|
|
5574
|
-
if (key.upArrow) {
|
|
5575
|
-
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
5576
|
-
}
|
|
5577
|
-
if (key.downArrow) {
|
|
5578
|
-
setSelectedIndex((prev) => Math.min(Math.max(0, lineItems.length - 1), prev + 1));
|
|
5579
|
-
}
|
|
5580
|
-
if (key.return) {
|
|
5581
|
-
const selected = lineItems[selectedIndex];
|
|
5582
|
-
if (selected && selected.type === "tool" && (selected.toolName === "apply_patch" || selected.toolName === "code_interpreter" || selected.toolName === "shell")) {
|
|
5583
|
-
const msg = messages.find((m) => m.id === selected.messageId);
|
|
5584
|
-
if (msg && msg.callId) {
|
|
5585
|
-
emitToListeners("OpenApprovalViewer", {
|
|
5586
|
-
type: selected.toolName,
|
|
5587
|
-
mode: "info",
|
|
5588
|
-
callId: msg.callId
|
|
5589
|
-
});
|
|
5590
|
-
}
|
|
5591
|
-
}
|
|
5592
|
-
}
|
|
5593
|
-
}
|
|
5594
|
-
if (focusedArea === "status") {
|
|
5595
|
-
if (key.leftArrow || key.rightArrow) {
|
|
5596
|
-
const tabNames = ["settings", "planDescription", "actions"];
|
|
5597
|
-
const currentIndex = tabNames.indexOf(activeTab);
|
|
5598
|
-
if (key.leftArrow) {
|
|
5599
|
-
const nextIndex = (currentIndex - 1 + tabNames.length) % tabNames.length;
|
|
5600
|
-
const newTab = tabNames[nextIndex];
|
|
5601
|
-
setActiveTab(newTab);
|
|
5602
|
-
setUserHasSwitchedTab(true);
|
|
5603
|
-
} else {
|
|
5604
|
-
const nextIndex = (currentIndex + 1) % tabNames.length;
|
|
5605
|
-
const newTab = tabNames[nextIndex];
|
|
5606
|
-
setActiveTab(newTab);
|
|
5607
|
-
setUserHasSwitchedTab(true);
|
|
5608
|
-
}
|
|
5609
|
-
}
|
|
5610
|
-
}
|
|
5611
|
-
if (key.ctrl && input === "x") {
|
|
5612
|
-
void handleExit();
|
|
5613
|
-
}
|
|
5614
|
-
if (key.escape && isThinking && focusedArea === "input" && !payload) {
|
|
5615
|
-
emitToListeners("InterruptThought", void 0);
|
|
5616
|
-
}
|
|
5617
|
-
});
|
|
5618
|
-
const contentHeight = size.rows - footerHeight;
|
|
5619
|
-
const timelineWidth = Math.floor(size.columns * 0.65);
|
|
5620
|
-
const statusWidth = size.columns - timelineWidth;
|
|
5621
|
-
const labelWidthFor = useCallback5((type) => {
|
|
5622
|
-
if (type === "agent") {
|
|
5623
|
-
return 7;
|
|
5624
|
-
}
|
|
5625
|
-
if (type === "prompt") {
|
|
5626
|
-
return 7;
|
|
5627
|
-
}
|
|
5628
|
-
if (type === "user") {
|
|
5629
|
-
return 6;
|
|
5630
|
-
}
|
|
5631
|
-
if (type === "interrupted") {
|
|
5632
|
-
return 0;
|
|
5633
|
-
}
|
|
5634
|
-
return 6;
|
|
5635
|
-
}, []);
|
|
5636
|
-
const availableTextWidth = timelineWidth - 4;
|
|
5637
|
-
const pullingHeight = pullingState ? 1 : 0;
|
|
5638
|
-
const lineItems = useMemo11(() => {
|
|
5639
|
-
const acc = [];
|
|
5640
|
-
for (const msg of messages) {
|
|
5641
|
-
const labelWidth = labelWidthFor(msg.type);
|
|
5642
|
-
const firstLineWidth = Math.max(1, availableTextWidth - labelWidth);
|
|
5643
|
-
let entry = wrapCache.current.get(msg.id);
|
|
5644
|
-
if (!entry || entry.version !== msg.version || entry.width !== firstLineWidth) {
|
|
5645
|
-
const textLines = wrapText(msg.text ?? "", firstLineWidth);
|
|
5646
|
-
const argsLines = msg.args ? wrapText(String(msg.args), firstLineWidth) : [];
|
|
5647
|
-
const msgLineItems = [];
|
|
5648
|
-
if (msg.type === "tool") {
|
|
5649
|
-
const toolName = msg.text ?? "";
|
|
5650
|
-
const toolArgs = msg.args ?? "";
|
|
5651
|
-
const toolNameWithSpace = `${toolName} `;
|
|
5652
|
-
const availableForArgs = firstLineWidth - toolNameWithSpace.length;
|
|
5653
|
-
let displayedArgs = toolArgs;
|
|
5654
|
-
if (availableForArgs < toolArgs.length) {
|
|
5655
|
-
if (availableForArgs > 3) {
|
|
5656
|
-
displayedArgs = toolArgs.slice(0, availableForArgs - 3) + "...";
|
|
5657
|
-
} else {
|
|
5658
|
-
displayedArgs = toolArgs.slice(0, Math.max(0, availableForArgs));
|
|
5659
|
-
}
|
|
5660
|
-
}
|
|
5661
|
-
msgLineItems.push({
|
|
5662
|
-
key: `${msg.id}:tool:0`,
|
|
5663
|
-
messageId: msg.id,
|
|
5664
|
-
type: msg.type,
|
|
5665
|
-
text: `${toolNameWithSpace}${displayedArgs}`,
|
|
5666
|
-
isFirstLine: true,
|
|
5667
|
-
toolName
|
|
5668
|
-
});
|
|
5669
|
-
} else {
|
|
5670
|
-
textLines.forEach((txt, idx) => {
|
|
5671
|
-
msgLineItems.push({
|
|
5672
|
-
key: `${msg.id}:text:${idx}`,
|
|
5673
|
-
messageId: msg.id,
|
|
5674
|
-
type: msg.type,
|
|
5675
|
-
text: txt,
|
|
5676
|
-
isFirstLine: idx === 0
|
|
5677
|
-
});
|
|
5678
|
-
});
|
|
5679
|
-
if (argsLines.length > 0) {
|
|
5680
|
-
argsLines.forEach((txt, idx) => {
|
|
5681
|
-
msgLineItems.push({
|
|
5682
|
-
key: `${msg.id}:args:${idx}`,
|
|
5683
|
-
messageId: msg.id,
|
|
5684
|
-
type: msg.type,
|
|
5685
|
-
text: txt,
|
|
5686
|
-
isFirstLine: idx === 0 && textLines.length === 0,
|
|
5687
|
-
isArgsLine: true
|
|
5688
|
-
});
|
|
5689
|
-
});
|
|
5690
|
-
}
|
|
5691
|
-
}
|
|
5692
|
-
entry = { version: msg.version, width: firstLineWidth, lineItems: msgLineItems };
|
|
5693
|
-
wrapCache.current.set(msg.id, entry);
|
|
5694
|
-
}
|
|
5695
|
-
acc.push(...entry.lineItems);
|
|
5696
|
-
}
|
|
5697
|
-
if (wrapCache.current.size > messages.length * 2) {
|
|
5698
|
-
const messageIds = new Set(messages.map((m) => m.id));
|
|
5699
|
-
for (const id of wrapCache.current.keys()) {
|
|
5700
|
-
if (!messageIds.has(id)) {
|
|
5701
|
-
wrapCache.current.delete(id);
|
|
5702
|
-
}
|
|
5703
|
-
}
|
|
5704
|
-
}
|
|
5705
|
-
return acc;
|
|
5706
|
-
}, [messages, availableTextWidth, labelWidthFor]);
|
|
5707
|
-
useEffect8(() => {
|
|
5708
|
-
if (lineItems.length > 0 && focusedArea !== "timeline") {
|
|
5709
|
-
setSelectedIndex(lineItems.length - 1);
|
|
5710
|
-
}
|
|
5711
|
-
}, [lineItems.length, focusedArea]);
|
|
5712
|
-
useEffect8(() => {
|
|
5713
|
-
if (lineItems.length > 0) {
|
|
5714
|
-
setSelectedIndex(lineItems.length - 1);
|
|
5715
|
-
}
|
|
5716
|
-
}, [size.rows, size.columns]);
|
|
5717
|
-
const tabs = useMemo11(() => [
|
|
5718
|
-
{ name: "settings", label: "SETTINGS" },
|
|
5719
|
-
{ name: "planDescription", label: "PLAN" },
|
|
5720
|
-
{ name: "actions", label: "ACTIONS" }
|
|
5721
|
-
], []);
|
|
5722
|
-
const timelineTitle = "TIMELINE:";
|
|
5723
|
-
const timelineHeaderWidth = timelineWidth - 1;
|
|
5724
|
-
const showSpinner = isCompacting || isThinking || Boolean(pullingState);
|
|
5725
|
-
const timelineDashes = timelineHeaderWidth - timelineTitle.length - (showSpinner ? 5 : 0);
|
|
5726
|
-
const tabsTotalWidth = tabs.reduce((acc, t) => acc + t.label.length + 2, 0) + (tabs.length - 1);
|
|
5727
|
-
const statusDashes = Math.max(0, statusWidth - tabsTotalWidth - 2);
|
|
5728
|
-
const timelineColor = focusedArea === "timeline" ? "cyan" : "gray";
|
|
5729
|
-
const statusColor = focusedArea === "status" ? "cyan" : "gray";
|
|
5730
|
-
const dividerColor = focusedArea === "timeline" || focusedArea === "status" ? "cyan" : "gray";
|
|
5731
|
-
const timelineBottomColor = focusedArea === "timeline" || focusedArea === "input" ? "cyan" : "gray";
|
|
5732
|
-
const statusBottomColor = focusedArea === "status" || focusedArea === "input" ? "cyan" : "gray";
|
|
5733
|
-
const junctionLeftColor = focusedArea === "timeline" || focusedArea === "input" ? "cyan" : "gray";
|
|
5734
|
-
const junctionMiddleColor = focusedArea === "timeline" || focusedArea === "status" || focusedArea === "input" ? "cyan" : "gray";
|
|
5735
|
-
const junctionRightColor = focusedArea === "status" || focusedArea === "input" ? "cyan" : "gray";
|
|
5736
|
-
const timelinePipeFiller = useCallback5((count) => /* @__PURE__ */ jsx17(Box10, { flexDirection: "column", children: Array.from({ length: count }).map((_, i) => /* @__PURE__ */ jsx17(Box10, { children: /* @__PURE__ */ jsx17(Text10, { color: "gray", dimColor: true, children: "\u2502" }) }, i)) }), []);
|
|
5737
|
-
return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", height: size.rows, padding: 0, children: [
|
|
5738
|
-
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", height: 1, children: [
|
|
5739
|
-
/* @__PURE__ */ jsx17(Text10, { color: timelineColor, children: "\u256D" }),
|
|
5740
|
-
/* @__PURE__ */ jsx17(Text10, { bold: true, color: timelineColor, children: timelineTitle }),
|
|
5741
|
-
showSpinner && /* @__PURE__ */ jsx17(Box10, { paddingLeft: 2, paddingRight: 1, children: /* @__PURE__ */ jsx17(Spinner2, { type: "clock" }) }),
|
|
5742
|
-
/* @__PURE__ */ jsx17(Text10, { color: timelineColor, children: "\u2500".repeat(Math.max(0, timelineDashes)) }),
|
|
5743
|
-
/* @__PURE__ */ jsx17(Text10, { color: dividerColor, children: "\u252C" }),
|
|
5744
|
-
tabs.map((tab, i) => /* @__PURE__ */ jsxs9(React11.Fragment, { children: [
|
|
5745
|
-
/* @__PURE__ */ jsx17(
|
|
5746
|
-
Text10,
|
|
5747
|
-
{
|
|
5748
|
-
color: activeTab === tab.name ? "black" : statusColor,
|
|
5749
|
-
backgroundColor: activeTab === tab.name ? statusColor : "",
|
|
5750
|
-
bold: activeTab === tab.name,
|
|
5751
|
-
children: ` ${tab.label} `
|
|
5752
|
-
}
|
|
5753
|
-
),
|
|
5754
|
-
i < tabs.length - 1 && /* @__PURE__ */ jsx17(Text10, { color: statusColor, children: "|" })
|
|
5755
|
-
] }, tab.name)),
|
|
5756
|
-
/* @__PURE__ */ jsxs9(Text10, { color: statusColor, children: [
|
|
5757
|
-
"\u2500".repeat(statusDashes),
|
|
5758
|
-
"\u256E"
|
|
5759
|
-
] })
|
|
5760
|
-
] }),
|
|
5761
|
-
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", height: contentHeight - 2 - pullingHeight, children: [
|
|
5762
|
-
/* @__PURE__ */ jsx17(
|
|
5763
|
-
Box10,
|
|
5764
|
-
{
|
|
5765
|
-
flexDirection: "column",
|
|
5766
|
-
width: timelineWidth,
|
|
5767
|
-
paddingLeft: 0,
|
|
5768
|
-
paddingRight: 1,
|
|
5769
|
-
children: /* @__PURE__ */ jsx17(Box10, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx17(
|
|
5770
|
-
VirtualList,
|
|
5771
|
-
{
|
|
5772
|
-
items: lineItems,
|
|
5773
|
-
itemHeight: 1,
|
|
5774
|
-
height: contentHeight - 2 - pullingHeight,
|
|
5775
|
-
selectedIndex,
|
|
5776
|
-
renderOverflowTop: useCallback5((count) => /* @__PURE__ */ jsxs9(Box10, { children: [
|
|
5777
|
-
/* @__PURE__ */ jsx17(Text10, { color: "gray", dimColor: true, children: "\u2502" }),
|
|
5778
|
-
count > 0 && /* @__PURE__ */ jsxs9(Text10, { dimColor: true, children: [
|
|
5779
|
-
" ",
|
|
5780
|
-
"\u25B2 ",
|
|
5781
|
-
count,
|
|
5782
|
-
" more"
|
|
5783
|
-
] })
|
|
5784
|
-
] }), []),
|
|
5785
|
-
renderOverflowBottom: useCallback5((count) => /* @__PURE__ */ jsxs9(Box10, { children: [
|
|
5786
|
-
/* @__PURE__ */ jsx17(Text10, { color: "gray", dimColor: true, children: "\u2502" }),
|
|
5787
|
-
count > 0 && /* @__PURE__ */ jsxs9(Text10, { dimColor: true, children: [
|
|
5788
|
-
" ",
|
|
5789
|
-
"\u25BC ",
|
|
5790
|
-
count,
|
|
5791
|
-
" more"
|
|
5792
|
-
] })
|
|
5793
|
-
] }), []),
|
|
5794
|
-
renderFiller: timelinePipeFiller,
|
|
5795
|
-
keyExtractor: (it) => it.key,
|
|
5796
|
-
renderItem: useCallback5(
|
|
5797
|
-
({ item, isSelected }) => /* @__PURE__ */ jsx17(
|
|
5798
|
-
MessageLineItem,
|
|
5799
|
-
{
|
|
5800
|
-
item,
|
|
5801
|
-
isSelected,
|
|
5802
|
-
isFocused: focusedArea === "timeline",
|
|
5803
|
-
indent: item.isFirstLine ? 0 : labelWidthFor(item.type)
|
|
5804
|
-
}
|
|
5805
|
-
),
|
|
5806
|
-
[labelWidthFor, focusedArea]
|
|
5807
|
-
)
|
|
5808
|
-
}
|
|
5809
|
-
) })
|
|
5810
|
-
}
|
|
5811
|
-
),
|
|
5812
|
-
activeTab !== "shell" && activeTab !== "actions" && /* @__PURE__ */ jsx17(
|
|
5813
|
-
Box10,
|
|
5814
|
-
{
|
|
5815
|
-
flexDirection: "column",
|
|
5816
|
-
width: 1,
|
|
5817
|
-
borderStyle: "round",
|
|
5818
|
-
borderColor: dividerColor,
|
|
5819
|
-
borderTop: false,
|
|
5820
|
-
borderBottom: false,
|
|
5821
|
-
borderRight: false
|
|
5822
|
-
}
|
|
5823
|
-
),
|
|
5824
|
-
/* @__PURE__ */ jsx17(
|
|
5825
|
-
Box10,
|
|
5826
|
-
{
|
|
5827
|
-
flexDirection: "column",
|
|
5828
|
-
width: activeTab === "shell" || activeTab === "actions" ? statusWidth : statusWidth - 1,
|
|
5829
|
-
borderStyle: "round",
|
|
5830
|
-
borderColor: statusColor,
|
|
5831
|
-
borderTop: false,
|
|
5832
|
-
borderBottom: false,
|
|
5833
|
-
borderLeft: false,
|
|
5834
|
-
paddingLeft: activeTab === "shell" || activeTab === "actions" ? 0 : 1,
|
|
5835
|
-
paddingRight: 1,
|
|
5836
|
-
children: /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", flexGrow: 1, marginTop: 0, children: [
|
|
5837
|
-
activeTab === "settings" && /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", children: [
|
|
5838
|
-
/* @__PURE__ */ jsx17(CostView, { isDense: true }),
|
|
5839
|
-
/* @__PURE__ */ jsx17(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx17(SettingsView, { isDense: true }) })
|
|
5840
|
-
] }),
|
|
5841
|
-
activeTab === "planDescription" && /* @__PURE__ */ jsx17(PlanView, {}),
|
|
5842
|
-
activeTab === "actions" && /* @__PURE__ */ jsx17(
|
|
5843
|
-
ActionsView,
|
|
5844
|
-
{
|
|
5845
|
-
height: contentHeight - 2 - pullingHeight,
|
|
5846
|
-
isFocused: focusedArea === "status"
|
|
5847
|
-
}
|
|
5848
|
-
)
|
|
5849
|
-
] })
|
|
5850
|
-
}
|
|
5851
|
-
)
|
|
5852
|
-
] }),
|
|
5853
|
-
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", children: [
|
|
5854
|
-
pullingState && /* @__PURE__ */ jsx17(Box10, { paddingLeft: 2, paddingRight: 2, marginBottom: 0, children: /* @__PURE__ */ jsxs9(Text10, { color: "yellow", children: [
|
|
5855
|
-
`\uF019 Downloading `,
|
|
5856
|
-
/* @__PURE__ */ jsx17(Text10, { bold: true, children: pullingState.modelName }),
|
|
5857
|
-
` from Ollama... `,
|
|
5858
|
-
/* @__PURE__ */ jsx17(Text10, { dimColor: true, children: pullingState.status === "pulling manifest" ? "initializing" : pullingState.status }),
|
|
5859
|
-
pullingState.total > 0 && /* @__PURE__ */ jsxs9(Text10, { children: [
|
|
5860
|
-
` [${"=".repeat(Math.floor(pullingState.completed / pullingState.total * 20))}${" ".repeat(20 - Math.floor(pullingState.completed / pullingState.total * 20))}] `,
|
|
5861
|
-
Math.round(pullingState.completed / pullingState.total * 100),
|
|
5862
|
-
"%"
|
|
5863
|
-
] })
|
|
5864
|
-
] }) }),
|
|
5865
|
-
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", height: 1, children: [
|
|
5866
|
-
/* @__PURE__ */ jsx17(Text10, { color: junctionLeftColor, children: "\u2522" }),
|
|
5867
|
-
/* @__PURE__ */ jsx17(Text10, { color: timelineBottomColor, children: "\u2501".repeat(timelineWidth - 1) }),
|
|
5868
|
-
/* @__PURE__ */ jsx17(Text10, { color: junctionMiddleColor, children: "\u2537" }),
|
|
5869
|
-
/* @__PURE__ */ jsx17(Text10, { color: statusBottomColor, children: "\u2501".repeat(Math.max(0, statusWidth - 2)) }),
|
|
5870
|
-
/* @__PURE__ */ jsx17(Text10, { color: junctionRightColor, children: "\u252A" })
|
|
5871
|
-
] })
|
|
5872
|
-
] }),
|
|
5873
|
-
/* @__PURE__ */ jsx17(UserInput, {})
|
|
5874
|
-
] });
|
|
5875
|
-
}
|
|
5876
|
-
|
|
5877
|
-
// ink/components/DiffApprovalView.tsx
|
|
5878
|
-
import { Box as Box11, Text as Text11, useInput as useInput5 } from "ink";
|
|
5879
|
-
import { useMemo as useMemo12, useState as useState15 } from "react";
|
|
5880
|
-
import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
5881
|
-
function DiffApprovalView() {
|
|
5882
|
-
const { payload } = useApproval();
|
|
5883
|
-
const size = useTerminalSize();
|
|
5884
|
-
const [scrollIndex, setScrollIndex] = useState15(0);
|
|
5885
|
-
const diffLines = useMemo12(() => {
|
|
5886
|
-
if (!payload?.diff) {
|
|
5887
|
-
return [];
|
|
5888
|
-
}
|
|
5889
|
-
return payload.diff.split("\n");
|
|
5890
|
-
}, [payload?.diff]);
|
|
5891
|
-
const wrappedLines = useMemo12(() => {
|
|
5892
|
-
const result = [];
|
|
5893
|
-
if (!payload) {
|
|
5894
|
-
return result;
|
|
5895
|
-
}
|
|
5896
|
-
if (payload.type === "delete_file") {
|
|
5897
|
-
result.push({ text: "Delete file: " + payload.path, color: "red" });
|
|
5898
|
-
return result;
|
|
5899
|
-
}
|
|
5900
|
-
if (payload.type === "overwrite_file") {
|
|
5901
|
-
for (const line of diffLines) {
|
|
5902
|
-
let color;
|
|
5903
|
-
if (line.startsWith("+")) {
|
|
5904
|
-
color = "green";
|
|
5905
|
-
} else if (line.startsWith("-")) {
|
|
5906
|
-
color = "red";
|
|
5907
|
-
}
|
|
5908
|
-
const wrapped = wrapText(line, size.columns - 4);
|
|
5909
|
-
for (const w of wrapped) {
|
|
5910
|
-
result.push({ text: w, color });
|
|
5911
|
-
}
|
|
5912
|
-
}
|
|
5913
|
-
return result;
|
|
5914
|
-
}
|
|
5915
|
-
if (payload.type === "code_interpreter" && payload.code) {
|
|
5916
|
-
const lines = payload.code.split("\n");
|
|
5917
|
-
for (const line of lines) {
|
|
5918
|
-
const wrapped = wrapText(line, size.columns - 4);
|
|
5919
|
-
for (const w of wrapped) {
|
|
5920
|
-
result.push({ text: w });
|
|
5921
|
-
}
|
|
5922
|
-
}
|
|
5923
|
-
return result;
|
|
5924
|
-
}
|
|
5925
|
-
if (payload.type === "shell" && payload.commands) {
|
|
5926
|
-
for (const cmd of payload.commands) {
|
|
5927
|
-
const wrapped = wrapText(`$ ${cmd}`, size.columns - 4);
|
|
5928
|
-
for (const w of wrapped) {
|
|
5929
|
-
result.push({ text: w, color: "yellow" });
|
|
5930
|
-
}
|
|
5931
|
-
}
|
|
5932
|
-
return result;
|
|
5933
|
-
}
|
|
5934
|
-
for (const line of diffLines) {
|
|
5935
|
-
let color;
|
|
5936
|
-
if (line.startsWith("+")) {
|
|
5937
|
-
color = "green";
|
|
5938
|
-
} else if (line.startsWith("-")) {
|
|
5939
|
-
color = "red";
|
|
5940
|
-
} else if (line.startsWith("@@")) {
|
|
5941
|
-
color = "cyan";
|
|
5942
|
-
}
|
|
5943
|
-
const wrapped = wrapText(line, size.columns - 4);
|
|
5944
|
-
for (const w of wrapped) {
|
|
5945
|
-
result.push({ text: w, color });
|
|
5946
|
-
}
|
|
5947
|
-
}
|
|
5948
|
-
return result;
|
|
5949
|
-
}, [diffLines, size.columns, payload]);
|
|
5950
|
-
const visibleHeight = size.rows - 6;
|
|
5951
|
-
useInput5((input, key) => {
|
|
5952
|
-
if (!payload) {
|
|
5953
|
-
return;
|
|
5954
|
-
}
|
|
5955
|
-
if (key.escape) {
|
|
5956
|
-
if (payload.mode === "ask") {
|
|
5957
|
-
emitToListeners("DenyCurrentApproval", void 0);
|
|
5958
|
-
}
|
|
5959
|
-
emitToListeners("CloseApprovalViewer", void 0);
|
|
5960
|
-
return;
|
|
5961
|
-
}
|
|
5962
|
-
if (key.return) {
|
|
5963
|
-
if (payload.mode === "ask") {
|
|
5964
|
-
const now = Date.now();
|
|
5965
|
-
if (payload.openedAt && now - payload.openedAt > 1e3) {
|
|
5966
|
-
emitToListeners("ApproveCurrentApproval", void 0);
|
|
5967
|
-
emitToListeners("CloseApprovalViewer", void 0);
|
|
5968
|
-
emitToListeners("ClearUserInput", void 0);
|
|
5969
|
-
}
|
|
5970
|
-
} else {
|
|
5971
|
-
emitToListeners("CloseApprovalViewer", void 0);
|
|
5972
|
-
}
|
|
5973
|
-
return;
|
|
5974
|
-
}
|
|
5975
|
-
if (payload.mode === "ask") {
|
|
5976
|
-
const now = Date.now();
|
|
5977
|
-
if (payload.openedAt && now - payload.openedAt > 1e3) {
|
|
5978
|
-
if (input === "y") {
|
|
5979
|
-
emitToListeners("ApproveCurrentApproval", void 0);
|
|
5980
|
-
emitToListeners("CloseApprovalViewer", void 0);
|
|
5981
|
-
emitToListeners("ClearUserInput", void 0);
|
|
5982
|
-
} else if (input === "n") {
|
|
5983
|
-
emitToListeners("DenyCurrentApproval", void 0);
|
|
5984
|
-
emitToListeners("CloseApprovalViewer", void 0);
|
|
5985
|
-
emitToListeners("ClearUserInput", void 0);
|
|
5986
|
-
}
|
|
5987
|
-
}
|
|
5988
|
-
}
|
|
5989
|
-
if (key.upArrow) {
|
|
5990
|
-
setScrollIndex((prev) => Math.max(0, prev - 1));
|
|
5991
|
-
}
|
|
5992
|
-
if (key.downArrow) {
|
|
5993
|
-
setScrollIndex((prev) => Math.min(Math.max(0, wrappedLines.length - visibleHeight), prev + 1));
|
|
5994
|
-
}
|
|
5995
|
-
if (key.pageUp) {
|
|
5996
|
-
setScrollIndex((prev) => Math.max(0, prev - visibleHeight));
|
|
5997
|
-
}
|
|
5998
|
-
if (key.pageDown) {
|
|
5999
|
-
setScrollIndex((prev) => Math.min(Math.max(0, wrappedLines.length - visibleHeight), prev + visibleHeight));
|
|
6000
|
-
}
|
|
6001
|
-
});
|
|
6002
|
-
if (!payload) {
|
|
6003
|
-
return null;
|
|
6004
|
-
}
|
|
6005
|
-
const canRespond = payload.mode === "ask" && payload.openedAt && Date.now() - payload.openedAt > 1e3;
|
|
6006
|
-
return /* @__PURE__ */ jsxs10(
|
|
6007
|
-
Box11,
|
|
6008
|
-
{
|
|
6009
|
-
position: "absolute",
|
|
6010
|
-
flexDirection: "column",
|
|
6011
|
-
width: size.columns,
|
|
6012
|
-
height: size.rows,
|
|
6013
|
-
backgroundColor: "black",
|
|
6014
|
-
borderStyle: "double",
|
|
6015
|
-
borderColor: "cyan",
|
|
6016
|
-
children: [
|
|
6017
|
-
/* @__PURE__ */ jsxs10(
|
|
6018
|
-
Box11,
|
|
6019
|
-
{
|
|
6020
|
-
paddingX: 1,
|
|
6021
|
-
borderStyle: "single",
|
|
6022
|
-
borderBottomColor: "gray",
|
|
6023
|
-
borderTop: false,
|
|
6024
|
-
borderLeft: false,
|
|
6025
|
-
borderRight: false,
|
|
6026
|
-
children: [
|
|
6027
|
-
/* @__PURE__ */ jsxs10(Text11, { bold: true, color: "cyan", children: [
|
|
6028
|
-
payload.mode === "ask" ? "APPROVE " : "VIEW ",
|
|
6029
|
-
payload.type.toUpperCase().replace("_", " ")
|
|
6030
|
-
] }),
|
|
6031
|
-
/* @__PURE__ */ jsx18(Text11, { color: "gray", children: "\u2502" }),
|
|
6032
|
-
payload.path && /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
6033
|
-
/* @__PURE__ */ jsxs10(Text11, { bold: true, children: [
|
|
6034
|
-
payload.type,
|
|
6035
|
-
":"
|
|
6036
|
-
] }),
|
|
6037
|
-
/* @__PURE__ */ jsx18(Text11, { children: payload.path })
|
|
6038
|
-
] })
|
|
6039
|
-
]
|
|
6040
|
-
}
|
|
6041
|
-
),
|
|
6042
|
-
/* @__PURE__ */ jsx18(Box11, { flexGrow: 1, flexDirection: "column", paddingX: 1, children: wrappedLines.length === 0 ? /* @__PURE__ */ jsx18(Text11, { italic: true, color: "gray", children: "No diff content." }) : wrappedLines.slice(scrollIndex, scrollIndex + visibleHeight).map((line, i) => /* @__PURE__ */ jsx18(Text11, { color: line.color || "white", children: line.text }, i)) }),
|
|
6043
|
-
/* @__PURE__ */ jsxs10(
|
|
6044
|
-
Box11,
|
|
6045
|
-
{
|
|
6046
|
-
paddingX: 1,
|
|
6047
|
-
borderStyle: "single",
|
|
6048
|
-
borderTopColor: "gray",
|
|
6049
|
-
borderBottom: false,
|
|
6050
|
-
borderLeft: false,
|
|
6051
|
-
borderRight: false,
|
|
6052
|
-
children: [
|
|
6053
|
-
payload.mode === "ask" ? /* @__PURE__ */ jsxs10(Box11, { flexGrow: 1, children: [
|
|
6054
|
-
/* @__PURE__ */ jsx18(Text11, { color: canRespond ? "green" : "gray", children: "[Enter/y] Approve" }),
|
|
6055
|
-
/* @__PURE__ */ jsx18(Text11, {}),
|
|
6056
|
-
/* @__PURE__ */ jsx18(Text11, { color: canRespond ? "red" : "gray", children: "[Esc/n] Deny" }),
|
|
6057
|
-
!canRespond && /* @__PURE__ */ jsx18(Text11, { color: "yellow", children: "(Wait 1s...)" })
|
|
6058
|
-
] }) : /* @__PURE__ */ jsx18(Box11, { flexGrow: 1, children: /* @__PURE__ */ jsx18(Text11, { color: "cyan", children: "[Enter/Esc] Close" }) }),
|
|
6059
|
-
/* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
6060
|
-
"Line ",
|
|
6061
|
-
scrollIndex + 1,
|
|
6062
|
-
"/",
|
|
6063
|
-
wrappedLines.length
|
|
6064
|
-
] })
|
|
6065
|
-
]
|
|
6066
|
-
}
|
|
6067
|
-
)
|
|
6068
|
-
]
|
|
6069
|
-
}
|
|
6070
|
-
);
|
|
6071
|
-
}
|
|
6072
|
-
|
|
6073
|
-
// ink/configurationWizard/ConfigurationWizard.tsx
|
|
6074
|
-
import { Box as Box18, useInput as useInput10 } from "ink";
|
|
6075
|
-
import { Step, Stepper } from "ink-stepper";
|
|
6076
|
-
import { useEffect as useEffect14, useState as useState17 } from "react";
|
|
6077
|
-
|
|
6078
|
-
// utils/files/getEnvVarForProvider.ts
|
|
6079
|
-
function getEnvVarForProvider(provider) {
|
|
6080
|
-
switch (provider) {
|
|
6081
|
-
case "Anthropic":
|
|
6082
|
-
return "ANTHROPIC_API_KEY";
|
|
6083
|
-
case "Google":
|
|
6084
|
-
return "GOOGLE_GENERATIVE_AI_API_KEY";
|
|
6085
|
-
case "OpenAI":
|
|
6086
|
-
return "OPENAI_API_KEY";
|
|
6087
|
-
case "Ollama":
|
|
6088
|
-
return "OLLAMA_BASE_URL";
|
|
6089
|
-
}
|
|
6090
|
-
}
|
|
6091
|
-
|
|
6092
|
-
// utils/files/updateEnvKeyForProvider.ts
|
|
6093
|
-
function updateEnvKeyForProvider(provider, key) {
|
|
6094
|
-
const envVar = getEnvVarForProvider(provider);
|
|
6095
|
-
return updateEnv(envVar, key);
|
|
6096
|
-
}
|
|
6097
|
-
|
|
6098
|
-
// ink/configurationWizard/ApiKeyStep.tsx
|
|
6099
|
-
import { PasswordInput } from "@inkjs/ui";
|
|
6100
|
-
import { Box as Box12, Text as Text12, useInput as useInput6 } from "ink";
|
|
6101
|
-
import { useStepperInput } from "ink-stepper";
|
|
6102
|
-
import { useEffect as useEffect9 } from "react";
|
|
6103
|
-
import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
6104
|
-
function ApiKeyStep({ provider, onConfirm, onBack }) {
|
|
6105
|
-
const { disableNavigation, enableNavigation } = useStepperInput();
|
|
6106
|
-
useEffect9(() => {
|
|
6107
|
-
disableNavigation();
|
|
6108
|
-
return () => enableNavigation();
|
|
6109
|
-
}, [disableNavigation, enableNavigation]);
|
|
6110
|
-
useInput6((input, key) => {
|
|
6111
|
-
if (key.escape) {
|
|
6112
|
-
onBack();
|
|
6113
|
-
}
|
|
6114
|
-
});
|
|
6115
|
-
const instructions = {
|
|
6116
|
-
OpenAI: "Get your key at: https://platform.openai.com/api-keys",
|
|
6117
|
-
Anthropic: "Get your key at: https://console.anthropic.com/settings/keys",
|
|
6118
|
-
Google: "Get your key at: https://aistudio.google.com/app/apikey",
|
|
6119
|
-
Ollama: ""
|
|
6120
|
-
};
|
|
6121
|
-
return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", children: [
|
|
6122
|
-
/* @__PURE__ */ jsxs11(Text12, { children: [
|
|
6123
|
-
"Can you provide us with your ",
|
|
6124
|
-
provider,
|
|
6125
|
-
" API key?"
|
|
6126
|
-
] }),
|
|
6127
|
-
/* @__PURE__ */ jsx19(Text12, { dimColor: true, children: instructions[provider] }),
|
|
6128
|
-
/* @__PURE__ */ jsx19(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx19(
|
|
6129
|
-
PasswordInput,
|
|
6130
|
-
{
|
|
6131
|
-
onSubmit: (v) => {
|
|
6132
|
-
if (v === "exit") {
|
|
6133
|
-
emitToListeners("ExitUI", void 0);
|
|
6134
|
-
} else {
|
|
6135
|
-
onConfirm(v);
|
|
6136
|
-
}
|
|
6137
|
-
}
|
|
6138
|
-
}
|
|
6139
|
-
) }),
|
|
6140
|
-
/* @__PURE__ */ jsx19(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx19(Text12, { dimColor: true, children: "Press ESC to go back" }) })
|
|
6141
|
-
] });
|
|
6142
|
-
}
|
|
6143
|
-
|
|
6144
|
-
// ink/configurationWizard/ApiUrlStep.tsx
|
|
6145
|
-
import { Box as Box13, Text as Text13, useInput as useInput7 } from "ink";
|
|
6146
|
-
import { useStepperInput as useStepperInput2 } from "ink-stepper";
|
|
6147
|
-
import { useEffect as useEffect10 } from "react";
|
|
6148
|
-
import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
6149
|
-
function ApiUrlStep({ provider, onConfirm, onBack }) {
|
|
6150
|
-
const { disableNavigation, enableNavigation } = useStepperInput2();
|
|
6151
|
-
useEffect10(() => {
|
|
6152
|
-
disableNavigation();
|
|
6153
|
-
return () => enableNavigation();
|
|
6154
|
-
}, [disableNavigation, enableNavigation]);
|
|
6155
|
-
useInput7((input, key) => {
|
|
6156
|
-
if (key.escape) {
|
|
6157
|
-
onBack();
|
|
6158
|
-
}
|
|
6159
|
-
});
|
|
6160
|
-
const defaultApi = {
|
|
6161
|
-
OpenAI: "",
|
|
6162
|
-
Anthropic: "",
|
|
6163
|
-
Google: "",
|
|
6164
|
-
Ollama: "http://localhost:11434/api"
|
|
6165
|
-
};
|
|
6166
|
-
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", children: [
|
|
6167
|
-
/* @__PURE__ */ jsxs12(Text13, { children: [
|
|
6168
|
-
"Where are you hosting ",
|
|
6169
|
-
provider,
|
|
6170
|
-
"?"
|
|
6171
|
-
] }),
|
|
6172
|
-
/* @__PURE__ */ jsx20(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx20(
|
|
6173
|
-
BlinkingTextInput,
|
|
6174
|
-
{
|
|
6175
|
-
placeholder: defaultApi[provider] || "",
|
|
6176
|
-
onSubmit: (v) => {
|
|
6177
|
-
if (v === "exit") {
|
|
6178
|
-
emitToListeners("ExitUI", void 0);
|
|
6179
|
-
} else {
|
|
6180
|
-
onConfirm(v || defaultApi[provider] || "");
|
|
6181
|
-
}
|
|
6182
|
-
}
|
|
6183
|
-
}
|
|
6184
|
-
) }),
|
|
6185
|
-
/* @__PURE__ */ jsx20(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx20(Text13, { dimColor: true, children: "Press ESC to go back" }) })
|
|
6186
|
-
] });
|
|
6187
|
-
}
|
|
6188
|
-
|
|
6189
|
-
// ink/configurationWizard/EnvironmentSettingsStep.tsx
|
|
6190
|
-
import { MultiSelect } from "@inkjs/ui";
|
|
6191
|
-
import { Box as Box14, Text as Text14, useInput as useInput8 } from "ink";
|
|
6192
|
-
import { useStepperInput as useStepperInput3 } from "ink-stepper";
|
|
6193
|
-
import { useEffect as useEffect11 } from "react";
|
|
6194
|
-
import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
6195
|
-
var SETTINGS = [
|
|
6196
|
-
{
|
|
6197
|
-
label: "Save Harper agent memory locally",
|
|
6198
|
-
value: "HARPER_AGENT_SESSION",
|
|
6199
|
-
defaultValue: "./harper-agent-memory.json"
|
|
6200
|
-
},
|
|
6201
|
-
{
|
|
6202
|
-
label: "Automatically approve code interpreter execution",
|
|
6203
|
-
value: "HARPER_AGENT_AUTO_APPROVE_CODE_INTERPRETER",
|
|
6204
|
-
defaultValue: "1"
|
|
6205
|
-
},
|
|
6206
|
-
{
|
|
6207
|
-
label: "Automatically approve file patches",
|
|
6208
|
-
value: "HARPER_AGENT_AUTO_APPROVE_PATCHES",
|
|
6209
|
-
defaultValue: "1"
|
|
6210
|
-
},
|
|
6211
|
-
{
|
|
6212
|
-
label: "Automatically approve shell commands",
|
|
6213
|
-
value: "HARPER_AGENT_AUTO_APPROVE_SHELL",
|
|
6214
|
-
defaultValue: "1"
|
|
6215
|
-
},
|
|
6216
|
-
{
|
|
6217
|
-
label: "Use flex tier for lower costs when possible",
|
|
6218
|
-
value: "HARPER_AGENT_FLEX_TIER",
|
|
6219
|
-
defaultValue: "true"
|
|
6220
|
-
}
|
|
6221
|
-
];
|
|
6222
|
-
function EnvironmentSettingsStep({ onConfirm, onBack }) {
|
|
6223
|
-
const { disableNavigation, enableNavigation } = useStepperInput3();
|
|
6224
|
-
useEffect11(() => {
|
|
6225
|
-
disableNavigation();
|
|
6226
|
-
return () => enableNavigation();
|
|
6227
|
-
}, [disableNavigation, enableNavigation]);
|
|
6228
|
-
useInput8((_input, key) => {
|
|
6229
|
-
if (key.escape) {
|
|
6230
|
-
onBack();
|
|
6231
|
-
}
|
|
6232
|
-
});
|
|
6233
|
-
const options = SETTINGS.map((s) => ({
|
|
6234
|
-
label: s.label,
|
|
6235
|
-
value: s.value
|
|
6236
|
-
}));
|
|
6237
|
-
const defaultValues = SETTINGS.map((s) => s.value);
|
|
6238
|
-
const handleSubmit = (values) => {
|
|
6239
|
-
for (const setting of SETTINGS) {
|
|
6240
|
-
if (values.includes(setting.value)) {
|
|
6241
|
-
updateEnv(setting.value, setting.defaultValue);
|
|
6242
|
-
}
|
|
6243
|
-
}
|
|
6244
|
-
onConfirm();
|
|
6245
|
-
};
|
|
6246
|
-
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
|
|
6247
|
-
/* @__PURE__ */ jsx21(Text14, { children: "Additional Settings (all enabled by default):" }),
|
|
6248
|
-
/* @__PURE__ */ jsx21(
|
|
6249
|
-
MultiSelect,
|
|
6250
|
-
{
|
|
6251
|
-
options,
|
|
6252
|
-
defaultValue: defaultValues,
|
|
6253
|
-
onSubmit: handleSubmit
|
|
6254
|
-
}
|
|
6255
|
-
),
|
|
6256
|
-
/* @__PURE__ */ jsxs13(Text14, { color: "gray", children: [
|
|
6257
|
-
"Press ",
|
|
6258
|
-
"<space>",
|
|
6259
|
-
" to toggle, ",
|
|
6260
|
-
"<enter>",
|
|
6261
|
-
" to confirm."
|
|
6262
|
-
] })
|
|
6263
|
-
] });
|
|
6264
|
-
}
|
|
6265
|
-
|
|
6266
|
-
// ink/configurationWizard/modelsByProvider.ts
|
|
6267
|
-
var modelsByProvider = {
|
|
6268
|
-
OpenAI: [defaultOpenAIModel, "gpt-5.0", defaultOpenAICompactionModel],
|
|
6269
|
-
Anthropic: [defaultAnthropicModel, "claude-4-5-sonnet-latest", defaultAnthropicCompactionModel],
|
|
6270
|
-
Google: [defaultGoogleModel, "gemini-3-flash", "gemini-2.5-flash", defaultGoogleCompactionModel],
|
|
6271
|
-
Ollama: [defaultOllamaModel, "ollama-qwen3.5:27b", defaultOllamaCompactionModel]
|
|
6272
|
-
};
|
|
6273
|
-
var compactorModelsByProvider = {
|
|
6274
|
-
OpenAI: modelsByProvider.OpenAI.slice().reverse(),
|
|
6275
|
-
Anthropic: modelsByProvider.Anthropic.slice().reverse(),
|
|
6276
|
-
Google: modelsByProvider.Google.slice().reverse(),
|
|
6277
|
-
Ollama: modelsByProvider.Ollama.slice().reverse()
|
|
6278
|
-
};
|
|
6279
|
-
|
|
6280
|
-
// ink/configurationWizard/ModelSelectionStep.tsx
|
|
6281
|
-
import { Select } from "@inkjs/ui";
|
|
6282
|
-
import { Box as Box15, Text as Text15, useInput as useInput9 } from "ink";
|
|
6283
|
-
import { useStepperInput as useStepperInput4 } from "ink-stepper";
|
|
6284
|
-
import { useEffect as useEffect12, useState as useState16 } from "react";
|
|
6285
|
-
import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
6286
|
-
function ModelSelectionStep({
|
|
6287
|
-
title,
|
|
6288
|
-
models,
|
|
6289
|
-
onConfirm,
|
|
6290
|
-
onBack
|
|
6291
|
-
}) {
|
|
6292
|
-
const { disableNavigation, enableNavigation } = useStepperInput4();
|
|
6293
|
-
const [isCustom, setIsCustom] = useState16(false);
|
|
6294
|
-
useEffect12(() => {
|
|
6295
|
-
disableNavigation();
|
|
6296
|
-
return () => enableNavigation();
|
|
6297
|
-
}, [isCustom, disableNavigation, enableNavigation]);
|
|
6298
|
-
useInput9((input, key) => {
|
|
6299
|
-
if (key.escape) {
|
|
6300
|
-
if (isCustom) {
|
|
6301
|
-
setIsCustom(false);
|
|
6302
|
-
} else {
|
|
6303
|
-
onBack();
|
|
6304
|
-
}
|
|
6305
|
-
}
|
|
6306
|
-
});
|
|
6307
|
-
if (isCustom) {
|
|
6308
|
-
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
|
|
6309
|
-
/* @__PURE__ */ jsxs14(Text15, { children: [
|
|
6310
|
-
"Enter custom model name for: ",
|
|
6311
|
-
title
|
|
6312
|
-
] }),
|
|
6313
|
-
/* @__PURE__ */ jsx22(
|
|
6314
|
-
BlinkingTextInput,
|
|
6315
|
-
{
|
|
6316
|
-
onSubmit: (v) => {
|
|
6317
|
-
if (v === "exit") {
|
|
6318
|
-
emitToListeners("ExitUI", void 0);
|
|
6319
|
-
} else {
|
|
6320
|
-
onConfirm(v);
|
|
6321
|
-
}
|
|
6322
|
-
}
|
|
6323
|
-
}
|
|
6324
|
-
),
|
|
6325
|
-
/* @__PURE__ */ jsx22(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx22(Text15, { dimColor: true, children: "Press ESC to go back to list" }) })
|
|
6326
|
-
] });
|
|
6327
|
-
}
|
|
6328
|
-
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
|
|
6329
|
-
/* @__PURE__ */ jsx22(Text15, { children: title }),
|
|
6330
|
-
/* @__PURE__ */ jsx22(
|
|
6331
|
-
Select,
|
|
6332
|
-
{
|
|
6333
|
-
options: [
|
|
6334
|
-
...models.map((m) => ({ label: m, value: m })),
|
|
6335
|
-
{ label: "Other...", value: "other" }
|
|
6336
|
-
],
|
|
6337
|
-
onChange: (v) => {
|
|
6338
|
-
if (v === "other") {
|
|
6339
|
-
setIsCustom(true);
|
|
6340
|
-
} else {
|
|
6341
|
-
onConfirm(v);
|
|
6342
|
-
}
|
|
6343
|
-
}
|
|
6344
|
-
}
|
|
6345
|
-
),
|
|
6346
|
-
/* @__PURE__ */ jsx22(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx22(Text15, { dimColor: true, children: "Press ESC to go back" }) })
|
|
6347
|
-
] });
|
|
6348
|
-
}
|
|
6349
|
-
|
|
6350
|
-
// ink/configurationWizard/ProviderStep.tsx
|
|
6351
|
-
import { Select as Select2 } from "@inkjs/ui";
|
|
6352
|
-
import { Box as Box16, Text as Text16 } from "ink";
|
|
6353
|
-
import { useStepperInput as useStepperInput5 } from "ink-stepper";
|
|
6354
|
-
import { useEffect as useEffect13 } from "react";
|
|
6355
|
-
|
|
6356
|
-
// ink/configurationWizard/providers.ts
|
|
6357
|
-
var providers = [
|
|
6358
|
-
{ label: "OpenAI", value: "OpenAI" },
|
|
6359
|
-
{ label: "Anthropic", value: "Anthropic" },
|
|
6360
|
-
{ label: "Google", value: "Google" },
|
|
6361
|
-
{ label: "Ollama", value: "Ollama" }
|
|
6362
|
-
];
|
|
6363
|
-
|
|
6364
|
-
// ink/configurationWizard/ProviderStep.tsx
|
|
6365
|
-
import { jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
6366
|
-
function ProviderStep({ onConfirm }) {
|
|
6367
|
-
const { disableNavigation, enableNavigation } = useStepperInput5();
|
|
6368
|
-
useEffect13(() => {
|
|
6369
|
-
disableNavigation();
|
|
6370
|
-
return () => enableNavigation();
|
|
6371
|
-
}, [disableNavigation, enableNavigation]);
|
|
6372
|
-
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
6373
|
-
/* @__PURE__ */ jsx23(Text16, { children: "What model provider would you like to use today?" }),
|
|
6374
|
-
/* @__PURE__ */ jsx23(
|
|
6375
|
-
Select2,
|
|
6376
|
-
{
|
|
6377
|
-
options: providers,
|
|
6378
|
-
onChange: (v) => onConfirm(v)
|
|
6379
|
-
}
|
|
6380
|
-
)
|
|
6381
|
-
] });
|
|
6382
|
-
}
|
|
6383
|
-
|
|
6384
|
-
// ink/configurationWizard/StepperProgress.tsx
|
|
6385
|
-
import { Box as Box17, Text as Text17 } from "ink";
|
|
6386
|
-
import { Fragment as Fragment3 } from "react";
|
|
6387
|
-
import { jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
6388
|
-
var markers = {
|
|
6389
|
-
completed: " \u2713 ",
|
|
6390
|
-
current: " \u25CF ",
|
|
6391
|
-
pending: " \u25CB "
|
|
6392
|
-
};
|
|
6393
|
-
var SEGMENT_WIDTH = 12;
|
|
6394
|
-
function StepperProgress({ steps, currentStep }) {
|
|
6395
|
-
return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", marginBottom: 1, children: [
|
|
6396
|
-
/* @__PURE__ */ jsx24(Box17, { children: steps.map((step) => {
|
|
6397
|
-
return /* @__PURE__ */ jsx24(Box17, { width: SEGMENT_WIDTH, justifyContent: "center", children: /* @__PURE__ */ jsx24(
|
|
6398
|
-
Text17,
|
|
6399
|
-
{
|
|
6400
|
-
color: step.completed ? "green" : step.current ? "cyan" : "gray",
|
|
6401
|
-
bold: step.current,
|
|
6402
|
-
dimColor: !step.completed && !step.current,
|
|
6403
|
-
children: step.name
|
|
6404
|
-
}
|
|
6405
|
-
) }, step.name);
|
|
6406
|
-
}) }),
|
|
6407
|
-
/* @__PURE__ */ jsx24(Box17, { children: steps.map((step, idx) => {
|
|
6408
|
-
const marker = step.completed ? markers.completed : step.current ? markers.current : markers.pending;
|
|
6409
|
-
const beforeLineColor = step.completed || idx <= currentStep ? "green" : "gray";
|
|
6410
|
-
const afterLineColor = step.completed ? "green" : "gray";
|
|
6411
|
-
const markerColor = step.completed ? "green" : step.current ? "cyan" : "gray";
|
|
6412
|
-
return /* @__PURE__ */ jsxs16(Fragment3, { children: [
|
|
6413
|
-
/* @__PURE__ */ jsx24(Text17, { color: beforeLineColor, children: "\u2501".repeat(SEGMENT_WIDTH / 2 - 2) }),
|
|
6414
|
-
/* @__PURE__ */ jsx24(Text17, { color: markerColor, bold: step.current, children: marker }),
|
|
6415
|
-
/* @__PURE__ */ jsx24(Text17, { color: afterLineColor, children: "\u2501".repeat(SEGMENT_WIDTH / 2 - 1) })
|
|
6416
|
-
] }, step.name);
|
|
6417
|
-
}) })
|
|
6418
|
-
] });
|
|
6419
|
-
}
|
|
6420
|
-
|
|
6421
|
-
// ink/configurationWizard/ConfigurationWizard.tsx
|
|
6422
|
-
import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
6423
|
-
function ConfigurationWizard({ onComplete }) {
|
|
6424
|
-
const [provider, setProvider] = useState17("OpenAI");
|
|
6425
|
-
const [ollamaModels, setOllamaModels] = useState17([]);
|
|
6426
|
-
useEffect14(() => {
|
|
6427
|
-
if (provider === "Ollama") {
|
|
6428
|
-
fetchOllamaModels().then((models2) => {
|
|
6429
|
-
if (models2.length > 0) {
|
|
6430
|
-
setOllamaModels(models2);
|
|
6431
|
-
}
|
|
6432
|
-
});
|
|
6433
|
-
}
|
|
6434
|
-
}, [provider]);
|
|
6435
|
-
useInput10((input, key) => {
|
|
6436
|
-
if (key.ctrl && input === "x") {
|
|
6437
|
-
emitToListeners("ExitUI", void 0);
|
|
6438
|
-
}
|
|
6439
|
-
});
|
|
6440
|
-
const models = provider === "Ollama" && ollamaModels.length > 0 ? [.../* @__PURE__ */ new Set([...ollamaModels, ...modelsByProvider[provider]])] : modelsByProvider[provider];
|
|
6441
|
-
const compactorModels = provider === "Ollama" && ollamaModels.length > 0 ? [.../* @__PURE__ */ new Set([...ollamaModels, ...compactorModelsByProvider[provider]])] : compactorModelsByProvider[provider];
|
|
6442
|
-
return /* @__PURE__ */ jsx25(Box18, { flexDirection: "column", padding: 1, minHeight: 10, children: /* @__PURE__ */ jsxs17(
|
|
6443
|
-
Stepper,
|
|
6444
|
-
{
|
|
6445
|
-
onComplete,
|
|
6446
|
-
onCancel: curryEmitToListeners("ExitUI", void 0),
|
|
6447
|
-
keyboardNav: true,
|
|
6448
|
-
renderProgress: StepperProgress,
|
|
6449
|
-
children: [
|
|
6450
|
-
/* @__PURE__ */ jsx25(Step, { name: "AI Provider", children: ({ goNext }) => /* @__PURE__ */ jsx25(
|
|
6451
|
-
ProviderStep,
|
|
6452
|
-
{
|
|
6453
|
-
onConfirm: (p) => {
|
|
6454
|
-
setProvider(p);
|
|
6455
|
-
goNext();
|
|
6456
|
-
}
|
|
6457
|
-
}
|
|
6458
|
-
) }),
|
|
6459
|
-
/* @__PURE__ */ jsx25(Step, { name: provider !== "Ollama" ? "API Key" : "API", children: ({ goNext, goBack }) => provider !== "Ollama" ? /* @__PURE__ */ jsx25(
|
|
6460
|
-
ApiKeyStep,
|
|
6461
|
-
{
|
|
6462
|
-
provider,
|
|
6463
|
-
onConfirm: (key) => {
|
|
6464
|
-
updateEnvKeyForProvider(provider, key);
|
|
6465
|
-
goNext();
|
|
6466
|
-
},
|
|
6467
|
-
onBack: goBack
|
|
6468
|
-
}
|
|
6469
|
-
) : /* @__PURE__ */ jsx25(
|
|
6470
|
-
ApiUrlStep,
|
|
6471
|
-
{
|
|
6472
|
-
provider,
|
|
6473
|
-
onConfirm: (key) => {
|
|
6474
|
-
updateEnvKeyForProvider(provider, key);
|
|
6475
|
-
goNext();
|
|
6476
|
-
},
|
|
6477
|
-
onBack: goBack
|
|
6478
|
-
}
|
|
6479
|
-
) }),
|
|
6480
|
-
/* @__PURE__ */ jsx25(Step, { name: "Model", children: ({ goNext, goBack }) => /* @__PURE__ */ jsx25(
|
|
6481
|
-
ModelSelectionStep,
|
|
6482
|
-
{
|
|
6483
|
-
title: "What model would you like to use?",
|
|
6484
|
-
models,
|
|
6485
|
-
onConfirm: (m) => {
|
|
6486
|
-
const finalModelName = provider === "Ollama" && !m.startsWith("ollama-") && !m.includes(":") ? `ollama-${m}` : m;
|
|
6487
|
-
updateEnv("HARPER_AGENT_MODEL", finalModelName);
|
|
6488
|
-
goNext();
|
|
6489
|
-
},
|
|
6490
|
-
onBack: goBack
|
|
6491
|
-
}
|
|
6492
|
-
) }),
|
|
6493
|
-
/* @__PURE__ */ jsx25(Step, { name: "Compactor", children: ({ goNext, goBack }) => /* @__PURE__ */ jsx25(
|
|
6494
|
-
ModelSelectionStep,
|
|
6495
|
-
{
|
|
6496
|
-
title: "What model should we use for memory compaction?",
|
|
6497
|
-
models: compactorModels,
|
|
6498
|
-
onConfirm: (m) => {
|
|
6499
|
-
const finalModelName = provider === "Ollama" && !m.startsWith("ollama-") && !m.includes(":") ? `ollama-${m}` : m;
|
|
6500
|
-
updateEnv("HARPER_AGENT_COMPACTION_MODEL", finalModelName);
|
|
6501
|
-
goNext();
|
|
6502
|
-
},
|
|
6503
|
-
onBack: goBack
|
|
6504
|
-
}
|
|
6505
|
-
) }),
|
|
6506
|
-
/* @__PURE__ */ jsx25(Step, { name: "Settings", children: ({ goNext, goBack }) => /* @__PURE__ */ jsx25(
|
|
6507
|
-
EnvironmentSettingsStep,
|
|
6508
|
-
{
|
|
6509
|
-
onConfirm: () => {
|
|
6510
|
-
goNext();
|
|
6511
|
-
},
|
|
6512
|
-
onBack: goBack
|
|
6513
|
-
}
|
|
6514
|
-
) })
|
|
6515
|
-
]
|
|
6516
|
-
}
|
|
6517
|
-
) });
|
|
6518
|
-
}
|
|
6519
|
-
|
|
6520
|
-
// ink/main.tsx
|
|
6521
|
-
import { jsx as jsx26, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
6522
|
-
function bootstrapConfig() {
|
|
6523
|
-
return new Promise((resolve2) => {
|
|
6524
|
-
render(/* @__PURE__ */ jsx26(MainConfig, { onComplete: resolve2 }));
|
|
6525
|
-
});
|
|
6526
|
-
}
|
|
6527
|
-
function MainConfig({ onComplete }) {
|
|
6528
|
-
const { exit } = useApp();
|
|
6529
|
-
useListener("ExitUI", () => exit(), [exit]);
|
|
6530
|
-
return /* @__PURE__ */ jsx26(ConfigurationWizard, { onComplete });
|
|
6531
|
-
}
|
|
6532
|
-
function bootstrapMain() {
|
|
6533
|
-
render(/* @__PURE__ */ jsx26(MainChat, {}));
|
|
6534
|
-
}
|
|
6535
|
-
function MainChat() {
|
|
6536
|
-
const { exit } = useApp();
|
|
6537
|
-
useListener("ExitUI", () => exit(), [exit]);
|
|
6538
|
-
return /* @__PURE__ */ jsx26(CostProvider, { children: /* @__PURE__ */ jsx26(PlanProvider, { children: /* @__PURE__ */ jsx26(ActionsProvider, { children: /* @__PURE__ */ jsx26(ApprovalProvider, { children: /* @__PURE__ */ jsx26(SettingsProvider, { children: /* @__PURE__ */ jsxs18(ChatProvider, { children: [
|
|
6539
|
-
/* @__PURE__ */ jsx26(ChatContent, {}),
|
|
6540
|
-
/* @__PURE__ */ jsx26(DiffApprovalView, {})
|
|
6541
|
-
] }) }) }) }) }) });
|
|
6542
|
-
}
|
|
6543
|
-
|
|
6544
|
-
// utils/models/deprecations.ts
|
|
6545
|
-
import chalk4 from "chalk";
|
|
6546
|
-
var DEPRECATION_RULES = [
|
|
6547
|
-
// Redirect any gpt-4o variants (including dated and -mini) to gpt-5-nano
|
|
6548
|
-
{
|
|
6549
|
-
match: (name) => name.toLowerCase().startsWith("gpt-4o"),
|
|
6550
|
-
replacement: "gpt-5-nano",
|
|
6551
|
-
reason: "OpenAI gpt-4o family is deprecated in this agent"
|
|
6552
|
-
}
|
|
6553
|
-
];
|
|
6554
|
-
function getDeprecatedReplacement(modelName) {
|
|
6555
|
-
if (!modelName) {
|
|
6556
|
-
return null;
|
|
6557
|
-
}
|
|
6558
|
-
for (const rule of DEPRECATION_RULES) {
|
|
6559
|
-
if (rule.match(modelName)) {
|
|
6560
|
-
return { replacement: rule.replacement, rule };
|
|
6561
|
-
}
|
|
6562
|
-
}
|
|
6563
|
-
return null;
|
|
6564
|
-
}
|
|
6565
|
-
function warnAndPersistRedirect(original, envKey, replacement, reason) {
|
|
6566
|
-
const reasonSuffix = reason ? ` Reason: ${reason}.` : "";
|
|
6567
|
-
console.warn(
|
|
6568
|
-
chalk4.yellow(
|
|
6569
|
-
`Warning: model "${original}" is deprecated and will be redirected to "${replacement}". Your environment setting (${envKey}) has been updated.${reasonSuffix}`
|
|
6570
|
-
)
|
|
6571
|
-
);
|
|
6572
|
-
updateEnv(envKey, replacement);
|
|
6573
|
-
}
|
|
6574
|
-
|
|
6575
|
-
// utils/shell/cli.ts
|
|
6576
|
-
import chalk5 from "chalk";
|
|
6577
|
-
|
|
6578
|
-
// utils/package/getOwnPackageJson.ts
|
|
6579
|
-
import { readFileSync as readFileSync7 } from "fs";
|
|
6580
|
-
import { join as join11 } from "path";
|
|
6581
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6582
|
-
var __dirname2 = fileURLToPath2(new URL(".", import.meta.url));
|
|
6583
|
-
function getOwnPackageJson() {
|
|
6584
|
-
try {
|
|
6585
|
-
const packageContents = readFileSync7(join11(__dirname2, "../package.json"), "utf8");
|
|
6586
|
-
return JSON.parse(packageContents);
|
|
6587
|
-
} catch {
|
|
6588
|
-
return { name: "@harperfast/agent", version: "0.0.0" };
|
|
6589
|
-
}
|
|
6590
|
-
}
|
|
6591
|
-
|
|
6592
|
-
// utils/shell/cli.ts
|
|
6593
|
-
function isHelpRequest(args) {
|
|
6594
|
-
const helpVariants = ["--help", "-h", "help"];
|
|
6595
|
-
return args.some((arg) => helpVariants.includes(arg.toLowerCase()));
|
|
6596
|
-
}
|
|
6597
|
-
function isVersionRequest(args) {
|
|
6598
|
-
const versionVariants = ["--version", "-v", "version"];
|
|
6599
|
-
return args.some((arg) => versionVariants.includes(arg.toLowerCase()));
|
|
6600
|
-
}
|
|
6601
|
-
function handleHelp() {
|
|
6602
|
-
console.log(`
|
|
6603
|
-
${chalk5.bold("harper-agent")} - AI to help you with Harper app creation and modification
|
|
6604
|
-
|
|
6605
|
-
${chalk5.bold("USAGE")}
|
|
6606
|
-
$ harper-agent [options]
|
|
6607
|
-
$ harper-agent [command]
|
|
6608
|
-
|
|
6609
|
-
${chalk5.bold("OPTIONS")}
|
|
6610
|
-
-h, --help Show help information
|
|
6611
|
-
-v, --version Show version information
|
|
6612
|
-
-m, --model Specify the model to use (e.g., ${defaultOpenAIModel}, ${defaultAnthropicModel}, ${defaultOllamaModel})
|
|
6613
|
-
Can also be set via HARPER_AGENT_MODEL environment variable.
|
|
6614
|
-
For Ollama, use the ollama- prefix (e.g., ${defaultOllamaCompactionModel}).
|
|
6615
|
-
-c, --compaction-model Specify the compaction model to use (defaults to ${defaultOpenAICompactionModel}).
|
|
6616
|
-
Can also be set via HARPER_AGENT_COMPACTION_MODEL environment variable.
|
|
6617
|
-
-s, --session Specify a path to a SQLite database file to persist the chat session.
|
|
6618
|
-
Can also be set via HARPER_AGENT_SESSION environment variable.
|
|
6619
|
-
--max-turns Specify the maximum number of turns for the agent run.
|
|
6620
|
-
Can also be set via HARPER_AGENT_MAX_TURNS environment variable.
|
|
6621
|
-
--max-cost Specify the maximum cost (in USD) for the agent run.
|
|
6622
|
-
If exceeded, the agent will exit with a non-zero code.
|
|
6623
|
-
Can also be set via HARPER_AGENT_MAX_COST environment variable.
|
|
6624
|
-
--flex-tier Force the use of the flex service tier for lower costs but potentially
|
|
6625
|
-
more errors under high system load.
|
|
6626
|
-
Can also be set via HARPER_AGENT_FLEX_TIER=true environment variable.
|
|
6627
|
-
-p, --prompt Specify a prompt to be executed autonomously until completion.
|
|
6628
|
-
|
|
6629
|
-
${chalk5.bold("COMMANDS")}
|
|
6630
|
-
--help Show help information
|
|
6631
|
-
--version Show version information
|
|
6632
|
-
|
|
6633
|
-
${chalk5.bold("EXAMPLES")}
|
|
6634
|
-
$ harper-agent --help
|
|
6635
|
-
$ harper-agent --version
|
|
6636
|
-
$ harper-agent
|
|
6637
|
-
`);
|
|
6638
|
-
process.exit(0);
|
|
6639
|
-
}
|
|
6640
|
-
function handleVersion() {
|
|
6641
|
-
const pkg = getOwnPackageJson();
|
|
6642
|
-
console.log(pkg.version);
|
|
6643
|
-
process.exit(0);
|
|
6644
|
-
}
|
|
6645
|
-
|
|
6646
|
-
// lifecycle/parseArgs.ts
|
|
6647
|
-
function stripQuotes(str) {
|
|
6648
|
-
if (str.startsWith('"') && str.endsWith('"') || str.startsWith("'") && str.endsWith("'")) {
|
|
6649
|
-
return str.slice(1, -1);
|
|
6650
|
-
}
|
|
6651
|
-
return str;
|
|
6652
|
-
}
|
|
6653
|
-
function parseArgs() {
|
|
6654
|
-
const args = process.argv.slice(2);
|
|
6655
|
-
if (isHelpRequest(args)) {
|
|
6656
|
-
handleHelp();
|
|
6657
|
-
}
|
|
6658
|
-
if (isVersionRequest(args)) {
|
|
6659
|
-
handleVersion();
|
|
6660
|
-
}
|
|
6661
|
-
for (let i = 0; i < args.length; i++) {
|
|
6662
|
-
const arg = args[i];
|
|
6663
|
-
const flagPairs = [
|
|
6664
|
-
["model", ["--model", "-m", "model"]],
|
|
6665
|
-
["compactionModel", ["--compaction-model", "-c", "compaction-model"]],
|
|
6666
|
-
["sessionPath", ["--session", "-s", "session"]],
|
|
6667
|
-
["maxTurns", ["--max-turns"]],
|
|
6668
|
-
["maxCost", ["--max-cost"]],
|
|
6669
|
-
["rateLimitThreshold", ["--rate-limit-threshold"]],
|
|
6670
|
-
["prompt", ["--prompt", "-p"]]
|
|
6671
|
-
];
|
|
6672
|
-
let handled = false;
|
|
6673
|
-
for (const [key, prefixes] of flagPairs) {
|
|
6674
|
-
for (const prefix of prefixes) {
|
|
6675
|
-
if (arg === prefix) {
|
|
6676
|
-
if (args[i + 1]) {
|
|
6677
|
-
const val = stripQuotes(args[++i]);
|
|
6678
|
-
if (key === "maxTurns" || key === "maxCost" || key === "rateLimitThreshold") {
|
|
6679
|
-
trackedState[key] = parseFloat(val);
|
|
6680
|
-
} else {
|
|
6681
|
-
trackedState[key] = val;
|
|
6682
|
-
}
|
|
6683
|
-
}
|
|
6684
|
-
handled = true;
|
|
6685
|
-
break;
|
|
6686
|
-
} else if (arg.startsWith(`${prefix}=`)) {
|
|
6687
|
-
const val = stripQuotes(arg.slice(prefix.length + 1));
|
|
6688
|
-
if (key === "maxTurns" || key === "maxCost" || key === "rateLimitThreshold") {
|
|
6689
|
-
trackedState[key] = parseFloat(val);
|
|
6690
|
-
} else {
|
|
6691
|
-
trackedState[key] = val;
|
|
6692
|
-
}
|
|
6693
|
-
handled = true;
|
|
6694
|
-
break;
|
|
6695
|
-
}
|
|
6696
|
-
}
|
|
6697
|
-
if (handled) {
|
|
6698
|
-
break;
|
|
6699
|
-
}
|
|
6700
|
-
}
|
|
6701
|
-
if (handled) {
|
|
6702
|
-
continue;
|
|
6703
|
-
}
|
|
6704
|
-
if (arg === "--flex-tier") {
|
|
6705
|
-
trackedState.useFlexTier = true;
|
|
6706
|
-
} else if (arg === "--no-monitor-rate-limits") {
|
|
6707
|
-
trackedState.monitorRateLimits = false;
|
|
6708
|
-
} else if (arg === "--autonomous" || arg === "-a") {
|
|
6709
|
-
trackedState.autonomous = true;
|
|
6710
|
-
}
|
|
6711
|
-
}
|
|
6712
|
-
if (!trackedState.model && process.env.HARPER_AGENT_MODEL) {
|
|
6713
|
-
trackedState.model = process.env.HARPER_AGENT_MODEL;
|
|
6714
|
-
}
|
|
6715
|
-
if (!trackedState.compactionModel && process.env.HARPER_AGENT_COMPACTION_MODEL) {
|
|
6716
|
-
trackedState.compactionModel = process.env.HARPER_AGENT_COMPACTION_MODEL;
|
|
6717
|
-
}
|
|
6718
|
-
if (!trackedState.sessionPath && process.env.HARPER_AGENT_SESSION) {
|
|
6719
|
-
trackedState.sessionPath = process.env.HARPER_AGENT_SESSION;
|
|
6720
|
-
}
|
|
6721
|
-
if (process.env.HARPER_AGENT_MAX_TURNS) {
|
|
6722
|
-
trackedState.maxTurns = parseFloat(process.env.HARPER_AGENT_MAX_TURNS);
|
|
6723
|
-
}
|
|
6724
|
-
if (process.env.HARPER_AGENT_MAX_COST) {
|
|
6725
|
-
trackedState.maxCost = parseFloat(process.env.HARPER_AGENT_MAX_COST);
|
|
6726
|
-
}
|
|
6727
|
-
if (process.env.HARPER_AGENT_RATE_LIMIT_THRESHOLD) {
|
|
6728
|
-
trackedState.rateLimitThreshold = parseFloat(process.env.HARPER_AGENT_RATE_LIMIT_THRESHOLD);
|
|
6729
|
-
}
|
|
6730
|
-
if (process.env.HARPER_AGENT_MONITOR_RATE_LIMITS === "false") {
|
|
6731
|
-
trackedState.monitorRateLimits = false;
|
|
6732
|
-
}
|
|
6733
|
-
if (!trackedState.useFlexTier && isTrue(process.env.HARPER_AGENT_FLEX_TIER)) {
|
|
6734
|
-
trackedState.useFlexTier = true;
|
|
6735
|
-
}
|
|
6736
|
-
if (isTrue(process.env.HARPER_AGENT_AUTO_APPROVE_CODE_INTERPRETER)) {
|
|
6737
|
-
trackedState.autoApproveCodeInterpreter = true;
|
|
6738
|
-
}
|
|
6739
|
-
if (isTrue(process.env.HARPER_AGENT_AUTO_APPROVE_PATCHES)) {
|
|
6740
|
-
trackedState.autoApprovePatches = true;
|
|
6741
|
-
}
|
|
6742
|
-
if (isTrue(process.env.HARPER_AGENT_AUTO_APPROVE_SHELL)) {
|
|
6743
|
-
trackedState.autoApproveShell = true;
|
|
6744
|
-
}
|
|
6745
|
-
if (isTrue(process.env.HARPER_AGENT_AUTONOMOUS)) {
|
|
6746
|
-
trackedState.autonomous = true;
|
|
6747
|
-
}
|
|
6748
|
-
if (!trackedState.model || trackedState.model === defaultModelToken) {
|
|
6749
|
-
if (process.env.ANTHROPIC_API_KEY) {
|
|
6750
|
-
trackedState.model = defaultAnthropicModel;
|
|
6751
|
-
} else if (process.env.GOOGLE_GENERATIVE_AI_API_KEY) {
|
|
6752
|
-
trackedState.model = defaultGoogleModel;
|
|
6753
|
-
} else if (process.env.OLLAMA_BASE_URL) {
|
|
6754
|
-
trackedState.model = defaultOllamaModel;
|
|
6755
|
-
} else {
|
|
6756
|
-
trackedState.model = defaultOpenAIModel;
|
|
6757
|
-
}
|
|
6758
|
-
}
|
|
6759
|
-
if (!trackedState.compactionModel || trackedState.compactionModel === defaultModelToken) {
|
|
6760
|
-
const m = trackedState.model;
|
|
6761
|
-
if (m.startsWith("claude-")) {
|
|
6762
|
-
trackedState.compactionModel = defaultAnthropicCompactionModel;
|
|
6763
|
-
} else if (m.startsWith("gemini-")) {
|
|
6764
|
-
trackedState.compactionModel = defaultGoogleCompactionModel;
|
|
6765
|
-
} else if (m.startsWith("ollama-")) {
|
|
6766
|
-
trackedState.compactionModel = defaultOllamaCompactionModel;
|
|
6767
|
-
} else {
|
|
6768
|
-
trackedState.compactionModel = defaultOpenAICompactionModel;
|
|
6769
|
-
}
|
|
6770
|
-
}
|
|
6771
|
-
if (!isOpenAIModel(trackedState.model)) {
|
|
6772
|
-
process.env.OPENAI_AGENTS_DISABLE_TRACING = process.env.OPENAI_AGENTS_DISABLE_TRACING || "1";
|
|
6773
|
-
}
|
|
6774
|
-
const maybeRedirect = (current, envKey) => {
|
|
6775
|
-
const hit = getDeprecatedReplacement(current);
|
|
6776
|
-
if (hit) {
|
|
6777
|
-
const { replacement, rule } = hit;
|
|
6778
|
-
warnAndPersistRedirect(current, envKey, replacement, rule.reason);
|
|
6779
|
-
return replacement;
|
|
6780
|
-
}
|
|
6781
|
-
return current;
|
|
6782
|
-
};
|
|
6783
|
-
trackedState.model = maybeRedirect(trackedState.model, "HARPER_AGENT_MODEL");
|
|
6784
|
-
trackedState.compactionModel = maybeRedirect(trackedState.compactionModel, "HARPER_AGENT_COMPACTION_MODEL");
|
|
6785
|
-
}
|
|
6786
|
-
|
|
6787
|
-
// utils/envLoader.ts
|
|
6788
|
-
import dotenv from "dotenv";
|
|
6789
|
-
import { existsSync as existsSync11 } from "fs";
|
|
6790
|
-
import { homedir as homedir4 } from "os";
|
|
6791
|
-
import { join as join12 } from "path";
|
|
6792
|
-
function loadEnv() {
|
|
6793
|
-
const topLevelEnvPath = join12(homedir4(), ".harper", "harper-agent-env");
|
|
6794
|
-
const localEnvPath = join12(process.cwd(), ".env");
|
|
6795
|
-
if (existsSync11(topLevelEnvPath)) {
|
|
6796
|
-
dotenv.config({ path: topLevelEnvPath, quiet: true });
|
|
6797
|
-
}
|
|
6798
|
-
if (existsSync11(localEnvPath)) {
|
|
6799
|
-
dotenv.config({ path: localEnvPath, override: true, quiet: true });
|
|
6800
|
-
}
|
|
6801
|
-
}
|
|
6802
|
-
|
|
6803
|
-
// utils/package/checkForUpdate.ts
|
|
6804
|
-
import { Select as Select3 } from "@inkjs/ui";
|
|
6805
|
-
import chalk6 from "chalk";
|
|
6806
|
-
import spawn2 from "cross-spawn";
|
|
6807
|
-
import { Box as Box19, render as render2, Text as Text18 } from "ink";
|
|
6808
|
-
import React21 from "react";
|
|
6809
|
-
|
|
6810
|
-
// utils/package/getLatestVersion.ts
|
|
6811
|
-
async function getLatestVersion(packageName) {
|
|
6812
|
-
try {
|
|
6813
|
-
const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
|
|
6814
|
-
signal: AbortSignal.timeout(1e3)
|
|
6815
|
-
// 1 second timeout
|
|
6816
|
-
});
|
|
6817
|
-
if (!response.ok) {
|
|
6818
|
-
return null;
|
|
6819
|
-
}
|
|
6820
|
-
const data = await response.json();
|
|
6821
|
-
return data.version;
|
|
6822
|
-
} catch {
|
|
6823
|
-
return null;
|
|
6824
|
-
}
|
|
6825
|
-
}
|
|
6826
|
-
|
|
6827
|
-
// utils/package/isVersionNewer.ts
|
|
6828
|
-
function isVersionNewer(latest, current) {
|
|
6829
|
-
const l = latest.split(".").map((x) => parseInt(x, 10));
|
|
6830
|
-
const c = current.split(".").map((x) => parseInt(x, 10));
|
|
6831
|
-
for (let i = 0; i < 3; i++) {
|
|
6832
|
-
let latestNumber = l[i];
|
|
6833
|
-
let currentNumber = c[i];
|
|
6834
|
-
if (latestNumber === void 0 || currentNumber === void 0 || isNaN(latestNumber) || isNaN(currentNumber)) {
|
|
6835
|
-
break;
|
|
6836
|
-
}
|
|
6837
|
-
if (latestNumber > currentNumber) {
|
|
6838
|
-
return true;
|
|
6839
|
-
}
|
|
6840
|
-
if (latestNumber < currentNumber) {
|
|
6841
|
-
return false;
|
|
6842
|
-
}
|
|
6843
|
-
}
|
|
6844
|
-
return false;
|
|
6845
|
-
}
|
|
6846
|
-
|
|
6847
|
-
// utils/package/checkForUpdate.ts
|
|
6848
|
-
async function checkForUpdate() {
|
|
6849
|
-
const pkg = getOwnPackageJson();
|
|
6850
|
-
const packageName = pkg.name;
|
|
6851
|
-
const packageVersion = pkg.version;
|
|
6852
|
-
if (process.env.HARPER_AGENT_SKIP_UPDATE) {
|
|
6853
|
-
return packageVersion;
|
|
6854
|
-
}
|
|
6855
|
-
try {
|
|
6856
|
-
const latestVersion = await getLatestVersion(packageName);
|
|
6857
|
-
if (latestVersion && isVersionNewer(latestVersion, packageVersion)) {
|
|
6858
|
-
const choice = await promptForUpdateChoice(packageName, packageVersion, latestVersion);
|
|
6859
|
-
if (choice === "later") {
|
|
6860
|
-
return packageVersion;
|
|
6861
|
-
}
|
|
6862
|
-
if (choice === "never") {
|
|
6863
|
-
updateEnv("HARPER_AGENT_SKIP_UPDATE", "1");
|
|
6864
|
-
return packageVersion;
|
|
6865
|
-
}
|
|
6866
|
-
let isGlobal = false;
|
|
6867
|
-
try {
|
|
6868
|
-
const globalRootResult = spawn2.sync("npm", ["root", "-g"], { encoding: "utf8" });
|
|
6869
|
-
const globalRoot = globalRootResult.stdout?.trim();
|
|
6870
|
-
if (globalRoot && process.argv[1] && process.argv[1].startsWith(globalRoot)) {
|
|
6871
|
-
isGlobal = true;
|
|
6872
|
-
}
|
|
6873
|
-
} catch {
|
|
6874
|
-
}
|
|
6875
|
-
if (isGlobal) {
|
|
6876
|
-
spawn2.sync("npm", ["install", "-g", `${packageName}@latest`], { stdio: "inherit" });
|
|
6877
|
-
const result2 = spawn2.sync("harper-agent", process.argv.slice(2), { stdio: "inherit" });
|
|
6878
|
-
process.exit(result2.status ?? 0);
|
|
6879
|
-
}
|
|
6880
|
-
const lsResult = spawn2.sync("npm", ["cache", "npx", "ls", packageName], { encoding: "utf8" });
|
|
6881
|
-
if (lsResult.stdout) {
|
|
6882
|
-
const keys = lsResult.stdout.split("\n").map((line) => line.trim()).filter((line) => line.includes(":")).filter((line) => {
|
|
6883
|
-
const [, pkgPart] = line.split(":");
|
|
6884
|
-
return pkgPart && pkgPart.trim().startsWith(`${packageName}@`);
|
|
6885
|
-
}).map((line) => line.split(":")[0].trim());
|
|
6886
|
-
if (keys.length > 0) {
|
|
6887
|
-
spawn2.sync("npm", ["cache", "npx", "rm", ...keys], { stdio: "inherit" });
|
|
6888
|
-
}
|
|
6889
|
-
}
|
|
6890
|
-
const result = spawn2.sync("npx", ["-y", `${packageName}@latest`, ...process.argv.slice(2)], { stdio: "inherit" });
|
|
6891
|
-
process.exit(result.status ?? 0);
|
|
6892
|
-
}
|
|
6893
|
-
} catch {
|
|
6894
|
-
}
|
|
6895
|
-
return packageVersion;
|
|
6896
|
-
}
|
|
6897
|
-
function promptForUpdateChoice(pkgName, currentVersion, latestVersion) {
|
|
6898
|
-
return new Promise((resolve2) => {
|
|
6899
|
-
const app = render2(
|
|
6900
|
-
React21.createElement(UpdatePrompt, {
|
|
6901
|
-
packageName: pkgName,
|
|
6902
|
-
currentVersion,
|
|
6903
|
-
latestVersion,
|
|
6904
|
-
onSelect: (c) => {
|
|
6905
|
-
resolve2(c);
|
|
6906
|
-
app.unmount();
|
|
6907
|
-
}
|
|
6908
|
-
})
|
|
6909
|
-
);
|
|
6910
|
-
});
|
|
6911
|
-
}
|
|
6912
|
-
function UpdatePrompt({ packageName, currentVersion, latestVersion, onSelect }) {
|
|
6913
|
-
const options = [
|
|
6914
|
-
{
|
|
6915
|
-
label: `Update right now (will run: npx -y @harperfast/agent@latest)`,
|
|
6916
|
-
value: "now"
|
|
6917
|
-
},
|
|
6918
|
-
{ label: "Update later", value: "later" },
|
|
6919
|
-
{ label: "Don\u2019t ask again", value: "never" }
|
|
6920
|
-
];
|
|
6921
|
-
return React21.createElement(
|
|
6922
|
-
Box19,
|
|
6923
|
-
{ flexDirection: "column", padding: 1 },
|
|
6924
|
-
React21.createElement(
|
|
6925
|
-
Text18,
|
|
6926
|
-
null,
|
|
6927
|
-
`${chalk6.yellow("Update available:")} ${chalk6.bold(packageName)} ${chalk6.dim(`v${currentVersion}`)} \u2192 ${chalk6.green(`v${latestVersion}`)}`
|
|
6928
|
-
),
|
|
6929
|
-
React21.createElement(
|
|
6930
|
-
Box19,
|
|
6931
|
-
{ marginTop: 1 },
|
|
6932
|
-
React21.createElement(Select3, {
|
|
6933
|
-
options,
|
|
6934
|
-
onChange: (v) => onSelect(v)
|
|
6935
|
-
})
|
|
6936
|
-
)
|
|
6937
|
-
);
|
|
6938
|
-
}
|
|
6939
|
-
|
|
6940
|
-
// utils/shell/ensureApiKey.ts
|
|
6941
|
-
function ensureApiKey() {
|
|
6942
|
-
const models = [
|
|
6943
|
-
trackedState.model,
|
|
6944
|
-
trackedState.compactionModel
|
|
6945
|
-
].filter(excludeFalsy);
|
|
6946
|
-
const requiredEnvVars = /* @__PURE__ */ new Set();
|
|
6947
|
-
for (const model of models) {
|
|
6948
|
-
if (model.startsWith("claude-")) {
|
|
6949
|
-
requiredEnvVars.add("ANTHROPIC_API_KEY");
|
|
6950
|
-
} else if (model.startsWith("gemini-")) {
|
|
6951
|
-
requiredEnvVars.add("GOOGLE_GENERATIVE_AI_API_KEY");
|
|
6952
|
-
} else if (model.startsWith("ollama-") || model.includes(":")) {
|
|
6953
|
-
} else {
|
|
6954
|
-
requiredEnvVars.add("OPENAI_API_KEY");
|
|
6955
|
-
}
|
|
6956
|
-
}
|
|
6957
|
-
for (const envVar of requiredEnvVars) {
|
|
6958
|
-
if (!process.env[envVar]) {
|
|
6959
|
-
return false;
|
|
6960
|
-
}
|
|
6961
|
-
}
|
|
6962
|
-
return true;
|
|
6963
|
-
}
|
|
6964
|
-
|
|
6965
|
-
// agent.ts
|
|
6966
|
-
(async function() {
|
|
6967
|
-
setupGlobalErrorHandlers();
|
|
6968
|
-
loadEnv();
|
|
6969
|
-
process.on("SIGINT", handleExit);
|
|
6970
|
-
process.on("SIGTERM", handleExit);
|
|
6971
|
-
await checkForUpdate();
|
|
6972
|
-
parseArgs();
|
|
6973
|
-
if (!ensureApiKey()) {
|
|
6974
|
-
resetTrackedState();
|
|
6975
|
-
await bootstrapConfig();
|
|
6976
|
-
emitToListeners("ExitUI", void 0);
|
|
6977
|
-
parseArgs();
|
|
6978
|
-
if (!ensureApiKey()) {
|
|
6979
|
-
console.log(chalk7.red("No key provided. Exiting."));
|
|
6980
|
-
process.exit(1);
|
|
6981
|
-
}
|
|
6982
|
-
}
|
|
6983
|
-
await agentManager.initialize();
|
|
6984
|
-
bootstrapMain();
|
|
6985
|
-
})();
|