@harperfast/agent 0.13.7-ink → 0.13.8-ink
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/README.md +8 -8
- package/dist/agent.js +1371 -828
- package/package.json +2 -1
package/dist/agent.js
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// agent.ts
|
|
4
|
-
import chalk6 from "chalk";
|
|
5
|
-
|
|
6
|
-
// agent/AgentManager.ts
|
|
7
|
-
import { Agent as Agent3 } from "@openai/agents";
|
|
8
|
-
|
|
9
3
|
// ink/emitters/listener.ts
|
|
10
4
|
import React, { useCallback, useEffect } from "react";
|
|
11
5
|
var listenersMap = {};
|
|
@@ -68,29 +62,215 @@ function curryEmitToListeners(name, value, trigger) {
|
|
|
68
62
|
return (e) => emitToListeners(name, value, trigger ?? e);
|
|
69
63
|
}
|
|
70
64
|
|
|
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
|
+
|
|
71
206
|
// lifecycle/defaultInstructions.ts
|
|
207
|
+
import { existsSync as existsSync2 } from "fs";
|
|
208
|
+
import { join as join2 } from "path";
|
|
209
|
+
|
|
210
|
+
// utils/files/harperApp.ts
|
|
72
211
|
import { existsSync } from "fs";
|
|
73
|
-
import { join } from "path";
|
|
212
|
+
import path, { dirname, join } from "path";
|
|
213
|
+
function findNearestHarperConfigDir(startDir) {
|
|
214
|
+
let dir = path.resolve(startDir);
|
|
215
|
+
let maxDepth = 10;
|
|
216
|
+
while (true) {
|
|
217
|
+
if (maxDepth-- <= 0) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
const candidate = join(dir, "config.yaml");
|
|
221
|
+
if (existsSync(candidate)) {
|
|
222
|
+
return dir;
|
|
223
|
+
}
|
|
224
|
+
const parent = dirname(dir);
|
|
225
|
+
if (parent === dir) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
dir = parent;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function resolveSessionPathConsideringHarper(raw, cwd, originalCwd) {
|
|
232
|
+
if (!raw) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
if (raw.startsWith("~") || path.isAbsolute(raw)) {
|
|
236
|
+
return raw;
|
|
237
|
+
}
|
|
238
|
+
const harperRoot = findNearestHarperConfigDir(cwd);
|
|
239
|
+
if (harperRoot) {
|
|
240
|
+
return path.resolve(harperRoot, raw);
|
|
241
|
+
}
|
|
242
|
+
return path.resolve(originalCwd, raw);
|
|
243
|
+
}
|
|
74
244
|
|
|
75
245
|
// lifecycle/trackedState.ts
|
|
76
246
|
var trackedState = {
|
|
247
|
+
originalCwd: process.cwd(),
|
|
77
248
|
cwd: process.cwd(),
|
|
78
249
|
model: "",
|
|
79
250
|
compactionModel: "",
|
|
80
|
-
|
|
251
|
+
originalSessionPath: null,
|
|
252
|
+
get sessionPath() {
|
|
253
|
+
return resolveSessionPathConsideringHarper(trackedState.originalSessionPath, this.cwd, this.originalCwd);
|
|
254
|
+
},
|
|
255
|
+
set sessionPath(value) {
|
|
256
|
+
trackedState.originalSessionPath = value;
|
|
257
|
+
},
|
|
81
258
|
useFlexTier: false,
|
|
259
|
+
currentTurn: 0,
|
|
82
260
|
maxTurns: 30,
|
|
83
261
|
maxCost: null,
|
|
84
262
|
autoApproveCodeInterpreter: false,
|
|
85
263
|
autoApprovePatches: false,
|
|
86
264
|
autoApproveShell: false,
|
|
87
265
|
monitorRateLimits: true,
|
|
88
|
-
rateLimitThreshold: 80
|
|
266
|
+
rateLimitThreshold: 80,
|
|
267
|
+
autonomous: false,
|
|
268
|
+
prompt: null
|
|
89
269
|
};
|
|
90
270
|
|
|
91
271
|
// lifecycle/defaultInstructions.ts
|
|
92
272
|
function defaultInstructions() {
|
|
93
|
-
const harperAppExists =
|
|
273
|
+
const harperAppExists = existsSync2(join2(trackedState.cwd, "config.yaml"));
|
|
94
274
|
const vibing = harperAppExists ? "updating" : "creating";
|
|
95
275
|
return `You are working on ${vibing} a harper app with the user.`;
|
|
96
276
|
}
|
|
@@ -102,8 +282,33 @@ import { openai } from "@ai-sdk/openai";
|
|
|
102
282
|
import { aisdk } from "@openai/agents-extensions";
|
|
103
283
|
import "@openai/agents-extensions/ai-sdk";
|
|
104
284
|
import { createOllama, ollama } from "ollama-ai-provider-v2";
|
|
285
|
+
|
|
286
|
+
// agent/defaults.ts
|
|
287
|
+
var defaultModelToken = "default";
|
|
288
|
+
var defaultOpenAIModel = "gpt-5.2";
|
|
289
|
+
var defaultOpenAICompactionModel = "gpt-5-nano";
|
|
290
|
+
var defaultAnthropicModel = "claude-4-6-opus-latest";
|
|
291
|
+
var defaultAnthropicCompactionModel = "claude-4-5-haiku-latest";
|
|
292
|
+
var defaultGoogleModel = "gemini-3-pro";
|
|
293
|
+
var defaultGoogleCompactionModel = "gemini-2.5-flash-lite";
|
|
294
|
+
var defaultOllamaModel = "ollama-qwen3-coder:30b";
|
|
295
|
+
var defaultOllamaCompactionModel = "ollama-qwen2.5-coder";
|
|
296
|
+
var defaultModels = [
|
|
297
|
+
defaultOpenAIModel,
|
|
298
|
+
defaultAnthropicModel,
|
|
299
|
+
defaultGoogleModel,
|
|
300
|
+
defaultOllamaModel
|
|
301
|
+
];
|
|
302
|
+
var defaultCompactionModels = [
|
|
303
|
+
defaultOpenAICompactionModel,
|
|
304
|
+
defaultAnthropicCompactionModel,
|
|
305
|
+
defaultGoogleCompactionModel,
|
|
306
|
+
defaultOllamaCompactionModel
|
|
307
|
+
];
|
|
308
|
+
|
|
309
|
+
// lifecycle/getModel.ts
|
|
105
310
|
function isOpenAIModel(modelName) {
|
|
106
|
-
if (!modelName || modelName ===
|
|
311
|
+
if (!modelName || modelName === defaultOpenAIModel) {
|
|
107
312
|
return true;
|
|
108
313
|
}
|
|
109
314
|
return !modelName.startsWith("claude-") && !modelName.startsWith("gemini-") && !modelName.startsWith("ollama-");
|
|
@@ -154,13 +359,136 @@ function normalizeOllamaBaseUrl(baseUrl) {
|
|
|
154
359
|
return urlObj.toString().replace(/\/$/, "");
|
|
155
360
|
}
|
|
156
361
|
|
|
362
|
+
// lifecycle/handleExit.ts
|
|
363
|
+
import { getGlobalTraceProvider } from "@openai/agents";
|
|
364
|
+
|
|
365
|
+
// utils/shell/harperProcess.ts
|
|
366
|
+
import spawn from "cross-spawn";
|
|
367
|
+
import { execSync } from "child_process";
|
|
368
|
+
import { homedir } from "os";
|
|
369
|
+
import { join as join3 } from "path";
|
|
370
|
+
var HarperProcess = class {
|
|
371
|
+
childProcess = null;
|
|
372
|
+
externalPid = null;
|
|
373
|
+
logTailProcess = null;
|
|
374
|
+
logs = [];
|
|
375
|
+
httpPort = 9926;
|
|
376
|
+
get running() {
|
|
377
|
+
if (this.childProcess !== null) {
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
this.updateExternalStatus();
|
|
381
|
+
return this.externalPid !== null;
|
|
382
|
+
}
|
|
383
|
+
get startedInternally() {
|
|
384
|
+
return this.childProcess !== null;
|
|
385
|
+
}
|
|
386
|
+
updateExternalStatus() {
|
|
387
|
+
try {
|
|
388
|
+
const status = execSync("harper status", { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
389
|
+
if (status.includes("status: running")) {
|
|
390
|
+
const pidMatch = status.match(/pid: (\d+)/);
|
|
391
|
+
if (pidMatch?.[1]) {
|
|
392
|
+
this.externalPid = parseInt(pidMatch[1], 10);
|
|
393
|
+
if (!this.logTailProcess) {
|
|
394
|
+
this.startTailingLogs();
|
|
395
|
+
}
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
} catch {
|
|
400
|
+
}
|
|
401
|
+
this.externalPid = null;
|
|
402
|
+
this.stopTailingLogs();
|
|
403
|
+
}
|
|
404
|
+
startTailingLogs() {
|
|
405
|
+
const logPath = join3(homedir(), "hdb", "log", "hdb.log");
|
|
406
|
+
this.logTailProcess = spawn("tail", ["-f", logPath], {
|
|
407
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
408
|
+
});
|
|
409
|
+
this.logTailProcess.stdout?.on("data", (data) => {
|
|
410
|
+
const string = data.toString();
|
|
411
|
+
this.updatePortFromLogs(string);
|
|
412
|
+
this.logs.push(string);
|
|
413
|
+
});
|
|
414
|
+
this.logTailProcess.stderr?.on("data", (data) => {
|
|
415
|
+
this.logs.push(data.toString());
|
|
416
|
+
});
|
|
417
|
+
this.logTailProcess.on("exit", () => {
|
|
418
|
+
this.logTailProcess = null;
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
stopTailingLogs() {
|
|
422
|
+
if (this.logTailProcess) {
|
|
423
|
+
this.logTailProcess.kill();
|
|
424
|
+
this.logTailProcess = null;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
updatePortFromLogs(string) {
|
|
428
|
+
if (string.includes("REST:") && string.includes("HTTP:")) {
|
|
429
|
+
this.httpPort = parseInt(string.split("HTTP:").pop().split(",")[0], 10);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
start(directoryName) {
|
|
433
|
+
if (this.running) {
|
|
434
|
+
throw new Error("Harper process is already running.");
|
|
435
|
+
}
|
|
436
|
+
this.logs = [];
|
|
437
|
+
this.childProcess = spawn("harperdb", ["dev", "."], {
|
|
438
|
+
cwd: directoryName,
|
|
439
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
440
|
+
});
|
|
441
|
+
this.childProcess.stdout?.on("data", (data) => {
|
|
442
|
+
const string = data.toString();
|
|
443
|
+
this.updatePortFromLogs(string);
|
|
444
|
+
this.logs.push(string);
|
|
445
|
+
});
|
|
446
|
+
this.childProcess.stderr?.on("data", (data) => {
|
|
447
|
+
this.logs.push(data.toString());
|
|
448
|
+
});
|
|
449
|
+
this.childProcess.on("exit", () => {
|
|
450
|
+
this.childProcess = null;
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
stop() {
|
|
454
|
+
if (this.childProcess) {
|
|
455
|
+
this.childProcess.kill();
|
|
456
|
+
this.childProcess = null;
|
|
457
|
+
}
|
|
458
|
+
if (this.externalPid) {
|
|
459
|
+
try {
|
|
460
|
+
process.kill(this.externalPid);
|
|
461
|
+
} catch {
|
|
462
|
+
}
|
|
463
|
+
this.externalPid = null;
|
|
464
|
+
}
|
|
465
|
+
this.stopTailingLogs();
|
|
466
|
+
}
|
|
467
|
+
getAndClearLogs() {
|
|
468
|
+
const logs2 = this.logs;
|
|
469
|
+
this.logs = [];
|
|
470
|
+
return logs2.join("");
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
var harperProcess = new HarperProcess();
|
|
474
|
+
|
|
475
|
+
// lifecycle/handleExit.ts
|
|
476
|
+
async function handleExit() {
|
|
477
|
+
if (harperProcess.startedInternally) {
|
|
478
|
+
harperProcess.stop();
|
|
479
|
+
}
|
|
480
|
+
await getGlobalTraceProvider().forceFlush();
|
|
481
|
+
emitToListeners("ExitUI", void 0);
|
|
482
|
+
process.exit(0);
|
|
483
|
+
}
|
|
484
|
+
|
|
157
485
|
// lifecycle/readAgentsMD.ts
|
|
158
|
-
import { existsSync as
|
|
159
|
-
import { join as
|
|
486
|
+
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
487
|
+
import { join as join4 } from "path";
|
|
160
488
|
function readAgentsMD() {
|
|
161
|
-
const agentsMdExists =
|
|
489
|
+
const agentsMdExists = existsSync3(join4(trackedState.cwd, "AGENTS.md"));
|
|
162
490
|
if (agentsMdExists) {
|
|
163
|
-
return readFileSync(
|
|
491
|
+
return readFileSync(join4(trackedState.cwd, "AGENTS.md"), "utf8");
|
|
164
492
|
}
|
|
165
493
|
}
|
|
166
494
|
|
|
@@ -460,14 +788,14 @@ function getEnv(newKey, oldKey) {
|
|
|
460
788
|
import { tool as tool10 } from "@openai/agents";
|
|
461
789
|
import { readdirSync, readFileSync as readFileSync2 } from "fs";
|
|
462
790
|
import { createRequire } from "module";
|
|
463
|
-
import { dirname, join as
|
|
791
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
464
792
|
import { z as z10 } from "zod";
|
|
465
|
-
var createHarper =
|
|
466
|
-
var agentsMarkdown =
|
|
793
|
+
var createHarper = dirname2(createRequire(import.meta.url).resolve("create-harper"));
|
|
794
|
+
var agentsMarkdown = join5(
|
|
467
795
|
createHarper,
|
|
468
796
|
"AGENTS.md"
|
|
469
797
|
);
|
|
470
|
-
var skillsDir =
|
|
798
|
+
var skillsDir = join5(
|
|
471
799
|
createHarper,
|
|
472
800
|
"template-vanilla",
|
|
473
801
|
"skills"
|
|
@@ -504,7 +832,7 @@ async function execute10({ skill }) {
|
|
|
504
832
|
return "No skills found.";
|
|
505
833
|
}
|
|
506
834
|
try {
|
|
507
|
-
const filePath =
|
|
835
|
+
const filePath = join5(skillsDir, `${skill}.md`);
|
|
508
836
|
const content = readFileSync2(filePath, "utf8");
|
|
509
837
|
agentManager.session?.addSkillRead?.(skill);
|
|
510
838
|
return content;
|
|
@@ -515,9 +843,9 @@ async function execute10({ skill }) {
|
|
|
515
843
|
|
|
516
844
|
// tools/files/workspaceEditor.ts
|
|
517
845
|
import { applyDiff } from "@openai/agents";
|
|
518
|
-
import { existsSync as
|
|
846
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
|
|
519
847
|
import { mkdir, rm, writeFile } from "fs/promises";
|
|
520
|
-
import
|
|
848
|
+
import path4 from "path";
|
|
521
849
|
|
|
522
850
|
// utils/files/normalizeDiff.ts
|
|
523
851
|
function normalizeDiff(diff) {
|
|
@@ -534,17 +862,17 @@ function normalizeDiff(diff) {
|
|
|
534
862
|
}
|
|
535
863
|
|
|
536
864
|
// utils/files/paths.ts
|
|
537
|
-
import
|
|
865
|
+
import path3 from "path";
|
|
538
866
|
|
|
539
867
|
// utils/files/aiignore.ts
|
|
540
|
-
import { existsSync as
|
|
541
|
-
import
|
|
868
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
869
|
+
import path2 from "path";
|
|
542
870
|
|
|
543
871
|
// utils/logger.ts
|
|
544
|
-
import { appendFileSync, existsSync as
|
|
545
|
-
import { homedir } from "os";
|
|
546
|
-
import { dirname as
|
|
547
|
-
var ERROR_LOG_PATH =
|
|
872
|
+
import { appendFileSync, existsSync as existsSync4, mkdirSync } from "fs";
|
|
873
|
+
import { homedir as homedir2 } from "os";
|
|
874
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
875
|
+
var ERROR_LOG_PATH = join6(homedir2(), ".harper", "harper-agent-errors");
|
|
548
876
|
function logError(error) {
|
|
549
877
|
const message = error instanceof Error ? error.stack || error.message : String(error);
|
|
550
878
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -552,8 +880,8 @@ function logError(error) {
|
|
|
552
880
|
|
|
553
881
|
`;
|
|
554
882
|
try {
|
|
555
|
-
const dir =
|
|
556
|
-
if (!
|
|
883
|
+
const dir = dirname3(ERROR_LOG_PATH);
|
|
884
|
+
if (!existsSync4(dir)) {
|
|
557
885
|
mkdirSync(dir, { recursive: true });
|
|
558
886
|
}
|
|
559
887
|
appendFileSync(ERROR_LOG_PATH, logEntry, "utf8");
|
|
@@ -577,8 +905,8 @@ function setupGlobalErrorHandlers() {
|
|
|
577
905
|
// utils/files/aiignore.ts
|
|
578
906
|
var ignorePatterns = [];
|
|
579
907
|
function loadAiIgnore() {
|
|
580
|
-
const ignorePath =
|
|
581
|
-
if (
|
|
908
|
+
const ignorePath = path2.join(trackedState.cwd, ".aiignore");
|
|
909
|
+
if (existsSync5(ignorePath)) {
|
|
582
910
|
try {
|
|
583
911
|
const content = readFileSync3(ignorePath, "utf8");
|
|
584
912
|
ignorePatterns = content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((pattern) => pattern.endsWith("/") || pattern.endsWith("\\") ? pattern.slice(0, -1) : pattern);
|
|
@@ -592,39 +920,39 @@ function loadAiIgnore() {
|
|
|
592
920
|
}
|
|
593
921
|
}
|
|
594
922
|
function isIgnored(filePath) {
|
|
595
|
-
const directParts =
|
|
923
|
+
const directParts = path2.normalize(filePath).split(path2.sep);
|
|
596
924
|
if (directParts.includes(".aiignore")) {
|
|
597
925
|
return true;
|
|
598
926
|
}
|
|
599
|
-
const absolutePath =
|
|
600
|
-
const relativePath =
|
|
601
|
-
if (relativePath.startsWith("..") ||
|
|
927
|
+
const absolutePath = path2.resolve(trackedState.cwd, filePath);
|
|
928
|
+
const relativePath = path2.relative(trackedState.cwd, absolutePath);
|
|
929
|
+
if (relativePath.startsWith("..") || path2.isAbsolute(relativePath)) {
|
|
602
930
|
return false;
|
|
603
931
|
}
|
|
604
|
-
const normalizedPath2 =
|
|
605
|
-
const parts = normalizedPath2.split(
|
|
932
|
+
const normalizedPath2 = path2.normalize(relativePath);
|
|
933
|
+
const parts = normalizedPath2.split(path2.sep);
|
|
606
934
|
loadAiIgnore();
|
|
607
935
|
if (ignorePatterns.length === 0) {
|
|
608
936
|
return false;
|
|
609
937
|
}
|
|
610
938
|
return ignorePatterns.some((pattern) => {
|
|
611
939
|
if (pattern.startsWith("/")) {
|
|
612
|
-
const normalizedPattern =
|
|
613
|
-
return normalizedPath2 === normalizedPattern || normalizedPath2.startsWith(normalizedPattern +
|
|
940
|
+
const normalizedPattern = path2.normalize(pattern.substring(1));
|
|
941
|
+
return normalizedPath2 === normalizedPattern || normalizedPath2.startsWith(normalizedPattern + path2.sep);
|
|
614
942
|
}
|
|
615
943
|
return parts.some((part, index) => {
|
|
616
|
-
const subPath = parts.slice(index).join(
|
|
617
|
-
const normalizedPattern =
|
|
618
|
-
return subPath === normalizedPattern || subPath.startsWith(normalizedPattern +
|
|
944
|
+
const subPath = parts.slice(index).join(path2.sep);
|
|
945
|
+
const normalizedPattern = path2.normalize(pattern);
|
|
946
|
+
return subPath === normalizedPattern || subPath.startsWith(normalizedPattern + path2.sep);
|
|
619
947
|
});
|
|
620
948
|
});
|
|
621
949
|
}
|
|
622
950
|
|
|
623
951
|
// utils/files/paths.ts
|
|
624
952
|
function resolvePath(root, relativePath) {
|
|
625
|
-
const resolved =
|
|
626
|
-
const relative =
|
|
627
|
-
if (relative.startsWith("..") ||
|
|
953
|
+
const resolved = path3.resolve(root, relativePath);
|
|
954
|
+
const relative = path3.relative(root, resolved);
|
|
955
|
+
if (relative.startsWith("..") || path3.isAbsolute(relative)) {
|
|
628
956
|
throw new Error(`Operation outside workspace: ${relativePath}`);
|
|
629
957
|
}
|
|
630
958
|
if (isIgnored(resolved)) {
|
|
@@ -642,7 +970,7 @@ var WorkspaceEditor = class {
|
|
|
642
970
|
async createFile(operation) {
|
|
643
971
|
try {
|
|
644
972
|
const targetPath = resolvePath(this.root(), operation.path);
|
|
645
|
-
await mkdir(
|
|
973
|
+
await mkdir(path4.dirname(targetPath), { recursive: true });
|
|
646
974
|
const normalizedDiff = normalizeDiff(operation.diff);
|
|
647
975
|
const content = applyDiff("", normalizedDiff, "create");
|
|
648
976
|
await writeFile(targetPath, content, "utf8");
|
|
@@ -654,7 +982,7 @@ var WorkspaceEditor = class {
|
|
|
654
982
|
async updateFile(operation) {
|
|
655
983
|
try {
|
|
656
984
|
const targetPath = resolvePath(this.root(), operation.path);
|
|
657
|
-
if (!
|
|
985
|
+
if (!existsSync6(targetPath)) {
|
|
658
986
|
return { status: "failed", output: "Error: file not found at path " + targetPath };
|
|
659
987
|
}
|
|
660
988
|
const original = readFileSync4(targetPath, "utf8");
|
|
@@ -669,7 +997,7 @@ var WorkspaceEditor = class {
|
|
|
669
997
|
async deleteFile(operation) {
|
|
670
998
|
try {
|
|
671
999
|
const targetPath = resolvePath(this.root(), operation.path);
|
|
672
|
-
if (!
|
|
1000
|
+
if (!existsSync6(targetPath)) {
|
|
673
1001
|
return { status: "failed", output: "Error: file not found at path " + targetPath };
|
|
674
1002
|
}
|
|
675
1003
|
await rm(targetPath, { force: true });
|
|
@@ -910,7 +1238,7 @@ var findTool = tool14({
|
|
|
910
1238
|
// tools/files/readDirTool.ts
|
|
911
1239
|
import { tool as tool15 } from "@openai/agents";
|
|
912
1240
|
import { readdir } from "fs/promises";
|
|
913
|
-
import
|
|
1241
|
+
import path5 from "path";
|
|
914
1242
|
import { z as z15 } from "zod";
|
|
915
1243
|
var ToolParameters14 = z15.object({
|
|
916
1244
|
directoryName: z15.string().describe("The name of the directory to read.")
|
|
@@ -923,7 +1251,7 @@ var readDirTool = tool15({
|
|
|
923
1251
|
try {
|
|
924
1252
|
const resolvedPath = resolvePath(trackedState.cwd, directoryName);
|
|
925
1253
|
const files = await readdir(resolvedPath, "utf8");
|
|
926
|
-
return files.filter((file) => !isIgnored(
|
|
1254
|
+
return files.filter((file) => !isIgnored(path5.join(resolvedPath, file)));
|
|
927
1255
|
} catch (error) {
|
|
928
1256
|
return `Error reading directory: ${error}`;
|
|
929
1257
|
}
|
|
@@ -979,7 +1307,7 @@ var readFileTool = tool16({
|
|
|
979
1307
|
import { tool as tool17 } from "@openai/agents";
|
|
980
1308
|
import { exec } from "child_process";
|
|
981
1309
|
import { unlink, writeFile as writeFile2 } from "fs/promises";
|
|
982
|
-
import
|
|
1310
|
+
import path6 from "path";
|
|
983
1311
|
import { promisify as promisify3 } from "util";
|
|
984
1312
|
import { z as z17 } from "zod";
|
|
985
1313
|
var execAsync = promisify3(exec);
|
|
@@ -1022,7 +1350,7 @@ async function needsApproval2(runContext, parameters, callId) {
|
|
|
1022
1350
|
async function execute14({ code, language }) {
|
|
1023
1351
|
const extension = language === "javascript" ? "js" : "py";
|
|
1024
1352
|
const interpreter = language === "javascript" ? "node" : "python3";
|
|
1025
|
-
const tempFile =
|
|
1353
|
+
const tempFile = path6.join(trackedState.cwd, `.temp_code_${Date.now()}.${extension}`);
|
|
1026
1354
|
try {
|
|
1027
1355
|
await writeFile2(tempFile, code, "utf8");
|
|
1028
1356
|
const { stdout, stderr } = await execAsync(`${interpreter} ${tempFile}`);
|
|
@@ -1040,45 +1368,82 @@ STDERR: ${error.stderr || ""}`;
|
|
|
1040
1368
|
}
|
|
1041
1369
|
}
|
|
1042
1370
|
|
|
1043
|
-
// tools/general/
|
|
1371
|
+
// tools/general/collectFeedbackTool.ts
|
|
1044
1372
|
import { tool as tool18 } from "@openai/agents";
|
|
1373
|
+
import { exec as exec2 } from "child_process";
|
|
1374
|
+
import { promisify as promisify4 } from "util";
|
|
1045
1375
|
import { z as z18 } from "zod";
|
|
1376
|
+
var execAsync2 = promisify4(exec2);
|
|
1377
|
+
var ToolParameters16 = z18.object({
|
|
1378
|
+
feedbackSummary: z18.string().describe("A brief summary of the feedback, used as the discussion title."),
|
|
1379
|
+
feedbackDetails: z18.string().describe("Detailed feedback from the user or agent observation."),
|
|
1380
|
+
recap: z18.string().describe(
|
|
1381
|
+
"A sanitized recap of what the agent and user did together and if it was successful. No sensitive information."
|
|
1382
|
+
)
|
|
1383
|
+
});
|
|
1384
|
+
async function execute15({ feedbackSummary, feedbackDetails, recap }) {
|
|
1385
|
+
const title = feedbackSummary;
|
|
1386
|
+
const body = `${feedbackDetails}
|
|
1387
|
+
|
|
1388
|
+
${recap}`;
|
|
1389
|
+
const encodedTitle = encodeURIComponent(title);
|
|
1390
|
+
const encodedBody = encodeURIComponent(body);
|
|
1391
|
+
const url = `https://github.com/HarperFast/harper-agent/discussions/new?category=usage-feedback&title=${encodedTitle}&body=${encodedBody}`;
|
|
1392
|
+
try {
|
|
1393
|
+
const command = process.platform === "win32" ? `start "" "${url.replace(/&/g, "^&")}"` : process.platform === "darwin" ? `open "${url}"` : `xdg-open "${url}"`;
|
|
1394
|
+
await execAsync2(command);
|
|
1395
|
+
return `Successfully opened feedback URL: ${url}`;
|
|
1396
|
+
} catch (error) {
|
|
1397
|
+
return `Error opening feedback URL: ${error}`;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
var collectFeedbackTool = tool18({
|
|
1401
|
+
name: "collect_feedback",
|
|
1402
|
+
description: "Collects feedback from the user by opening a pre-populated GitHub Discussion. Use this if the user lets us know we did something well, or if the user seems frustrated, or wants to report a bug. You MUST ask the user if they are willing to share these results before calling this tool.",
|
|
1403
|
+
parameters: ToolParameters16,
|
|
1404
|
+
execute: execute15
|
|
1405
|
+
});
|
|
1406
|
+
|
|
1407
|
+
// tools/general/setInterpreterAutoApproveTool.ts
|
|
1408
|
+
import { tool as tool19 } from "@openai/agents";
|
|
1409
|
+
import { z as z19 } from "zod";
|
|
1046
1410
|
|
|
1047
1411
|
// utils/files/updateEnv.ts
|
|
1048
|
-
import { existsSync as
|
|
1049
|
-
import { homedir as
|
|
1050
|
-
import { dirname as
|
|
1412
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync } from "fs";
|
|
1413
|
+
import { homedir as homedir3 } from "os";
|
|
1414
|
+
import { dirname as dirname4, join as join7 } from "path";
|
|
1051
1415
|
function updateEnv(key, value) {
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
const
|
|
1055
|
-
const
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1416
|
+
const normalizedValue = key === "HARPER_AGENT_MODEL" && defaultModels.includes(value) ? defaultModelToken : key === "HARPER_AGENT_COMPACTION_MODEL" && defaultCompactionModels.includes(value) ? defaultModelToken : value;
|
|
1417
|
+
process.env[key] = normalizedValue;
|
|
1418
|
+
const topLevelEnvPath = join7(homedir3(), ".harper", "harper-agent-env");
|
|
1419
|
+
const localEnvPath = join7(trackedState.cwd, ".env");
|
|
1420
|
+
const envPath = existsSync7(topLevelEnvPath) || !existsSync7(localEnvPath) ? topLevelEnvPath : localEnvPath;
|
|
1421
|
+
const dir = dirname4(envPath);
|
|
1422
|
+
if (!existsSync7(dir)) {
|
|
1058
1423
|
mkdirSync2(dir, { recursive: true });
|
|
1059
1424
|
}
|
|
1060
1425
|
let envContent = "";
|
|
1061
|
-
if (
|
|
1426
|
+
if (existsSync7(envPath)) {
|
|
1062
1427
|
envContent = readFileSync5(envPath, "utf8");
|
|
1063
1428
|
}
|
|
1064
1429
|
const regex = new RegExp(`^${key}=.*`, "m");
|
|
1065
1430
|
if (regex.test(envContent)) {
|
|
1066
|
-
envContent = envContent.replace(regex, `${key}=${
|
|
1431
|
+
envContent = envContent.replace(regex, `${key}=${normalizedValue}`);
|
|
1067
1432
|
} else {
|
|
1068
1433
|
if (envContent && !envContent.endsWith("\n")) {
|
|
1069
1434
|
envContent += "\n";
|
|
1070
1435
|
}
|
|
1071
|
-
envContent += `${key}=${
|
|
1436
|
+
envContent += `${key}=${normalizedValue}
|
|
1072
1437
|
`;
|
|
1073
1438
|
}
|
|
1074
1439
|
writeFileSync(envPath, envContent);
|
|
1075
1440
|
}
|
|
1076
1441
|
|
|
1077
1442
|
// tools/general/setInterpreterAutoApproveTool.ts
|
|
1078
|
-
var SetInterpreterAutoApproveParameters =
|
|
1079
|
-
autoApprove:
|
|
1443
|
+
var SetInterpreterAutoApproveParameters = z19.object({
|
|
1444
|
+
autoApprove: z19.boolean()
|
|
1080
1445
|
});
|
|
1081
|
-
var setInterpreterAutoApproveTool =
|
|
1446
|
+
var setInterpreterAutoApproveTool = tool19({
|
|
1082
1447
|
name: "set_interpreter_auto_approve",
|
|
1083
1448
|
description: "Enable or disable automatic approval for code interpreter by setting HARPER_AGENT_AUTO_APPROVE_CODE_INTERPRETER=1 or 0 in .env and current process.",
|
|
1084
1449
|
parameters: SetInterpreterAutoApproveParameters,
|
|
@@ -1103,12 +1468,12 @@ var setInterpreterAutoApproveTool = tool18({
|
|
|
1103
1468
|
});
|
|
1104
1469
|
|
|
1105
1470
|
// tools/general/setPatchAutoApproveTool.ts
|
|
1106
|
-
import { tool as
|
|
1107
|
-
import { z as
|
|
1108
|
-
var SetPatchAutoApproveParameters =
|
|
1109
|
-
autoApprove:
|
|
1471
|
+
import { tool as tool20 } from "@openai/agents";
|
|
1472
|
+
import { z as z20 } from "zod";
|
|
1473
|
+
var SetPatchAutoApproveParameters = z20.object({
|
|
1474
|
+
autoApprove: z20.boolean()
|
|
1110
1475
|
});
|
|
1111
|
-
var setPatchAutoApproveTool =
|
|
1476
|
+
var setPatchAutoApproveTool = tool20({
|
|
1112
1477
|
name: "set_patch_auto_approve",
|
|
1113
1478
|
description: "Enable or disable automatic approval for patch commands by setting HARPER_AGENT_AUTO_APPROVE_PATCHES=1 or 0 in .env and current process.",
|
|
1114
1479
|
parameters: SetPatchAutoApproveParameters,
|
|
@@ -1133,12 +1498,12 @@ var setPatchAutoApproveTool = tool19({
|
|
|
1133
1498
|
});
|
|
1134
1499
|
|
|
1135
1500
|
// tools/general/setShellAutoApproveTool.ts
|
|
1136
|
-
import { tool as
|
|
1137
|
-
import { z as
|
|
1138
|
-
var SetShellAutoApproveParameters =
|
|
1139
|
-
autoApprove:
|
|
1501
|
+
import { tool as tool21 } from "@openai/agents";
|
|
1502
|
+
import { z as z21 } from "zod";
|
|
1503
|
+
var SetShellAutoApproveParameters = z21.object({
|
|
1504
|
+
autoApprove: z21.boolean()
|
|
1140
1505
|
});
|
|
1141
|
-
var setShellAutoApproveTool =
|
|
1506
|
+
var setShellAutoApproveTool = tool21({
|
|
1142
1507
|
name: "set_shell_auto_approve",
|
|
1143
1508
|
description: "Enable or disable automatic approval for shell commands by setting HARPER_AGENT_AUTO_APPROVE_SHELL=1 or 0 in .env and current process.",
|
|
1144
1509
|
parameters: SetShellAutoApproveParameters,
|
|
@@ -1163,8 +1528,8 @@ var setShellAutoApproveTool = tool20({
|
|
|
1163
1528
|
});
|
|
1164
1529
|
|
|
1165
1530
|
// tools/general/shellTool.ts
|
|
1166
|
-
import { tool as
|
|
1167
|
-
import { z as
|
|
1531
|
+
import { tool as tool22 } from "@openai/agents";
|
|
1532
|
+
import { z as z22 } from "zod";
|
|
1168
1533
|
|
|
1169
1534
|
// utils/files/mentionsIgnoredPath.ts
|
|
1170
1535
|
function mentionsIgnoredPath(command) {
|
|
@@ -1218,8 +1583,8 @@ function isRiskyCommand(command) {
|
|
|
1218
1583
|
}
|
|
1219
1584
|
|
|
1220
1585
|
// utils/shell/LocalShell.ts
|
|
1221
|
-
import { exec as
|
|
1222
|
-
import { promisify as
|
|
1586
|
+
import { exec as exec3 } from "child_process";
|
|
1587
|
+
import { promisify as promisify5 } from "util";
|
|
1223
1588
|
|
|
1224
1589
|
// ink/contexts/ShellContext.tsx
|
|
1225
1590
|
import { createContext, useContext, useMemo, useState } from "react";
|
|
@@ -1228,7 +1593,7 @@ var ShellContext = createContext(void 0);
|
|
|
1228
1593
|
var commandId = 0;
|
|
1229
1594
|
|
|
1230
1595
|
// utils/shell/LocalShell.ts
|
|
1231
|
-
var
|
|
1596
|
+
var execAsync3 = promisify5(exec3);
|
|
1232
1597
|
var LocalShell = class {
|
|
1233
1598
|
defaultTimeoutMs;
|
|
1234
1599
|
constructor(options) {
|
|
@@ -1263,7 +1628,7 @@ var LocalShell = class {
|
|
|
1263
1628
|
running: true
|
|
1264
1629
|
});
|
|
1265
1630
|
try {
|
|
1266
|
-
const { stdout: localStdout, stderr: localStderr } = await
|
|
1631
|
+
const { stdout: localStdout, stderr: localStderr } = await execAsync3(
|
|
1267
1632
|
command,
|
|
1268
1633
|
{
|
|
1269
1634
|
cwd: trackedState.cwd,
|
|
@@ -1311,11 +1676,11 @@ var LocalShell = class {
|
|
|
1311
1676
|
};
|
|
1312
1677
|
|
|
1313
1678
|
// tools/general/shellTool.ts
|
|
1314
|
-
var ShellParameters =
|
|
1315
|
-
commands:
|
|
1679
|
+
var ShellParameters = z22.object({
|
|
1680
|
+
commands: z22.array(z22.string()).describe("The commands to execute.")
|
|
1316
1681
|
});
|
|
1317
1682
|
var shell = new LocalShell();
|
|
1318
|
-
var shellTool =
|
|
1683
|
+
var shellTool = tool22({
|
|
1319
1684
|
name: "shell",
|
|
1320
1685
|
description: "Executes shell commands. Only use when we do not have a better tool.",
|
|
1321
1686
|
parameters: ShellParameters,
|
|
@@ -1364,15 +1729,15 @@ TIMEOUT`;
|
|
|
1364
1729
|
});
|
|
1365
1730
|
|
|
1366
1731
|
// tools/git/gitAddTool.ts
|
|
1367
|
-
import { tool as
|
|
1732
|
+
import { tool as tool23 } from "@openai/agents";
|
|
1368
1733
|
import { execFile as execFile3 } from "child_process";
|
|
1369
|
-
import { promisify as
|
|
1370
|
-
import { z as
|
|
1371
|
-
var execFileAsync3 =
|
|
1372
|
-
var GitAddParameters =
|
|
1373
|
-
files:
|
|
1734
|
+
import { promisify as promisify6 } from "util";
|
|
1735
|
+
import { z as z23 } from "zod";
|
|
1736
|
+
var execFileAsync3 = promisify6(execFile3);
|
|
1737
|
+
var GitAddParameters = z23.object({
|
|
1738
|
+
files: z23.array(z23.string()).describe("The files to add. If not provided, all changes will be added.")
|
|
1374
1739
|
});
|
|
1375
|
-
var gitAddTool =
|
|
1740
|
+
var gitAddTool = tool23({
|
|
1376
1741
|
name: "git_add",
|
|
1377
1742
|
description: "Add file contents to the index.",
|
|
1378
1743
|
parameters: GitAddParameters,
|
|
@@ -1393,16 +1758,16 @@ var gitAddTool = tool22({
|
|
|
1393
1758
|
});
|
|
1394
1759
|
|
|
1395
1760
|
// tools/git/gitBranchTool.ts
|
|
1396
|
-
import { tool as
|
|
1761
|
+
import { tool as tool24 } from "@openai/agents";
|
|
1397
1762
|
import { execFile as execFile4 } from "child_process";
|
|
1398
|
-
import { promisify as
|
|
1399
|
-
import { z as
|
|
1400
|
-
var execFileAsync4 =
|
|
1401
|
-
var GitBranchParameters =
|
|
1402
|
-
branchName:
|
|
1403
|
-
create:
|
|
1763
|
+
import { promisify as promisify7 } from "util";
|
|
1764
|
+
import { z as z24 } from "zod";
|
|
1765
|
+
var execFileAsync4 = promisify7(execFile4);
|
|
1766
|
+
var GitBranchParameters = z24.object({
|
|
1767
|
+
branchName: z24.string().describe("The name of the branch to create or switch to."),
|
|
1768
|
+
create: z24.boolean().optional().default(false).describe("Whether to create a new branch.")
|
|
1404
1769
|
});
|
|
1405
|
-
var gitBranchTool =
|
|
1770
|
+
var gitBranchTool = tool24({
|
|
1406
1771
|
name: "git_branch",
|
|
1407
1772
|
description: "Create or switch to a git branch.",
|
|
1408
1773
|
parameters: GitBranchParameters,
|
|
@@ -1419,18 +1784,18 @@ var gitBranchTool = tool23({
|
|
|
1419
1784
|
});
|
|
1420
1785
|
|
|
1421
1786
|
// tools/git/gitCommitTool.ts
|
|
1422
|
-
import { tool as
|
|
1787
|
+
import { tool as tool25 } from "@openai/agents";
|
|
1423
1788
|
import { execFile as execFile5 } from "child_process";
|
|
1424
|
-
import { promisify as
|
|
1425
|
-
import { z as
|
|
1426
|
-
var execFileAsync5 =
|
|
1427
|
-
var GitCommitParameters =
|
|
1428
|
-
message:
|
|
1429
|
-
addAll:
|
|
1789
|
+
import { promisify as promisify8 } from "util";
|
|
1790
|
+
import { z as z25 } from "zod";
|
|
1791
|
+
var execFileAsync5 = promisify8(execFile5);
|
|
1792
|
+
var GitCommitParameters = z25.object({
|
|
1793
|
+
message: z25.string().describe("The commit message."),
|
|
1794
|
+
addAll: z25.boolean().optional().default(false).describe(
|
|
1430
1795
|
"Whether to add all changes before committing (git commit -am)."
|
|
1431
1796
|
)
|
|
1432
1797
|
});
|
|
1433
|
-
var gitCommitTool =
|
|
1798
|
+
var gitCommitTool = tool25({
|
|
1434
1799
|
name: "git_commit",
|
|
1435
1800
|
description: "Commit changes to the repository.",
|
|
1436
1801
|
parameters: GitCommitParameters,
|
|
@@ -1446,16 +1811,16 @@ var gitCommitTool = tool24({
|
|
|
1446
1811
|
});
|
|
1447
1812
|
|
|
1448
1813
|
// tools/git/gitLogTool.ts
|
|
1449
|
-
import { tool as
|
|
1814
|
+
import { tool as tool26 } from "@openai/agents";
|
|
1450
1815
|
import { execFile as execFile6 } from "child_process";
|
|
1451
|
-
import { promisify as
|
|
1452
|
-
import { z as
|
|
1453
|
-
var execFileAsync6 =
|
|
1454
|
-
var GitLogParameters =
|
|
1455
|
-
count:
|
|
1456
|
-
oneline:
|
|
1816
|
+
import { promisify as promisify9 } from "util";
|
|
1817
|
+
import { z as z26 } from "zod";
|
|
1818
|
+
var execFileAsync6 = promisify9(execFile6);
|
|
1819
|
+
var GitLogParameters = z26.object({
|
|
1820
|
+
count: z26.number().optional().default(10).describe("Number of commits to show."),
|
|
1821
|
+
oneline: z26.boolean().optional().default(true).describe("Whether to show log in oneline format.")
|
|
1457
1822
|
});
|
|
1458
|
-
var gitLogTool =
|
|
1823
|
+
var gitLogTool = tool26({
|
|
1459
1824
|
name: "git_log",
|
|
1460
1825
|
description: "Show commit logs.",
|
|
1461
1826
|
parameters: GitLogParameters,
|
|
@@ -1474,17 +1839,17 @@ var gitLogTool = tool25({
|
|
|
1474
1839
|
});
|
|
1475
1840
|
|
|
1476
1841
|
// tools/git/gitStashTool.ts
|
|
1477
|
-
import { tool as
|
|
1842
|
+
import { tool as tool27 } from "@openai/agents";
|
|
1478
1843
|
import { execFile as execFile7 } from "child_process";
|
|
1479
|
-
import { promisify as
|
|
1480
|
-
import { z as
|
|
1481
|
-
var execFileAsync7 =
|
|
1844
|
+
import { promisify as promisify10 } from "util";
|
|
1845
|
+
import { z as z27 } from "zod";
|
|
1846
|
+
var execFileAsync7 = promisify10(execFile7);
|
|
1482
1847
|
var allowedActions = ["push", "pop", "apply", "list"];
|
|
1483
|
-
var GitStashParameters =
|
|
1484
|
-
action:
|
|
1485
|
-
message:
|
|
1848
|
+
var GitStashParameters = z27.object({
|
|
1849
|
+
action: z27.string().describe("The stash action to perform: " + allowedActions.join(", ")),
|
|
1850
|
+
message: z27.string().describe("A message for the stash change.")
|
|
1486
1851
|
});
|
|
1487
|
-
var gitStashTool =
|
|
1852
|
+
var gitStashTool = tool27({
|
|
1488
1853
|
name: "git_stash",
|
|
1489
1854
|
description: "Stash changes or apply a stash.",
|
|
1490
1855
|
parameters: GitStashParameters,
|
|
@@ -1506,15 +1871,15 @@ var gitStashTool = tool26({
|
|
|
1506
1871
|
});
|
|
1507
1872
|
|
|
1508
1873
|
// tools/git/gitStatusTool.ts
|
|
1509
|
-
import { tool as
|
|
1874
|
+
import { tool as tool28 } from "@openai/agents";
|
|
1510
1875
|
import { execFile as execFile8 } from "child_process";
|
|
1511
|
-
import { promisify as
|
|
1512
|
-
import { z as
|
|
1513
|
-
var execFileAsync8 =
|
|
1514
|
-
var GitStatusParameters =
|
|
1515
|
-
short:
|
|
1876
|
+
import { promisify as promisify11 } from "util";
|
|
1877
|
+
import { z as z28 } from "zod";
|
|
1878
|
+
var execFileAsync8 = promisify11(execFile8);
|
|
1879
|
+
var GitStatusParameters = z28.object({
|
|
1880
|
+
short: z28.boolean().optional().default(false).describe("Whether to show the status in short format.")
|
|
1516
1881
|
});
|
|
1517
|
-
var gitStatusTool =
|
|
1882
|
+
var gitStatusTool = tool28({
|
|
1518
1883
|
name: "git_status",
|
|
1519
1884
|
description: "Show the working tree status.",
|
|
1520
1885
|
parameters: GitStatusParameters,
|
|
@@ -1533,17 +1898,17 @@ var gitStatusTool = tool27({
|
|
|
1533
1898
|
});
|
|
1534
1899
|
|
|
1535
1900
|
// tools/git/gitWorkspaceTool.ts
|
|
1536
|
-
import { tool as
|
|
1901
|
+
import { tool as tool29 } from "@openai/agents";
|
|
1537
1902
|
import { execFile as execFile9 } from "child_process";
|
|
1538
|
-
import { promisify as
|
|
1539
|
-
import { z as
|
|
1540
|
-
var execFileAsync9 =
|
|
1541
|
-
var GitWorkspaceParameters =
|
|
1542
|
-
path:
|
|
1543
|
-
branchName:
|
|
1544
|
-
createBranch:
|
|
1903
|
+
import { promisify as promisify12 } from "util";
|
|
1904
|
+
import { z as z29 } from "zod";
|
|
1905
|
+
var execFileAsync9 = promisify12(execFile9);
|
|
1906
|
+
var GitWorkspaceParameters = z29.object({
|
|
1907
|
+
path: z29.string().describe("The path where the new workspace (worktree) should be created."),
|
|
1908
|
+
branchName: z29.string().describe("The name of the branch to use in the new workspace."),
|
|
1909
|
+
createBranch: z29.boolean().optional().default(false).describe("Whether to create a new branch for this workspace.")
|
|
1545
1910
|
});
|
|
1546
|
-
var gitWorkspaceTool =
|
|
1911
|
+
var gitWorkspaceTool = tool29({
|
|
1547
1912
|
name: "git_workspace",
|
|
1548
1913
|
description: "Create a new workspace (git worktree) for parallel work.",
|
|
1549
1914
|
parameters: GitWorkspaceParameters,
|
|
@@ -1560,125 +1925,13 @@ var gitWorkspaceTool = tool28({
|
|
|
1560
1925
|
});
|
|
1561
1926
|
|
|
1562
1927
|
// tools/harper/checkHarperStatusTool.ts
|
|
1563
|
-
import { tool as
|
|
1564
|
-
import { z as
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
import spawn from "cross-spawn";
|
|
1568
|
-
import { execSync } from "child_process";
|
|
1569
|
-
import { homedir as homedir3 } from "os";
|
|
1570
|
-
import { join as join6 } from "path";
|
|
1571
|
-
var HarperProcess = class {
|
|
1572
|
-
childProcess = null;
|
|
1573
|
-
externalPid = null;
|
|
1574
|
-
logTailProcess = null;
|
|
1575
|
-
logs = [];
|
|
1576
|
-
httpPort = 9926;
|
|
1577
|
-
get running() {
|
|
1578
|
-
if (this.childProcess !== null) {
|
|
1579
|
-
return true;
|
|
1580
|
-
}
|
|
1581
|
-
this.updateExternalStatus();
|
|
1582
|
-
return this.externalPid !== null;
|
|
1583
|
-
}
|
|
1584
|
-
get startedInternally() {
|
|
1585
|
-
return this.childProcess !== null;
|
|
1586
|
-
}
|
|
1587
|
-
updateExternalStatus() {
|
|
1588
|
-
try {
|
|
1589
|
-
const status = execSync("harper status", { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
1590
|
-
if (status.includes("status: running")) {
|
|
1591
|
-
const pidMatch = status.match(/pid: (\d+)/);
|
|
1592
|
-
if (pidMatch?.[1]) {
|
|
1593
|
-
this.externalPid = parseInt(pidMatch[1], 10);
|
|
1594
|
-
if (!this.logTailProcess) {
|
|
1595
|
-
this.startTailingLogs();
|
|
1596
|
-
}
|
|
1597
|
-
return;
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1600
|
-
} catch {
|
|
1601
|
-
}
|
|
1602
|
-
this.externalPid = null;
|
|
1603
|
-
this.stopTailingLogs();
|
|
1604
|
-
}
|
|
1605
|
-
startTailingLogs() {
|
|
1606
|
-
const logPath = join6(homedir3(), "hdb", "log", "hdb.log");
|
|
1607
|
-
this.logTailProcess = spawn("tail", ["-f", logPath], {
|
|
1608
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
1609
|
-
});
|
|
1610
|
-
this.logTailProcess.stdout?.on("data", (data) => {
|
|
1611
|
-
const string = data.toString();
|
|
1612
|
-
this.updatePortFromLogs(string);
|
|
1613
|
-
this.logs.push(string);
|
|
1614
|
-
});
|
|
1615
|
-
this.logTailProcess.stderr?.on("data", (data) => {
|
|
1616
|
-
this.logs.push(data.toString());
|
|
1617
|
-
});
|
|
1618
|
-
this.logTailProcess.on("exit", () => {
|
|
1619
|
-
this.logTailProcess = null;
|
|
1620
|
-
});
|
|
1621
|
-
}
|
|
1622
|
-
stopTailingLogs() {
|
|
1623
|
-
if (this.logTailProcess) {
|
|
1624
|
-
this.logTailProcess.kill();
|
|
1625
|
-
this.logTailProcess = null;
|
|
1626
|
-
}
|
|
1627
|
-
}
|
|
1628
|
-
updatePortFromLogs(string) {
|
|
1629
|
-
if (string.includes("REST:") && string.includes("HTTP:")) {
|
|
1630
|
-
this.httpPort = parseInt(string.split("HTTP:").pop().split(",")[0], 10);
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
start(directoryName) {
|
|
1634
|
-
if (this.running) {
|
|
1635
|
-
throw new Error("Harper process is already running.");
|
|
1636
|
-
}
|
|
1637
|
-
this.logs = [];
|
|
1638
|
-
this.childProcess = spawn("harperdb", ["dev", "."], {
|
|
1639
|
-
cwd: directoryName,
|
|
1640
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
1641
|
-
});
|
|
1642
|
-
this.childProcess.stdout?.on("data", (data) => {
|
|
1643
|
-
const string = data.toString();
|
|
1644
|
-
this.updatePortFromLogs(string);
|
|
1645
|
-
this.logs.push(string);
|
|
1646
|
-
});
|
|
1647
|
-
this.childProcess.stderr?.on("data", (data) => {
|
|
1648
|
-
this.logs.push(data.toString());
|
|
1649
|
-
});
|
|
1650
|
-
this.childProcess.on("exit", () => {
|
|
1651
|
-
this.childProcess = null;
|
|
1652
|
-
});
|
|
1653
|
-
}
|
|
1654
|
-
stop() {
|
|
1655
|
-
if (this.childProcess) {
|
|
1656
|
-
this.childProcess.kill();
|
|
1657
|
-
this.childProcess = null;
|
|
1658
|
-
}
|
|
1659
|
-
if (this.externalPid) {
|
|
1660
|
-
try {
|
|
1661
|
-
process.kill(this.externalPid);
|
|
1662
|
-
} catch {
|
|
1663
|
-
}
|
|
1664
|
-
this.externalPid = null;
|
|
1665
|
-
}
|
|
1666
|
-
this.stopTailingLogs();
|
|
1667
|
-
}
|
|
1668
|
-
getAndClearLogs() {
|
|
1669
|
-
const logs2 = this.logs;
|
|
1670
|
-
this.logs = [];
|
|
1671
|
-
return logs2.join("");
|
|
1672
|
-
}
|
|
1673
|
-
};
|
|
1674
|
-
var harperProcess = new HarperProcess();
|
|
1675
|
-
|
|
1676
|
-
// tools/harper/checkHarperStatusTool.ts
|
|
1677
|
-
var ToolParameters16 = z29.object({});
|
|
1678
|
-
var checkHarperStatusTool = tool29({
|
|
1928
|
+
import { tool as tool30 } from "@openai/agents";
|
|
1929
|
+
import { z as z30 } from "zod";
|
|
1930
|
+
var ToolParameters17 = z30.object({});
|
|
1931
|
+
var checkHarperStatusTool = tool30({
|
|
1679
1932
|
name: "check_harper_status",
|
|
1680
1933
|
description: "Checks if a Harper application is currently running.",
|
|
1681
|
-
parameters:
|
|
1934
|
+
parameters: ToolParameters17,
|
|
1682
1935
|
async execute() {
|
|
1683
1936
|
if (harperProcess.running) {
|
|
1684
1937
|
return "A Harper application is currently running.";
|
|
@@ -1689,10 +1942,10 @@ var checkHarperStatusTool = tool29({
|
|
|
1689
1942
|
});
|
|
1690
1943
|
|
|
1691
1944
|
// tools/harper/createNewHarperApplicationTool.ts
|
|
1692
|
-
import { tool as
|
|
1945
|
+
import { tool as tool31 } from "@openai/agents";
|
|
1693
1946
|
import { execSync as execSync3 } from "child_process";
|
|
1694
|
-
import
|
|
1695
|
-
import { z as
|
|
1947
|
+
import path7 from "path";
|
|
1948
|
+
import { z as z31 } from "zod";
|
|
1696
1949
|
|
|
1697
1950
|
// utils/package/buildHarperCreateCommand.ts
|
|
1698
1951
|
function buildCreateCommand(pm, appName, template) {
|
|
@@ -1742,16 +1995,16 @@ var PM_DISPLAY = {
|
|
|
1742
1995
|
};
|
|
1743
1996
|
|
|
1744
1997
|
// tools/harper/createNewHarperApplicationTool.ts
|
|
1745
|
-
var
|
|
1746
|
-
directoryName:
|
|
1747
|
-
template:
|
|
1998
|
+
var ToolParameters18 = z31.object({
|
|
1999
|
+
directoryName: z31.string().describe("The name of the directory to create the application in."),
|
|
2000
|
+
template: z31.enum(["vanilla-ts", "vanilla", "react-ts", "react"]).optional().describe("The template to use for the new application. Defaults to vanilla-ts.").default("vanilla-ts")
|
|
1748
2001
|
});
|
|
1749
|
-
async function
|
|
2002
|
+
async function execute16({ directoryName, template }) {
|
|
1750
2003
|
const currentCwd = trackedState.cwd;
|
|
1751
2004
|
const resolvedPath = resolvePath(currentCwd, directoryName);
|
|
1752
2005
|
const isCurrentDir = resolvedPath === currentCwd;
|
|
1753
|
-
const executionCwd = isCurrentDir ? resolvedPath :
|
|
1754
|
-
const appName = isCurrentDir ? "." :
|
|
2006
|
+
const executionCwd = isCurrentDir ? resolvedPath : path7.dirname(resolvedPath);
|
|
2007
|
+
const appName = isCurrentDir ? "." : path7.basename(resolvedPath);
|
|
1755
2008
|
try {
|
|
1756
2009
|
const pm = pickPreferredPackageManager();
|
|
1757
2010
|
const { cmd, label } = buildCreateCommand(pm, appName, template);
|
|
@@ -1783,33 +2036,33 @@ ${error.stdout}`;
|
|
|
1783
2036
|
return errorMsg;
|
|
1784
2037
|
}
|
|
1785
2038
|
}
|
|
1786
|
-
var createNewHarperApplicationTool =
|
|
2039
|
+
var createNewHarperApplicationTool = tool31({
|
|
1787
2040
|
name: "create_new_harper_application",
|
|
1788
2041
|
description: "Creates a new Harper application using the best available package manager (yarn/pnpm/bun/deno, falling back to npm).",
|
|
1789
|
-
parameters:
|
|
1790
|
-
execute:
|
|
2042
|
+
parameters: ToolParameters18,
|
|
2043
|
+
execute: execute16
|
|
1791
2044
|
});
|
|
1792
2045
|
|
|
1793
2046
|
// tools/harper/getHarperConfigSchemaTool.ts
|
|
1794
|
-
import { tool as
|
|
2047
|
+
import { tool as tool32 } from "@openai/agents";
|
|
1795
2048
|
import { readFile as readFile2 } from "fs/promises";
|
|
1796
2049
|
import { createRequire as createRequire2 } from "module";
|
|
1797
|
-
import { dirname as
|
|
1798
|
-
import { z as
|
|
1799
|
-
var
|
|
1800
|
-
schemaType:
|
|
2050
|
+
import { dirname as dirname5, join as join8 } from "path";
|
|
2051
|
+
import { z as z32 } from "zod";
|
|
2052
|
+
var ToolParameters19 = z32.object({
|
|
2053
|
+
schemaType: z32.enum(["app", "root"]).describe(
|
|
1801
2054
|
'The type of configuration schema to retrieve: "app" for application configuration or "root" for root Harper configuration.'
|
|
1802
2055
|
)
|
|
1803
2056
|
});
|
|
1804
|
-
var getHarperConfigSchemaTool =
|
|
2057
|
+
var getHarperConfigSchemaTool = tool32({
|
|
1805
2058
|
name: "get_harper_config_schema",
|
|
1806
2059
|
description: "Returns the JSON schema for HarperDB configuration files (either app or root), which describes the config.yaml or harperdb-config.yaml files.",
|
|
1807
|
-
parameters:
|
|
2060
|
+
parameters: ToolParameters19,
|
|
1808
2061
|
async execute({ schemaType }) {
|
|
1809
2062
|
try {
|
|
1810
2063
|
return await readFile2(
|
|
1811
|
-
|
|
1812
|
-
|
|
2064
|
+
join8(
|
|
2065
|
+
dirname5(createRequire2(import.meta.url).resolve("harperdb")),
|
|
1813
2066
|
`config-${schemaType}.schema.json`
|
|
1814
2067
|
),
|
|
1815
2068
|
"utf8"
|
|
@@ -1821,13 +2074,13 @@ var getHarperConfigSchemaTool = tool31({
|
|
|
1821
2074
|
});
|
|
1822
2075
|
|
|
1823
2076
|
// tools/harper/getHarperResourceInterfaceTool.ts
|
|
1824
|
-
import { tool as
|
|
2077
|
+
import { tool as tool33 } from "@openai/agents";
|
|
1825
2078
|
import { readFile as readFile3 } from "fs/promises";
|
|
1826
2079
|
import { createRequire as createRequire3 } from "module";
|
|
1827
|
-
import { dirname as
|
|
1828
|
-
import { z as
|
|
1829
|
-
var
|
|
1830
|
-
resourceFile:
|
|
2080
|
+
import { dirname as dirname6, join as join9 } from "path";
|
|
2081
|
+
import { z as z33 } from "zod";
|
|
2082
|
+
var ToolParameters20 = z33.object({
|
|
2083
|
+
resourceFile: z33.enum([
|
|
1831
2084
|
"ResourceInterfaceV2",
|
|
1832
2085
|
"ResourceInterface",
|
|
1833
2086
|
"Table",
|
|
@@ -1837,15 +2090,15 @@ var ToolParameters19 = z32.object({
|
|
|
1837
2090
|
"The resource-related definition file to read. Defaults to ResourceInterfaceV2."
|
|
1838
2091
|
).default("ResourceInterfaceV2")
|
|
1839
2092
|
});
|
|
1840
|
-
var getHarperResourceInterfaceTool =
|
|
2093
|
+
var getHarperResourceInterfaceTool = tool33({
|
|
1841
2094
|
name: "get_harper_resource_interface",
|
|
1842
2095
|
description: "Reads HarperDB resource interface and class definitions (like ResourceInterfaceV2.d.ts) to understand how resources and tables are structured.",
|
|
1843
|
-
parameters:
|
|
2096
|
+
parameters: ToolParameters20,
|
|
1844
2097
|
async execute({ resourceFile }) {
|
|
1845
2098
|
try {
|
|
1846
2099
|
return await readFile3(
|
|
1847
|
-
|
|
1848
|
-
|
|
2100
|
+
join9(
|
|
2101
|
+
dirname6(createRequire3(import.meta.url).resolve("harperdb")),
|
|
1849
2102
|
"resources",
|
|
1850
2103
|
`${resourceFile}.d.ts`
|
|
1851
2104
|
),
|
|
@@ -1858,21 +2111,21 @@ var getHarperResourceInterfaceTool = tool32({
|
|
|
1858
2111
|
});
|
|
1859
2112
|
|
|
1860
2113
|
// tools/harper/getHarperSchemaGraphQLTool.ts
|
|
1861
|
-
import { tool as
|
|
2114
|
+
import { tool as tool34 } from "@openai/agents";
|
|
1862
2115
|
import { readFile as readFile4 } from "fs/promises";
|
|
1863
2116
|
import { createRequire as createRequire4 } from "module";
|
|
1864
|
-
import { dirname as
|
|
1865
|
-
import { z as
|
|
1866
|
-
var
|
|
1867
|
-
var getHarperSchemaGraphQLTool =
|
|
2117
|
+
import { dirname as dirname7, join as join10 } from "path";
|
|
2118
|
+
import { z as z34 } from "zod";
|
|
2119
|
+
var ToolParameters21 = z34.object({});
|
|
2120
|
+
var getHarperSchemaGraphQLTool = tool34({
|
|
1868
2121
|
name: "get_harper_schema_graphql",
|
|
1869
2122
|
description: "Returns the GraphQL schema for HarperDB schema files, which define the structure of HarperDB database tables.",
|
|
1870
|
-
parameters:
|
|
2123
|
+
parameters: ToolParameters21,
|
|
1871
2124
|
async execute() {
|
|
1872
2125
|
try {
|
|
1873
2126
|
return await readFile4(
|
|
1874
|
-
|
|
1875
|
-
|
|
2127
|
+
join10(
|
|
2128
|
+
dirname7(createRequire4(import.meta.url).resolve("harperdb")),
|
|
1876
2129
|
`schema.graphql`
|
|
1877
2130
|
),
|
|
1878
2131
|
"utf8"
|
|
@@ -1884,22 +2137,22 @@ var getHarperSchemaGraphQLTool = tool33({
|
|
|
1884
2137
|
});
|
|
1885
2138
|
|
|
1886
2139
|
// tools/harper/hitHarperAPITool.ts
|
|
1887
|
-
import { tool as
|
|
1888
|
-
import { z as
|
|
1889
|
-
var
|
|
1890
|
-
method:
|
|
2140
|
+
import { tool as tool35 } from "@openai/agents";
|
|
2141
|
+
import { z as z35 } from "zod";
|
|
2142
|
+
var ToolParameters22 = z35.object({
|
|
2143
|
+
method: z35.enum(["POST", "GET", "PUT", "DELETE"]).optional().default("GET").describe(
|
|
1891
2144
|
'The HTTP method to use, defaults to "get". Notably, POST and PUT require the full body be sent.'
|
|
1892
2145
|
),
|
|
1893
|
-
path:
|
|
1894
|
-
port:
|
|
2146
|
+
path: z35.string().optional().default("/openapi").describe("The path to fetch from localhost (defaults to /openapi)."),
|
|
2147
|
+
port: z35.number().optional().default(harperProcess.httpPort).describe(
|
|
1895
2148
|
"The port to fetch from localhost (defaults to the running Harper port)."
|
|
1896
2149
|
),
|
|
1897
|
-
body:
|
|
2150
|
+
body: z35.string().optional().default("").describe("An optional JSON string body to send along with the request.")
|
|
1898
2151
|
});
|
|
1899
|
-
var hitHarperAPITool =
|
|
2152
|
+
var hitHarperAPITool = tool35({
|
|
1900
2153
|
name: "hit_harper_api",
|
|
1901
2154
|
description: "Performs a request against the running Harper API. Use /openapi to look up Harper APIs.",
|
|
1902
|
-
parameters:
|
|
2155
|
+
parameters: ToolParameters22,
|
|
1903
2156
|
needsApproval: async (runContext, input, callId) => {
|
|
1904
2157
|
if (callId && runContext.isToolApproved({ toolName: "hit_harper_api", callId })) {
|
|
1905
2158
|
return false;
|
|
@@ -1937,13 +2190,13 @@ var hitHarperAPITool = tool34({
|
|
|
1937
2190
|
});
|
|
1938
2191
|
|
|
1939
2192
|
// tools/harper/readHarperLogsTool.ts
|
|
1940
|
-
import { tool as
|
|
1941
|
-
import { z as
|
|
1942
|
-
var
|
|
1943
|
-
var readHarperLogsTool =
|
|
2193
|
+
import { tool as tool36 } from "@openai/agents";
|
|
2194
|
+
import { z as z36 } from "zod";
|
|
2195
|
+
var ToolParameters23 = z36.object({});
|
|
2196
|
+
var readHarperLogsTool = tool36({
|
|
1944
2197
|
name: "read_harper_logs",
|
|
1945
2198
|
description: "Reads the most recent console logs of a started Harper app and clears them so that subsequent reads will only show new logs.",
|
|
1946
|
-
parameters:
|
|
2199
|
+
parameters: ToolParameters23,
|
|
1947
2200
|
async execute() {
|
|
1948
2201
|
if (!harperProcess.running) {
|
|
1949
2202
|
return `Error: No Harper application is currently running.`;
|
|
@@ -1958,10 +2211,10 @@ var readHarperLogsTool = tool35({
|
|
|
1958
2211
|
});
|
|
1959
2212
|
|
|
1960
2213
|
// tools/harper/startHarperTool.ts
|
|
1961
|
-
import { tool as
|
|
1962
|
-
import { existsSync as
|
|
2214
|
+
import { tool as tool37 } from "@openai/agents";
|
|
2215
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1963
2216
|
import { basename, resolve } from "path";
|
|
1964
|
-
import { z as
|
|
2217
|
+
import { z as z37 } from "zod";
|
|
1965
2218
|
|
|
1966
2219
|
// utils/promises/sleep.ts
|
|
1967
2220
|
function sleep(ms) {
|
|
@@ -1969,13 +2222,13 @@ function sleep(ms) {
|
|
|
1969
2222
|
}
|
|
1970
2223
|
|
|
1971
2224
|
// tools/harper/startHarperTool.ts
|
|
1972
|
-
var
|
|
1973
|
-
directoryName:
|
|
2225
|
+
var ToolParameters24 = z37.object({
|
|
2226
|
+
directoryName: z37.string().describe("The name of the directory that the Harper app is in.")
|
|
1974
2227
|
});
|
|
1975
|
-
var startHarperTool =
|
|
2228
|
+
var startHarperTool = tool37({
|
|
1976
2229
|
name: "start_harper",
|
|
1977
2230
|
description: "Starts a Harper app background process, allowing you to observe the app in action (by readHarperLogsTool, hitHarperAPITool, etc).",
|
|
1978
|
-
parameters:
|
|
2231
|
+
parameters: ToolParameters24,
|
|
1979
2232
|
async execute({ directoryName }) {
|
|
1980
2233
|
if (isIgnored(directoryName)) {
|
|
1981
2234
|
return `Error: Target directory ${directoryName} is restricted by .aiignore`;
|
|
@@ -1986,7 +2239,7 @@ var startHarperTool = tool36({
|
|
|
1986
2239
|
try {
|
|
1987
2240
|
let effectiveDirectory = directoryName;
|
|
1988
2241
|
const candidatePath = resolve(process.cwd(), directoryName);
|
|
1989
|
-
if (!
|
|
2242
|
+
if (!existsSync8(candidatePath)) {
|
|
1990
2243
|
const cwd = process.cwd();
|
|
1991
2244
|
if (basename(cwd) === directoryName) {
|
|
1992
2245
|
effectiveDirectory = cwd;
|
|
@@ -2004,13 +2257,13 @@ ${logs2}`;
|
|
|
2004
2257
|
});
|
|
2005
2258
|
|
|
2006
2259
|
// tools/harper/stopHarperTool.ts
|
|
2007
|
-
import { tool as
|
|
2008
|
-
import { z as
|
|
2009
|
-
var
|
|
2010
|
-
var stopHarperTool =
|
|
2260
|
+
import { tool as tool38 } from "@openai/agents";
|
|
2261
|
+
import { z as z38 } from "zod";
|
|
2262
|
+
var ToolParameters25 = z38.object({});
|
|
2263
|
+
var stopHarperTool = tool38({
|
|
2011
2264
|
name: "stop_harper",
|
|
2012
2265
|
description: "Stops all previously started Harper app background process.",
|
|
2013
|
-
parameters:
|
|
2266
|
+
parameters: ToolParameters25,
|
|
2014
2267
|
async execute() {
|
|
2015
2268
|
if (!harperProcess.running) {
|
|
2016
2269
|
return `Error: No Harper application is currently running.`;
|
|
@@ -2025,21 +2278,12 @@ var stopHarperTool = tool37({
|
|
|
2025
2278
|
});
|
|
2026
2279
|
|
|
2027
2280
|
// tools/plan/addPlanItemTool.ts
|
|
2028
|
-
import { tool as
|
|
2029
|
-
import { z as
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
var globalPlanContext = {
|
|
2033
|
-
planDescription: "",
|
|
2034
|
-
planItems: [],
|
|
2035
|
-
progress: 0
|
|
2036
|
-
};
|
|
2037
|
-
|
|
2038
|
-
// tools/plan/addPlanItemTool.ts
|
|
2039
|
-
var AddPlanItemParameters = z38.object({
|
|
2040
|
-
text: z38.string().describe("The description of the task or milestone to add to the plan.")
|
|
2281
|
+
import { tool as tool39 } from "@openai/agents";
|
|
2282
|
+
import { z as z39 } from "zod";
|
|
2283
|
+
var AddPlanItemParameters = z39.object({
|
|
2284
|
+
text: z39.string().describe("The description of the task or milestone to add to the plan.")
|
|
2041
2285
|
});
|
|
2042
|
-
var addPlanItemTool =
|
|
2286
|
+
var addPlanItemTool = tool39({
|
|
2043
2287
|
name: "add_plan_item",
|
|
2044
2288
|
description: "Add a new item to the plan.",
|
|
2045
2289
|
parameters: AddPlanItemParameters,
|
|
@@ -2058,12 +2302,12 @@ var addPlanItemTool = tool38({
|
|
|
2058
2302
|
});
|
|
2059
2303
|
|
|
2060
2304
|
// tools/plan/setPlanDescriptionTool.ts
|
|
2061
|
-
import { tool as
|
|
2062
|
-
import { z as
|
|
2063
|
-
var SetPlanDescriptionParameters =
|
|
2064
|
-
description:
|
|
2305
|
+
import { tool as tool40 } from "@openai/agents";
|
|
2306
|
+
import { z as z40 } from "zod";
|
|
2307
|
+
var SetPlanDescriptionParameters = z40.object({
|
|
2308
|
+
description: z40.string().describe("A high-level description of the overall plan and goals.")
|
|
2065
2309
|
});
|
|
2066
|
-
var setPlanDescriptionTool =
|
|
2310
|
+
var setPlanDescriptionTool = tool40({
|
|
2067
2311
|
name: "set_plan_description",
|
|
2068
2312
|
description: "Set the high-level description for the current plan.",
|
|
2069
2313
|
parameters: SetPlanDescriptionParameters,
|
|
@@ -2074,12 +2318,12 @@ var setPlanDescriptionTool = tool39({
|
|
|
2074
2318
|
});
|
|
2075
2319
|
|
|
2076
2320
|
// tools/plan/setPlanItemsTool.ts
|
|
2077
|
-
import { tool as
|
|
2078
|
-
import { z as
|
|
2079
|
-
var SetPlanItemsParameters =
|
|
2080
|
-
items:
|
|
2321
|
+
import { tool as tool41 } from "@openai/agents";
|
|
2322
|
+
import { z as z41 } from "zod";
|
|
2323
|
+
var SetPlanItemsParameters = z41.object({
|
|
2324
|
+
items: z41.array(z41.string()).describe("An array of task descriptions to set as the plan items.")
|
|
2081
2325
|
});
|
|
2082
|
-
var setPlanItemsTool =
|
|
2326
|
+
var setPlanItemsTool = tool41({
|
|
2083
2327
|
name: "set_plan_items",
|
|
2084
2328
|
description: "Set multiple plan items at once, replacing any existing items.",
|
|
2085
2329
|
parameters: SetPlanItemsParameters,
|
|
@@ -2095,16 +2339,16 @@ var setPlanItemsTool = tool40({
|
|
|
2095
2339
|
});
|
|
2096
2340
|
|
|
2097
2341
|
// tools/plan/updatePlanItemTool.ts
|
|
2098
|
-
import { tool as
|
|
2099
|
-
import { z as
|
|
2100
|
-
var UpdatePlanItemParameters =
|
|
2101
|
-
id:
|
|
2102
|
-
text:
|
|
2103
|
-
status:
|
|
2342
|
+
import { tool as tool42 } from "@openai/agents";
|
|
2343
|
+
import { z as z42 } from "zod";
|
|
2344
|
+
var UpdatePlanItemParameters = z42.object({
|
|
2345
|
+
id: z42.number().describe("The ID of the plan item to update."),
|
|
2346
|
+
text: z42.string().describe("The new description of the task."),
|
|
2347
|
+
status: z42.enum(["unchanged", "todo", "in-progress", "done", "not-needed"]).describe(
|
|
2104
2348
|
"The new status of the task."
|
|
2105
2349
|
)
|
|
2106
2350
|
});
|
|
2107
|
-
var updatePlanItemTool =
|
|
2351
|
+
var updatePlanItemTool = tool42({
|
|
2108
2352
|
name: "update_plan_item",
|
|
2109
2353
|
description: "Update an existing plan item.",
|
|
2110
2354
|
parameters: UpdatePlanItemParameters,
|
|
@@ -2143,6 +2387,7 @@ function createTools() {
|
|
|
2143
2387
|
addPlanItemTool,
|
|
2144
2388
|
changeCwdTool,
|
|
2145
2389
|
checkHarperStatusTool,
|
|
2390
|
+
collectFeedbackTool,
|
|
2146
2391
|
codeInterpreterTool,
|
|
2147
2392
|
createApplyPatchTool(),
|
|
2148
2393
|
createNewHarperApplicationTool,
|
|
@@ -2185,6 +2430,7 @@ function trackCompaction(session) {
|
|
|
2185
2430
|
try {
|
|
2186
2431
|
return await originalRunCompaction(args);
|
|
2187
2432
|
} catch (error) {
|
|
2433
|
+
logError(error);
|
|
2188
2434
|
const err = error ?? {};
|
|
2189
2435
|
const name = err.name || "Error";
|
|
2190
2436
|
const message = err.message || String(err);
|
|
@@ -2255,16 +2501,15 @@ Stack: ${String(err.stack).split("\n").slice(0, 8).join("\n")}` : "";
|
|
|
2255
2501
|
text: `${name}:${code}${statusStr} ${message}${hint}${compactionCtx}${argsSnippet}${responseDataSnippet}${stack}`,
|
|
2256
2502
|
version: 1
|
|
2257
2503
|
}]);
|
|
2258
|
-
return void 0;
|
|
2259
2504
|
}
|
|
2260
2505
|
};
|
|
2261
2506
|
}
|
|
2262
2507
|
|
|
2263
2508
|
// utils/sessions/DiskSession.ts
|
|
2264
2509
|
import { MemorySession } from "@openai/agents";
|
|
2265
|
-
import { existsSync as
|
|
2510
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2266
2511
|
import { mkdir as mkdir2, readFile as readFile5, rename, writeFile as writeFile3 } from "fs/promises";
|
|
2267
|
-
import { dirname as
|
|
2512
|
+
import { dirname as dirname8 } from "path";
|
|
2268
2513
|
var DiskSession = class extends MemorySession {
|
|
2269
2514
|
filePath;
|
|
2270
2515
|
ready;
|
|
@@ -2277,9 +2522,23 @@ var DiskSession = class extends MemorySession {
|
|
|
2277
2522
|
const storage = await this.loadStorage();
|
|
2278
2523
|
let sessionId = this.sessionId;
|
|
2279
2524
|
if (!options?.sessionId) {
|
|
2280
|
-
|
|
2525
|
+
let candidate;
|
|
2526
|
+
const sessionIds = Object.keys(storage.sessions ?? {});
|
|
2281
2527
|
if (sessionIds.length > 0) {
|
|
2282
|
-
|
|
2528
|
+
candidate = sessionIds[0];
|
|
2529
|
+
} else {
|
|
2530
|
+
const planIds = Object.keys(storage.plan ?? {});
|
|
2531
|
+
if (planIds.length > 0) {
|
|
2532
|
+
candidate = planIds[0];
|
|
2533
|
+
} else {
|
|
2534
|
+
const skillsIds = Object.keys(storage.skillsRead ?? {});
|
|
2535
|
+
if (skillsIds.length > 0) {
|
|
2536
|
+
candidate = skillsIds[0];
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
if (candidate) {
|
|
2541
|
+
sessionId = candidate;
|
|
2283
2542
|
this.sessionId = sessionId;
|
|
2284
2543
|
}
|
|
2285
2544
|
}
|
|
@@ -2299,24 +2558,25 @@ var DiskSession = class extends MemorySession {
|
|
|
2299
2558
|
}
|
|
2300
2559
|
}
|
|
2301
2560
|
async loadStorage() {
|
|
2302
|
-
if (
|
|
2561
|
+
if (existsSync9(this.filePath)) {
|
|
2303
2562
|
try {
|
|
2304
2563
|
const data = await readFile5(this.filePath, "utf-8");
|
|
2305
2564
|
const parsed = JSON.parse(data);
|
|
2306
2565
|
parsed.sessions = parsed.sessions || {};
|
|
2307
2566
|
parsed.skillsRead = parsed.skillsRead || {};
|
|
2567
|
+
parsed.plan = parsed.plan || {};
|
|
2308
2568
|
return parsed;
|
|
2309
2569
|
} catch (e) {
|
|
2310
2570
|
console.error(`Failed to read session file ${this.filePath}:`, e);
|
|
2311
2571
|
}
|
|
2312
2572
|
}
|
|
2313
|
-
return { sessions: {}, skillsRead: {} };
|
|
2573
|
+
return { sessions: {}, skillsRead: {}, plan: {} };
|
|
2314
2574
|
}
|
|
2315
2575
|
async updateStorage(update) {
|
|
2316
2576
|
const storage = await this.loadStorage();
|
|
2317
2577
|
update(storage);
|
|
2318
|
-
const dir =
|
|
2319
|
-
if (!
|
|
2578
|
+
const dir = dirname8(this.filePath);
|
|
2579
|
+
if (!existsSync9(dir)) {
|
|
2320
2580
|
await mkdir2(dir, { recursive: true });
|
|
2321
2581
|
}
|
|
2322
2582
|
const data = JSON.stringify(storage, null, 2);
|
|
@@ -2365,6 +2625,9 @@ var DiskSession = class extends MemorySession {
|
|
|
2365
2625
|
if (storage.skillsRead) {
|
|
2366
2626
|
delete storage.skillsRead[sessionId];
|
|
2367
2627
|
}
|
|
2628
|
+
if (storage.plan) {
|
|
2629
|
+
delete storage.plan[sessionId];
|
|
2630
|
+
}
|
|
2368
2631
|
});
|
|
2369
2632
|
}
|
|
2370
2633
|
async addSkillRead(skill) {
|
|
@@ -2386,6 +2649,28 @@ var DiskSession = class extends MemorySession {
|
|
|
2386
2649
|
const storage = await this.loadStorage();
|
|
2387
2650
|
return storage.skillsRead?.[sessionId] ?? [];
|
|
2388
2651
|
}
|
|
2652
|
+
async getPlanState() {
|
|
2653
|
+
await this.ready;
|
|
2654
|
+
const sessionId = await this.getSessionId();
|
|
2655
|
+
const storage = await this.loadStorage();
|
|
2656
|
+
return storage.plan?.[sessionId] ?? null;
|
|
2657
|
+
}
|
|
2658
|
+
async setPlanState(state) {
|
|
2659
|
+
await this.ready;
|
|
2660
|
+
const sessionId = await this.getSessionId();
|
|
2661
|
+
await this.updateStorage((storage) => {
|
|
2662
|
+
if (!storage.plan) {
|
|
2663
|
+
storage.plan = {};
|
|
2664
|
+
}
|
|
2665
|
+
const existing = storage.plan[sessionId] ?? { planDescription: "", planItems: [], progress: 0 };
|
|
2666
|
+
storage.plan[sessionId] = {
|
|
2667
|
+
...existing,
|
|
2668
|
+
...state,
|
|
2669
|
+
// Ensure arrays/objects are properly merged for planItems
|
|
2670
|
+
planItems: Array.isArray(state.planItems) ? state.planItems : existing.planItems
|
|
2671
|
+
};
|
|
2672
|
+
});
|
|
2673
|
+
}
|
|
2389
2674
|
};
|
|
2390
2675
|
|
|
2391
2676
|
// utils/sessions/MemoryCompactionSession.ts
|
|
@@ -2421,8 +2706,17 @@ function getModelSettings(modelName) {
|
|
|
2421
2706
|
|
|
2422
2707
|
// utils/sessions/compactConversation.ts
|
|
2423
2708
|
async function compactConversation(items) {
|
|
2424
|
-
|
|
2425
|
-
|
|
2709
|
+
let splitIndex = Math.max(0, items.length - 3);
|
|
2710
|
+
while (splitIndex > 0 && splitIndex < items.length) {
|
|
2711
|
+
const itemAtSplit = items[splitIndex];
|
|
2712
|
+
if (itemAtSplit.type === "function_call_result") {
|
|
2713
|
+
splitIndex--;
|
|
2714
|
+
} else {
|
|
2715
|
+
break;
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
const recentItems = items.slice(splitIndex);
|
|
2719
|
+
const itemsToCompact = items.slice(0, splitIndex);
|
|
2426
2720
|
let noticeContent = "... conversation history compacted ...";
|
|
2427
2721
|
if (trackedState.compactionModel && itemsToCompact.length > 0) {
|
|
2428
2722
|
try {
|
|
@@ -2430,17 +2724,17 @@ async function compactConversation(items) {
|
|
|
2430
2724
|
name: "History Compactor",
|
|
2431
2725
|
model: isOpenAIModel(trackedState.compactionModel) ? trackedState.compactionModel : getModel(trackedState.compactionModel),
|
|
2432
2726
|
modelSettings: getModelSettings(trackedState.compactionModel),
|
|
2433
|
-
instructions: "Compact the provided conversation history
|
|
2727
|
+
instructions: "Compact the provided conversation history.\n- Focus on what is NOT completed and needs to be remembered for later.\n- Do NOT include file content or patches, it is available on the filesystem already. \n- Be concise."
|
|
2434
2728
|
});
|
|
2729
|
+
emitToListeners("SetCompacting", true);
|
|
2435
2730
|
const result = await run(
|
|
2436
2731
|
agent,
|
|
2437
2732
|
itemsToCompact
|
|
2438
2733
|
);
|
|
2439
2734
|
const summary = result.finalOutput;
|
|
2440
2735
|
if (summary && summary.trim().length > 0) {
|
|
2441
|
-
const s = summary.replace(/\s+/g, " ").trim();
|
|
2442
2736
|
noticeContent = `Key observations from earlier:
|
|
2443
|
-
${
|
|
2737
|
+
${summary.trim()}`;
|
|
2444
2738
|
}
|
|
2445
2739
|
} catch (err) {
|
|
2446
2740
|
const msg = String(err?.message || err || "");
|
|
@@ -2448,6 +2742,8 @@ ${s}`;
|
|
|
2448
2742
|
if (!isNoTrace) {
|
|
2449
2743
|
console.warn("Compaction summarization failed:", msg);
|
|
2450
2744
|
}
|
|
2745
|
+
} finally {
|
|
2746
|
+
emitToListeners("SetCompacting", false);
|
|
2451
2747
|
}
|
|
2452
2748
|
}
|
|
2453
2749
|
const itemsToAdd = [system(noticeContent), ...recentItems].filter(excludeFalsy);
|
|
@@ -2469,7 +2765,10 @@ function getModelContextLimit(modelName) {
|
|
|
2469
2765
|
if (name.startsWith("claude-3.5") || name.startsWith("claude-3")) {
|
|
2470
2766
|
return 2e5;
|
|
2471
2767
|
}
|
|
2472
|
-
if (name.startsWith("
|
|
2768
|
+
if (name.startsWith("claude-4.6") || name.startsWith("claude-4.5")) {
|
|
2769
|
+
return 1e6;
|
|
2770
|
+
}
|
|
2771
|
+
if (name.startsWith("gemini-1.5") || name.startsWith("gemini-3")) {
|
|
2473
2772
|
return 1e6;
|
|
2474
2773
|
}
|
|
2475
2774
|
if (name.startsWith("gemini-")) {
|
|
@@ -2493,6 +2792,7 @@ var MemoryCompactionSession = class {
|
|
|
2493
2792
|
triggerTokens;
|
|
2494
2793
|
itemsAddedSinceLastCompaction = 0;
|
|
2495
2794
|
skillsReadLocal = /* @__PURE__ */ new Set();
|
|
2795
|
+
planStateLocal;
|
|
2496
2796
|
constructor(options) {
|
|
2497
2797
|
this.underlyingSession = options.underlyingSession ?? new MemorySession2();
|
|
2498
2798
|
if (trackedState.compactionModel) {
|
|
@@ -2522,11 +2822,68 @@ var MemoryCompactionSession = class {
|
|
|
2522
2822
|
const merged = /* @__PURE__ */ new Set([...base, ...this.skillsReadLocal]);
|
|
2523
2823
|
return Array.from(merged);
|
|
2524
2824
|
}
|
|
2825
|
+
async getPlanState() {
|
|
2826
|
+
const u = this.underlyingSession;
|
|
2827
|
+
let base = null;
|
|
2828
|
+
if (u && typeof u.getPlanState === "function") {
|
|
2829
|
+
try {
|
|
2830
|
+
base = await Promise.resolve(u.getPlanState());
|
|
2831
|
+
} catch {
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
if (this.planStateLocal) {
|
|
2835
|
+
const existing = base ?? { planDescription: "", planItems: [], progress: 0 };
|
|
2836
|
+
const merged = {
|
|
2837
|
+
...existing,
|
|
2838
|
+
...this.planStateLocal,
|
|
2839
|
+
planItems: Array.isArray(this.planStateLocal.planItems) ? this.planStateLocal.planItems : existing.planItems
|
|
2840
|
+
};
|
|
2841
|
+
return merged;
|
|
2842
|
+
}
|
|
2843
|
+
return base;
|
|
2844
|
+
}
|
|
2845
|
+
async setPlanState(state) {
|
|
2846
|
+
const u = this.underlyingSession;
|
|
2847
|
+
if (u && typeof u.setPlanState === "function") {
|
|
2848
|
+
try {
|
|
2849
|
+
return await Promise.resolve(u.setPlanState(state));
|
|
2850
|
+
} catch {
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
const existing = this.planStateLocal ?? { planDescription: "", planItems: [], progress: 0 };
|
|
2854
|
+
this.planStateLocal = {
|
|
2855
|
+
...existing,
|
|
2856
|
+
...state,
|
|
2857
|
+
planItems: Array.isArray(state.planItems) ? state.planItems : existing.planItems
|
|
2858
|
+
};
|
|
2859
|
+
}
|
|
2525
2860
|
async getItems(limit) {
|
|
2526
|
-
|
|
2861
|
+
const items = await this.underlyingSession.getItems(limit);
|
|
2862
|
+
return items.map((it) => sanitizeItem(it));
|
|
2863
|
+
}
|
|
2864
|
+
/**
|
|
2865
|
+
* Returns the provider-stamped timestamp of the latest-added item, or null when unavailable.
|
|
2866
|
+
* Uses the underlying session directly to access our providerData without sanitation.
|
|
2867
|
+
*/
|
|
2868
|
+
async getLatestAddedTimestamp() {
|
|
2869
|
+
const items = await this.underlyingSession.getItems();
|
|
2870
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
2871
|
+
return null;
|
|
2872
|
+
}
|
|
2873
|
+
const last = items[items.length - 1];
|
|
2874
|
+
const ts = last?.providerData?.harper?.addedAtMs;
|
|
2875
|
+
return typeof ts === "number" && Number.isFinite(ts) ? ts : null;
|
|
2527
2876
|
}
|
|
2528
2877
|
async addItems(items) {
|
|
2529
|
-
|
|
2878
|
+
const now = Date.now();
|
|
2879
|
+
const stamped = items.map((it) => {
|
|
2880
|
+
const pd = { ...it?.providerData };
|
|
2881
|
+
const ours = { ...pd["harper"] };
|
|
2882
|
+
ours.addedAtMs = now;
|
|
2883
|
+
pd["harper"] = ours;
|
|
2884
|
+
return { ...it, providerData: pd };
|
|
2885
|
+
});
|
|
2886
|
+
await this.underlyingSession.addItems(stamped);
|
|
2530
2887
|
this.itemsAddedSinceLastCompaction += items.length;
|
|
2531
2888
|
await this.runCompaction({ reason: "post-add-check", mode: "auto" });
|
|
2532
2889
|
}
|
|
@@ -2552,7 +2909,7 @@ var MemoryCompactionSession = class {
|
|
|
2552
2909
|
* summarized by the model), and retains the last 3 recent items.
|
|
2553
2910
|
*/
|
|
2554
2911
|
async runCompaction(args) {
|
|
2555
|
-
const items = await this.
|
|
2912
|
+
const items = await this.getItems();
|
|
2556
2913
|
if (items.length <= 1) {
|
|
2557
2914
|
return null;
|
|
2558
2915
|
}
|
|
@@ -2573,6 +2930,37 @@ var MemoryCompactionSession = class {
|
|
|
2573
2930
|
return null;
|
|
2574
2931
|
}
|
|
2575
2932
|
};
|
|
2933
|
+
function sanitizeItem(it) {
|
|
2934
|
+
if (!it || typeof it !== "object") {
|
|
2935
|
+
return it;
|
|
2936
|
+
}
|
|
2937
|
+
const out = { ...it };
|
|
2938
|
+
if ("harper" in out) {
|
|
2939
|
+
try {
|
|
2940
|
+
delete out.harper;
|
|
2941
|
+
} catch {
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
const pd = out.providerData && typeof out.providerData === "object" ? { ...out.providerData } : void 0;
|
|
2945
|
+
if (pd) {
|
|
2946
|
+
if ("harper" in pd) {
|
|
2947
|
+
try {
|
|
2948
|
+
delete pd.harper;
|
|
2949
|
+
} catch {
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
if (Object.keys(pd).length === 0) {
|
|
2953
|
+
try {
|
|
2954
|
+
delete out.providerData;
|
|
2955
|
+
} catch {
|
|
2956
|
+
out.providerData = void 0;
|
|
2957
|
+
}
|
|
2958
|
+
} else {
|
|
2959
|
+
out.providerData = pd;
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
return out;
|
|
2963
|
+
}
|
|
2576
2964
|
function estimateTokens(items) {
|
|
2577
2965
|
let chars = 0;
|
|
2578
2966
|
for (const it of items) {
|
|
@@ -2657,54 +3045,48 @@ var ActionsProvider = ({ children }) => {
|
|
|
2657
3045
|
return /* @__PURE__ */ jsx2(ActionsContext.Provider, { value, children });
|
|
2658
3046
|
};
|
|
2659
3047
|
|
|
2660
|
-
// lifecycle/handleExit.ts
|
|
2661
|
-
import { getGlobalTraceProvider } from "@openai/agents";
|
|
2662
|
-
async function handleExit() {
|
|
2663
|
-
if (harperProcess.startedInternally) {
|
|
2664
|
-
harperProcess.stop();
|
|
2665
|
-
}
|
|
2666
|
-
await getGlobalTraceProvider().forceFlush();
|
|
2667
|
-
emitToListeners("ExitUI", void 0);
|
|
2668
|
-
process.exit(0);
|
|
2669
|
-
}
|
|
2670
|
-
|
|
2671
3048
|
// utils/sessions/cost.ts
|
|
2672
3049
|
import chalk2 from "chalk";
|
|
2673
3050
|
var FLEX_PRICES = {
|
|
2674
|
-
"gpt-5.2": { input: 0.875 / 1e6, cachedInput: 0.0875 / 1e6, output: 7 / 1e6 },
|
|
2675
|
-
"gpt-5.1": { input: 0.625 / 1e6, cachedInput: 0.0625 / 1e6, output: 5 / 1e6 },
|
|
2676
3051
|
"gpt-5": { input: 0.625 / 1e6, cachedInput: 0.0625 / 1e6, output: 5 / 1e6 },
|
|
2677
3052
|
"gpt-5-mini": { input: 0.125 / 1e6, cachedInput: 0.0125 / 1e6, output: 1 / 1e6 },
|
|
2678
3053
|
"gpt-5-nano": { input: 0.025 / 1e6, cachedInput: 25e-4 / 1e6, output: 0.2 / 1e6 },
|
|
3054
|
+
"gpt-5.1": { input: 0.625 / 1e6, cachedInput: 0.0625 / 1e6, output: 5 / 1e6 },
|
|
3055
|
+
"gpt-5.2": { input: 0.875 / 1e6, cachedInput: 0.0875 / 1e6, output: 7 / 1e6 },
|
|
2679
3056
|
"o3": { input: 1 / 1e6, cachedInput: 0.25 / 1e6, output: 4 / 1e6 },
|
|
2680
3057
|
"o4-mini": { input: 0.55 / 1e6, cachedInput: 0.138 / 1e6, output: 2.2 / 1e6 }
|
|
2681
3058
|
};
|
|
2682
3059
|
var STANDARD_PRICES = {
|
|
2683
|
-
"gpt-
|
|
2684
|
-
"gpt-
|
|
3060
|
+
"gpt-4o": { input: 2.5 / 1e6, cachedInput: 1.25 / 1e6, output: 10 / 1e6 },
|
|
3061
|
+
"gpt-4o-2024-05-13": { input: 5 / 1e6, cachedInput: 5 / 1e6, output: 15 / 1e6 },
|
|
3062
|
+
"gpt-4o-mini": { input: 0.15 / 1e6, cachedInput: 0.075 / 1e6, output: 0.6 / 1e6 },
|
|
2685
3063
|
"gpt-5": { input: 1.25 / 1e6, cachedInput: 0.125 / 1e6, output: 10 / 1e6 },
|
|
3064
|
+
"gpt-5-chat-latest": { input: 1.25 / 1e6, cachedInput: 0.125 / 1e6, output: 10 / 1e6 },
|
|
3065
|
+
"gpt-5-codex": { input: 1.25 / 1e6, cachedInput: 0.125 / 1e6, output: 10 / 1e6 },
|
|
2686
3066
|
"gpt-5-mini": { input: 0.25 / 1e6, cachedInput: 0.025 / 1e6, output: 2 / 1e6 },
|
|
2687
3067
|
"gpt-5-nano": { input: 0.05 / 1e6, cachedInput: 5e-3 / 1e6, output: 0.4 / 1e6 },
|
|
2688
|
-
"gpt-5.2-chat-latest": { input: 1.75 / 1e6, cachedInput: 0.175 / 1e6, output: 14 / 1e6 },
|
|
2689
|
-
"gpt-5.1-chat-latest": { input: 1.25 / 1e6, cachedInput: 0.125 / 1e6, output: 10 / 1e6 },
|
|
2690
|
-
"gpt-5-chat-latest": { input: 1.25 / 1e6, cachedInput: 0.125 / 1e6, output: 10 / 1e6 },
|
|
2691
|
-
"gpt-5.2-pro": { input: 21 / 1e6, cachedInput: 21 / 1e6, output: 168 / 1e6 },
|
|
2692
3068
|
"gpt-5-pro": { input: 15 / 1e6, cachedInput: 15 / 1e6, output: 120 / 1e6 },
|
|
2693
3069
|
"gpt-4.1": { input: 2 / 1e6, cachedInput: 0.5 / 1e6, output: 8 / 1e6 },
|
|
2694
3070
|
"gpt-4.1-mini": { input: 0.4 / 1e6, cachedInput: 0.1 / 1e6, output: 1.6 / 1e6 },
|
|
2695
3071
|
"gpt-4.1-nano": { input: 0.1 / 1e6, cachedInput: 0.025 / 1e6, output: 0.4 / 1e6 },
|
|
2696
|
-
"gpt-
|
|
2697
|
-
"gpt-
|
|
2698
|
-
"gpt-
|
|
3072
|
+
"gpt-5.1": { input: 1.25 / 1e6, cachedInput: 0.125 / 1e6, output: 10 / 1e6 },
|
|
3073
|
+
"gpt-5.1-chat-latest": { input: 1.25 / 1e6, cachedInput: 0.125 / 1e6, output: 10 / 1e6 },
|
|
3074
|
+
"gpt-5.1-codex": { input: 1.25 / 1e6, cachedInput: 0.125 / 1e6, output: 10 / 1e6 },
|
|
3075
|
+
"gpt-5.1-codex-max": { input: 1.25 / 1e6, cachedInput: 0.125 / 1e6, output: 10 / 1e6 },
|
|
3076
|
+
"gpt-5.2": { input: 1.75 / 1e6, cachedInput: 0.175 / 1e6, output: 14 / 1e6 },
|
|
3077
|
+
"gpt-5.2-chat-latest": { input: 1.75 / 1e6, cachedInput: 0.175 / 1e6, output: 14 / 1e6 },
|
|
3078
|
+
"gpt-5.2-codex": { input: 1.75 / 1e6, cachedInput: 0.175 / 1e6, output: 14 / 1e6 },
|
|
3079
|
+
"gpt-5.2-pro": { input: 21 / 1e6, cachedInput: 21 / 1e6, output: 168 / 1e6 },
|
|
3080
|
+
"gpt-5.3-codex": { input: 1.75 / 1e6, cachedInput: 0.175 / 1e6, output: 14 / 1e6 },
|
|
2699
3081
|
"o1": { input: 15 / 1e6, cachedInput: 7.5 / 1e6, output: 60 / 1e6 },
|
|
3082
|
+
"o1-mini": { input: 1.1 / 1e6, cachedInput: 0.55 / 1e6, output: 4.4 / 1e6 },
|
|
2700
3083
|
"o1-pro": { input: 150 / 1e6, cachedInput: 150 / 1e6, output: 600 / 1e6 },
|
|
2701
|
-
"o3-pro": { input: 20 / 1e6, cachedInput: 20 / 1e6, output: 80 / 1e6 },
|
|
2702
3084
|
"o3": { input: 2 / 1e6, cachedInput: 0.5 / 1e6, output: 8 / 1e6 },
|
|
2703
3085
|
"o3-deep-research": { input: 10 / 1e6, cachedInput: 2.5 / 1e6, output: 40 / 1e6 },
|
|
2704
|
-
"o4-mini": { input: 1.1 / 1e6, cachedInput: 0.275 / 1e6, output: 4.4 / 1e6 },
|
|
2705
|
-
"o4-mini-deep-research": { input: 2 / 1e6, cachedInput: 0.5 / 1e6, output: 8 / 1e6 },
|
|
2706
3086
|
"o3-mini": { input: 1.1 / 1e6, cachedInput: 0.55 / 1e6, output: 4.4 / 1e6 },
|
|
2707
|
-
"
|
|
3087
|
+
"o3-pro": { input: 20 / 1e6, cachedInput: 20 / 1e6, output: 80 / 1e6 },
|
|
3088
|
+
"o4-mini": { input: 1.1 / 1e6, cachedInput: 0.275 / 1e6, output: 4.4 / 1e6 },
|
|
3089
|
+
"o4-mini-deep-research": { input: 2 / 1e6, cachedInput: 0.5 / 1e6, output: 8 / 1e6 }
|
|
2708
3090
|
};
|
|
2709
3091
|
var MODEL_PRICES = {
|
|
2710
3092
|
flex: FLEX_PRICES,
|
|
@@ -2842,64 +3224,6 @@ var CostTracker = class {
|
|
|
2842
3224
|
};
|
|
2843
3225
|
var costTracker = new CostTracker();
|
|
2844
3226
|
|
|
2845
|
-
// utils/sessions/rateLimits.ts
|
|
2846
|
-
var RateLimitTracker = class {
|
|
2847
|
-
status = {
|
|
2848
|
-
limitRequests: null,
|
|
2849
|
-
limitTokens: null,
|
|
2850
|
-
remainingRequests: null,
|
|
2851
|
-
remainingTokens: null,
|
|
2852
|
-
resetRequests: null,
|
|
2853
|
-
resetTokens: null
|
|
2854
|
-
};
|
|
2855
|
-
updateFromHeaders(headers) {
|
|
2856
|
-
const getHeader = (name) => {
|
|
2857
|
-
const value = headers[name] || headers[name.toLowerCase()];
|
|
2858
|
-
return Array.isArray(value) ? value[0] : value;
|
|
2859
|
-
};
|
|
2860
|
-
const limitRequests = getHeader("x-ratelimit-limit-requests");
|
|
2861
|
-
const limitTokens = getHeader("x-ratelimit-limit-tokens");
|
|
2862
|
-
const remainingRequests = getHeader("x-ratelimit-remaining-requests");
|
|
2863
|
-
const remainingTokens = getHeader("x-ratelimit-remaining-tokens");
|
|
2864
|
-
const resetRequests = getHeader("x-ratelimit-reset-requests");
|
|
2865
|
-
const resetTokens = getHeader("x-ratelimit-reset-tokens");
|
|
2866
|
-
if (limitRequests) {
|
|
2867
|
-
this.status.limitRequests = parseInt(limitRequests, 10);
|
|
2868
|
-
}
|
|
2869
|
-
if (limitTokens) {
|
|
2870
|
-
this.status.limitTokens = parseInt(limitTokens, 10);
|
|
2871
|
-
}
|
|
2872
|
-
if (remainingRequests) {
|
|
2873
|
-
this.status.remainingRequests = parseInt(remainingRequests, 10);
|
|
2874
|
-
}
|
|
2875
|
-
if (remainingTokens) {
|
|
2876
|
-
this.status.remainingTokens = parseInt(remainingTokens, 10);
|
|
2877
|
-
}
|
|
2878
|
-
if (resetRequests) {
|
|
2879
|
-
this.status.resetRequests = resetRequests;
|
|
2880
|
-
}
|
|
2881
|
-
if (resetTokens) {
|
|
2882
|
-
this.status.resetTokens = resetTokens;
|
|
2883
|
-
}
|
|
2884
|
-
}
|
|
2885
|
-
isApproachingLimit(threshold) {
|
|
2886
|
-
const usage = this.getUsagePercentage();
|
|
2887
|
-
return {
|
|
2888
|
-
requests: usage.requests >= threshold,
|
|
2889
|
-
tokens: usage.tokens >= threshold
|
|
2890
|
-
};
|
|
2891
|
-
}
|
|
2892
|
-
getStatus() {
|
|
2893
|
-
return { ...this.status };
|
|
2894
|
-
}
|
|
2895
|
-
getUsagePercentage() {
|
|
2896
|
-
const requests = this.status.limitRequests && this.status.remainingRequests !== null ? 100 * (1 - this.status.remainingRequests / this.status.limitRequests) : 0;
|
|
2897
|
-
const tokens = this.status.limitTokens && this.status.remainingTokens !== null ? 100 * (1 - this.status.remainingTokens / this.status.limitTokens) : 0;
|
|
2898
|
-
return { requests, tokens };
|
|
2899
|
-
}
|
|
2900
|
-
};
|
|
2901
|
-
var rateLimitTracker = new RateLimitTracker();
|
|
2902
|
-
|
|
2903
3227
|
// utils/strings/isTrue.ts
|
|
2904
3228
|
function isTrue(v) {
|
|
2905
3229
|
if (v === void 0) {
|
|
@@ -2948,7 +3272,7 @@ Last tool call: ${lastToolCallInfo}` : "";
|
|
|
2948
3272
|
}
|
|
2949
3273
|
|
|
2950
3274
|
// agent/runAgentForOnePass.ts
|
|
2951
|
-
async function runAgentForOnePass(agent, session, input, controller) {
|
|
3275
|
+
async function runAgentForOnePass(agent, session, input, controller, isPrompt) {
|
|
2952
3276
|
let lastToolCallInfo = null;
|
|
2953
3277
|
const toolInfoMap = /* @__PURE__ */ new Map();
|
|
2954
3278
|
const removeToolListener = addListener("RegisterToolInfo", (info) => {
|
|
@@ -2958,9 +3282,9 @@ async function runAgentForOnePass(agent, session, input, controller) {
|
|
|
2958
3282
|
let hasStartedResponse = false;
|
|
2959
3283
|
let adjustedInput = input;
|
|
2960
3284
|
const noPlanYet = globalPlanContext.planItems.length === 0 && (!globalPlanContext.planDescription || globalPlanContext.planDescription.trim().length === 0);
|
|
2961
|
-
if (noPlanYet &&
|
|
3285
|
+
if (noPlanYet && typeof input === "string") {
|
|
2962
3286
|
const planningInstruction = [
|
|
2963
|
-
"If there is no current plan, first establish one and keep it updated:",
|
|
3287
|
+
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:",
|
|
2964
3288
|
"- Use the tools to manage the plan:",
|
|
2965
3289
|
" \u2022 set_plan_description(description)",
|
|
2966
3290
|
" \u2022 set_plan_items(items: string[])",
|
|
@@ -2969,14 +3293,10 @@ async function runAgentForOnePass(agent, session, input, controller) {
|
|
|
2969
3293
|
"- After setting the plan, as you progress, mark items as in-progress, done, or not-needed.",
|
|
2970
3294
|
"- Keep the plan concise and actionable. Update statuses as you move forward."
|
|
2971
3295
|
].join("\n");
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
];
|
|
2977
|
-
} else {
|
|
2978
|
-
adjustedInput = [system2(planningInstruction), ...input];
|
|
2979
|
-
}
|
|
3296
|
+
adjustedInput = [
|
|
3297
|
+
system2(planningInstruction),
|
|
3298
|
+
{ type: "message", role: "user", content: input }
|
|
3299
|
+
];
|
|
2980
3300
|
}
|
|
2981
3301
|
const stream = await run2(agent, adjustedInput, {
|
|
2982
3302
|
session,
|
|
@@ -2984,6 +3304,49 @@ async function runAgentForOnePass(agent, session, input, controller) {
|
|
|
2984
3304
|
signal: controller.signal,
|
|
2985
3305
|
maxTurns: trackedState.maxTurns
|
|
2986
3306
|
});
|
|
3307
|
+
const pushNewItemsIntoSession = async () => {
|
|
3308
|
+
const newItems = stream.newItems.filter((item) => {
|
|
3309
|
+
if ("status" in item) {
|
|
3310
|
+
return item.status === "completed";
|
|
3311
|
+
}
|
|
3312
|
+
return true;
|
|
3313
|
+
}).map((item) => {
|
|
3314
|
+
const json = typeof item.toJSON === "function" ? item.toJSON() : item;
|
|
3315
|
+
if (json && typeof json === "object" && "rawItem" in json) {
|
|
3316
|
+
return json.rawItem;
|
|
3317
|
+
}
|
|
3318
|
+
return json;
|
|
3319
|
+
});
|
|
3320
|
+
const itemsToPush = [];
|
|
3321
|
+
const callIds = /* @__PURE__ */ new Set();
|
|
3322
|
+
const resultIds = /* @__PURE__ */ new Set();
|
|
3323
|
+
for (const item of newItems) {
|
|
3324
|
+
if (item.type === "function_call") {
|
|
3325
|
+
callIds.add(item.callId);
|
|
3326
|
+
} else if (item.type === "function_call_result") {
|
|
3327
|
+
resultIds.add(item.callId);
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
const existingItems = await session.getItems();
|
|
3331
|
+
for (const item of existingItems) {
|
|
3332
|
+
if (item.type === "function_call") {
|
|
3333
|
+
callIds.add(item.callId);
|
|
3334
|
+
} else if (item.type === "function_call_result") {
|
|
3335
|
+
resultIds.add(item.callId);
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
for (const item of newItems) {
|
|
3339
|
+
if (item.type === "function_call" || item.type === "function_call_result") {
|
|
3340
|
+
if (!callIds.has(item.callId) || !resultIds.has(item.callId)) {
|
|
3341
|
+
continue;
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
itemsToPush.push(item);
|
|
3345
|
+
}
|
|
3346
|
+
if (itemsToPush.length > 0) {
|
|
3347
|
+
await session.addItems(itemsToPush);
|
|
3348
|
+
}
|
|
3349
|
+
};
|
|
2987
3350
|
for await (const event of stream) {
|
|
2988
3351
|
if (trackedState.monitorRateLimits) {
|
|
2989
3352
|
const { requests, tokens } = rateLimitTracker.isApproachingLimit(trackedState.rateLimitThreshold);
|
|
@@ -3015,9 +3378,8 @@ async function runAgentForOnePass(agent, session, input, controller) {
|
|
|
3015
3378
|
text: "Operation canceled due to rate limits.",
|
|
3016
3379
|
version: 1
|
|
3017
3380
|
}]);
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
}
|
|
3381
|
+
await pushNewItemsIntoSession();
|
|
3382
|
+
controller.abort();
|
|
3021
3383
|
process.exitCode = 1;
|
|
3022
3384
|
await handleExit();
|
|
3023
3385
|
}
|
|
@@ -3089,6 +3451,21 @@ async function runAgentForOnePass(agent, session, input, controller) {
|
|
|
3089
3451
|
cachedInputTokens: sessionStats2.cachedInputTokens + costTracker.extractCachedTokens(stream.state.usage.inputTokensDetails),
|
|
3090
3452
|
hasUnknownPrices: sessionStats2.hasUnknownPrices
|
|
3091
3453
|
});
|
|
3454
|
+
if (trackedState.currentTurn !== stream.state._currentTurn || trackedState.maxTurns !== stream.state._maxTurns) {
|
|
3455
|
+
trackedState.currentTurn = stream.state._currentTurn;
|
|
3456
|
+
trackedState.maxTurns = stream.state._maxTurns;
|
|
3457
|
+
emitToListeners("SettingsUpdated", void 0);
|
|
3458
|
+
if (trackedState.currentTurn + 1 >= trackedState.maxTurns) {
|
|
3459
|
+
await pushNewItemsIntoSession();
|
|
3460
|
+
emitToListeners("PushNewMessages", [{
|
|
3461
|
+
type: "interrupted",
|
|
3462
|
+
text: `- max turns reached${trackedState.autonomous ? ", thinking for a moment" : ""} -`,
|
|
3463
|
+
version: 1
|
|
3464
|
+
}]);
|
|
3465
|
+
controller.abort();
|
|
3466
|
+
return null;
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3092
3469
|
break;
|
|
3093
3470
|
case "run_item_stream_event":
|
|
3094
3471
|
if (event.name === "tool_called") {
|
|
@@ -3124,8 +3501,8 @@ async function runAgentForOnePass(agent, session, input, controller) {
|
|
|
3124
3501
|
if (trackedState.maxCost !== null) {
|
|
3125
3502
|
const estimatedTotalCost2 = costTracker.getEstimatedTotalCost(
|
|
3126
3503
|
stream.state.usage,
|
|
3127
|
-
trackedState.model
|
|
3128
|
-
trackedState.compactionModel
|
|
3504
|
+
trackedState.model,
|
|
3505
|
+
trackedState.compactionModel
|
|
3129
3506
|
);
|
|
3130
3507
|
if (estimatedTotalCost2 > trackedState.maxCost) {
|
|
3131
3508
|
emitToListeners("SetInputMode", "denied");
|
|
@@ -3136,9 +3513,8 @@ async function runAgentForOnePass(agent, session, input, controller) {
|
|
|
3136
3513
|
text: `Cost limit exceeded: $${estimatedTotalCost2.toFixed(4)} > $${trackedState.maxCost.toFixed(4)}`,
|
|
3137
3514
|
version: 1
|
|
3138
3515
|
}]);
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
}
|
|
3516
|
+
await pushNewItemsIntoSession();
|
|
3517
|
+
controller.abort();
|
|
3142
3518
|
process.exitCode = 1;
|
|
3143
3519
|
await handleExit();
|
|
3144
3520
|
}
|
|
@@ -3157,6 +3533,9 @@ async function runAgentForOnePass(agent, session, input, controller) {
|
|
|
3157
3533
|
cachedInputTokens: sessionStats.cachedInputTokens + costTracker.extractCachedTokens(stream.state.usage.inputTokensDetails),
|
|
3158
3534
|
hasUnknownPrices: sessionStats.hasUnknownPrices
|
|
3159
3535
|
});
|
|
3536
|
+
trackedState.currentTurn = stream.state._currentTurn;
|
|
3537
|
+
trackedState.maxTurns = stream.state._maxTurns;
|
|
3538
|
+
emitToListeners("SettingsUpdated", void 0);
|
|
3160
3539
|
if (stream.interruptions?.length) {
|
|
3161
3540
|
emitToListeners("SetThinking", false);
|
|
3162
3541
|
emitToListeners("SetInputMode", "approving");
|
|
@@ -3237,6 +3616,22 @@ async function runAgentForOnePass(agent, session, input, controller) {
|
|
|
3237
3616
|
removeToolListener();
|
|
3238
3617
|
return null;
|
|
3239
3618
|
} catch (error) {
|
|
3619
|
+
if (error?.name?.includes("MaxTurnsExceededError")) {
|
|
3620
|
+
emitToListeners("PushNewMessages", [{
|
|
3621
|
+
type: "interrupted",
|
|
3622
|
+
text: `Max turns exceeded. Please ask me to continue or pivot.`,
|
|
3623
|
+
version: 1
|
|
3624
|
+
}]);
|
|
3625
|
+
return null;
|
|
3626
|
+
}
|
|
3627
|
+
if (error?.name?.includes("rate_limit_exceeded")) {
|
|
3628
|
+
emitToListeners("PushNewMessages", [{
|
|
3629
|
+
type: "interrupted",
|
|
3630
|
+
text: `Rate limit reached. Please ask me to continue in a few minutes.`,
|
|
3631
|
+
version: 1
|
|
3632
|
+
}]);
|
|
3633
|
+
return null;
|
|
3634
|
+
}
|
|
3240
3635
|
showErrorToUser(error, lastToolCallInfo);
|
|
3241
3636
|
return null;
|
|
3242
3637
|
}
|
|
@@ -3247,6 +3642,7 @@ var AgentManager = class {
|
|
|
3247
3642
|
isInitialized = false;
|
|
3248
3643
|
controller = null;
|
|
3249
3644
|
queuedUserInputs = [];
|
|
3645
|
+
resumeState = null;
|
|
3250
3646
|
agent = null;
|
|
3251
3647
|
session = null;
|
|
3252
3648
|
initialMessages = [];
|
|
@@ -3254,14 +3650,55 @@ var AgentManager = class {
|
|
|
3254
3650
|
if (this.isInitialized) {
|
|
3255
3651
|
return;
|
|
3256
3652
|
}
|
|
3257
|
-
this.agent = new Agent3({
|
|
3258
|
-
name: "Harper Agent",
|
|
3259
|
-
model: isOpenAIModel(trackedState.model) ? trackedState.model : getModel(trackedState.model),
|
|
3260
|
-
modelSettings: getModelSettings(trackedState.model),
|
|
3261
|
-
instructions: readAgentsMD() || defaultInstructions(),
|
|
3262
|
-
tools: createTools()
|
|
3263
|
-
});
|
|
3264
|
-
this.session = createSession(trackedState.sessionPath);
|
|
3653
|
+
this.agent = new Agent3({
|
|
3654
|
+
name: "Harper Agent",
|
|
3655
|
+
model: isOpenAIModel(trackedState.model) ? trackedState.model : getModel(trackedState.model),
|
|
3656
|
+
modelSettings: getModelSettings(trackedState.model),
|
|
3657
|
+
instructions: readAgentsMD() || defaultInstructions(),
|
|
3658
|
+
tools: createTools()
|
|
3659
|
+
});
|
|
3660
|
+
this.session = createSession(trackedState.sessionPath);
|
|
3661
|
+
try {
|
|
3662
|
+
const plan = await this.session?.getPlanState?.();
|
|
3663
|
+
if (plan && typeof plan === "object") {
|
|
3664
|
+
if (typeof plan.planDescription === "string") {
|
|
3665
|
+
globalPlanContext.planDescription = plan.planDescription;
|
|
3666
|
+
emitToListeners("SetPlanDescription", plan.planDescription);
|
|
3667
|
+
}
|
|
3668
|
+
if (Array.isArray(plan.planItems)) {
|
|
3669
|
+
globalPlanContext.planItems = plan.planItems;
|
|
3670
|
+
const completedCount = plan.planItems.filter(
|
|
3671
|
+
(it) => it?.status === "done" || it?.status === "not-needed"
|
|
3672
|
+
).length;
|
|
3673
|
+
const progress = plan.planItems.length === 0 ? 0 : Math.round(completedCount / plan.planItems.length * 100);
|
|
3674
|
+
globalPlanContext.progress = progress;
|
|
3675
|
+
emitToListeners("SetPlanItems", plan.planItems);
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
} catch {
|
|
3679
|
+
}
|
|
3680
|
+
try {
|
|
3681
|
+
addListener("SetPlanDescription", async (desc) => {
|
|
3682
|
+
try {
|
|
3683
|
+
await this.session?.setPlanState?.({ planDescription: desc });
|
|
3684
|
+
} catch {
|
|
3685
|
+
}
|
|
3686
|
+
});
|
|
3687
|
+
addListener("SetPlanItems", async (items) => {
|
|
3688
|
+
if (Array.isArray(items)) {
|
|
3689
|
+
globalPlanContext.planItems = items;
|
|
3690
|
+
const completedCount = items.filter((it) => it?.status === "done" || it?.status === "not-needed").length;
|
|
3691
|
+
globalPlanContext.progress = items.length > 0 ? Math.round(completedCount / items.length * 100) : 0;
|
|
3692
|
+
}
|
|
3693
|
+
try {
|
|
3694
|
+
const completedCount = Array.isArray(items) ? items.filter((it) => it?.status === "done" || it?.status === "not-needed").length : 0;
|
|
3695
|
+
const progress = Array.isArray(items) && items.length > 0 ? Math.round(completedCount / items.length * 100) : 0;
|
|
3696
|
+
await this.session?.setPlanState?.({ planItems: items, progress });
|
|
3697
|
+
} catch {
|
|
3698
|
+
}
|
|
3699
|
+
});
|
|
3700
|
+
} catch {
|
|
3701
|
+
}
|
|
3265
3702
|
if (trackedState.sessionPath) {
|
|
3266
3703
|
const items = await this.session.getItems();
|
|
3267
3704
|
if (items.length > 0) {
|
|
@@ -3329,12 +3766,54 @@ var AgentManager = class {
|
|
|
3329
3766
|
this.queuedUserInputs.push(text);
|
|
3330
3767
|
}
|
|
3331
3768
|
}
|
|
3332
|
-
async runTask(task) {
|
|
3769
|
+
async runTask(task, isPrompt) {
|
|
3333
3770
|
this.controller = new AbortController();
|
|
3771
|
+
await this.runCompactionIfWeWereIdle();
|
|
3334
3772
|
emitToListeners("SetThinking", true);
|
|
3335
3773
|
let taskOrState = task;
|
|
3774
|
+
const lowerTask = task.toLowerCase();
|
|
3775
|
+
if (this.resumeState && (lowerTask.includes("continue") || lowerTask.includes("keep going") || lowerTask.includes("more") || lowerTask === "y" || lowerTask === "yes")) {
|
|
3776
|
+
taskOrState = this.resumeState;
|
|
3777
|
+
this.resumeState = null;
|
|
3778
|
+
} else {
|
|
3779
|
+
this.resumeState = null;
|
|
3780
|
+
}
|
|
3336
3781
|
while (taskOrState) {
|
|
3337
|
-
taskOrState = await runAgentForOnePass(this.agent, this.session, taskOrState, this.controller);
|
|
3782
|
+
taskOrState = await runAgentForOnePass(this.agent, this.session, taskOrState, this.controller, isPrompt);
|
|
3783
|
+
if (taskOrState && !trackedState.autonomous) {
|
|
3784
|
+
if (taskOrState.getInterruptions().length === 0) {
|
|
3785
|
+
this.resumeState = taskOrState;
|
|
3786
|
+
emitToListeners("SetThinking", false);
|
|
3787
|
+
emitToListeners("PushNewMessages", [{
|
|
3788
|
+
type: "interrupted",
|
|
3789
|
+
text: `Would you like me to continue?`,
|
|
3790
|
+
version: 1
|
|
3791
|
+
}]);
|
|
3792
|
+
break;
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
if (trackedState.autonomous && !this.resumeState) {
|
|
3796
|
+
const planItems = globalPlanContext.planItems;
|
|
3797
|
+
const hasPlan = planItems.length > 0;
|
|
3798
|
+
const allDone = hasPlan && planItems.every((item) => item.status === "done" || item.status === "not-needed");
|
|
3799
|
+
if (allDone) {
|
|
3800
|
+
emitToListeners("SetThinking", false);
|
|
3801
|
+
emitToListeners("PushNewMessages", [{
|
|
3802
|
+
type: "agent",
|
|
3803
|
+
text: "Plan 100% accomplished. Exiting autonomous mode.",
|
|
3804
|
+
version: 1
|
|
3805
|
+
}]);
|
|
3806
|
+
await sleep(1e3);
|
|
3807
|
+
await handleExit();
|
|
3808
|
+
} else {
|
|
3809
|
+
await sleep(1e3);
|
|
3810
|
+
await this.session?.runCompaction({ force: true });
|
|
3811
|
+
trackedState.currentTurn = 0;
|
|
3812
|
+
this.resumeState = null;
|
|
3813
|
+
this.controller = new AbortController();
|
|
3814
|
+
taskOrState = "Keep going";
|
|
3815
|
+
}
|
|
3816
|
+
}
|
|
3338
3817
|
}
|
|
3339
3818
|
emitToListeners("SetThinking", false);
|
|
3340
3819
|
if (this.queuedUserInputs.length > 0) {
|
|
@@ -3342,6 +3821,22 @@ var AgentManager = class {
|
|
|
3342
3821
|
void this.runTask(batched);
|
|
3343
3822
|
}
|
|
3344
3823
|
}
|
|
3824
|
+
async runCompactionIfWeWereIdle() {
|
|
3825
|
+
if (this.session) {
|
|
3826
|
+
const lastTs = await this.session.getLatestAddedTimestamp();
|
|
3827
|
+
if (typeof lastTs === "number" && Number.isFinite(lastTs)) {
|
|
3828
|
+
const ONE_HOUR_MS = 60 * 60 * 1e3;
|
|
3829
|
+
const idleMs = Date.now() - lastTs;
|
|
3830
|
+
if (idleMs > ONE_HOUR_MS) {
|
|
3831
|
+
try {
|
|
3832
|
+
await this.session.runCompaction({ force: true });
|
|
3833
|
+
} catch (err) {
|
|
3834
|
+
logError(err);
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3345
3840
|
interrupt() {
|
|
3346
3841
|
if (this.controller) {
|
|
3347
3842
|
this.controller.abort();
|
|
@@ -3357,7 +3852,7 @@ import "react";
|
|
|
3357
3852
|
// ink/components/ChatContent.tsx
|
|
3358
3853
|
import { Spinner as Spinner2 } from "@inkjs/ui";
|
|
3359
3854
|
import { Box as Box10, Text as Text10, useInput as useInput4 } from "ink";
|
|
3360
|
-
import React11, { useCallback as
|
|
3855
|
+
import React11, { useCallback as useCallback5, useEffect as useEffect8, useMemo as useMemo11, useRef as useRef2, useState as useState14 } from "react";
|
|
3361
3856
|
|
|
3362
3857
|
// ink/contexts/ChatContext.tsx
|
|
3363
3858
|
import { createContext as createContext3, useContext as useContext3, useMemo as useMemo3, useState as useState3 } from "react";
|
|
@@ -3392,6 +3887,7 @@ var ChatProvider = ({
|
|
|
3392
3887
|
const [messages, setMessages] = useState3(getInitialMessages());
|
|
3393
3888
|
const [userInputMode, setUserInputMode] = useState3("waiting");
|
|
3394
3889
|
const [isThinking, setIsThinking] = useState3(false);
|
|
3890
|
+
const [isCompacting, setIsCompacting] = useState3(false);
|
|
3395
3891
|
const [focusedArea, setFocusedArea] = useState3("input");
|
|
3396
3892
|
useListener("PushNewMessages", (messages2) => {
|
|
3397
3893
|
setMessages((prev) => {
|
|
@@ -3406,6 +3902,9 @@ var ChatProvider = ({
|
|
|
3406
3902
|
useListener("SetThinking", (value2) => {
|
|
3407
3903
|
setIsThinking(Boolean(value2));
|
|
3408
3904
|
}, []);
|
|
3905
|
+
useListener("SetCompacting", (value2) => {
|
|
3906
|
+
setIsCompacting(Boolean(value2));
|
|
3907
|
+
}, []);
|
|
3409
3908
|
useListener("InterruptThought", () => {
|
|
3410
3909
|
setIsThinking(false);
|
|
3411
3910
|
}, []);
|
|
@@ -3433,9 +3932,10 @@ var ChatProvider = ({
|
|
|
3433
3932
|
messages,
|
|
3434
3933
|
userInputMode,
|
|
3435
3934
|
isThinking,
|
|
3935
|
+
isCompacting,
|
|
3436
3936
|
focusedArea,
|
|
3437
3937
|
setFocusedArea
|
|
3438
|
-
}), [messages, userInputMode, isThinking, focusedArea]);
|
|
3938
|
+
}), [messages, userInputMode, isThinking, isCompacting, focusedArea]);
|
|
3439
3939
|
return /* @__PURE__ */ jsx3(ChatContext.Provider, { value, children });
|
|
3440
3940
|
};
|
|
3441
3941
|
|
|
@@ -3447,7 +3947,6 @@ function useMessageListener() {
|
|
|
3447
3947
|
agentManager.interrupt();
|
|
3448
3948
|
emitToListeners("PushNewMessages", [
|
|
3449
3949
|
{
|
|
3450
|
-
id: Date.now(),
|
|
3451
3950
|
type: "interrupted",
|
|
3452
3951
|
text: "- thought interrupted -",
|
|
3453
3952
|
version: 1
|
|
@@ -3456,7 +3955,7 @@ function useMessageListener() {
|
|
|
3456
3955
|
}, []);
|
|
3457
3956
|
useListener("PushNewMessages", async (messages) => {
|
|
3458
3957
|
for (const message of messages) {
|
|
3459
|
-
if (message.type === "user" && message.text) {
|
|
3958
|
+
if ((message.type === "user" || message.type === "prompt") && message.text) {
|
|
3460
3959
|
const lowerText = message.text.toLowerCase();
|
|
3461
3960
|
if (lowerText === "exit" || lowerText === "bye") {
|
|
3462
3961
|
await handleExit();
|
|
@@ -3473,7 +3972,7 @@ function useMessageListener() {
|
|
|
3473
3972
|
agentManager.enqueueUserInput(message.text);
|
|
3474
3973
|
message.handled = true;
|
|
3475
3974
|
} else if (!message.handled) {
|
|
3476
|
-
void agentManager.runTask(message.text);
|
|
3975
|
+
void agentManager.runTask(message.text, message.type === "prompt");
|
|
3477
3976
|
}
|
|
3478
3977
|
}
|
|
3479
3978
|
}
|
|
@@ -3483,12 +3982,102 @@ function useMessageListener() {
|
|
|
3483
3982
|
// ink/constants/footerHeight.ts
|
|
3484
3983
|
var footerHeight = 2;
|
|
3485
3984
|
|
|
3985
|
+
// ink/contexts/ApprovalContext.tsx
|
|
3986
|
+
import { createContext as createContext4, useCallback as useCallback2, useContext as useContext4, useMemo as useMemo4, useState as useState4 } from "react";
|
|
3987
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
3988
|
+
var ApprovalContext = createContext4(void 0);
|
|
3989
|
+
var useApproval = () => {
|
|
3990
|
+
const context = useContext4(ApprovalContext);
|
|
3991
|
+
if (!context) {
|
|
3992
|
+
throw new Error("useApproval must be used within an ApprovalProvider");
|
|
3993
|
+
}
|
|
3994
|
+
return context;
|
|
3995
|
+
};
|
|
3996
|
+
var ApprovalProvider = ({ children }) => {
|
|
3997
|
+
const [payload, setPayload] = useState4(null);
|
|
3998
|
+
const [toolInfos] = useState4(/* @__PURE__ */ new Map());
|
|
3999
|
+
const registerToolInfo = useCallback2(
|
|
4000
|
+
(info) => {
|
|
4001
|
+
toolInfos.set(info.callId, info);
|
|
4002
|
+
},
|
|
4003
|
+
[toolInfos]
|
|
4004
|
+
);
|
|
4005
|
+
useListener("OpenApprovalViewer", (p) => {
|
|
4006
|
+
let finalPayload = { ...p, openedAt: Date.now() };
|
|
4007
|
+
if (p.mode === "info" && p.callId) {
|
|
4008
|
+
const info = toolInfos.get(p.callId);
|
|
4009
|
+
if (info) {
|
|
4010
|
+
finalPayload = { ...finalPayload, ...info };
|
|
4011
|
+
}
|
|
4012
|
+
} else if (p.callId) {
|
|
4013
|
+
registerToolInfo({
|
|
4014
|
+
callId: p.callId,
|
|
4015
|
+
type: p.type,
|
|
4016
|
+
path: p.path,
|
|
4017
|
+
diff: p.diff,
|
|
4018
|
+
code: p.code,
|
|
4019
|
+
commands: p.commands
|
|
4020
|
+
});
|
|
4021
|
+
}
|
|
4022
|
+
setPayload(finalPayload);
|
|
4023
|
+
}, [registerToolInfo, toolInfos]);
|
|
4024
|
+
useListener("RegisterToolInfo", (info) => {
|
|
4025
|
+
registerToolInfo(info);
|
|
4026
|
+
}, [registerToolInfo]);
|
|
4027
|
+
useListener("CloseApprovalViewer", () => {
|
|
4028
|
+
setPayload(null);
|
|
4029
|
+
}, []);
|
|
4030
|
+
const value = useMemo4(() => ({
|
|
4031
|
+
payload,
|
|
4032
|
+
setPayload,
|
|
4033
|
+
registerToolInfo
|
|
4034
|
+
}), [payload, registerToolInfo]);
|
|
4035
|
+
return /* @__PURE__ */ jsx4(ApprovalContext.Provider, { value, children });
|
|
4036
|
+
};
|
|
4037
|
+
|
|
4038
|
+
// ink/contexts/PlanContext.tsx
|
|
4039
|
+
import { createContext as createContext5, useContext as useContext5, useMemo as useMemo5, useState as useState5 } from "react";
|
|
4040
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
4041
|
+
var PlanContext = createContext5(globalPlanContext);
|
|
4042
|
+
var usePlan = () => {
|
|
4043
|
+
const context = useContext5(PlanContext);
|
|
4044
|
+
if (!context) {
|
|
4045
|
+
throw new Error("usePlan must be used within a PlanProvider");
|
|
4046
|
+
}
|
|
4047
|
+
return context;
|
|
4048
|
+
};
|
|
4049
|
+
var PlanProvider = ({
|
|
4050
|
+
children
|
|
4051
|
+
}) => {
|
|
4052
|
+
const [planDescription, setPlanDescription] = useState5(globalPlanContext.planDescription);
|
|
4053
|
+
const [planItems, setPlanItems] = useState5(globalPlanContext.planItems);
|
|
4054
|
+
const [progress, setProgress] = useState5(globalPlanContext.progress);
|
|
4055
|
+
useListener("SetPlanDescription", (newGoal) => {
|
|
4056
|
+
globalPlanContext.planDescription = newGoal;
|
|
4057
|
+
setPlanDescription(newGoal);
|
|
4058
|
+
}, []);
|
|
4059
|
+
useListener("SetPlanItems", (planItems2) => {
|
|
4060
|
+
globalPlanContext.planItems = planItems2;
|
|
4061
|
+
const completedCount = planItems2.filter((item) => item.status === "done" || item.status === "not-needed").length;
|
|
4062
|
+
const progress2 = planItems2.length === 0 ? 0 : Math.round(completedCount / planItems2.length * 100);
|
|
4063
|
+
globalPlanContext.progress = progress2;
|
|
4064
|
+
setPlanItems(planItems2);
|
|
4065
|
+
setProgress(progress2);
|
|
4066
|
+
}, []);
|
|
4067
|
+
const value = useMemo5(() => ({
|
|
4068
|
+
progress,
|
|
4069
|
+
planDescription,
|
|
4070
|
+
planItems
|
|
4071
|
+
}), [progress, planDescription, planItems]);
|
|
4072
|
+
return /* @__PURE__ */ jsx5(PlanContext.Provider, { value, children });
|
|
4073
|
+
};
|
|
4074
|
+
|
|
3486
4075
|
// ink/library/useTerminalSize.ts
|
|
3487
4076
|
import { useStdout } from "ink";
|
|
3488
|
-
import { useEffect as useEffect3, useState as
|
|
4077
|
+
import { useEffect as useEffect3, useState as useState6 } from "react";
|
|
3489
4078
|
function useTerminalSize() {
|
|
3490
4079
|
const { stdout } = useStdout();
|
|
3491
|
-
const [size, setSize] =
|
|
4080
|
+
const [size, setSize] = useState6({
|
|
3492
4081
|
rows: stdout?.rows ?? 24,
|
|
3493
4082
|
columns: stdout?.columns ?? 80
|
|
3494
4083
|
});
|
|
@@ -3559,18 +4148,18 @@ function wrapText(text, width) {
|
|
|
3559
4148
|
|
|
3560
4149
|
// ink/components/ActionsView.tsx
|
|
3561
4150
|
import { Box as Box3, Text as Text3, useInput } from "ink";
|
|
3562
|
-
import { useCallback as
|
|
4151
|
+
import { useCallback as useCallback3, useEffect as useEffect5, useState as useState8 } from "react";
|
|
3563
4152
|
|
|
3564
4153
|
// ink/components/ActionItemRow.tsx
|
|
3565
4154
|
import { Spinner } from "@inkjs/ui";
|
|
3566
4155
|
import { Box, Text } from "ink";
|
|
3567
4156
|
import { memo } from "react";
|
|
3568
|
-
import { jsx as
|
|
4157
|
+
import { jsx as jsx6, jsxs } from "react/jsx-runtime";
|
|
3569
4158
|
var ActionItemRow = memo(
|
|
3570
4159
|
({ item, isSelected, isFocused, width }) => {
|
|
3571
4160
|
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";
|
|
3572
4161
|
const selectionColor = isFocused ? "cyan" : "gray";
|
|
3573
|
-
const pipe = /* @__PURE__ */
|
|
4162
|
+
const pipe = /* @__PURE__ */ jsx6(Text, { color: isSelected ? selectionColor : "gray", bold: isSelected, children: isSelected ? "\u2503 " : "\u2502 " });
|
|
3574
4163
|
const statusLabel = item.running ? " " : item.kind === "approval" ? item.status === "approved" ? " approved" : item.status === "denied" ? " denied" : "" : typeof item.exitCode === "number" ? ` ${item.exitCode}` : "";
|
|
3575
4164
|
const statusLabelWidth = item.running ? 2 : statusLabel.length;
|
|
3576
4165
|
const pipeWidth = 3;
|
|
@@ -3588,24 +4177,24 @@ var ActionItemRow = memo(
|
|
|
3588
4177
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
3589
4178
|
pipe,
|
|
3590
4179
|
/* @__PURE__ */ jsxs(Box, { flexGrow: 1, children: [
|
|
3591
|
-
/* @__PURE__ */
|
|
4180
|
+
/* @__PURE__ */ jsx6(Text, { color: statusColor, bold: true, children: item.title }),
|
|
3592
4181
|
/* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
3593
4182
|
" ",
|
|
3594
4183
|
displayedDetail
|
|
3595
4184
|
] })
|
|
3596
4185
|
] }),
|
|
3597
|
-
/* @__PURE__ */
|
|
3598
|
-
/* @__PURE__ */
|
|
3599
|
-
/* @__PURE__ */
|
|
3600
|
-
] }) : /* @__PURE__ */
|
|
4186
|
+
/* @__PURE__ */ jsx6(Box, { children: item.running ? /* @__PURE__ */ jsxs(Box, { children: [
|
|
4187
|
+
/* @__PURE__ */ jsx6(Text, { color: "yellow" }),
|
|
4188
|
+
/* @__PURE__ */ jsx6(Spinner, { type: "dots" })
|
|
4189
|
+
] }) : /* @__PURE__ */ jsx6(Text, { color: statusColor, bold: true, children: statusLabel }) })
|
|
3601
4190
|
] });
|
|
3602
4191
|
}
|
|
3603
4192
|
);
|
|
3604
4193
|
|
|
3605
4194
|
// ink/components/VirtualList.tsx
|
|
3606
4195
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
3607
|
-
import { forwardRef, useEffect as useEffect4, useImperativeHandle, useMemo as
|
|
3608
|
-
import { jsx as
|
|
4196
|
+
import { forwardRef, useEffect as useEffect4, useImperativeHandle, useMemo as useMemo6, useRef, useState as useState7 } from "react";
|
|
4197
|
+
import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
3609
4198
|
function getDefaultKey(item, index) {
|
|
3610
4199
|
if (item && typeof item === "object") {
|
|
3611
4200
|
if ("id" in item && (typeof item.id === "string" || typeof item.id === "number")) {
|
|
@@ -3635,7 +4224,7 @@ var VirtualListInner = (props, ref) => {
|
|
|
3635
4224
|
onViewportChange
|
|
3636
4225
|
} = props;
|
|
3637
4226
|
const { rows: terminalRows } = useTerminalSize();
|
|
3638
|
-
const resolvedHeight =
|
|
4227
|
+
const resolvedHeight = useMemo6(() => {
|
|
3639
4228
|
if (typeof propHeight === "number") {
|
|
3640
4229
|
return propHeight;
|
|
3641
4230
|
}
|
|
@@ -3645,7 +4234,7 @@ var VirtualListInner = (props, ref) => {
|
|
|
3645
4234
|
}
|
|
3646
4235
|
return Math.max(1, terminalRows - reservedLines);
|
|
3647
4236
|
}, [propHeight, terminalRows, reservedLines]);
|
|
3648
|
-
const getItemHeight =
|
|
4237
|
+
const getItemHeight = useMemo6(() => {
|
|
3649
4238
|
if (getItemHeightProp) {
|
|
3650
4239
|
return getItemHeightProp;
|
|
3651
4240
|
}
|
|
@@ -3656,14 +4245,14 @@ var VirtualListInner = (props, ref) => {
|
|
|
3656
4245
|
}, [getItemHeightProp, fixedItemHeight]);
|
|
3657
4246
|
const indicatorLines = showOverflowIndicators ? 2 : 0;
|
|
3658
4247
|
const availableHeight = Math.max(0, resolvedHeight - indicatorLines);
|
|
3659
|
-
const [viewportOffset, setViewportOffset] =
|
|
4248
|
+
const [viewportOffset, setViewportOffset] = useState7(0);
|
|
3660
4249
|
const lastItemsLength = useRef(items.length);
|
|
3661
4250
|
const viewportOffsetRef = useRef(viewportOffset);
|
|
3662
4251
|
useEffect4(() => {
|
|
3663
4252
|
viewportOffsetRef.current = viewportOffset;
|
|
3664
4253
|
}, [viewportOffset]);
|
|
3665
4254
|
const clampedSelectedIndex = Math.max(0, Math.min(selectedIndex, items.length - 1));
|
|
3666
|
-
const { visibleCount, visibleItems } =
|
|
4255
|
+
const { visibleCount, visibleItems } = useMemo6(() => {
|
|
3667
4256
|
if (availableHeight <= 0) {
|
|
3668
4257
|
return { visibleCount: 0, visibleItems: [] };
|
|
3669
4258
|
}
|
|
@@ -3728,7 +4317,7 @@ var VirtualListInner = (props, ref) => {
|
|
|
3728
4317
|
}
|
|
3729
4318
|
}
|
|
3730
4319
|
}, [clampedSelectedIndex, items, availableHeight, getItemHeight, visibleCount]);
|
|
3731
|
-
const viewport =
|
|
4320
|
+
const viewport = useMemo6(
|
|
3732
4321
|
() => ({
|
|
3733
4322
|
offset: viewportOffset,
|
|
3734
4323
|
visibleCount,
|
|
@@ -3860,7 +4449,7 @@ var VirtualListInner = (props, ref) => {
|
|
|
3860
4449
|
if (count < overflowIndicatorThreshold) {
|
|
3861
4450
|
return null;
|
|
3862
4451
|
}
|
|
3863
|
-
return /* @__PURE__ */
|
|
4452
|
+
return /* @__PURE__ */ jsx7(Box2, { paddingLeft: 2, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
3864
4453
|
"\u25B2 ",
|
|
3865
4454
|
count,
|
|
3866
4455
|
" more"
|
|
@@ -3870,7 +4459,7 @@ var VirtualListInner = (props, ref) => {
|
|
|
3870
4459
|
if (count < overflowIndicatorThreshold) {
|
|
3871
4460
|
return null;
|
|
3872
4461
|
}
|
|
3873
|
-
return /* @__PURE__ */
|
|
4462
|
+
return /* @__PURE__ */ jsx7(Box2, { paddingLeft: 2, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
3874
4463
|
"\u25BC ",
|
|
3875
4464
|
count,
|
|
3876
4465
|
" more"
|
|
@@ -3879,7 +4468,7 @@ var VirtualListInner = (props, ref) => {
|
|
|
3879
4468
|
const topIndicator = renderOverflowTop ?? defaultOverflowTop;
|
|
3880
4469
|
const bottomIndicator = renderOverflowBottom ?? defaultOverflowBottom;
|
|
3881
4470
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: resolvedHeight, children: [
|
|
3882
|
-
showOverflowIndicators && /* @__PURE__ */
|
|
4471
|
+
showOverflowIndicators && /* @__PURE__ */ jsx7(Box2, { height: 1, children: topIndicator(overflowTop) }),
|
|
3883
4472
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", flexGrow: 1, children: [
|
|
3884
4473
|
visibleItems.map((item, idx) => {
|
|
3885
4474
|
const actualIndex = viewportOffset + idx;
|
|
@@ -3889,20 +4478,20 @@ var VirtualListInner = (props, ref) => {
|
|
|
3889
4478
|
index: actualIndex,
|
|
3890
4479
|
isSelected: actualIndex === clampedSelectedIndex
|
|
3891
4480
|
};
|
|
3892
|
-
return /* @__PURE__ */
|
|
4481
|
+
return /* @__PURE__ */ jsx7(Box2, { height: getItemHeight(item, actualIndex), overflow: "hidden", children: renderItem(itemProps) }, key);
|
|
3893
4482
|
}),
|
|
3894
|
-
renderFiller && hSum < availableHeight && /* @__PURE__ */
|
|
4483
|
+
renderFiller && hSum < availableHeight && /* @__PURE__ */ jsx7(Box2, { flexDirection: "column", flexGrow: 1, children: renderFiller(availableHeight - hSum) })
|
|
3895
4484
|
] }),
|
|
3896
|
-
showOverflowIndicators && /* @__PURE__ */
|
|
4485
|
+
showOverflowIndicators && /* @__PURE__ */ jsx7(Box2, { height: 1, children: bottomIndicator(overflowBottom) })
|
|
3897
4486
|
] });
|
|
3898
4487
|
};
|
|
3899
4488
|
var VirtualList = forwardRef(VirtualListInner);
|
|
3900
4489
|
|
|
3901
4490
|
// ink/components/ActionsView.tsx
|
|
3902
|
-
import { jsx as
|
|
4491
|
+
import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3903
4492
|
function ActionsView({ height, isFocused }) {
|
|
3904
4493
|
const { actions } = useActions();
|
|
3905
|
-
const [selectedIndex, setSelectedIndex] =
|
|
4494
|
+
const [selectedIndex, setSelectedIndex] = useState8(0);
|
|
3906
4495
|
const size = useTerminalSize();
|
|
3907
4496
|
const timelineWidth = Math.floor(size.columns * 0.65);
|
|
3908
4497
|
const statusWidth = size.columns - timelineWidth;
|
|
@@ -3935,8 +4524,8 @@ function ActionsView({ height, isFocused }) {
|
|
|
3935
4524
|
}
|
|
3936
4525
|
}
|
|
3937
4526
|
});
|
|
3938
|
-
const renderOverflowTop =
|
|
3939
|
-
/* @__PURE__ */
|
|
4527
|
+
const renderOverflowTop = useCallback3((count) => /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
4528
|
+
/* @__PURE__ */ jsx8(Text3, { color: "gray", dimColor: true, children: "\u2502" }),
|
|
3940
4529
|
count > 0 && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
3941
4530
|
" ",
|
|
3942
4531
|
"\u25B2 ",
|
|
@@ -3944,8 +4533,8 @@ function ActionsView({ height, isFocused }) {
|
|
|
3944
4533
|
" more"
|
|
3945
4534
|
] })
|
|
3946
4535
|
] }), []);
|
|
3947
|
-
const renderOverflowBottom =
|
|
3948
|
-
/* @__PURE__ */
|
|
4536
|
+
const renderOverflowBottom = useCallback3((count) => /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
4537
|
+
/* @__PURE__ */ jsx8(Text3, { color: "gray", dimColor: true, children: "\u2502" }),
|
|
3949
4538
|
count > 0 && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
3950
4539
|
" ",
|
|
3951
4540
|
"\u25BC ",
|
|
@@ -3953,15 +4542,15 @@ function ActionsView({ height, isFocused }) {
|
|
|
3953
4542
|
" more"
|
|
3954
4543
|
] })
|
|
3955
4544
|
] }), []);
|
|
3956
|
-
const renderFiller =
|
|
3957
|
-
const getItemHeight =
|
|
4545
|
+
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)) }), []);
|
|
4546
|
+
const getItemHeight = useCallback3((_item) => 1, []);
|
|
3958
4547
|
if (actions.length === 0) {
|
|
3959
4548
|
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", flexGrow: 1, height, children: [
|
|
3960
|
-
/* @__PURE__ */
|
|
3961
|
-
Array.from({ length: height - 1 }).map((_, i) => /* @__PURE__ */
|
|
4549
|
+
/* @__PURE__ */ jsx8(Box3, { children: /* @__PURE__ */ jsx8(Text3, { italic: true, color: "gray", children: "No actions yet." }) }),
|
|
4550
|
+
Array.from({ length: height - 1 }).map((_, i) => /* @__PURE__ */ jsx8(Box3, { children: /* @__PURE__ */ jsx8(Text3, {}) }, i))
|
|
3962
4551
|
] });
|
|
3963
4552
|
}
|
|
3964
|
-
return /* @__PURE__ */
|
|
4553
|
+
return /* @__PURE__ */ jsx8(Box3, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx8(
|
|
3965
4554
|
VirtualList,
|
|
3966
4555
|
{
|
|
3967
4556
|
items: actions,
|
|
@@ -3971,7 +4560,7 @@ function ActionsView({ height, isFocused }) {
|
|
|
3971
4560
|
renderOverflowTop,
|
|
3972
4561
|
renderOverflowBottom,
|
|
3973
4562
|
renderFiller,
|
|
3974
|
-
renderItem: ({ item, isSelected }) => /* @__PURE__ */
|
|
4563
|
+
renderItem: ({ item, isSelected }) => /* @__PURE__ */ jsx8(ActionItemRow, { item, isSelected, isFocused, width: statusWidth - 2 })
|
|
3975
4564
|
}
|
|
3976
4565
|
) });
|
|
3977
4566
|
}
|
|
@@ -3981,11 +4570,11 @@ import { Box as Box4, Text as Text4 } from "ink";
|
|
|
3981
4570
|
import "react";
|
|
3982
4571
|
|
|
3983
4572
|
// ink/contexts/CostContext.tsx
|
|
3984
|
-
import { createContext as
|
|
3985
|
-
import { jsx as
|
|
3986
|
-
var CostContext =
|
|
4573
|
+
import { createContext as createContext6, useContext as useContext6, useMemo as useMemo7, useState as useState9 } from "react";
|
|
4574
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
4575
|
+
var CostContext = createContext6(void 0);
|
|
3987
4576
|
var useCost = () => {
|
|
3988
|
-
const context =
|
|
4577
|
+
const context = useContext6(CostContext);
|
|
3989
4578
|
if (!context) {
|
|
3990
4579
|
throw new Error("useCost must be used within a CostProvider");
|
|
3991
4580
|
}
|
|
@@ -3994,7 +4583,7 @@ var useCost = () => {
|
|
|
3994
4583
|
var CostProvider = ({
|
|
3995
4584
|
children
|
|
3996
4585
|
}) => {
|
|
3997
|
-
const [cost, setCost] =
|
|
4586
|
+
const [cost, setCost] = useState9({
|
|
3998
4587
|
totalCost: 0,
|
|
3999
4588
|
inputTokens: 0,
|
|
4000
4589
|
outputTokens: 0,
|
|
@@ -4004,37 +4593,37 @@ var CostProvider = ({
|
|
|
4004
4593
|
useListener("UpdateCost", (newCost) => {
|
|
4005
4594
|
setCost(newCost);
|
|
4006
4595
|
}, []);
|
|
4007
|
-
const value =
|
|
4596
|
+
const value = useMemo7(() => ({
|
|
4008
4597
|
cost
|
|
4009
4598
|
}), [cost]);
|
|
4010
|
-
return /* @__PURE__ */
|
|
4599
|
+
return /* @__PURE__ */ jsx9(CostContext.Provider, { value, children });
|
|
4011
4600
|
};
|
|
4012
4601
|
|
|
4013
4602
|
// ink/components/CostView.tsx
|
|
4014
|
-
import { jsx as
|
|
4603
|
+
import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
4015
4604
|
function CostView({ isDense = false }) {
|
|
4016
4605
|
const { cost } = useCost();
|
|
4017
4606
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingLeft: 1, paddingTop: isDense ? 0 : 1, children: [
|
|
4018
|
-
/* @__PURE__ */
|
|
4607
|
+
/* @__PURE__ */ jsx10(Box4, { marginBottom: isDense ? 0 : 1, children: /* @__PURE__ */ jsx10(Text4, { bold: true, underline: true, color: "cyan", children: "Session Cost Breakdown" }) }),
|
|
4019
4608
|
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
4020
|
-
/* @__PURE__ */
|
|
4609
|
+
/* @__PURE__ */ jsx10(Box4, { width: 20, children: /* @__PURE__ */ jsx10(Text4, { children: "Total Cost:" }) }),
|
|
4021
4610
|
/* @__PURE__ */ jsxs4(Text4, { color: "green", children: [
|
|
4022
4611
|
"$",
|
|
4023
4612
|
cost.totalCost.toFixed(4)
|
|
4024
4613
|
] }),
|
|
4025
|
-
cost.hasUnknownPrices && /* @__PURE__ */
|
|
4614
|
+
cost.hasUnknownPrices && /* @__PURE__ */ jsx10(Text4, { color: "yellow", children: "(est.)" })
|
|
4026
4615
|
] }),
|
|
4027
4616
|
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
4028
|
-
/* @__PURE__ */
|
|
4029
|
-
/* @__PURE__ */
|
|
4617
|
+
/* @__PURE__ */ jsx10(Box4, { width: 20, children: /* @__PURE__ */ jsx10(Text4, { children: "Input Tokens:" }) }),
|
|
4618
|
+
/* @__PURE__ */ jsx10(Text4, { children: cost.inputTokens.toLocaleString() })
|
|
4030
4619
|
] }),
|
|
4031
4620
|
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
4032
|
-
/* @__PURE__ */
|
|
4033
|
-
/* @__PURE__ */
|
|
4621
|
+
/* @__PURE__ */ jsx10(Box4, { width: 20, children: /* @__PURE__ */ jsx10(Text4, { children: "Cached Tokens:" }) }),
|
|
4622
|
+
/* @__PURE__ */ jsx10(Text4, { color: "gray", children: cost.cachedInputTokens.toLocaleString() })
|
|
4034
4623
|
] }),
|
|
4035
4624
|
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
4036
|
-
/* @__PURE__ */
|
|
4037
|
-
/* @__PURE__ */
|
|
4625
|
+
/* @__PURE__ */ jsx10(Box4, { width: 20, children: /* @__PURE__ */ jsx10(Text4, { children: "Output Tokens:" }) }),
|
|
4626
|
+
/* @__PURE__ */ jsx10(Text4, { children: cost.outputTokens.toLocaleString() })
|
|
4038
4627
|
] })
|
|
4039
4628
|
] });
|
|
4040
4629
|
}
|
|
@@ -4042,7 +4631,7 @@ function CostView({ isDense = false }) {
|
|
|
4042
4631
|
// ink/components/MessageLineItem.tsx
|
|
4043
4632
|
import { Box as Box5, Text as Text5 } from "ink";
|
|
4044
4633
|
import { memo as memo2 } from "react";
|
|
4045
|
-
import { Fragment, jsx as
|
|
4634
|
+
import { Fragment, jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
4046
4635
|
var MessageLineItem = memo2(
|
|
4047
4636
|
({
|
|
4048
4637
|
item,
|
|
@@ -4054,22 +4643,23 @@ var MessageLineItem = memo2(
|
|
|
4054
4643
|
const label = item.type === "interrupted" ? "" : item.type.toUpperCase();
|
|
4055
4644
|
const selectionColor = isFocused ? "cyan" : "gray";
|
|
4056
4645
|
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
4057
|
-
/* @__PURE__ */
|
|
4058
|
-
/* @__PURE__ */
|
|
4646
|
+
/* @__PURE__ */ jsx11(Text5, { color: isSelected ? selectionColor : "gray", dimColor: !isSelected, bold: isSelected, children: isSelected ? "\u2503 " : "\u2502 " }),
|
|
4647
|
+
/* @__PURE__ */ jsx11(Box5, { paddingLeft: indent, children: item.isFirstLine ? /* @__PURE__ */ jsxs5(Text5, { children: [
|
|
4059
4648
|
label && /* @__PURE__ */ jsxs5(Text5, { bold: true, color, children: [
|
|
4060
4649
|
label,
|
|
4061
4650
|
item.type === "tool" ? ": " : item.isArgsLine ? " args: " : ": "
|
|
4062
4651
|
] }),
|
|
4063
|
-
item.type === "tool" && item.toolName ? /* @__PURE__ */
|
|
4064
|
-
/* @__PURE__ */
|
|
4065
|
-
/* @__PURE__ */
|
|
4066
|
-
] }) : /* @__PURE__ */
|
|
4067
|
-
] }) : /* @__PURE__ */
|
|
4652
|
+
item.type === "tool" && item.toolName ? /* @__PURE__ */ jsx11(Text5, { children: item.text.startsWith(item.toolName) ? /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
4653
|
+
/* @__PURE__ */ jsx11(Text5, { color: "white", bold: true, children: item.toolName }),
|
|
4654
|
+
/* @__PURE__ */ jsx11(Text5, { dimColor: true, italic: true, children: item.text.slice(item.toolName.length) })
|
|
4655
|
+
] }) : /* @__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 })
|
|
4656
|
+
] }) : /* @__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 }) }) })
|
|
4068
4657
|
] });
|
|
4069
4658
|
}
|
|
4070
4659
|
);
|
|
4071
4660
|
function messageTypeToColor(type) {
|
|
4072
4661
|
switch (type) {
|
|
4662
|
+
case "prompt":
|
|
4073
4663
|
case "user":
|
|
4074
4664
|
return "green";
|
|
4075
4665
|
case "agent":
|
|
@@ -4085,51 +4675,12 @@ function messageTypeToColor(type) {
|
|
|
4085
4675
|
import { ProgressBar } from "@inkjs/ui";
|
|
4086
4676
|
import { Box as Box6, Text as Text6 } from "ink";
|
|
4087
4677
|
import "react";
|
|
4088
|
-
|
|
4089
|
-
// ink/contexts/PlanContext.tsx
|
|
4090
|
-
import { createContext as createContext5, useContext as useContext5, useMemo as useMemo6, useState as useState8 } from "react";
|
|
4091
|
-
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
4092
|
-
var PlanContext = createContext5(globalPlanContext);
|
|
4093
|
-
var usePlan = () => {
|
|
4094
|
-
const context = useContext5(PlanContext);
|
|
4095
|
-
if (!context) {
|
|
4096
|
-
throw new Error("usePlan must be used within a PlanProvider");
|
|
4097
|
-
}
|
|
4098
|
-
return context;
|
|
4099
|
-
};
|
|
4100
|
-
var PlanProvider = ({
|
|
4101
|
-
children
|
|
4102
|
-
}) => {
|
|
4103
|
-
const [planDescription, setPlanDescription] = useState8(globalPlanContext.planDescription);
|
|
4104
|
-
const [planItems, setPlanItems] = useState8(globalPlanContext.planItems);
|
|
4105
|
-
const [progress, setProgress] = useState8(globalPlanContext.progress);
|
|
4106
|
-
useListener("SetPlanDescription", (newGoal) => {
|
|
4107
|
-
globalPlanContext.planDescription = newGoal;
|
|
4108
|
-
setPlanDescription(newGoal);
|
|
4109
|
-
}, []);
|
|
4110
|
-
useListener("SetPlanItems", (planItems2) => {
|
|
4111
|
-
globalPlanContext.planItems = planItems2;
|
|
4112
|
-
const completedCount = planItems2.filter((item) => item.status === "done" || item.status === "not-needed").length;
|
|
4113
|
-
const progress2 = planItems2.length === 0 ? 0 : Math.round(completedCount / planItems2.length * 100);
|
|
4114
|
-
globalPlanContext.progress = progress2;
|
|
4115
|
-
setPlanItems(planItems2);
|
|
4116
|
-
setProgress(progress2);
|
|
4117
|
-
}, []);
|
|
4118
|
-
const value = useMemo6(() => ({
|
|
4119
|
-
progress,
|
|
4120
|
-
planDescription,
|
|
4121
|
-
planItems
|
|
4122
|
-
}), [progress, planDescription, planItems]);
|
|
4123
|
-
return /* @__PURE__ */ jsx10(PlanContext.Provider, { value, children });
|
|
4124
|
-
};
|
|
4125
|
-
|
|
4126
|
-
// ink/components/PlanView.tsx
|
|
4127
|
-
import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
4678
|
+
import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
4128
4679
|
function PlanView() {
|
|
4129
4680
|
const { planDescription, planItems, progress } = usePlan();
|
|
4130
4681
|
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", flexGrow: 1, children: [
|
|
4131
|
-
/* @__PURE__ */
|
|
4132
|
-
/* @__PURE__ */
|
|
4682
|
+
/* @__PURE__ */ jsx12(Box6, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx12(Text6, { italic: true, children: planDescription }) }),
|
|
4683
|
+
/* @__PURE__ */ jsx12(Box6, { flexDirection: "column", flexGrow: 1, children: planItems.map((planItem) => /* @__PURE__ */ jsx12(Box6, { children: /* @__PURE__ */ jsxs6(
|
|
4133
4684
|
Text6,
|
|
4134
4685
|
{
|
|
4135
4686
|
color: planItem.status === "done" ? "green" : planItem.status === "in-progress" ? "yellow" : planItem.status === "not-needed" ? "gray" : "white",
|
|
@@ -4146,22 +4697,22 @@ function PlanView() {
|
|
|
4146
4697
|
progress,
|
|
4147
4698
|
"%"
|
|
4148
4699
|
] }),
|
|
4149
|
-
/* @__PURE__ */
|
|
4700
|
+
/* @__PURE__ */ jsx12(ProgressBar, { value: progress })
|
|
4150
4701
|
] })
|
|
4151
4702
|
] });
|
|
4152
4703
|
}
|
|
4153
4704
|
|
|
4154
4705
|
// ink/components/SettingsView.tsx
|
|
4155
4706
|
import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
|
|
4156
|
-
import
|
|
4157
|
-
import { useEffect as useEffect6, useMemo as
|
|
4707
|
+
import path8 from "path";
|
|
4708
|
+
import { useEffect as useEffect6, useMemo as useMemo9, useState as useState11 } from "react";
|
|
4158
4709
|
|
|
4159
4710
|
// ink/contexts/SettingsContext.tsx
|
|
4160
|
-
import { createContext as
|
|
4161
|
-
import { jsx as
|
|
4162
|
-
var SettingsContext =
|
|
4711
|
+
import { createContext as createContext7, useContext as useContext7, useMemo as useMemo8, useState as useState10 } from "react";
|
|
4712
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
4713
|
+
var SettingsContext = createContext7(void 0);
|
|
4163
4714
|
var useSettings = () => {
|
|
4164
|
-
const context =
|
|
4715
|
+
const context = useContext7(SettingsContext);
|
|
4165
4716
|
if (!context) {
|
|
4166
4717
|
throw new Error("useSettings must be used within a SettingsProvider");
|
|
4167
4718
|
}
|
|
@@ -4170,17 +4721,18 @@ var useSettings = () => {
|
|
|
4170
4721
|
var SettingsProvider = ({
|
|
4171
4722
|
children
|
|
4172
4723
|
}) => {
|
|
4173
|
-
const [version, setVersion] =
|
|
4724
|
+
const [version, setVersion] = useState10(0);
|
|
4174
4725
|
useListener("SettingsUpdated", () => {
|
|
4175
4726
|
setVersion((v) => v + 1);
|
|
4176
4727
|
}, []);
|
|
4177
|
-
const value =
|
|
4728
|
+
const value = useMemo8(() => ({
|
|
4178
4729
|
version,
|
|
4179
4730
|
model: trackedState.model,
|
|
4180
4731
|
compactionModel: trackedState.compactionModel,
|
|
4181
4732
|
sessionPath: trackedState.sessionPath,
|
|
4182
4733
|
cwd: trackedState.cwd,
|
|
4183
4734
|
useFlexTier: trackedState.useFlexTier,
|
|
4735
|
+
currentTurn: trackedState.currentTurn,
|
|
4184
4736
|
maxTurns: trackedState.maxTurns,
|
|
4185
4737
|
maxCost: trackedState.maxCost,
|
|
4186
4738
|
autoApproveCodeInterpreter: trackedState.autoApproveCodeInterpreter,
|
|
@@ -4190,11 +4742,11 @@ var SettingsProvider = ({
|
|
|
4190
4742
|
rateLimitThreshold: trackedState.rateLimitThreshold,
|
|
4191
4743
|
rateLimitStatus: rateLimitTracker.getStatus()
|
|
4192
4744
|
}), [version]);
|
|
4193
|
-
return /* @__PURE__ */
|
|
4745
|
+
return /* @__PURE__ */ jsx13(SettingsContext.Provider, { value, children });
|
|
4194
4746
|
};
|
|
4195
4747
|
|
|
4196
4748
|
// ink/components/SettingsView.tsx
|
|
4197
|
-
import { jsx as
|
|
4749
|
+
import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
4198
4750
|
function SettingsView({ isDense = false }) {
|
|
4199
4751
|
const {
|
|
4200
4752
|
model,
|
|
@@ -4202,6 +4754,7 @@ function SettingsView({ isDense = false }) {
|
|
|
4202
4754
|
sessionPath,
|
|
4203
4755
|
cwd,
|
|
4204
4756
|
useFlexTier,
|
|
4757
|
+
currentTurn,
|
|
4205
4758
|
maxTurns,
|
|
4206
4759
|
maxCost,
|
|
4207
4760
|
autoApproveCodeInterpreter: initialAutoApproveCodeInterpreter,
|
|
@@ -4212,10 +4765,10 @@ function SettingsView({ isDense = false }) {
|
|
|
4212
4765
|
rateLimitStatus
|
|
4213
4766
|
} = useSettings();
|
|
4214
4767
|
const { focusedArea } = useChat();
|
|
4215
|
-
const [autoApproveCodeInterpreter, setAutoApproveCodeInterpreter] =
|
|
4216
|
-
const [autoApprovePatches, setAutoApprovePatches] =
|
|
4217
|
-
const [autoApproveShell, setAutoApproveShell] =
|
|
4218
|
-
const [monitorRateLimits, setMonitorRateLimits] =
|
|
4768
|
+
const [autoApproveCodeInterpreter, setAutoApproveCodeInterpreter] = useState11(initialAutoApproveCodeInterpreter);
|
|
4769
|
+
const [autoApprovePatches, setAutoApprovePatches] = useState11(initialAutoApprovePatches);
|
|
4770
|
+
const [autoApproveShell, setAutoApproveShell] = useState11(initialAutoApproveShell);
|
|
4771
|
+
const [monitorRateLimits, setMonitorRateLimits] = useState11(initialMonitorRateLimits);
|
|
4219
4772
|
useEffect6(() => {
|
|
4220
4773
|
setAutoApproveCodeInterpreter(initialAutoApproveCodeInterpreter);
|
|
4221
4774
|
}, [initialAutoApproveCodeInterpreter]);
|
|
@@ -4228,8 +4781,8 @@ function SettingsView({ isDense = false }) {
|
|
|
4228
4781
|
useEffect6(() => {
|
|
4229
4782
|
setMonitorRateLimits(initialMonitorRateLimits);
|
|
4230
4783
|
}, [initialMonitorRateLimits]);
|
|
4231
|
-
const [selectedIndex, setSelectedIndex] =
|
|
4232
|
-
const selectableOptions =
|
|
4784
|
+
const [selectedIndex, setSelectedIndex] = useState11(0);
|
|
4785
|
+
const selectableOptions = useMemo9(() => [
|
|
4233
4786
|
{
|
|
4234
4787
|
label: "Code Interpreter",
|
|
4235
4788
|
value: autoApproveCodeInterpreter,
|
|
@@ -4292,83 +4845,95 @@ function SettingsView({ isDense = false }) {
|
|
|
4292
4845
|
}
|
|
4293
4846
|
}
|
|
4294
4847
|
});
|
|
4295
|
-
const displayPath =
|
|
4848
|
+
const displayPath = useMemo9(() => {
|
|
4296
4849
|
if (!sessionPath) {
|
|
4297
4850
|
return null;
|
|
4298
4851
|
}
|
|
4299
|
-
const relative =
|
|
4300
|
-
if (!relative.startsWith("..") && !
|
|
4852
|
+
const relative = path8.relative(cwd, sessionPath);
|
|
4853
|
+
if (!relative.startsWith("..") && !path8.isAbsolute(relative)) {
|
|
4301
4854
|
return `./${relative}`;
|
|
4302
4855
|
}
|
|
4303
4856
|
return sessionPath;
|
|
4304
4857
|
}, [cwd, sessionPath]);
|
|
4305
4858
|
const marginBottom = isDense ? 0 : 1;
|
|
4306
4859
|
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", flexGrow: 1, paddingLeft: 1, children: [
|
|
4307
|
-
/* @__PURE__ */
|
|
4860
|
+
/* @__PURE__ */ jsx14(Box7, { marginBottom, children: /* @__PURE__ */ jsx14(Text7, { bold: true, underline: true, color: "cyan", children: "Configuration" }) }),
|
|
4308
4861
|
/* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
4309
|
-
/* @__PURE__ */
|
|
4310
|
-
/* @__PURE__ */
|
|
4862
|
+
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "Model:" }) }),
|
|
4863
|
+
/* @__PURE__ */ jsx14(Text7, { children: model })
|
|
4311
4864
|
] }),
|
|
4312
4865
|
/* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
4313
|
-
/* @__PURE__ */
|
|
4314
|
-
/* @__PURE__ */
|
|
4866
|
+
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "Compaction Model:" }) }),
|
|
4867
|
+
/* @__PURE__ */ jsx14(Text7, { children: compactionModel })
|
|
4315
4868
|
] }),
|
|
4316
4869
|
displayPath && /* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
4317
|
-
/* @__PURE__ */
|
|
4318
|
-
/* @__PURE__ */
|
|
4870
|
+
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "Session Path:" }) }),
|
|
4871
|
+
/* @__PURE__ */ jsx14(Text7, { children: displayPath })
|
|
4319
4872
|
] }),
|
|
4320
4873
|
/* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
4321
|
-
/* @__PURE__ */
|
|
4322
|
-
/* @__PURE__ */
|
|
4874
|
+
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "Flex Tier:" }) }),
|
|
4875
|
+
/* @__PURE__ */ jsx14(Text7, { children: useFlexTier ? "Yes" : "No" })
|
|
4323
4876
|
] }),
|
|
4324
4877
|
/* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
4325
|
-
/* @__PURE__ */
|
|
4326
|
-
/* @__PURE__ */
|
|
4878
|
+
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "Current Turn:" }) }),
|
|
4879
|
+
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
4880
|
+
currentTurn,
|
|
4881
|
+
" / ",
|
|
4882
|
+
maxTurns
|
|
4883
|
+
] })
|
|
4327
4884
|
] }),
|
|
4328
4885
|
/* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
4329
|
-
/* @__PURE__ */
|
|
4886
|
+
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "Rate Limit @:" }) }),
|
|
4330
4887
|
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
4331
4888
|
initialRateLimitThreshold,
|
|
4332
4889
|
"%"
|
|
4333
4890
|
] })
|
|
4334
4891
|
] }),
|
|
4335
|
-
rateLimitStatus &&
|
|
4336
|
-
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
4337
|
-
/* @__PURE__ */
|
|
4892
|
+
rateLimitStatus && /* @__PURE__ */ jsxs7(Box7, { marginBottom, flexDirection: "column", children: [
|
|
4893
|
+
rateLimitStatus.retryAfter !== null && /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
4894
|
+
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { color: "red", children: "Retry After:" }) }),
|
|
4895
|
+
/* @__PURE__ */ jsxs7(Text7, { color: "red", children: [
|
|
4896
|
+
rateLimitStatus.retryAfter,
|
|
4897
|
+
"s"
|
|
4898
|
+
] })
|
|
4899
|
+
] }),
|
|
4900
|
+
(rateLimitStatus.limitRequests !== null || rateLimitStatus.remainingRequests !== null) && /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
4901
|
+
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "RPM Limit:" }) }),
|
|
4338
4902
|
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
4339
|
-
rateLimitStatus.remainingRequests,
|
|
4903
|
+
rateLimitStatus.remainingRequests ?? "?",
|
|
4340
4904
|
" / ",
|
|
4341
|
-
rateLimitStatus.limitRequests,
|
|
4905
|
+
rateLimitStatus.limitRequests ?? "?",
|
|
4342
4906
|
" (Reset:",
|
|
4343
4907
|
" ",
|
|
4344
|
-
rateLimitStatus.resetRequests,
|
|
4908
|
+
rateLimitStatus.resetRequests ?? "?",
|
|
4345
4909
|
")"
|
|
4346
4910
|
] })
|
|
4347
4911
|
] }),
|
|
4348
|
-
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
4349
|
-
/* @__PURE__ */
|
|
4912
|
+
rateLimitStatus.remainingTokens !== null && /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
4913
|
+
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "TPM Limit:" }) }),
|
|
4350
4914
|
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
4351
|
-
rateLimitStatus.remainingTokens,
|
|
4915
|
+
rateLimitStatus.remainingTokens ?? "?",
|
|
4352
4916
|
" / ",
|
|
4353
|
-
rateLimitStatus.limitTokens,
|
|
4354
|
-
" (Reset:
|
|
4355
|
-
|
|
4917
|
+
rateLimitStatus.limitTokens ?? "?",
|
|
4918
|
+
" (Reset:",
|
|
4919
|
+
" ",
|
|
4920
|
+
rateLimitStatus.resetTokens ?? "?",
|
|
4356
4921
|
")"
|
|
4357
4922
|
] })
|
|
4358
4923
|
] })
|
|
4359
4924
|
] }),
|
|
4360
4925
|
maxCost !== null && /* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
4361
|
-
/* @__PURE__ */
|
|
4926
|
+
/* @__PURE__ */ jsx14(Box7, { width: 20, children: /* @__PURE__ */ jsx14(Text7, { children: "Max Cost:" }) }),
|
|
4362
4927
|
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
4363
4928
|
"$",
|
|
4364
4929
|
maxCost.toFixed(2)
|
|
4365
4930
|
] })
|
|
4366
4931
|
] }),
|
|
4367
4932
|
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
4368
|
-
/* @__PURE__ */
|
|
4933
|
+
/* @__PURE__ */ jsx14(Text7, { bold: true, children: "Auto-approvals (up/down & space to toggle):" }),
|
|
4369
4934
|
selectableOptions.map((option, index) => {
|
|
4370
4935
|
const isSelected = index === selectedIndex && focusedArea === "status";
|
|
4371
|
-
return /* @__PURE__ */
|
|
4936
|
+
return /* @__PURE__ */ jsx14(Box7, { children: /* @__PURE__ */ jsxs7(Text7, { color: isSelected ? "cyan" : "white", children: [
|
|
4372
4937
|
isSelected ? "> " : " ",
|
|
4373
4938
|
option.label,
|
|
4374
4939
|
"isAction" in option ? "" : `: ${option.value ? "ON" : "OFF"}`
|
|
@@ -4380,13 +4945,13 @@ function SettingsView({ isDense = false }) {
|
|
|
4380
4945
|
|
|
4381
4946
|
// ink/components/UserInput.tsx
|
|
4382
4947
|
import { Box as Box9, Text as Text9 } from "ink";
|
|
4383
|
-
import { useCallback as
|
|
4948
|
+
import { useCallback as useCallback4, useState as useState13 } from "react";
|
|
4384
4949
|
|
|
4385
4950
|
// ink/components/BlinkingTextInput.tsx
|
|
4386
4951
|
import chalk3 from "chalk";
|
|
4387
4952
|
import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
|
|
4388
|
-
import { useEffect as useEffect7, useMemo as
|
|
4389
|
-
import { jsx as
|
|
4953
|
+
import { useEffect as useEffect7, useMemo as useMemo10, useReducer, useState as useState12 } from "react";
|
|
4954
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
4390
4955
|
var reducer = (state, action) => {
|
|
4391
4956
|
switch (action.type) {
|
|
4392
4957
|
case "move-cursor-left":
|
|
@@ -4439,7 +5004,7 @@ function BlinkingTextInput({
|
|
|
4439
5004
|
previousValue: defaultValue,
|
|
4440
5005
|
cursorOffset: defaultValue.length
|
|
4441
5006
|
});
|
|
4442
|
-
const [isCursorVisible, setIsCursorVisible] =
|
|
5007
|
+
const [isCursorVisible, setIsCursorVisible] = useState12(true);
|
|
4443
5008
|
useEffect7(() => {
|
|
4444
5009
|
if (isDisabled) {
|
|
4445
5010
|
setIsCursorVisible(false);
|
|
@@ -4455,7 +5020,7 @@ function BlinkingTextInput({
|
|
|
4455
5020
|
onChange?.(state.value);
|
|
4456
5021
|
}
|
|
4457
5022
|
}, [state.value, state.previousValue, onChange]);
|
|
4458
|
-
const suggestion =
|
|
5023
|
+
const suggestion = useMemo10(() => {
|
|
4459
5024
|
if (state.value.length === 0) {
|
|
4460
5025
|
return "";
|
|
4461
5026
|
}
|
|
@@ -4491,10 +5056,10 @@ function BlinkingTextInput({
|
|
|
4491
5056
|
},
|
|
4492
5057
|
{ isActive: !isDisabled }
|
|
4493
5058
|
);
|
|
4494
|
-
const renderedValue =
|
|
5059
|
+
const renderedValue = useMemo10(() => {
|
|
4495
5060
|
if (isDisabled) {
|
|
4496
5061
|
const lines2 = (state.value || (placeholder ? chalk3.dim(placeholder) : "")).split("\n");
|
|
4497
|
-
return /* @__PURE__ */
|
|
5062
|
+
return /* @__PURE__ */ jsx15(Box8, { flexDirection: "column", children: lines2.map((line, i) => /* @__PURE__ */ jsx15(Box8, { children: /* @__PURE__ */ jsx15(Text8, { children: line }) }, i)) });
|
|
4498
5063
|
}
|
|
4499
5064
|
if (state.value.length === 0) {
|
|
4500
5065
|
let displayContent = "";
|
|
@@ -4503,7 +5068,7 @@ function BlinkingTextInput({
|
|
|
4503
5068
|
} else {
|
|
4504
5069
|
displayContent = isCursorVisible ? chalk3.inverse(" ") : " ";
|
|
4505
5070
|
}
|
|
4506
|
-
return /* @__PURE__ */
|
|
5071
|
+
return /* @__PURE__ */ jsx15(Box8, { flexGrow: 1, minWidth: 1, children: /* @__PURE__ */ jsx15(Text8, { wrap: "end", children: displayContent }) });
|
|
4507
5072
|
}
|
|
4508
5073
|
const lines = state.value.split("\n");
|
|
4509
5074
|
const result = [];
|
|
@@ -4529,19 +5094,19 @@ function BlinkingTextInput({
|
|
|
4529
5094
|
lineContent += chalk3.dim(suggestion);
|
|
4530
5095
|
}
|
|
4531
5096
|
result.push(
|
|
4532
|
-
/* @__PURE__ */
|
|
5097
|
+
/* @__PURE__ */ jsx15(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx15(Text8, { wrap: "end", children: lineContent }) }, lineIndex)
|
|
4533
5098
|
);
|
|
4534
5099
|
if (lineIndex < lines.length - 1) {
|
|
4535
5100
|
totalOffset++;
|
|
4536
5101
|
}
|
|
4537
5102
|
});
|
|
4538
|
-
return /* @__PURE__ */
|
|
5103
|
+
return /* @__PURE__ */ jsx15(Box8, { flexDirection: "column", flexGrow: 1, children: result });
|
|
4539
5104
|
}, [state.value, state.cursorOffset, suggestion, isCursorVisible, isDisabled, placeholder]);
|
|
4540
|
-
return /* @__PURE__ */
|
|
5105
|
+
return /* @__PURE__ */ jsx15(Box8, { flexGrow: 1, minWidth: 1, children: renderedValue });
|
|
4541
5106
|
}
|
|
4542
5107
|
|
|
4543
5108
|
// ink/components/UserInput.tsx
|
|
4544
|
-
import { jsx as
|
|
5109
|
+
import { jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
4545
5110
|
var modeSuggestion = {
|
|
4546
5111
|
approved: [],
|
|
4547
5112
|
approving: ["yes", "approve", "no", "deny"],
|
|
@@ -4550,14 +5115,14 @@ var modeSuggestion = {
|
|
|
4550
5115
|
};
|
|
4551
5116
|
function UserInput() {
|
|
4552
5117
|
const { userInputMode, focusedArea } = useChat();
|
|
4553
|
-
const [resetKey, setResetKey] =
|
|
4554
|
-
const [, setBlankLines] =
|
|
5118
|
+
const [resetKey, setResetKey] = useState13(0);
|
|
5119
|
+
const [, setBlankLines] = useState13(0);
|
|
4555
5120
|
useListener("ClearUserInput", () => {
|
|
4556
5121
|
setResetKey((prev) => prev + 1);
|
|
4557
5122
|
}, []);
|
|
4558
5123
|
const borderColor = focusedArea === "input" ? "cyan" : "gray";
|
|
4559
5124
|
const placeholder = calculatePlaceholder(userInputMode);
|
|
4560
|
-
const onSubmitResetKey =
|
|
5125
|
+
const onSubmitResetKey = useCallback4((value) => {
|
|
4561
5126
|
if (value.length) {
|
|
4562
5127
|
setResetKey((prev) => prev + 1);
|
|
4563
5128
|
emitToListeners("PushNewMessages", [{ type: "user", text: value.trim(), version: 1 }]);
|
|
@@ -4583,8 +5148,8 @@ function UserInput() {
|
|
|
4583
5148
|
alignItems: "flex-start",
|
|
4584
5149
|
gap: 1,
|
|
4585
5150
|
children: [
|
|
4586
|
-
/* @__PURE__ */
|
|
4587
|
-
/* @__PURE__ */
|
|
5151
|
+
/* @__PURE__ */ jsx16(Box9, { marginLeft: 1, height: 1, children: /* @__PURE__ */ jsx16(Text9, { bold: true, color: borderColor, children: "\u276F" }) }),
|
|
5152
|
+
/* @__PURE__ */ jsx16(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx16(
|
|
4588
5153
|
BlinkingTextInput,
|
|
4589
5154
|
{
|
|
4590
5155
|
placeholder,
|
|
@@ -4613,13 +5178,24 @@ function calculatePlaceholder(mode) {
|
|
|
4613
5178
|
}
|
|
4614
5179
|
|
|
4615
5180
|
// ink/components/ChatContent.tsx
|
|
4616
|
-
import { jsx as
|
|
5181
|
+
import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
4617
5182
|
function ChatContent() {
|
|
4618
|
-
const { messages, isThinking, focusedArea, setFocusedArea } = useChat();
|
|
5183
|
+
const { messages, isThinking, isCompacting, focusedArea, setFocusedArea } = useChat();
|
|
5184
|
+
const { payload } = useApproval();
|
|
4619
5185
|
const size = useTerminalSize();
|
|
4620
5186
|
useMessageListener();
|
|
4621
|
-
const [activeTab, setActiveTab] =
|
|
4622
|
-
const [
|
|
5187
|
+
const [activeTab, setActiveTab] = useState14("settings");
|
|
5188
|
+
const [userHasSwitchedTab, setUserHasSwitchedTab] = useState14(false);
|
|
5189
|
+
const { planDescription, planItems } = usePlan();
|
|
5190
|
+
useEffect8(() => {
|
|
5191
|
+
if (!userHasSwitchedTab && activeTab === "settings") {
|
|
5192
|
+
const hasPlan = planDescription.trim().length > 0 || planItems.length > 0;
|
|
5193
|
+
if (hasPlan) {
|
|
5194
|
+
setActiveTab("planDescription");
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
}, [planDescription, planItems, userHasSwitchedTab, activeTab]);
|
|
5198
|
+
const [selectedIndex, setSelectedIndex] = useState14(0);
|
|
4623
5199
|
const wrapCache = useRef2(
|
|
4624
5200
|
/* @__PURE__ */ new Map()
|
|
4625
5201
|
);
|
|
@@ -4663,27 +5239,34 @@ function ChatContent() {
|
|
|
4663
5239
|
const currentIndex = tabNames.indexOf(activeTab);
|
|
4664
5240
|
if (key.leftArrow) {
|
|
4665
5241
|
const nextIndex = (currentIndex - 1 + tabNames.length) % tabNames.length;
|
|
4666
|
-
|
|
5242
|
+
const newTab = tabNames[nextIndex];
|
|
5243
|
+
setActiveTab(newTab);
|
|
5244
|
+
setUserHasSwitchedTab(true);
|
|
4667
5245
|
} else {
|
|
4668
5246
|
const nextIndex = (currentIndex + 1) % tabNames.length;
|
|
4669
|
-
|
|
5247
|
+
const newTab = tabNames[nextIndex];
|
|
5248
|
+
setActiveTab(newTab);
|
|
5249
|
+
setUserHasSwitchedTab(true);
|
|
4670
5250
|
}
|
|
4671
5251
|
}
|
|
4672
5252
|
}
|
|
4673
5253
|
if (key.ctrl && input === "x") {
|
|
4674
5254
|
void handleExit();
|
|
4675
5255
|
}
|
|
4676
|
-
if (key.escape && isThinking) {
|
|
5256
|
+
if (key.escape && isThinking && focusedArea === "input" && !payload) {
|
|
4677
5257
|
emitToListeners("InterruptThought", void 0);
|
|
4678
5258
|
}
|
|
4679
5259
|
});
|
|
4680
5260
|
const contentHeight = size.rows - footerHeight;
|
|
4681
5261
|
const timelineWidth = Math.floor(size.columns * 0.65);
|
|
4682
5262
|
const statusWidth = size.columns - timelineWidth;
|
|
4683
|
-
const labelWidthFor =
|
|
5263
|
+
const labelWidthFor = useCallback5((type) => {
|
|
4684
5264
|
if (type === "agent") {
|
|
4685
5265
|
return 7;
|
|
4686
5266
|
}
|
|
5267
|
+
if (type === "prompt") {
|
|
5268
|
+
return 7;
|
|
5269
|
+
}
|
|
4687
5270
|
if (type === "user") {
|
|
4688
5271
|
return 6;
|
|
4689
5272
|
}
|
|
@@ -4693,7 +5276,7 @@ function ChatContent() {
|
|
|
4693
5276
|
return 6;
|
|
4694
5277
|
}, []);
|
|
4695
5278
|
const availableTextWidth = timelineWidth - 4;
|
|
4696
|
-
const lineItems =
|
|
5279
|
+
const lineItems = useMemo11(() => {
|
|
4697
5280
|
const acc = [];
|
|
4698
5281
|
for (const msg of messages) {
|
|
4699
5282
|
const labelWidth = labelWidthFor(msg.type);
|
|
@@ -4772,14 +5355,15 @@ function ChatContent() {
|
|
|
4772
5355
|
setSelectedIndex(lineItems.length - 1);
|
|
4773
5356
|
}
|
|
4774
5357
|
}, [size.rows, size.columns]);
|
|
4775
|
-
const tabs =
|
|
5358
|
+
const tabs = useMemo11(() => [
|
|
4776
5359
|
{ name: "settings", label: "SETTINGS" },
|
|
4777
5360
|
{ name: "planDescription", label: "PLAN" },
|
|
4778
5361
|
{ name: "actions", label: "ACTIONS" }
|
|
4779
5362
|
], []);
|
|
4780
5363
|
const timelineTitle = "TIMELINE:";
|
|
4781
5364
|
const timelineHeaderWidth = timelineWidth - 1;
|
|
4782
|
-
const
|
|
5365
|
+
const showSpinner = isCompacting || isThinking;
|
|
5366
|
+
const timelineDashes = timelineHeaderWidth - timelineTitle.length - (showSpinner ? 5 : 0);
|
|
4783
5367
|
const tabsTotalWidth = tabs.reduce((acc, t) => acc + t.label.length + 2, 0) + (tabs.length - 1);
|
|
4784
5368
|
const statusDashes = Math.max(0, statusWidth - tabsTotalWidth - 2);
|
|
4785
5369
|
const timelineColor = focusedArea === "timeline" ? "cyan" : "gray";
|
|
@@ -4790,16 +5374,16 @@ function ChatContent() {
|
|
|
4790
5374
|
const junctionLeftColor = focusedArea === "timeline" || focusedArea === "input" ? "cyan" : "gray";
|
|
4791
5375
|
const junctionMiddleColor = focusedArea === "timeline" || focusedArea === "status" || focusedArea === "input" ? "cyan" : "gray";
|
|
4792
5376
|
const junctionRightColor = focusedArea === "status" || focusedArea === "input" ? "cyan" : "gray";
|
|
4793
|
-
const timelinePipeFiller =
|
|
5377
|
+
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)) }), []);
|
|
4794
5378
|
return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", height: size.rows, padding: 0, children: [
|
|
4795
5379
|
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", height: 1, children: [
|
|
4796
|
-
/* @__PURE__ */
|
|
4797
|
-
/* @__PURE__ */
|
|
4798
|
-
|
|
4799
|
-
/* @__PURE__ */
|
|
4800
|
-
/* @__PURE__ */
|
|
5380
|
+
/* @__PURE__ */ jsx17(Text10, { color: timelineColor, children: "\u256D" }),
|
|
5381
|
+
/* @__PURE__ */ jsx17(Text10, { bold: true, color: timelineColor, children: timelineTitle }),
|
|
5382
|
+
showSpinner && /* @__PURE__ */ jsx17(Box10, { paddingLeft: 2, paddingRight: 1, children: /* @__PURE__ */ jsx17(Spinner2, { type: "clock" }) }),
|
|
5383
|
+
/* @__PURE__ */ jsx17(Text10, { color: timelineColor, children: "\u2500".repeat(Math.max(0, timelineDashes)) }),
|
|
5384
|
+
/* @__PURE__ */ jsx17(Text10, { color: dividerColor, children: "\u252C" }),
|
|
4801
5385
|
tabs.map((tab, i) => /* @__PURE__ */ jsxs9(React11.Fragment, { children: [
|
|
4802
|
-
/* @__PURE__ */
|
|
5386
|
+
/* @__PURE__ */ jsx17(
|
|
4803
5387
|
Text10,
|
|
4804
5388
|
{
|
|
4805
5389
|
color: activeTab === tab.name ? "black" : statusColor,
|
|
@@ -4808,7 +5392,7 @@ function ChatContent() {
|
|
|
4808
5392
|
children: ` ${tab.label} `
|
|
4809
5393
|
}
|
|
4810
5394
|
),
|
|
4811
|
-
i < tabs.length - 1 && /* @__PURE__ */
|
|
5395
|
+
i < tabs.length - 1 && /* @__PURE__ */ jsx17(Text10, { color: statusColor, children: "|" })
|
|
4812
5396
|
] }, tab.name)),
|
|
4813
5397
|
/* @__PURE__ */ jsxs9(Text10, { color: statusColor, children: [
|
|
4814
5398
|
"\u2500".repeat(statusDashes),
|
|
@@ -4816,22 +5400,22 @@ function ChatContent() {
|
|
|
4816
5400
|
] })
|
|
4817
5401
|
] }),
|
|
4818
5402
|
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", height: contentHeight - 2, children: [
|
|
4819
|
-
/* @__PURE__ */
|
|
5403
|
+
/* @__PURE__ */ jsx17(
|
|
4820
5404
|
Box10,
|
|
4821
5405
|
{
|
|
4822
5406
|
flexDirection: "column",
|
|
4823
5407
|
width: timelineWidth,
|
|
4824
5408
|
paddingLeft: 0,
|
|
4825
5409
|
paddingRight: 1,
|
|
4826
|
-
children: /* @__PURE__ */
|
|
5410
|
+
children: /* @__PURE__ */ jsx17(Box10, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx17(
|
|
4827
5411
|
VirtualList,
|
|
4828
5412
|
{
|
|
4829
5413
|
items: lineItems,
|
|
4830
5414
|
itemHeight: 1,
|
|
4831
5415
|
height: contentHeight - 2,
|
|
4832
5416
|
selectedIndex,
|
|
4833
|
-
renderOverflowTop:
|
|
4834
|
-
/* @__PURE__ */
|
|
5417
|
+
renderOverflowTop: useCallback5((count) => /* @__PURE__ */ jsxs9(Box10, { children: [
|
|
5418
|
+
/* @__PURE__ */ jsx17(Text10, { color: "gray", dimColor: true, children: "\u2502" }),
|
|
4835
5419
|
count > 0 && /* @__PURE__ */ jsxs9(Text10, { dimColor: true, children: [
|
|
4836
5420
|
" ",
|
|
4837
5421
|
"\u25B2 ",
|
|
@@ -4839,8 +5423,8 @@ function ChatContent() {
|
|
|
4839
5423
|
" more"
|
|
4840
5424
|
] })
|
|
4841
5425
|
] }), []),
|
|
4842
|
-
renderOverflowBottom:
|
|
4843
|
-
/* @__PURE__ */
|
|
5426
|
+
renderOverflowBottom: useCallback5((count) => /* @__PURE__ */ jsxs9(Box10, { children: [
|
|
5427
|
+
/* @__PURE__ */ jsx17(Text10, { color: "gray", dimColor: true, children: "\u2502" }),
|
|
4844
5428
|
count > 0 && /* @__PURE__ */ jsxs9(Text10, { dimColor: true, children: [
|
|
4845
5429
|
" ",
|
|
4846
5430
|
"\u25BC ",
|
|
@@ -4850,8 +5434,8 @@ function ChatContent() {
|
|
|
4850
5434
|
] }), []),
|
|
4851
5435
|
renderFiller: timelinePipeFiller,
|
|
4852
5436
|
keyExtractor: (it) => it.key,
|
|
4853
|
-
renderItem:
|
|
4854
|
-
({ item, isSelected }) => /* @__PURE__ */
|
|
5437
|
+
renderItem: useCallback5(
|
|
5438
|
+
({ item, isSelected }) => /* @__PURE__ */ jsx17(
|
|
4855
5439
|
MessageLineItem,
|
|
4856
5440
|
{
|
|
4857
5441
|
item,
|
|
@@ -4866,7 +5450,7 @@ function ChatContent() {
|
|
|
4866
5450
|
) })
|
|
4867
5451
|
}
|
|
4868
5452
|
),
|
|
4869
|
-
activeTab !== "shell" && activeTab !== "actions" && /* @__PURE__ */
|
|
5453
|
+
activeTab !== "shell" && activeTab !== "actions" && /* @__PURE__ */ jsx17(
|
|
4870
5454
|
Box10,
|
|
4871
5455
|
{
|
|
4872
5456
|
flexDirection: "column",
|
|
@@ -4878,7 +5462,7 @@ function ChatContent() {
|
|
|
4878
5462
|
borderRight: false
|
|
4879
5463
|
}
|
|
4880
5464
|
),
|
|
4881
|
-
/* @__PURE__ */
|
|
5465
|
+
/* @__PURE__ */ jsx17(
|
|
4882
5466
|
Box10,
|
|
4883
5467
|
{
|
|
4884
5468
|
flexDirection: "column",
|
|
@@ -4892,84 +5476,29 @@ function ChatContent() {
|
|
|
4892
5476
|
paddingRight: 1,
|
|
4893
5477
|
children: /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", flexGrow: 1, marginTop: 0, children: [
|
|
4894
5478
|
activeTab === "settings" && /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", children: [
|
|
4895
|
-
/* @__PURE__ */
|
|
4896
|
-
/* @__PURE__ */
|
|
5479
|
+
/* @__PURE__ */ jsx17(CostView, { isDense: true }),
|
|
5480
|
+
/* @__PURE__ */ jsx17(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx17(SettingsView, { isDense: true }) })
|
|
4897
5481
|
] }),
|
|
4898
|
-
activeTab === "planDescription" && /* @__PURE__ */
|
|
4899
|
-
activeTab === "actions" && /* @__PURE__ */
|
|
5482
|
+
activeTab === "planDescription" && /* @__PURE__ */ jsx17(PlanView, {}),
|
|
5483
|
+
activeTab === "actions" && /* @__PURE__ */ jsx17(ActionsView, { height: contentHeight - 2, isFocused: focusedArea === "status" })
|
|
4900
5484
|
] })
|
|
4901
5485
|
}
|
|
4902
5486
|
)
|
|
4903
5487
|
] }),
|
|
4904
5488
|
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", height: 1, children: [
|
|
4905
|
-
/* @__PURE__ */
|
|
4906
|
-
/* @__PURE__ */
|
|
4907
|
-
/* @__PURE__ */
|
|
4908
|
-
/* @__PURE__ */
|
|
4909
|
-
/* @__PURE__ */
|
|
5489
|
+
/* @__PURE__ */ jsx17(Text10, { color: junctionLeftColor, children: "\u2522" }),
|
|
5490
|
+
/* @__PURE__ */ jsx17(Text10, { color: timelineBottomColor, children: "\u2501".repeat(timelineWidth - 1) }),
|
|
5491
|
+
/* @__PURE__ */ jsx17(Text10, { color: junctionMiddleColor, children: "\u2537" }),
|
|
5492
|
+
/* @__PURE__ */ jsx17(Text10, { color: statusBottomColor, children: "\u2501".repeat(Math.max(0, statusWidth - 2)) }),
|
|
5493
|
+
/* @__PURE__ */ jsx17(Text10, { color: junctionRightColor, children: "\u252A" })
|
|
4910
5494
|
] }),
|
|
4911
|
-
/* @__PURE__ */
|
|
5495
|
+
/* @__PURE__ */ jsx17(UserInput, {})
|
|
4912
5496
|
] });
|
|
4913
5497
|
}
|
|
4914
5498
|
|
|
4915
5499
|
// ink/components/DiffApprovalView.tsx
|
|
4916
5500
|
import { Box as Box11, Text as Text11, useInput as useInput5 } from "ink";
|
|
4917
5501
|
import { useMemo as useMemo12, useState as useState15 } from "react";
|
|
4918
|
-
|
|
4919
|
-
// ink/contexts/ApprovalContext.tsx
|
|
4920
|
-
import { createContext as createContext7, useCallback as useCallback5, useContext as useContext7, useMemo as useMemo11, useState as useState14 } from "react";
|
|
4921
|
-
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
4922
|
-
var ApprovalContext = createContext7(void 0);
|
|
4923
|
-
var useApproval = () => {
|
|
4924
|
-
const context = useContext7(ApprovalContext);
|
|
4925
|
-
if (!context) {
|
|
4926
|
-
throw new Error("useApproval must be used within an ApprovalProvider");
|
|
4927
|
-
}
|
|
4928
|
-
return context;
|
|
4929
|
-
};
|
|
4930
|
-
var ApprovalProvider = ({ children }) => {
|
|
4931
|
-
const [payload, setPayload] = useState14(null);
|
|
4932
|
-
const [toolInfos] = useState14(/* @__PURE__ */ new Map());
|
|
4933
|
-
const registerToolInfo = useCallback5(
|
|
4934
|
-
(info) => {
|
|
4935
|
-
toolInfos.set(info.callId, info);
|
|
4936
|
-
},
|
|
4937
|
-
[toolInfos]
|
|
4938
|
-
);
|
|
4939
|
-
useListener("OpenApprovalViewer", (p) => {
|
|
4940
|
-
let finalPayload = { ...p, openedAt: Date.now() };
|
|
4941
|
-
if (p.mode === "info" && p.callId) {
|
|
4942
|
-
const info = toolInfos.get(p.callId);
|
|
4943
|
-
if (info) {
|
|
4944
|
-
finalPayload = { ...finalPayload, ...info };
|
|
4945
|
-
}
|
|
4946
|
-
} else if (p.callId) {
|
|
4947
|
-
registerToolInfo({
|
|
4948
|
-
callId: p.callId,
|
|
4949
|
-
type: p.type,
|
|
4950
|
-
path: p.path,
|
|
4951
|
-
diff: p.diff,
|
|
4952
|
-
code: p.code,
|
|
4953
|
-
commands: p.commands
|
|
4954
|
-
});
|
|
4955
|
-
}
|
|
4956
|
-
setPayload(finalPayload);
|
|
4957
|
-
}, [registerToolInfo, toolInfos]);
|
|
4958
|
-
useListener("RegisterToolInfo", (info) => {
|
|
4959
|
-
registerToolInfo(info);
|
|
4960
|
-
}, [registerToolInfo]);
|
|
4961
|
-
useListener("CloseApprovalViewer", () => {
|
|
4962
|
-
setPayload(null);
|
|
4963
|
-
}, []);
|
|
4964
|
-
const value = useMemo11(() => ({
|
|
4965
|
-
payload,
|
|
4966
|
-
setPayload,
|
|
4967
|
-
registerToolInfo
|
|
4968
|
-
}), [payload, registerToolInfo]);
|
|
4969
|
-
return /* @__PURE__ */ jsx17(ApprovalContext.Provider, { value, children });
|
|
4970
|
-
};
|
|
4971
|
-
|
|
4972
|
-
// ink/components/DiffApprovalView.tsx
|
|
4973
5502
|
import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
4974
5503
|
function DiffApprovalView() {
|
|
4975
5504
|
const { payload } = useApproval();
|
|
@@ -5343,10 +5872,10 @@ function EnvironmentSettingsStep({ onConfirm, onBack }) {
|
|
|
5343
5872
|
|
|
5344
5873
|
// ink/configurationWizard/modelsByProvider.ts
|
|
5345
5874
|
var modelsByProvider = {
|
|
5346
|
-
OpenAI: [
|
|
5347
|
-
Anthropic: [
|
|
5348
|
-
Google: [
|
|
5349
|
-
Ollama: [
|
|
5875
|
+
OpenAI: [defaultOpenAIModel, "gpt-5.0", defaultOpenAICompactionModel],
|
|
5876
|
+
Anthropic: [defaultAnthropicModel, "claude-4-5-sonnet-latest", defaultAnthropicCompactionModel],
|
|
5877
|
+
Google: [defaultGoogleModel, "gemini-3-flash", "gemini-2.5-flash", defaultGoogleCompactionModel],
|
|
5878
|
+
Ollama: [defaultOllamaModel, "mistral", defaultOllamaCompactionModel]
|
|
5350
5879
|
};
|
|
5351
5880
|
var compactorModelsByProvider = {
|
|
5352
5881
|
OpenAI: modelsByProvider.OpenAI.slice().reverse(),
|
|
@@ -5605,20 +6134,48 @@ function MainChat() {
|
|
|
5605
6134
|
] }) }) }) }) }) });
|
|
5606
6135
|
}
|
|
5607
6136
|
|
|
5608
|
-
//
|
|
5609
|
-
import
|
|
6137
|
+
// utils/models/deprecations.ts
|
|
6138
|
+
import chalk4 from "chalk";
|
|
6139
|
+
var DEPRECATION_RULES = [
|
|
6140
|
+
// Redirect any gpt-4o variants (including dated and -mini) to gpt-5-nano
|
|
6141
|
+
{
|
|
6142
|
+
match: (name) => name.toLowerCase().startsWith("gpt-4o"),
|
|
6143
|
+
replacement: "gpt-5-nano",
|
|
6144
|
+
reason: "OpenAI gpt-4o family is deprecated in this agent"
|
|
6145
|
+
}
|
|
6146
|
+
];
|
|
6147
|
+
function getDeprecatedReplacement(modelName) {
|
|
6148
|
+
if (!modelName) {
|
|
6149
|
+
return null;
|
|
6150
|
+
}
|
|
6151
|
+
for (const rule of DEPRECATION_RULES) {
|
|
6152
|
+
if (rule.match(modelName)) {
|
|
6153
|
+
return { replacement: rule.replacement, rule };
|
|
6154
|
+
}
|
|
6155
|
+
}
|
|
6156
|
+
return null;
|
|
6157
|
+
}
|
|
6158
|
+
function warnAndPersistRedirect(original, envKey, replacement, reason) {
|
|
6159
|
+
const reasonSuffix = reason ? ` Reason: ${reason}.` : "";
|
|
6160
|
+
console.warn(
|
|
6161
|
+
chalk4.yellow(
|
|
6162
|
+
`Warning: model "${original}" is deprecated and will be redirected to "${replacement}". Your environment setting (${envKey}) has been updated.${reasonSuffix}`
|
|
6163
|
+
)
|
|
6164
|
+
);
|
|
6165
|
+
updateEnv(envKey, replacement);
|
|
6166
|
+
}
|
|
5610
6167
|
|
|
5611
6168
|
// utils/shell/cli.ts
|
|
5612
|
-
import
|
|
6169
|
+
import chalk5 from "chalk";
|
|
5613
6170
|
|
|
5614
6171
|
// utils/package/getOwnPackageJson.ts
|
|
5615
6172
|
import { readFileSync as readFileSync6 } from "fs";
|
|
5616
|
-
import { join as
|
|
6173
|
+
import { join as join11 } from "path";
|
|
5617
6174
|
import { fileURLToPath } from "url";
|
|
5618
6175
|
var __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
5619
6176
|
function getOwnPackageJson() {
|
|
5620
6177
|
try {
|
|
5621
|
-
const packageContents = readFileSync6(
|
|
6178
|
+
const packageContents = readFileSync6(join11(__dirname, "../package.json"), "utf8");
|
|
5622
6179
|
return JSON.parse(packageContents);
|
|
5623
6180
|
} catch {
|
|
5624
6181
|
return { name: "@harperfast/agent", version: "0.0.0" };
|
|
@@ -5636,24 +6193,23 @@ function isVersionRequest(args) {
|
|
|
5636
6193
|
}
|
|
5637
6194
|
function handleHelp() {
|
|
5638
6195
|
console.log(`
|
|
5639
|
-
${
|
|
6196
|
+
${chalk5.bold("harper-agent")} - AI to help you with Harper app creation and modification
|
|
5640
6197
|
|
|
5641
|
-
${
|
|
6198
|
+
${chalk5.bold("USAGE")}
|
|
5642
6199
|
$ harper-agent [options]
|
|
5643
6200
|
$ harper-agent [command]
|
|
5644
6201
|
|
|
5645
|
-
${
|
|
6202
|
+
${chalk5.bold("OPTIONS")}
|
|
5646
6203
|
-h, --help Show help information
|
|
5647
6204
|
-v, --version Show version information
|
|
5648
|
-
-m, --model Specify the model to use (e.g.,
|
|
6205
|
+
-m, --model Specify the model to use (e.g., ${defaultOpenAIModel}, ${defaultAnthropicModel}, ${defaultOllamaModel})
|
|
5649
6206
|
Can also be set via HARPER_AGENT_MODEL environment variable.
|
|
5650
|
-
For Ollama, use the ollama- prefix (e.g.,
|
|
5651
|
-
-c, --compaction-model Specify the compaction model to use (defaults to
|
|
6207
|
+
For Ollama, use the ollama- prefix (e.g., ${defaultOllamaCompactionModel}).
|
|
6208
|
+
-c, --compaction-model Specify the compaction model to use (defaults to ${defaultOpenAICompactionModel}).
|
|
5652
6209
|
Can also be set via HARPER_AGENT_COMPACTION_MODEL environment variable.
|
|
5653
6210
|
-s, --session Specify a path to a SQLite database file to persist the chat session.
|
|
5654
6211
|
Can also be set via HARPER_AGENT_SESSION environment variable.
|
|
5655
6212
|
--max-turns Specify the maximum number of turns for the agent run.
|
|
5656
|
-
In task-driven mode, this defaults to unlimited.
|
|
5657
6213
|
Can also be set via HARPER_AGENT_MAX_TURNS environment variable.
|
|
5658
6214
|
--max-cost Specify the maximum cost (in USD) for the agent run.
|
|
5659
6215
|
If exceeded, the agent will exit with a non-zero code.
|
|
@@ -5661,12 +6217,13 @@ ${chalk4.bold("OPTIONS")}
|
|
|
5661
6217
|
--flex-tier Force the use of the flex service tier for lower costs but potentially
|
|
5662
6218
|
more errors under high system load.
|
|
5663
6219
|
Can also be set via HARPER_AGENT_FLEX_TIER=true environment variable.
|
|
6220
|
+
-p, --prompt Specify a prompt to be executed autonomously until completion.
|
|
5664
6221
|
|
|
5665
|
-
${
|
|
6222
|
+
${chalk5.bold("COMMANDS")}
|
|
5666
6223
|
--help Show help information
|
|
5667
6224
|
--version Show version information
|
|
5668
6225
|
|
|
5669
|
-
${
|
|
6226
|
+
${chalk5.bold("EXAMPLES")}
|
|
5670
6227
|
$ harper-agent --help
|
|
5671
6228
|
$ harper-agent --version
|
|
5672
6229
|
$ harper-agent
|
|
@@ -5702,7 +6259,8 @@ function parseArgs() {
|
|
|
5702
6259
|
["sessionPath", ["--session", "-s", "session"]],
|
|
5703
6260
|
["maxTurns", ["--max-turns"]],
|
|
5704
6261
|
["maxCost", ["--max-cost"]],
|
|
5705
|
-
["rateLimitThreshold", ["--rate-limit-threshold"]]
|
|
6262
|
+
["rateLimitThreshold", ["--rate-limit-threshold"]],
|
|
6263
|
+
["prompt", ["--prompt", "-p"]]
|
|
5706
6264
|
];
|
|
5707
6265
|
let handled = false;
|
|
5708
6266
|
for (const [key, prefixes] of flagPairs) {
|
|
@@ -5740,6 +6298,8 @@ function parseArgs() {
|
|
|
5740
6298
|
trackedState.useFlexTier = true;
|
|
5741
6299
|
} else if (arg === "--no-monitor-rate-limits") {
|
|
5742
6300
|
trackedState.monitorRateLimits = false;
|
|
6301
|
+
} else if (arg === "--autonomous" || arg === "-a") {
|
|
6302
|
+
trackedState.autonomous = true;
|
|
5743
6303
|
}
|
|
5744
6304
|
}
|
|
5745
6305
|
if (!trackedState.model && process.env.HARPER_AGENT_MODEL) {
|
|
@@ -5763,10 +6323,6 @@ function parseArgs() {
|
|
|
5763
6323
|
if (process.env.HARPER_AGENT_MONITOR_RATE_LIMITS === "false") {
|
|
5764
6324
|
trackedState.monitorRateLimits = false;
|
|
5765
6325
|
}
|
|
5766
|
-
const sp = trackedState.sessionPath;
|
|
5767
|
-
if (sp) {
|
|
5768
|
-
trackedState.sessionPath = sp && !sp.startsWith("~") && !path8.isAbsolute(sp) ? path8.resolve(process.cwd(), sp) : sp;
|
|
5769
|
-
}
|
|
5770
6326
|
if (!trackedState.useFlexTier && isTrue(process.env.HARPER_AGENT_FLEX_TIER)) {
|
|
5771
6327
|
trackedState.useFlexTier = true;
|
|
5772
6328
|
}
|
|
@@ -5779,55 +6335,67 @@ function parseArgs() {
|
|
|
5779
6335
|
if (isTrue(process.env.HARPER_AGENT_AUTO_APPROVE_SHELL)) {
|
|
5780
6336
|
trackedState.autoApproveShell = true;
|
|
5781
6337
|
}
|
|
5782
|
-
if (
|
|
6338
|
+
if (isTrue(process.env.HARPER_AGENT_AUTONOMOUS)) {
|
|
6339
|
+
trackedState.autonomous = true;
|
|
6340
|
+
}
|
|
6341
|
+
if (!trackedState.model || trackedState.model === defaultModelToken) {
|
|
5783
6342
|
if (process.env.ANTHROPIC_API_KEY) {
|
|
5784
|
-
trackedState.model =
|
|
6343
|
+
trackedState.model = defaultAnthropicModel;
|
|
5785
6344
|
} else if (process.env.GOOGLE_GENERATIVE_AI_API_KEY) {
|
|
5786
|
-
trackedState.model =
|
|
5787
|
-
} else if (process.env.OPENAI_API_KEY) {
|
|
5788
|
-
trackedState.model = "gpt-5.2";
|
|
6345
|
+
trackedState.model = defaultGoogleModel;
|
|
5789
6346
|
} else if (process.env.OLLAMA_BASE_URL) {
|
|
5790
|
-
trackedState.model =
|
|
6347
|
+
trackedState.model = defaultOllamaModel;
|
|
5791
6348
|
} else {
|
|
5792
|
-
trackedState.model =
|
|
6349
|
+
trackedState.model = defaultOpenAIModel;
|
|
5793
6350
|
}
|
|
5794
6351
|
}
|
|
5795
|
-
if (!trackedState.compactionModel) {
|
|
6352
|
+
if (!trackedState.compactionModel || trackedState.compactionModel === defaultModelToken) {
|
|
5796
6353
|
const m = trackedState.model;
|
|
5797
6354
|
if (m.startsWith("claude-")) {
|
|
5798
|
-
trackedState.compactionModel =
|
|
6355
|
+
trackedState.compactionModel = defaultAnthropicCompactionModel;
|
|
5799
6356
|
} else if (m.startsWith("gemini-")) {
|
|
5800
|
-
trackedState.compactionModel =
|
|
6357
|
+
trackedState.compactionModel = defaultGoogleCompactionModel;
|
|
5801
6358
|
} else if (m.startsWith("ollama-")) {
|
|
5802
|
-
trackedState.compactionModel =
|
|
6359
|
+
trackedState.compactionModel = defaultOllamaCompactionModel;
|
|
5803
6360
|
} else {
|
|
5804
|
-
trackedState.compactionModel =
|
|
6361
|
+
trackedState.compactionModel = defaultOpenAICompactionModel;
|
|
5805
6362
|
}
|
|
5806
6363
|
}
|
|
5807
6364
|
if (!isOpenAIModel(trackedState.model)) {
|
|
5808
6365
|
process.env.OPENAI_AGENTS_DISABLE_TRACING = process.env.OPENAI_AGENTS_DISABLE_TRACING || "1";
|
|
5809
6366
|
}
|
|
6367
|
+
const maybeRedirect = (current, envKey) => {
|
|
6368
|
+
const hit = getDeprecatedReplacement(current);
|
|
6369
|
+
if (hit) {
|
|
6370
|
+
const { replacement, rule } = hit;
|
|
6371
|
+
warnAndPersistRedirect(current, envKey, replacement, rule.reason);
|
|
6372
|
+
return replacement;
|
|
6373
|
+
}
|
|
6374
|
+
return current;
|
|
6375
|
+
};
|
|
6376
|
+
trackedState.model = maybeRedirect(trackedState.model, "HARPER_AGENT_MODEL");
|
|
6377
|
+
trackedState.compactionModel = maybeRedirect(trackedState.compactionModel, "HARPER_AGENT_COMPACTION_MODEL");
|
|
5810
6378
|
}
|
|
5811
6379
|
|
|
5812
6380
|
// utils/envLoader.ts
|
|
5813
6381
|
import dotenv from "dotenv";
|
|
5814
|
-
import { existsSync as
|
|
6382
|
+
import { existsSync as existsSync10 } from "fs";
|
|
5815
6383
|
import { homedir as homedir4 } from "os";
|
|
5816
|
-
import { join as
|
|
6384
|
+
import { join as join12 } from "path";
|
|
5817
6385
|
function loadEnv() {
|
|
5818
|
-
const topLevelEnvPath =
|
|
5819
|
-
const localEnvPath =
|
|
5820
|
-
if (
|
|
6386
|
+
const topLevelEnvPath = join12(homedir4(), ".harper", "harper-agent-env");
|
|
6387
|
+
const localEnvPath = join12(process.cwd(), ".env");
|
|
6388
|
+
if (existsSync10(topLevelEnvPath)) {
|
|
5821
6389
|
dotenv.config({ path: topLevelEnvPath, quiet: true });
|
|
5822
6390
|
}
|
|
5823
|
-
if (
|
|
6391
|
+
if (existsSync10(localEnvPath)) {
|
|
5824
6392
|
dotenv.config({ path: localEnvPath, override: true, quiet: true });
|
|
5825
6393
|
}
|
|
5826
6394
|
}
|
|
5827
6395
|
|
|
5828
6396
|
// utils/package/checkForUpdate.ts
|
|
5829
6397
|
import { Select as Select3 } from "@inkjs/ui";
|
|
5830
|
-
import
|
|
6398
|
+
import chalk6 from "chalk";
|
|
5831
6399
|
import spawn2 from "cross-spawn";
|
|
5832
6400
|
import { Box as Box19, render as render2, Text as Text18 } from "ink";
|
|
5833
6401
|
import React21 from "react";
|
|
@@ -5949,7 +6517,7 @@ function UpdatePrompt({ packageName, currentVersion, latestVersion, onSelect })
|
|
|
5949
6517
|
React21.createElement(
|
|
5950
6518
|
Text18,
|
|
5951
6519
|
null,
|
|
5952
|
-
`${
|
|
6520
|
+
`${chalk6.yellow("Update available:")} ${chalk6.bold(packageName)} ${chalk6.dim(`v${currentVersion}`)} \u2192 ${chalk6.green(`v${latestVersion}`)}`
|
|
5953
6521
|
),
|
|
5954
6522
|
React21.createElement(
|
|
5955
6523
|
Box19,
|
|
@@ -5987,34 +6555,10 @@ function ensureApiKey() {
|
|
|
5987
6555
|
return true;
|
|
5988
6556
|
}
|
|
5989
6557
|
|
|
5990
|
-
// utils/shell/getStdin.ts
|
|
5991
|
-
async function getStdin() {
|
|
5992
|
-
if (process.stdin.isTTY) {
|
|
5993
|
-
return "";
|
|
5994
|
-
}
|
|
5995
|
-
let result = "";
|
|
5996
|
-
process.stdin.setEncoding("utf8");
|
|
5997
|
-
for await (const chunk of process.stdin) {
|
|
5998
|
-
result += chunk;
|
|
5999
|
-
}
|
|
6000
|
-
return result.trim();
|
|
6001
|
-
}
|
|
6002
|
-
|
|
6003
6558
|
// agent.ts
|
|
6004
6559
|
(async function() {
|
|
6005
6560
|
setupGlobalErrorHandlers();
|
|
6006
6561
|
loadEnv();
|
|
6007
|
-
const originalFetch = globalThis.fetch;
|
|
6008
|
-
globalThis.fetch = async (...args) => {
|
|
6009
|
-
const response = await originalFetch(...args);
|
|
6010
|
-
const headers = {};
|
|
6011
|
-
response.headers.forEach((value, key) => {
|
|
6012
|
-
headers[key] = value;
|
|
6013
|
-
});
|
|
6014
|
-
rateLimitTracker.updateFromHeaders(headers);
|
|
6015
|
-
emitToListeners("SettingsUpdated", void 0);
|
|
6016
|
-
return response;
|
|
6017
|
-
};
|
|
6018
6562
|
process.on("SIGINT", handleExit);
|
|
6019
6563
|
process.on("SIGTERM", handleExit);
|
|
6020
6564
|
await checkForUpdate();
|
|
@@ -6026,17 +6570,16 @@ async function getStdin() {
|
|
|
6026
6570
|
emitToListeners("ExitUI", void 0);
|
|
6027
6571
|
parseArgs();
|
|
6028
6572
|
if (!ensureApiKey()) {
|
|
6029
|
-
console.log(
|
|
6573
|
+
console.log(chalk7.red("No key provided. Exiting."));
|
|
6030
6574
|
process.exit(1);
|
|
6031
6575
|
}
|
|
6032
6576
|
}
|
|
6033
6577
|
await agentManager.initialize();
|
|
6034
6578
|
bootstrapMain();
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
});
|
|
6579
|
+
if (trackedState.prompt?.trim?.()?.length) {
|
|
6580
|
+
trackedState.autonomous = true;
|
|
6581
|
+
emitToListeners("PushNewMessages", [
|
|
6582
|
+
{ type: "prompt", text: trackedState.prompt.trim(), version: 1 }
|
|
6583
|
+
]);
|
|
6584
|
+
}
|
|
6042
6585
|
})();
|