@harperfast/agent 0.13.6-ink → 0.13.7-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/dist/agent.js +1288 -177
- package/package.json +2 -2
package/dist/agent.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// agent.ts
|
|
4
|
-
import "
|
|
5
|
-
import chalk5 from "chalk";
|
|
4
|
+
import chalk6 from "chalk";
|
|
6
5
|
|
|
7
6
|
// agent/AgentManager.ts
|
|
8
7
|
import { Agent as Agent3 } from "@openai/agents";
|
|
@@ -47,11 +46,24 @@ async function onceListener(name) {
|
|
|
47
46
|
function emitToListeners(name, value, trigger) {
|
|
48
47
|
const listeners = listenersMap[name];
|
|
49
48
|
if (listeners) {
|
|
50
|
-
|
|
49
|
+
const stableCopyOfListeners = listeners.slice();
|
|
50
|
+
for (const listener of stableCopyOfListeners) {
|
|
51
51
|
listener(value, trigger);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
+
function addListener(name, callback) {
|
|
56
|
+
if (!listenersMap[name]) {
|
|
57
|
+
listenersMap[name] = [];
|
|
58
|
+
}
|
|
59
|
+
listenersMap[name].push(callback);
|
|
60
|
+
return () => {
|
|
61
|
+
const index = listenersMap[name].indexOf(callback);
|
|
62
|
+
if (index >= 0) {
|
|
63
|
+
listenersMap[name].splice(index, 1);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
55
67
|
function curryEmitToListeners(name, value, trigger) {
|
|
56
68
|
return (e) => emitToListeners(name, value, trigger ?? e);
|
|
57
69
|
}
|
|
@@ -68,7 +80,12 @@ var trackedState = {
|
|
|
68
80
|
sessionPath: null,
|
|
69
81
|
useFlexTier: false,
|
|
70
82
|
maxTurns: 30,
|
|
71
|
-
maxCost: null
|
|
83
|
+
maxCost: null,
|
|
84
|
+
autoApproveCodeInterpreter: false,
|
|
85
|
+
autoApprovePatches: false,
|
|
86
|
+
autoApproveShell: false,
|
|
87
|
+
monitorRateLimits: true,
|
|
88
|
+
rateLimitThreshold: 80
|
|
72
89
|
};
|
|
73
90
|
|
|
74
91
|
// lifecycle/defaultInstructions.ts
|
|
@@ -498,7 +515,7 @@ async function execute10({ skill }) {
|
|
|
498
515
|
|
|
499
516
|
// tools/files/workspaceEditor.ts
|
|
500
517
|
import { applyDiff } from "@openai/agents";
|
|
501
|
-
import { existsSync as
|
|
518
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
502
519
|
import { mkdir, rm, writeFile } from "fs/promises";
|
|
503
520
|
import path3 from "path";
|
|
504
521
|
|
|
@@ -520,16 +537,53 @@ function normalizeDiff(diff) {
|
|
|
520
537
|
import path2 from "path";
|
|
521
538
|
|
|
522
539
|
// utils/files/aiignore.ts
|
|
523
|
-
import { existsSync as
|
|
540
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
524
541
|
import path from "path";
|
|
542
|
+
|
|
543
|
+
// utils/logger.ts
|
|
544
|
+
import { appendFileSync, existsSync as existsSync3, mkdirSync } from "fs";
|
|
545
|
+
import { homedir } from "os";
|
|
546
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
547
|
+
var ERROR_LOG_PATH = join4(homedir(), ".harper", "harper-agent-errors");
|
|
548
|
+
function logError(error) {
|
|
549
|
+
const message = error instanceof Error ? error.stack || error.message : String(error);
|
|
550
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
551
|
+
const logEntry = `[${timestamp}] ${message}
|
|
552
|
+
|
|
553
|
+
`;
|
|
554
|
+
try {
|
|
555
|
+
const dir = dirname2(ERROR_LOG_PATH);
|
|
556
|
+
if (!existsSync3(dir)) {
|
|
557
|
+
mkdirSync(dir, { recursive: true });
|
|
558
|
+
}
|
|
559
|
+
appendFileSync(ERROR_LOG_PATH, logEntry, "utf8");
|
|
560
|
+
} catch (err) {
|
|
561
|
+
console.error("Failed to write to error log:", err);
|
|
562
|
+
console.error("Original error:", error);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
function setupGlobalErrorHandlers() {
|
|
566
|
+
process.on("uncaughtException", (error) => {
|
|
567
|
+
logError(error);
|
|
568
|
+
console.error("Uncaught Exception:", error);
|
|
569
|
+
process.exit(1);
|
|
570
|
+
});
|
|
571
|
+
process.on("unhandledRejection", (reason) => {
|
|
572
|
+
logError(reason);
|
|
573
|
+
console.error("Unhandled Rejection:", reason);
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// utils/files/aiignore.ts
|
|
525
578
|
var ignorePatterns = [];
|
|
526
579
|
function loadAiIgnore() {
|
|
527
580
|
const ignorePath = path.join(trackedState.cwd, ".aiignore");
|
|
528
|
-
if (
|
|
581
|
+
if (existsSync4(ignorePath)) {
|
|
529
582
|
try {
|
|
530
583
|
const content = readFileSync3(ignorePath, "utf8");
|
|
531
584
|
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);
|
|
532
585
|
} catch (error) {
|
|
586
|
+
logError(error);
|
|
533
587
|
console.error(`Error reading .aiignore: ${error}`);
|
|
534
588
|
ignorePatterns = [];
|
|
535
589
|
}
|
|
@@ -600,7 +654,7 @@ var WorkspaceEditor = class {
|
|
|
600
654
|
async updateFile(operation) {
|
|
601
655
|
try {
|
|
602
656
|
const targetPath = resolvePath(this.root(), operation.path);
|
|
603
|
-
if (!
|
|
657
|
+
if (!existsSync5(targetPath)) {
|
|
604
658
|
return { status: "failed", output: "Error: file not found at path " + targetPath };
|
|
605
659
|
}
|
|
606
660
|
const original = readFileSync4(targetPath, "utf8");
|
|
@@ -615,7 +669,7 @@ var WorkspaceEditor = class {
|
|
|
615
669
|
async deleteFile(operation) {
|
|
616
670
|
try {
|
|
617
671
|
const targetPath = resolvePath(this.root(), operation.path);
|
|
618
|
-
if (!
|
|
672
|
+
if (!existsSync5(targetPath)) {
|
|
619
673
|
return { status: "failed", output: "Error: file not found at path " + targetPath };
|
|
620
674
|
}
|
|
621
675
|
await rm(targetPath, { force: true });
|
|
@@ -683,7 +737,25 @@ async function needsApproval(runContext, operation, callId) {
|
|
|
683
737
|
return false;
|
|
684
738
|
}
|
|
685
739
|
const autoApproved = getEnv("HARPER_AGENT_AUTO_APPROVE_PATCHES", "APPLY_PATCH_AUTO_APPROVE") === "1";
|
|
686
|
-
|
|
740
|
+
if (autoApproved) {
|
|
741
|
+
if (callId) {
|
|
742
|
+
emitToListeners("RegisterToolInfo", {
|
|
743
|
+
type: operation.type,
|
|
744
|
+
path: operation.path,
|
|
745
|
+
diff: operation.diff,
|
|
746
|
+
callId
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
return false;
|
|
750
|
+
}
|
|
751
|
+
emitToListeners("OpenApprovalViewer", {
|
|
752
|
+
type: operation.type,
|
|
753
|
+
path: operation.path,
|
|
754
|
+
diff: operation.diff,
|
|
755
|
+
mode: "ask",
|
|
756
|
+
callId
|
|
757
|
+
});
|
|
758
|
+
return true;
|
|
687
759
|
} catch (err) {
|
|
688
760
|
console.error("apply_patch approval step failed:", err);
|
|
689
761
|
return false;
|
|
@@ -694,6 +766,7 @@ async function execute11(operation) {
|
|
|
694
766
|
const needed = await requiredSkillForOperation(operation.path, operation.type);
|
|
695
767
|
if (needed) {
|
|
696
768
|
const content = await execute10({ skill: needed });
|
|
769
|
+
console.error(`Understanding ${needed} is necessary before applying this patch.`);
|
|
697
770
|
return { status: "failed, skill guarded", output: content };
|
|
698
771
|
}
|
|
699
772
|
switch (operation.type) {
|
|
@@ -923,12 +996,28 @@ var codeInterpreterTool = tool17({
|
|
|
923
996
|
execute: execute14,
|
|
924
997
|
needsApproval: needsApproval2
|
|
925
998
|
});
|
|
926
|
-
async function needsApproval2(runContext,
|
|
999
|
+
async function needsApproval2(runContext, parameters, callId) {
|
|
927
1000
|
if (callId && runContext.isToolApproved({ toolName: "code_interpreter", callId })) {
|
|
928
1001
|
return false;
|
|
929
1002
|
}
|
|
930
1003
|
const autoApproved = getEnv("HARPER_AGENT_AUTO_APPROVE_CODE_INTERPRETER", "CODE_INTERPRETER_AUTO_APPROVE") === "1";
|
|
931
|
-
|
|
1004
|
+
if (autoApproved) {
|
|
1005
|
+
if (callId) {
|
|
1006
|
+
emitToListeners("RegisterToolInfo", {
|
|
1007
|
+
type: "code_interpreter",
|
|
1008
|
+
code: parameters.code,
|
|
1009
|
+
callId
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
return false;
|
|
1013
|
+
}
|
|
1014
|
+
emitToListeners("OpenApprovalViewer", {
|
|
1015
|
+
type: "code_interpreter",
|
|
1016
|
+
code: parameters.code,
|
|
1017
|
+
mode: "ask",
|
|
1018
|
+
callId
|
|
1019
|
+
});
|
|
1020
|
+
return true;
|
|
932
1021
|
}
|
|
933
1022
|
async function execute14({ code, language }) {
|
|
934
1023
|
const extension = language === "javascript" ? "js" : "py";
|
|
@@ -956,14 +1045,20 @@ import { tool as tool18 } from "@openai/agents";
|
|
|
956
1045
|
import { z as z18 } from "zod";
|
|
957
1046
|
|
|
958
1047
|
// utils/files/updateEnv.ts
|
|
959
|
-
import { existsSync as
|
|
960
|
-
import {
|
|
961
|
-
import { join as
|
|
1048
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync } from "fs";
|
|
1049
|
+
import { homedir as homedir2 } from "os";
|
|
1050
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
962
1051
|
function updateEnv(key, value) {
|
|
963
1052
|
process.env[key] = value;
|
|
964
|
-
const
|
|
1053
|
+
const topLevelEnvPath = join5(homedir2(), ".harper", "harper-agent-env");
|
|
1054
|
+
const localEnvPath = join5(trackedState.cwd, ".env");
|
|
1055
|
+
const envPath = existsSync6(topLevelEnvPath) || !existsSync6(localEnvPath) ? topLevelEnvPath : localEnvPath;
|
|
1056
|
+
const dir = dirname3(envPath);
|
|
1057
|
+
if (!existsSync6(dir)) {
|
|
1058
|
+
mkdirSync2(dir, { recursive: true });
|
|
1059
|
+
}
|
|
965
1060
|
let envContent = "";
|
|
966
|
-
if (
|
|
1061
|
+
if (existsSync6(envPath)) {
|
|
967
1062
|
envContent = readFileSync5(envPath, "utf8");
|
|
968
1063
|
}
|
|
969
1064
|
const regex = new RegExp(`^${key}=.*`, "m");
|
|
@@ -998,6 +1093,8 @@ var setInterpreterAutoApproveTool = tool18({
|
|
|
998
1093
|
}
|
|
999
1094
|
try {
|
|
1000
1095
|
updateEnv("HARPER_AGENT_AUTO_APPROVE_CODE_INTERPRETER", newValue);
|
|
1096
|
+
trackedState.autoApproveCodeInterpreter = autoApprove;
|
|
1097
|
+
emitToListeners("SettingsUpdated", void 0);
|
|
1001
1098
|
return `HARPER_AGENT_AUTO_APPROVE_CODE_INTERPRETER has been set to ${newValue} in .env and current process.`;
|
|
1002
1099
|
} catch (error) {
|
|
1003
1100
|
return `Error updating .env file: ${error.message}`;
|
|
@@ -1026,6 +1123,8 @@ var setPatchAutoApproveTool = tool19({
|
|
|
1026
1123
|
}
|
|
1027
1124
|
try {
|
|
1028
1125
|
updateEnv("HARPER_AGENT_AUTO_APPROVE_PATCHES", newValue);
|
|
1126
|
+
trackedState.autoApprovePatches = autoApprove;
|
|
1127
|
+
emitToListeners("SettingsUpdated", void 0);
|
|
1029
1128
|
return `HARPER_AGENT_AUTO_APPROVE_PATCHES has been set to ${newValue} in .env and current process.`;
|
|
1030
1129
|
} catch (error) {
|
|
1031
1130
|
return `Error updating .env file: ${error.message}`;
|
|
@@ -1054,6 +1153,8 @@ var setShellAutoApproveTool = tool20({
|
|
|
1054
1153
|
}
|
|
1055
1154
|
try {
|
|
1056
1155
|
updateEnv("HARPER_AGENT_AUTO_APPROVE_SHELL", newValue);
|
|
1156
|
+
trackedState.autoApproveShell = autoApprove;
|
|
1157
|
+
emitToListeners("SettingsUpdated", void 0);
|
|
1057
1158
|
return `HARPER_AGENT_AUTO_APPROVE_SHELL has been set to ${newValue} in .env and current process.`;
|
|
1058
1159
|
} catch (error) {
|
|
1059
1160
|
return `Error updating .env file: ${error.message}`;
|
|
@@ -1242,7 +1343,23 @@ TIMEOUT`;
|
|
|
1242
1343
|
const foundRiskyCommand = commands.find((command) => isRiskyCommand(command));
|
|
1243
1344
|
const foundIgnoredInteraction = commands.find((command) => mentionsIgnoredPath(command));
|
|
1244
1345
|
const autoApproved = getEnv("HARPER_AGENT_AUTO_APPROVE_SHELL", "SHELL_AUTO_APPROVE") === "1" && !foundRiskyCommand && !foundIgnoredInteraction;
|
|
1245
|
-
|
|
1346
|
+
if (autoApproved) {
|
|
1347
|
+
if (callId) {
|
|
1348
|
+
emitToListeners("RegisterToolInfo", {
|
|
1349
|
+
type: "shell",
|
|
1350
|
+
commands,
|
|
1351
|
+
callId
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
return false;
|
|
1355
|
+
}
|
|
1356
|
+
emitToListeners("OpenApprovalViewer", {
|
|
1357
|
+
type: "shell",
|
|
1358
|
+
commands,
|
|
1359
|
+
mode: "ask",
|
|
1360
|
+
callId
|
|
1361
|
+
});
|
|
1362
|
+
return true;
|
|
1246
1363
|
}
|
|
1247
1364
|
});
|
|
1248
1365
|
|
|
@@ -1449,8 +1566,8 @@ import { z as z29 } from "zod";
|
|
|
1449
1566
|
// utils/shell/harperProcess.ts
|
|
1450
1567
|
import spawn from "cross-spawn";
|
|
1451
1568
|
import { execSync } from "child_process";
|
|
1452
|
-
import { homedir } from "os";
|
|
1453
|
-
import { join as
|
|
1569
|
+
import { homedir as homedir3 } from "os";
|
|
1570
|
+
import { join as join6 } from "path";
|
|
1454
1571
|
var HarperProcess = class {
|
|
1455
1572
|
childProcess = null;
|
|
1456
1573
|
externalPid = null;
|
|
@@ -1486,7 +1603,7 @@ var HarperProcess = class {
|
|
|
1486
1603
|
this.stopTailingLogs();
|
|
1487
1604
|
}
|
|
1488
1605
|
startTailingLogs() {
|
|
1489
|
-
const logPath =
|
|
1606
|
+
const logPath = join6(homedir3(), "hdb", "log", "hdb.log");
|
|
1490
1607
|
this.logTailProcess = spawn("tail", ["-f", logPath], {
|
|
1491
1608
|
stdio: ["ignore", "pipe", "pipe"]
|
|
1492
1609
|
});
|
|
@@ -1677,7 +1794,7 @@ var createNewHarperApplicationTool = tool30({
|
|
|
1677
1794
|
import { tool as tool31 } from "@openai/agents";
|
|
1678
1795
|
import { readFile as readFile2 } from "fs/promises";
|
|
1679
1796
|
import { createRequire as createRequire2 } from "module";
|
|
1680
|
-
import { dirname as
|
|
1797
|
+
import { dirname as dirname4, join as join7 } from "path";
|
|
1681
1798
|
import { z as z31 } from "zod";
|
|
1682
1799
|
var ToolParameters18 = z31.object({
|
|
1683
1800
|
schemaType: z31.enum(["app", "root"]).describe(
|
|
@@ -1691,8 +1808,8 @@ var getHarperConfigSchemaTool = tool31({
|
|
|
1691
1808
|
async execute({ schemaType }) {
|
|
1692
1809
|
try {
|
|
1693
1810
|
return await readFile2(
|
|
1694
|
-
|
|
1695
|
-
|
|
1811
|
+
join7(
|
|
1812
|
+
dirname4(createRequire2(import.meta.url).resolve("harperdb")),
|
|
1696
1813
|
`config-${schemaType}.schema.json`
|
|
1697
1814
|
),
|
|
1698
1815
|
"utf8"
|
|
@@ -1707,7 +1824,7 @@ var getHarperConfigSchemaTool = tool31({
|
|
|
1707
1824
|
import { tool as tool32 } from "@openai/agents";
|
|
1708
1825
|
import { readFile as readFile3 } from "fs/promises";
|
|
1709
1826
|
import { createRequire as createRequire3 } from "module";
|
|
1710
|
-
import { dirname as
|
|
1827
|
+
import { dirname as dirname5, join as join8 } from "path";
|
|
1711
1828
|
import { z as z32 } from "zod";
|
|
1712
1829
|
var ToolParameters19 = z32.object({
|
|
1713
1830
|
resourceFile: z32.enum([
|
|
@@ -1727,8 +1844,8 @@ var getHarperResourceInterfaceTool = tool32({
|
|
|
1727
1844
|
async execute({ resourceFile }) {
|
|
1728
1845
|
try {
|
|
1729
1846
|
return await readFile3(
|
|
1730
|
-
|
|
1731
|
-
|
|
1847
|
+
join8(
|
|
1848
|
+
dirname5(createRequire3(import.meta.url).resolve("harperdb")),
|
|
1732
1849
|
"resources",
|
|
1733
1850
|
`${resourceFile}.d.ts`
|
|
1734
1851
|
),
|
|
@@ -1744,7 +1861,7 @@ var getHarperResourceInterfaceTool = tool32({
|
|
|
1744
1861
|
import { tool as tool33 } from "@openai/agents";
|
|
1745
1862
|
import { readFile as readFile4 } from "fs/promises";
|
|
1746
1863
|
import { createRequire as createRequire4 } from "module";
|
|
1747
|
-
import { dirname as
|
|
1864
|
+
import { dirname as dirname6, join as join9 } from "path";
|
|
1748
1865
|
import { z as z33 } from "zod";
|
|
1749
1866
|
var ToolParameters20 = z33.object({});
|
|
1750
1867
|
var getHarperSchemaGraphQLTool = tool33({
|
|
@@ -1754,8 +1871,8 @@ var getHarperSchemaGraphQLTool = tool33({
|
|
|
1754
1871
|
async execute() {
|
|
1755
1872
|
try {
|
|
1756
1873
|
return await readFile4(
|
|
1757
|
-
|
|
1758
|
-
|
|
1874
|
+
join9(
|
|
1875
|
+
dirname6(createRequire4(import.meta.url).resolve("harperdb")),
|
|
1759
1876
|
`schema.graphql`
|
|
1760
1877
|
),
|
|
1761
1878
|
"utf8"
|
|
@@ -1842,7 +1959,7 @@ var readHarperLogsTool = tool35({
|
|
|
1842
1959
|
|
|
1843
1960
|
// tools/harper/startHarperTool.ts
|
|
1844
1961
|
import { tool as tool36 } from "@openai/agents";
|
|
1845
|
-
import { existsSync as
|
|
1962
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1846
1963
|
import { basename, resolve } from "path";
|
|
1847
1964
|
import { z as z36 } from "zod";
|
|
1848
1965
|
|
|
@@ -1869,7 +1986,7 @@ var startHarperTool = tool36({
|
|
|
1869
1986
|
try {
|
|
1870
1987
|
let effectiveDirectory = directoryName;
|
|
1871
1988
|
const candidatePath = resolve(process.cwd(), directoryName);
|
|
1872
|
-
if (!
|
|
1989
|
+
if (!existsSync7(candidatePath)) {
|
|
1873
1990
|
const cwd = process.cwd();
|
|
1874
1991
|
if (basename(cwd) === directoryName) {
|
|
1875
1992
|
effectiveDirectory = cwd;
|
|
@@ -1907,6 +2024,110 @@ var stopHarperTool = tool37({
|
|
|
1907
2024
|
}
|
|
1908
2025
|
});
|
|
1909
2026
|
|
|
2027
|
+
// tools/plan/addPlanItemTool.ts
|
|
2028
|
+
import { tool as tool38 } from "@openai/agents";
|
|
2029
|
+
import { z as z38 } from "zod";
|
|
2030
|
+
|
|
2031
|
+
// ink/contexts/globalPlanContext.ts
|
|
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.")
|
|
2041
|
+
});
|
|
2042
|
+
var addPlanItemTool = tool38({
|
|
2043
|
+
name: "add_plan_item",
|
|
2044
|
+
description: "Add a new item to the plan.",
|
|
2045
|
+
parameters: AddPlanItemParameters,
|
|
2046
|
+
async execute({ text }) {
|
|
2047
|
+
const newItems = [
|
|
2048
|
+
...globalPlanContext.planItems,
|
|
2049
|
+
{
|
|
2050
|
+
id: globalPlanContext.planItems.length + 1,
|
|
2051
|
+
text,
|
|
2052
|
+
status: "todo"
|
|
2053
|
+
}
|
|
2054
|
+
];
|
|
2055
|
+
emitToListeners("SetPlanItems", newItems);
|
|
2056
|
+
return `Added plan item: ${text}`;
|
|
2057
|
+
}
|
|
2058
|
+
});
|
|
2059
|
+
|
|
2060
|
+
// tools/plan/setPlanDescriptionTool.ts
|
|
2061
|
+
import { tool as tool39 } from "@openai/agents";
|
|
2062
|
+
import { z as z39 } from "zod";
|
|
2063
|
+
var SetPlanDescriptionParameters = z39.object({
|
|
2064
|
+
description: z39.string().describe("A high-level description of the overall plan and goals.")
|
|
2065
|
+
});
|
|
2066
|
+
var setPlanDescriptionTool = tool39({
|
|
2067
|
+
name: "set_plan_description",
|
|
2068
|
+
description: "Set the high-level description for the current plan.",
|
|
2069
|
+
parameters: SetPlanDescriptionParameters,
|
|
2070
|
+
async execute({ description }) {
|
|
2071
|
+
emitToListeners("SetPlanDescription", description);
|
|
2072
|
+
return `Plan description updated to: ${description}`;
|
|
2073
|
+
}
|
|
2074
|
+
});
|
|
2075
|
+
|
|
2076
|
+
// tools/plan/setPlanItemsTool.ts
|
|
2077
|
+
import { tool as tool40 } from "@openai/agents";
|
|
2078
|
+
import { z as z40 } from "zod";
|
|
2079
|
+
var SetPlanItemsParameters = z40.object({
|
|
2080
|
+
items: z40.array(z40.string()).describe("An array of task descriptions to set as the plan items.")
|
|
2081
|
+
});
|
|
2082
|
+
var setPlanItemsTool = tool40({
|
|
2083
|
+
name: "set_plan_items",
|
|
2084
|
+
description: "Set multiple plan items at once, replacing any existing items.",
|
|
2085
|
+
parameters: SetPlanItemsParameters,
|
|
2086
|
+
async execute({ items }) {
|
|
2087
|
+
const newItems = items.map((text, index) => ({
|
|
2088
|
+
id: index + 1,
|
|
2089
|
+
text,
|
|
2090
|
+
status: "todo"
|
|
2091
|
+
}));
|
|
2092
|
+
emitToListeners("SetPlanItems", newItems);
|
|
2093
|
+
return `Set ${newItems.length} plan items.`;
|
|
2094
|
+
}
|
|
2095
|
+
});
|
|
2096
|
+
|
|
2097
|
+
// tools/plan/updatePlanItemTool.ts
|
|
2098
|
+
import { tool as tool41 } from "@openai/agents";
|
|
2099
|
+
import { z as z41 } from "zod";
|
|
2100
|
+
var UpdatePlanItemParameters = z41.object({
|
|
2101
|
+
id: z41.number().describe("The ID of the plan item to update."),
|
|
2102
|
+
text: z41.string().describe("The new description of the task."),
|
|
2103
|
+
status: z41.enum(["unchanged", "todo", "in-progress", "done", "not-needed"]).describe(
|
|
2104
|
+
"The new status of the task."
|
|
2105
|
+
)
|
|
2106
|
+
});
|
|
2107
|
+
var updatePlanItemTool = tool41({
|
|
2108
|
+
name: "update_plan_item",
|
|
2109
|
+
description: "Update an existing plan item.",
|
|
2110
|
+
parameters: UpdatePlanItemParameters,
|
|
2111
|
+
async execute({ id, text, status }) {
|
|
2112
|
+
const newItems = globalPlanContext.planItems.map((item) => {
|
|
2113
|
+
if (item.id === id) {
|
|
2114
|
+
return {
|
|
2115
|
+
...item,
|
|
2116
|
+
text: text || item.text,
|
|
2117
|
+
status: status && status !== "unchanged" ? status : item.status
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
return item;
|
|
2121
|
+
});
|
|
2122
|
+
const itemExists = globalPlanContext.planItems.some((item) => item.id === id);
|
|
2123
|
+
if (!itemExists) {
|
|
2124
|
+
return `Error: Plan item with ID ${id} not found.`;
|
|
2125
|
+
}
|
|
2126
|
+
emitToListeners("SetPlanItems", newItems);
|
|
2127
|
+
return `Updated plan item ${id}`;
|
|
2128
|
+
}
|
|
2129
|
+
});
|
|
2130
|
+
|
|
1910
2131
|
// tools/factory.ts
|
|
1911
2132
|
function createTools() {
|
|
1912
2133
|
return [
|
|
@@ -1919,6 +2140,7 @@ function createTools() {
|
|
|
1919
2140
|
browserNavigateTool,
|
|
1920
2141
|
browserScreenshotTool,
|
|
1921
2142
|
browserTypeTool,
|
|
2143
|
+
addPlanItemTool,
|
|
1922
2144
|
changeCwdTool,
|
|
1923
2145
|
checkHarperStatusTool,
|
|
1924
2146
|
codeInterpreterTool,
|
|
@@ -1943,10 +2165,13 @@ function createTools() {
|
|
|
1943
2165
|
readHarperLogsTool,
|
|
1944
2166
|
setInterpreterAutoApproveTool,
|
|
1945
2167
|
setPatchAutoApproveTool,
|
|
2168
|
+
setPlanDescriptionTool,
|
|
2169
|
+
setPlanItemsTool,
|
|
1946
2170
|
setShellAutoApproveTool,
|
|
1947
2171
|
shellTool,
|
|
1948
2172
|
startHarperTool,
|
|
1949
|
-
stopHarperTool
|
|
2173
|
+
stopHarperTool,
|
|
2174
|
+
updatePlanItemTool
|
|
1950
2175
|
];
|
|
1951
2176
|
}
|
|
1952
2177
|
|
|
@@ -2037,9 +2262,9 @@ Stack: ${String(err.stack).split("\n").slice(0, 8).join("\n")}` : "";
|
|
|
2037
2262
|
|
|
2038
2263
|
// utils/sessions/DiskSession.ts
|
|
2039
2264
|
import { MemorySession } from "@openai/agents";
|
|
2040
|
-
import { existsSync as
|
|
2265
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2041
2266
|
import { mkdir as mkdir2, readFile as readFile5, rename, writeFile as writeFile3 } from "fs/promises";
|
|
2042
|
-
import { dirname as
|
|
2267
|
+
import { dirname as dirname7 } from "path";
|
|
2043
2268
|
var DiskSession = class extends MemorySession {
|
|
2044
2269
|
filePath;
|
|
2045
2270
|
ready;
|
|
@@ -2074,7 +2299,7 @@ var DiskSession = class extends MemorySession {
|
|
|
2074
2299
|
}
|
|
2075
2300
|
}
|
|
2076
2301
|
async loadStorage() {
|
|
2077
|
-
if (
|
|
2302
|
+
if (existsSync8(this.filePath)) {
|
|
2078
2303
|
try {
|
|
2079
2304
|
const data = await readFile5(this.filePath, "utf-8");
|
|
2080
2305
|
const parsed = JSON.parse(data);
|
|
@@ -2090,8 +2315,8 @@ var DiskSession = class extends MemorySession {
|
|
|
2090
2315
|
async updateStorage(update) {
|
|
2091
2316
|
const storage = await this.loadStorage();
|
|
2092
2317
|
update(storage);
|
|
2093
|
-
const dir =
|
|
2094
|
-
if (!
|
|
2318
|
+
const dir = dirname7(this.filePath);
|
|
2319
|
+
if (!existsSync8(dir)) {
|
|
2095
2320
|
await mkdir2(dir, { recursive: true });
|
|
2096
2321
|
}
|
|
2097
2322
|
const data = JSON.stringify(storage, null, 2);
|
|
@@ -2389,7 +2614,7 @@ function createSession(sessionPath = null) {
|
|
|
2389
2614
|
}
|
|
2390
2615
|
|
|
2391
2616
|
// agent/runAgentForOnePass.ts
|
|
2392
|
-
import { run as run2 } from "@openai/agents";
|
|
2617
|
+
import { run as run2, system as system2 } from "@openai/agents";
|
|
2393
2618
|
|
|
2394
2619
|
// ink/contexts/ActionsContext.tsx
|
|
2395
2620
|
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
|
|
@@ -2547,6 +2772,9 @@ var CostTracker = class {
|
|
|
2547
2772
|
);
|
|
2548
2773
|
return this.getTotalCost() + turnCost + compactionCost;
|
|
2549
2774
|
}
|
|
2775
|
+
extractCachedTokens(inputTokenDetails) {
|
|
2776
|
+
return extractCachedTokens(inputTokenDetails);
|
|
2777
|
+
}
|
|
2550
2778
|
recordTurn(model, usage, compactionModel) {
|
|
2551
2779
|
const { turnCost, compactionCost, unknownPrices } = this.calculateUsageCosts(model, usage, compactionModel);
|
|
2552
2780
|
this.totalInputTokens += usage.inputTokens;
|
|
@@ -2614,6 +2842,64 @@ var CostTracker = class {
|
|
|
2614
2842
|
};
|
|
2615
2843
|
var costTracker = new CostTracker();
|
|
2616
2844
|
|
|
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
|
+
|
|
2617
2903
|
// utils/strings/isTrue.ts
|
|
2618
2904
|
function isTrue(v) {
|
|
2619
2905
|
if (v === void 0) {
|
|
@@ -2625,6 +2911,7 @@ function isTrue(v) {
|
|
|
2625
2911
|
|
|
2626
2912
|
// agent/showErrorToUser.ts
|
|
2627
2913
|
function showErrorToUser(error, lastToolCallInfo) {
|
|
2914
|
+
logError(error);
|
|
2628
2915
|
const err = error ?? {};
|
|
2629
2916
|
const name = err.name || "Error";
|
|
2630
2917
|
const message = err.message || String(err);
|
|
@@ -2663,15 +2950,106 @@ Last tool call: ${lastToolCallInfo}` : "";
|
|
|
2663
2950
|
// agent/runAgentForOnePass.ts
|
|
2664
2951
|
async function runAgentForOnePass(agent, session, input, controller) {
|
|
2665
2952
|
let lastToolCallInfo = null;
|
|
2953
|
+
const toolInfoMap = /* @__PURE__ */ new Map();
|
|
2954
|
+
const removeToolListener = addListener("RegisterToolInfo", (info) => {
|
|
2955
|
+
toolInfoMap.set(info.callId, info);
|
|
2956
|
+
});
|
|
2666
2957
|
try {
|
|
2667
2958
|
let hasStartedResponse = false;
|
|
2668
|
-
|
|
2959
|
+
let adjustedInput = input;
|
|
2960
|
+
const noPlanYet = globalPlanContext.planItems.length === 0 && (!globalPlanContext.planDescription || globalPlanContext.planDescription.trim().length === 0);
|
|
2961
|
+
if (noPlanYet && (typeof input === "string" || Array.isArray(input))) {
|
|
2962
|
+
const planningInstruction = [
|
|
2963
|
+
"If there is no current plan, first establish one and keep it updated:",
|
|
2964
|
+
"- Use the tools to manage the plan:",
|
|
2965
|
+
" \u2022 set_plan_description(description)",
|
|
2966
|
+
" \u2022 set_plan_items(items: string[])",
|
|
2967
|
+
" \u2022 add_plan_item(text)",
|
|
2968
|
+
" \u2022 update_plan_item(id, text, status: 'todo' | 'in-progress' | 'done' | 'not-needed' | 'unchanged')",
|
|
2969
|
+
"- After setting the plan, as you progress, mark items as in-progress, done, or not-needed.",
|
|
2970
|
+
"- Keep the plan concise and actionable. Update statuses as you move forward."
|
|
2971
|
+
].join("\n");
|
|
2972
|
+
if (typeof input === "string") {
|
|
2973
|
+
adjustedInput = [
|
|
2974
|
+
system2(planningInstruction),
|
|
2975
|
+
{ type: "message", role: "user", content: input }
|
|
2976
|
+
];
|
|
2977
|
+
} else {
|
|
2978
|
+
adjustedInput = [system2(planningInstruction), ...input];
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
const stream = await run2(agent, adjustedInput, {
|
|
2669
2982
|
session,
|
|
2670
2983
|
stream: true,
|
|
2671
2984
|
signal: controller.signal,
|
|
2672
2985
|
maxTurns: trackedState.maxTurns
|
|
2673
2986
|
});
|
|
2674
2987
|
for await (const event of stream) {
|
|
2988
|
+
if (trackedState.monitorRateLimits) {
|
|
2989
|
+
const { requests, tokens } = rateLimitTracker.isApproachingLimit(trackedState.rateLimitThreshold);
|
|
2990
|
+
const veryCloseThreshold = Math.min(99, trackedState.rateLimitThreshold + 15);
|
|
2991
|
+
const veryClose = rateLimitTracker.isApproachingLimit(veryCloseThreshold);
|
|
2992
|
+
if (veryClose.requests || veryClose.tokens) {
|
|
2993
|
+
emitToListeners("SetInputMode", "approving");
|
|
2994
|
+
emitToListeners("PushNewMessages", [{
|
|
2995
|
+
type: "agent",
|
|
2996
|
+
text: "Rate limit nearly exhausted. Approve to continue or wait for reset.",
|
|
2997
|
+
version: 1
|
|
2998
|
+
}]);
|
|
2999
|
+
const approval = await new Promise((resolve2) => {
|
|
3000
|
+
const removeApprove = addListener("ApproveCurrentApproval", () => {
|
|
3001
|
+
removeApprove();
|
|
3002
|
+
removeDeny();
|
|
3003
|
+
resolve2("approved");
|
|
3004
|
+
});
|
|
3005
|
+
const removeDeny = addListener("DenyCurrentApproval", () => {
|
|
3006
|
+
removeApprove();
|
|
3007
|
+
removeDeny();
|
|
3008
|
+
resolve2("denied");
|
|
3009
|
+
});
|
|
3010
|
+
});
|
|
3011
|
+
if (approval === "denied") {
|
|
3012
|
+
emitToListeners("SetInputMode", "denied");
|
|
3013
|
+
emitToListeners("PushNewMessages", [{
|
|
3014
|
+
type: "agent",
|
|
3015
|
+
text: "Operation canceled due to rate limits.",
|
|
3016
|
+
version: 1
|
|
3017
|
+
}]);
|
|
3018
|
+
if (controller) {
|
|
3019
|
+
controller.abort();
|
|
3020
|
+
}
|
|
3021
|
+
process.exitCode = 1;
|
|
3022
|
+
await handleExit();
|
|
3023
|
+
}
|
|
3024
|
+
} else if (requests || tokens) {
|
|
3025
|
+
emitToListeners("PushNewMessages", [{ type: "agent", text: "Throttling to avoid rate limits\u2026", version: 1 }]);
|
|
3026
|
+
const status = rateLimitTracker.getStatus();
|
|
3027
|
+
let backoffMs = 1500;
|
|
3028
|
+
const parseReset = (s) => {
|
|
3029
|
+
if (!s) {
|
|
3030
|
+
return null;
|
|
3031
|
+
}
|
|
3032
|
+
const ms = /([0-9]+)ms/.exec(s)?.[1];
|
|
3033
|
+
if (ms) {
|
|
3034
|
+
return parseInt(ms, 10);
|
|
3035
|
+
}
|
|
3036
|
+
const sec = /([0-9]+)s/.exec(s)?.[1];
|
|
3037
|
+
if (sec) {
|
|
3038
|
+
return parseInt(sec, 10) * 1e3;
|
|
3039
|
+
}
|
|
3040
|
+
const min = /([0-9]+)m/.exec(s)?.[1];
|
|
3041
|
+
if (min) {
|
|
3042
|
+
return parseInt(min, 10) * 6e4;
|
|
3043
|
+
}
|
|
3044
|
+
return null;
|
|
3045
|
+
};
|
|
3046
|
+
const resets = [parseReset(status.resetRequests || void 0), parseReset(status.resetTokens || void 0)].filter(Boolean);
|
|
3047
|
+
if (resets.length > 0) {
|
|
3048
|
+
backoffMs = Math.max(backoffMs, Math.min(...resets));
|
|
3049
|
+
}
|
|
3050
|
+
await sleep(backoffMs);
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
2675
3053
|
switch (event.type) {
|
|
2676
3054
|
case "raw_model_stream_event":
|
|
2677
3055
|
const data = event.data;
|
|
@@ -2698,6 +3076,19 @@ async function runAgentForOnePass(agent, session, input, controller) {
|
|
|
2698
3076
|
emitToListeners("SetInputMode", "waiting");
|
|
2699
3077
|
break;
|
|
2700
3078
|
}
|
|
3079
|
+
const currentEstimatedCost = costTracker.getEstimatedTotalCost(
|
|
3080
|
+
stream.state.usage,
|
|
3081
|
+
trackedState.model,
|
|
3082
|
+
trackedState.compactionModel
|
|
3083
|
+
);
|
|
3084
|
+
const sessionStats2 = costTracker.getSessionStats();
|
|
3085
|
+
emitToListeners("UpdateCost", {
|
|
3086
|
+
totalCost: currentEstimatedCost,
|
|
3087
|
+
inputTokens: sessionStats2.inputTokens + stream.state.usage.inputTokens,
|
|
3088
|
+
outputTokens: sessionStats2.outputTokens + stream.state.usage.outputTokens,
|
|
3089
|
+
cachedInputTokens: sessionStats2.cachedInputTokens + costTracker.extractCachedTokens(stream.state.usage.inputTokensDetails),
|
|
3090
|
+
hasUnknownPrices: sessionStats2.hasUnknownPrices
|
|
3091
|
+
});
|
|
2701
3092
|
break;
|
|
2702
3093
|
case "run_item_stream_event":
|
|
2703
3094
|
if (event.name === "tool_called") {
|
|
@@ -2711,17 +3102,20 @@ async function runAgentForOnePass(agent, session, input, controller) {
|
|
|
2711
3102
|
args = JSON.stringify(item.operation);
|
|
2712
3103
|
}
|
|
2713
3104
|
const displayedArgs = args ? `(${args})` : "()";
|
|
3105
|
+
const callId = item.callId || item.id;
|
|
2714
3106
|
emitToListeners("PushNewMessages", [{
|
|
2715
3107
|
type: "tool",
|
|
2716
3108
|
text: name,
|
|
2717
3109
|
args: displayedArgs,
|
|
2718
|
-
version: 1
|
|
3110
|
+
version: 1,
|
|
3111
|
+
callId
|
|
2719
3112
|
}]);
|
|
2720
3113
|
emitToListeners("AddActionItem", {
|
|
2721
3114
|
kind: name === "apply_patch" || item.type === "apply_patch_call" ? "apply_patch" : name === "create_new_harper_application" ? "create_app" : "tool",
|
|
2722
3115
|
title: name,
|
|
2723
3116
|
detail: displayedArgs,
|
|
2724
|
-
running: false
|
|
3117
|
+
running: false,
|
|
3118
|
+
callId
|
|
2725
3119
|
});
|
|
2726
3120
|
lastToolCallInfo = `${name}${displayedArgs}`;
|
|
2727
3121
|
}
|
|
@@ -2755,31 +3149,59 @@ async function runAgentForOnePass(agent, session, input, controller) {
|
|
|
2755
3149
|
trackedState.model,
|
|
2756
3150
|
trackedState.compactionModel
|
|
2757
3151
|
);
|
|
3152
|
+
const sessionStats = costTracker.getSessionStats();
|
|
2758
3153
|
emitToListeners("UpdateCost", {
|
|
2759
|
-
|
|
2760
|
-
|
|
3154
|
+
totalCost: estimatedTotalCost,
|
|
3155
|
+
inputTokens: sessionStats.inputTokens + stream.state.usage.inputTokens,
|
|
3156
|
+
outputTokens: sessionStats.outputTokens + stream.state.usage.outputTokens,
|
|
3157
|
+
cachedInputTokens: sessionStats.cachedInputTokens + costTracker.extractCachedTokens(stream.state.usage.inputTokensDetails),
|
|
3158
|
+
hasUnknownPrices: sessionStats.hasUnknownPrices
|
|
2761
3159
|
});
|
|
2762
3160
|
if (stream.interruptions?.length) {
|
|
2763
3161
|
emitToListeners("SetThinking", false);
|
|
2764
3162
|
emitToListeners("SetInputMode", "approving");
|
|
2765
3163
|
for (const interruption of stream.interruptions) {
|
|
3164
|
+
const callId = interruption.callId || interruption.id;
|
|
3165
|
+
const toolName = interruption.toolName;
|
|
3166
|
+
const isModalTool = toolName === "apply_patch" || toolName === "code_interpreter" || toolName === "shell";
|
|
2766
3167
|
const myApprovalId = actionId;
|
|
2767
3168
|
emitToListeners("AddActionItem", {
|
|
2768
3169
|
id: myApprovalId,
|
|
2769
|
-
kind: "approval",
|
|
2770
|
-
title: "approval",
|
|
3170
|
+
kind: isModalTool ? toolName === "apply_patch" ? "apply_patch" : "approval" : "approval",
|
|
3171
|
+
title: isModalTool ? toolName : "approval",
|
|
2771
3172
|
detail: lastToolCallInfo ?? "awaiting approval",
|
|
2772
|
-
running: true
|
|
3173
|
+
running: true,
|
|
3174
|
+
callId
|
|
2773
3175
|
});
|
|
2774
|
-
const
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
approved
|
|
2779
|
-
}
|
|
2780
|
-
|
|
3176
|
+
const approvalPromise = new Promise((resolve2) => {
|
|
3177
|
+
const removeApprove = addListener("ApproveCurrentApproval", () => {
|
|
3178
|
+
removeApprove();
|
|
3179
|
+
removeDeny();
|
|
3180
|
+
resolve2("approved");
|
|
3181
|
+
});
|
|
3182
|
+
const removeDeny = addListener("DenyCurrentApproval", () => {
|
|
3183
|
+
removeApprove();
|
|
3184
|
+
removeDeny();
|
|
3185
|
+
resolve2("denied");
|
|
3186
|
+
});
|
|
3187
|
+
});
|
|
3188
|
+
let result;
|
|
3189
|
+
if (isModalTool) {
|
|
3190
|
+
result = await approvalPromise;
|
|
3191
|
+
} else {
|
|
3192
|
+
const textInputPromise = onceListener("PushNewMessages").then((messages) => {
|
|
3193
|
+
let approved = false;
|
|
3194
|
+
for (const newMessage of messages) {
|
|
3195
|
+
if (newMessage.type === "user" && isTrue(newMessage.text)) {
|
|
3196
|
+
approved = true;
|
|
3197
|
+
}
|
|
3198
|
+
newMessage.handled = true;
|
|
3199
|
+
}
|
|
3200
|
+
return approved ? "approved" : "denied";
|
|
3201
|
+
});
|
|
3202
|
+
result = await Promise.race([approvalPromise, textInputPromise]);
|
|
2781
3203
|
}
|
|
2782
|
-
if (approved) {
|
|
3204
|
+
if (result === "approved") {
|
|
2783
3205
|
emitToListeners("SetInputMode", "approved");
|
|
2784
3206
|
emitToListeners("UpdateActionItem", {
|
|
2785
3207
|
id: myApprovalId,
|
|
@@ -2798,9 +3220,11 @@ async function runAgentForOnePass(agent, session, input, controller) {
|
|
|
2798
3220
|
});
|
|
2799
3221
|
stream.state.reject(interruption);
|
|
2800
3222
|
}
|
|
3223
|
+
emitToListeners("CloseApprovalViewer", void 0);
|
|
2801
3224
|
}
|
|
2802
3225
|
emitToListeners("SetThinking", true);
|
|
2803
3226
|
setTimeout(curryEmitToListeners("SetInputMode", "waiting"), 1e3);
|
|
3227
|
+
removeToolListener();
|
|
2804
3228
|
return stream.state;
|
|
2805
3229
|
} else {
|
|
2806
3230
|
costTracker.recordTurn(
|
|
@@ -2810,6 +3234,7 @@ async function runAgentForOnePass(agent, session, input, controller) {
|
|
|
2810
3234
|
);
|
|
2811
3235
|
emitToListeners("UpdateCost", costTracker.getSessionStats());
|
|
2812
3236
|
}
|
|
3237
|
+
removeToolListener();
|
|
2813
3238
|
return null;
|
|
2814
3239
|
} catch (error) {
|
|
2815
3240
|
showErrorToUser(error, lastToolCallInfo);
|
|
@@ -2931,8 +3356,8 @@ import "react";
|
|
|
2931
3356
|
|
|
2932
3357
|
// ink/components/ChatContent.tsx
|
|
2933
3358
|
import { Spinner as Spinner2 } from "@inkjs/ui";
|
|
2934
|
-
import { Box as Box10, Text as Text10, useInput as
|
|
2935
|
-
import React11, { useCallback as useCallback4, useEffect as
|
|
3359
|
+
import { Box as Box10, Text as Text10, useInput as useInput4 } from "ink";
|
|
3360
|
+
import React11, { useCallback as useCallback4, useEffect as useEffect8, useMemo as useMemo10, useRef as useRef2, useState as useState13 } from "react";
|
|
2936
3361
|
|
|
2937
3362
|
// ink/contexts/ChatContext.tsx
|
|
2938
3363
|
import { createContext as createContext3, useContext as useContext3, useMemo as useMemo3, useState as useState3 } from "react";
|
|
@@ -3496,6 +3921,19 @@ function ActionsView({ height, isFocused }) {
|
|
|
3496
3921
|
if (key.downArrow) {
|
|
3497
3922
|
setSelectedIndex((prev) => Math.min(actions.length - 1, prev + 1));
|
|
3498
3923
|
}
|
|
3924
|
+
if (key.return) {
|
|
3925
|
+
const selected = actions[selectedIndex];
|
|
3926
|
+
if (selected && (selected.kind === "apply_patch" || selected.kind === "approval" || selected.title === "shell" || selected.title === "code_interpreter")) {
|
|
3927
|
+
if (selected.callId) {
|
|
3928
|
+
emitToListeners("OpenApprovalViewer", {
|
|
3929
|
+
type: selected.kind === "apply_patch" ? "update_file" : selected.title,
|
|
3930
|
+
mode: "info",
|
|
3931
|
+
callId: selected.callId,
|
|
3932
|
+
actionId: selected.id
|
|
3933
|
+
});
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
}
|
|
3499
3937
|
});
|
|
3500
3938
|
const renderOverflowTop = useCallback2((count) => /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
3501
3939
|
/* @__PURE__ */ jsx6(Text3, { color: "gray", dimColor: true, children: "\u2502" }),
|
|
@@ -3650,15 +4088,6 @@ import "react";
|
|
|
3650
4088
|
|
|
3651
4089
|
// ink/contexts/PlanContext.tsx
|
|
3652
4090
|
import { createContext as createContext5, useContext as useContext5, useMemo as useMemo6, useState as useState8 } from "react";
|
|
3653
|
-
|
|
3654
|
-
// ink/contexts/globalPlanContext.ts
|
|
3655
|
-
var globalPlanContext = {
|
|
3656
|
-
planDescription: "",
|
|
3657
|
-
planItems: [],
|
|
3658
|
-
progress: 0
|
|
3659
|
-
};
|
|
3660
|
-
|
|
3661
|
-
// ink/contexts/PlanContext.tsx
|
|
3662
4091
|
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
3663
4092
|
var PlanContext = createContext5(globalPlanContext);
|
|
3664
4093
|
var usePlan = () => {
|
|
@@ -3671,26 +4100,26 @@ var usePlan = () => {
|
|
|
3671
4100
|
var PlanProvider = ({
|
|
3672
4101
|
children
|
|
3673
4102
|
}) => {
|
|
3674
|
-
const [
|
|
3675
|
-
const [progress, setProgress] = useState8(globalPlanContext.progress);
|
|
4103
|
+
const [planDescription, setPlanDescription] = useState8(globalPlanContext.planDescription);
|
|
3676
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
|
+
}, []);
|
|
3677
4110
|
useListener("SetPlanItems", (planItems2) => {
|
|
3678
4111
|
globalPlanContext.planItems = planItems2;
|
|
3679
|
-
const completedCount = planItems2.filter((item) => item.
|
|
4112
|
+
const completedCount = planItems2.filter((item) => item.status === "done" || item.status === "not-needed").length;
|
|
3680
4113
|
const progress2 = planItems2.length === 0 ? 0 : Math.round(completedCount / planItems2.length * 100);
|
|
3681
4114
|
globalPlanContext.progress = progress2;
|
|
3682
4115
|
setPlanItems(planItems2);
|
|
3683
4116
|
setProgress(progress2);
|
|
3684
4117
|
}, []);
|
|
3685
|
-
useListener("SetGoal", (newGoal) => {
|
|
3686
|
-
globalPlanContext.planDescription = newGoal;
|
|
3687
|
-
setGoal(newGoal);
|
|
3688
|
-
}, []);
|
|
3689
4118
|
const value = useMemo6(() => ({
|
|
3690
4119
|
progress,
|
|
3691
|
-
|
|
4120
|
+
planDescription,
|
|
3692
4121
|
planItems
|
|
3693
|
-
}), [progress,
|
|
4122
|
+
}), [progress, planDescription, planItems]);
|
|
3694
4123
|
return /* @__PURE__ */ jsx10(PlanContext.Provider, { value, children });
|
|
3695
4124
|
};
|
|
3696
4125
|
|
|
@@ -3700,10 +4129,17 @@ function PlanView() {
|
|
|
3700
4129
|
const { planDescription, planItems, progress } = usePlan();
|
|
3701
4130
|
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", flexGrow: 1, children: [
|
|
3702
4131
|
/* @__PURE__ */ jsx11(Box6, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx11(Text6, { italic: true, children: planDescription }) }),
|
|
3703
|
-
/* @__PURE__ */ jsx11(Box6, { flexDirection: "column", flexGrow: 1, children: planItems.map((planItem) => /* @__PURE__ */ jsx11(Box6, { children: /* @__PURE__ */ jsxs6(
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
4132
|
+
/* @__PURE__ */ jsx11(Box6, { flexDirection: "column", flexGrow: 1, children: planItems.map((planItem) => /* @__PURE__ */ jsx11(Box6, { children: /* @__PURE__ */ jsxs6(
|
|
4133
|
+
Text6,
|
|
4134
|
+
{
|
|
4135
|
+
color: planItem.status === "done" ? "green" : planItem.status === "in-progress" ? "yellow" : planItem.status === "not-needed" ? "gray" : "white",
|
|
4136
|
+
dimColor: planItem.status === "not-needed",
|
|
4137
|
+
children: [
|
|
4138
|
+
planItem.status === "done" ? " \u25CF " : planItem.status === "in-progress" ? " \u25B6 " : planItem.status === "not-needed" ? " \u25CC " : " \u25CB ",
|
|
4139
|
+
planItem.text
|
|
4140
|
+
]
|
|
4141
|
+
}
|
|
4142
|
+
) }, planItem.id)) }),
|
|
3707
4143
|
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, children: [
|
|
3708
4144
|
/* @__PURE__ */ jsxs6(Text6, { bold: true, children: [
|
|
3709
4145
|
"PROGRESS: ",
|
|
@@ -3716,12 +4152,12 @@ function PlanView() {
|
|
|
3716
4152
|
}
|
|
3717
4153
|
|
|
3718
4154
|
// ink/components/SettingsView.tsx
|
|
3719
|
-
import { Box as Box7, Text as Text7 } from "ink";
|
|
4155
|
+
import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
|
|
3720
4156
|
import path7 from "path";
|
|
3721
|
-
import { useMemo as useMemo8 } from "react";
|
|
4157
|
+
import { useEffect as useEffect6, useMemo as useMemo8, useState as useState10 } from "react";
|
|
3722
4158
|
|
|
3723
4159
|
// ink/contexts/SettingsContext.tsx
|
|
3724
|
-
import { createContext as createContext6, useContext as useContext6, useMemo as useMemo7 } from "react";
|
|
4160
|
+
import { createContext as createContext6, useContext as useContext6, useMemo as useMemo7, useState as useState9 } from "react";
|
|
3725
4161
|
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
3726
4162
|
var SettingsContext = createContext6(void 0);
|
|
3727
4163
|
var useSettings = () => {
|
|
@@ -3734,22 +4170,128 @@ var useSettings = () => {
|
|
|
3734
4170
|
var SettingsProvider = ({
|
|
3735
4171
|
children
|
|
3736
4172
|
}) => {
|
|
4173
|
+
const [version, setVersion] = useState9(0);
|
|
4174
|
+
useListener("SettingsUpdated", () => {
|
|
4175
|
+
setVersion((v) => v + 1);
|
|
4176
|
+
}, []);
|
|
3737
4177
|
const value = useMemo7(() => ({
|
|
4178
|
+
version,
|
|
3738
4179
|
model: trackedState.model,
|
|
3739
4180
|
compactionModel: trackedState.compactionModel,
|
|
3740
4181
|
sessionPath: trackedState.sessionPath,
|
|
3741
4182
|
cwd: trackedState.cwd,
|
|
3742
4183
|
useFlexTier: trackedState.useFlexTier,
|
|
3743
4184
|
maxTurns: trackedState.maxTurns,
|
|
3744
|
-
maxCost: trackedState.maxCost
|
|
3745
|
-
|
|
4185
|
+
maxCost: trackedState.maxCost,
|
|
4186
|
+
autoApproveCodeInterpreter: trackedState.autoApproveCodeInterpreter,
|
|
4187
|
+
autoApprovePatches: trackedState.autoApprovePatches,
|
|
4188
|
+
autoApproveShell: trackedState.autoApproveShell,
|
|
4189
|
+
monitorRateLimits: trackedState.monitorRateLimits,
|
|
4190
|
+
rateLimitThreshold: trackedState.rateLimitThreshold,
|
|
4191
|
+
rateLimitStatus: rateLimitTracker.getStatus()
|
|
4192
|
+
}), [version]);
|
|
3746
4193
|
return /* @__PURE__ */ jsx12(SettingsContext.Provider, { value, children });
|
|
3747
4194
|
};
|
|
3748
4195
|
|
|
3749
4196
|
// ink/components/SettingsView.tsx
|
|
3750
4197
|
import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3751
4198
|
function SettingsView({ isDense = false }) {
|
|
3752
|
-
const {
|
|
4199
|
+
const {
|
|
4200
|
+
model,
|
|
4201
|
+
compactionModel,
|
|
4202
|
+
sessionPath,
|
|
4203
|
+
cwd,
|
|
4204
|
+
useFlexTier,
|
|
4205
|
+
maxTurns,
|
|
4206
|
+
maxCost,
|
|
4207
|
+
autoApproveCodeInterpreter: initialAutoApproveCodeInterpreter,
|
|
4208
|
+
autoApprovePatches: initialAutoApprovePatches,
|
|
4209
|
+
autoApproveShell: initialAutoApproveShell,
|
|
4210
|
+
monitorRateLimits: initialMonitorRateLimits,
|
|
4211
|
+
rateLimitThreshold: initialRateLimitThreshold,
|
|
4212
|
+
rateLimitStatus
|
|
4213
|
+
} = useSettings();
|
|
4214
|
+
const { focusedArea } = useChat();
|
|
4215
|
+
const [autoApproveCodeInterpreter, setAutoApproveCodeInterpreter] = useState10(initialAutoApproveCodeInterpreter);
|
|
4216
|
+
const [autoApprovePatches, setAutoApprovePatches] = useState10(initialAutoApprovePatches);
|
|
4217
|
+
const [autoApproveShell, setAutoApproveShell] = useState10(initialAutoApproveShell);
|
|
4218
|
+
const [monitorRateLimits, setMonitorRateLimits] = useState10(initialMonitorRateLimits);
|
|
4219
|
+
useEffect6(() => {
|
|
4220
|
+
setAutoApproveCodeInterpreter(initialAutoApproveCodeInterpreter);
|
|
4221
|
+
}, [initialAutoApproveCodeInterpreter]);
|
|
4222
|
+
useEffect6(() => {
|
|
4223
|
+
setAutoApprovePatches(initialAutoApprovePatches);
|
|
4224
|
+
}, [initialAutoApprovePatches]);
|
|
4225
|
+
useEffect6(() => {
|
|
4226
|
+
setAutoApproveShell(initialAutoApproveShell);
|
|
4227
|
+
}, [initialAutoApproveShell]);
|
|
4228
|
+
useEffect6(() => {
|
|
4229
|
+
setMonitorRateLimits(initialMonitorRateLimits);
|
|
4230
|
+
}, [initialMonitorRateLimits]);
|
|
4231
|
+
const [selectedIndex, setSelectedIndex] = useState10(0);
|
|
4232
|
+
const selectableOptions = useMemo8(() => [
|
|
4233
|
+
{
|
|
4234
|
+
label: "Code Interpreter",
|
|
4235
|
+
value: autoApproveCodeInterpreter,
|
|
4236
|
+
envKey: "HARPER_AGENT_AUTO_APPROVE_CODE_INTERPRETER",
|
|
4237
|
+
setter: setAutoApproveCodeInterpreter
|
|
4238
|
+
},
|
|
4239
|
+
{
|
|
4240
|
+
label: "File Patches",
|
|
4241
|
+
value: autoApprovePatches,
|
|
4242
|
+
envKey: "HARPER_AGENT_AUTO_APPROVE_PATCHES",
|
|
4243
|
+
setter: setAutoApprovePatches
|
|
4244
|
+
},
|
|
4245
|
+
{
|
|
4246
|
+
label: "Shell Commands",
|
|
4247
|
+
value: autoApproveShell,
|
|
4248
|
+
envKey: "HARPER_AGENT_AUTO_APPROVE_SHELL",
|
|
4249
|
+
setter: setAutoApproveShell
|
|
4250
|
+
},
|
|
4251
|
+
{
|
|
4252
|
+
label: "Monitor Rate Limits",
|
|
4253
|
+
value: monitorRateLimits,
|
|
4254
|
+
envKey: "HARPER_AGENT_MONITOR_RATE_LIMITS",
|
|
4255
|
+
setter: (val) => {
|
|
4256
|
+
setMonitorRateLimits(val);
|
|
4257
|
+
updateEnv("HARPER_AGENT_MONITOR_RATE_LIMITS", val ? "true" : "false");
|
|
4258
|
+
emitToListeners("SettingsUpdated", void 0);
|
|
4259
|
+
}
|
|
4260
|
+
},
|
|
4261
|
+
{
|
|
4262
|
+
label: "<edit settings>",
|
|
4263
|
+
isAction: true,
|
|
4264
|
+
action: () => {
|
|
4265
|
+
bootstrapConfig(() => {
|
|
4266
|
+
});
|
|
4267
|
+
}
|
|
4268
|
+
}
|
|
4269
|
+
], [autoApproveCodeInterpreter, autoApprovePatches, autoApproveShell, monitorRateLimits]);
|
|
4270
|
+
useInput2((_input, key) => {
|
|
4271
|
+
if (focusedArea !== "status") {
|
|
4272
|
+
return;
|
|
4273
|
+
}
|
|
4274
|
+
if (key.upArrow) {
|
|
4275
|
+
setSelectedIndex((prev) => prev > 0 ? prev - 1 : selectableOptions.length - 1);
|
|
4276
|
+
}
|
|
4277
|
+
if (key.downArrow) {
|
|
4278
|
+
setSelectedIndex((prev) => prev < selectableOptions.length - 1 ? prev + 1 : 0);
|
|
4279
|
+
}
|
|
4280
|
+
if (key.return || _input === " ") {
|
|
4281
|
+
const selected = selectableOptions[selectedIndex];
|
|
4282
|
+
if (!selected) {
|
|
4283
|
+
return;
|
|
4284
|
+
}
|
|
4285
|
+
if ("isAction" in selected && selected.isAction) {
|
|
4286
|
+
selected.action();
|
|
4287
|
+
} else if ("setter" in selected) {
|
|
4288
|
+
const newValue = !selected.value;
|
|
4289
|
+
selected.setter(newValue);
|
|
4290
|
+
updateEnv(selected.envKey, newValue ? "1" : "0");
|
|
4291
|
+
emitToListeners("SettingsUpdated", void 0);
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
});
|
|
3753
4295
|
const displayPath = useMemo8(() => {
|
|
3754
4296
|
if (!sessionPath) {
|
|
3755
4297
|
return null;
|
|
@@ -3783,24 +4325,67 @@ function SettingsView({ isDense = false }) {
|
|
|
3783
4325
|
/* @__PURE__ */ jsx13(Box7, { width: 20, children: /* @__PURE__ */ jsx13(Text7, { children: "Max Turns:" }) }),
|
|
3784
4326
|
/* @__PURE__ */ jsx13(Text7, { children: maxTurns })
|
|
3785
4327
|
] }),
|
|
4328
|
+
/* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
4329
|
+
/* @__PURE__ */ jsx13(Box7, { width: 20, children: /* @__PURE__ */ jsx13(Text7, { children: "Rate Limit Threshold:" }) }),
|
|
4330
|
+
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
4331
|
+
initialRateLimitThreshold,
|
|
4332
|
+
"%"
|
|
4333
|
+
] })
|
|
4334
|
+
] }),
|
|
4335
|
+
rateLimitStatus && rateLimitStatus.limitRequests !== null && /* @__PURE__ */ jsxs7(Box7, { marginBottom, flexDirection: "column", children: [
|
|
4336
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
4337
|
+
/* @__PURE__ */ jsx13(Box7, { width: 20, children: /* @__PURE__ */ jsx13(Text7, { children: "RPM Limit:" }) }),
|
|
4338
|
+
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
4339
|
+
rateLimitStatus.remainingRequests,
|
|
4340
|
+
" / ",
|
|
4341
|
+
rateLimitStatus.limitRequests,
|
|
4342
|
+
" (Reset:",
|
|
4343
|
+
" ",
|
|
4344
|
+
rateLimitStatus.resetRequests,
|
|
4345
|
+
")"
|
|
4346
|
+
] })
|
|
4347
|
+
] }),
|
|
4348
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
4349
|
+
/* @__PURE__ */ jsx13(Box7, { width: 20, children: /* @__PURE__ */ jsx13(Text7, { children: "TPM Limit:" }) }),
|
|
4350
|
+
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
4351
|
+
rateLimitStatus.remainingTokens,
|
|
4352
|
+
" / ",
|
|
4353
|
+
rateLimitStatus.limitTokens,
|
|
4354
|
+
" (Reset: ",
|
|
4355
|
+
rateLimitStatus.resetTokens,
|
|
4356
|
+
")"
|
|
4357
|
+
] })
|
|
4358
|
+
] })
|
|
4359
|
+
] }),
|
|
3786
4360
|
maxCost !== null && /* @__PURE__ */ jsxs7(Box7, { marginBottom, children: [
|
|
3787
4361
|
/* @__PURE__ */ jsx13(Box7, { width: 20, children: /* @__PURE__ */ jsx13(Text7, { children: "Max Cost:" }) }),
|
|
3788
4362
|
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
3789
4363
|
"$",
|
|
3790
4364
|
maxCost.toFixed(2)
|
|
3791
4365
|
] })
|
|
4366
|
+
] }),
|
|
4367
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
4368
|
+
/* @__PURE__ */ jsx13(Text7, { bold: true, children: "Auto-approvals (up/down & space to toggle):" }),
|
|
4369
|
+
selectableOptions.map((option, index) => {
|
|
4370
|
+
const isSelected = index === selectedIndex && focusedArea === "status";
|
|
4371
|
+
return /* @__PURE__ */ jsx13(Box7, { children: /* @__PURE__ */ jsxs7(Text7, { color: isSelected ? "cyan" : "white", children: [
|
|
4372
|
+
isSelected ? "> " : " ",
|
|
4373
|
+
option.label,
|
|
4374
|
+
"isAction" in option ? "" : `: ${option.value ? "ON" : "OFF"}`
|
|
4375
|
+
] }) }, option.label);
|
|
4376
|
+
})
|
|
3792
4377
|
] })
|
|
3793
4378
|
] });
|
|
3794
4379
|
}
|
|
3795
4380
|
|
|
3796
4381
|
// ink/components/UserInput.tsx
|
|
3797
4382
|
import { Box as Box9, Text as Text9 } from "ink";
|
|
3798
|
-
import { useCallback as useCallback3, useState as
|
|
4383
|
+
import { useCallback as useCallback3, useState as useState12 } from "react";
|
|
3799
4384
|
|
|
3800
4385
|
// ink/components/BlinkingTextInput.tsx
|
|
3801
4386
|
import chalk3 from "chalk";
|
|
3802
|
-
import { Box as Box8, Text as Text8, useInput as
|
|
3803
|
-
import { useEffect as
|
|
4387
|
+
import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
|
|
4388
|
+
import { useEffect as useEffect7, useMemo as useMemo9, useReducer, useState as useState11 } from "react";
|
|
3804
4389
|
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
3805
4390
|
var reducer = (state, action) => {
|
|
3806
4391
|
switch (action.type) {
|
|
@@ -3854,8 +4439,8 @@ function BlinkingTextInput({
|
|
|
3854
4439
|
previousValue: defaultValue,
|
|
3855
4440
|
cursorOffset: defaultValue.length
|
|
3856
4441
|
});
|
|
3857
|
-
const [isCursorVisible, setIsCursorVisible] =
|
|
3858
|
-
|
|
4442
|
+
const [isCursorVisible, setIsCursorVisible] = useState11(true);
|
|
4443
|
+
useEffect7(() => {
|
|
3859
4444
|
if (isDisabled) {
|
|
3860
4445
|
setIsCursorVisible(false);
|
|
3861
4446
|
return;
|
|
@@ -3865,7 +4450,7 @@ function BlinkingTextInput({
|
|
|
3865
4450
|
}, 500);
|
|
3866
4451
|
return () => clearInterval(interval);
|
|
3867
4452
|
}, [isDisabled]);
|
|
3868
|
-
|
|
4453
|
+
useEffect7(() => {
|
|
3869
4454
|
if (state.value !== state.previousValue) {
|
|
3870
4455
|
onChange?.(state.value);
|
|
3871
4456
|
}
|
|
@@ -3876,7 +4461,7 @@ function BlinkingTextInput({
|
|
|
3876
4461
|
}
|
|
3877
4462
|
return suggestions.find((s) => s.startsWith(state.value))?.slice(state.value.length) || "";
|
|
3878
4463
|
}, [state.value, suggestions]);
|
|
3879
|
-
|
|
4464
|
+
useInput3(
|
|
3880
4465
|
(input, key) => {
|
|
3881
4466
|
if (key.upArrow || key.downArrow || key.ctrl && input === "c" || key.tab || key.shift && key.tab) {
|
|
3882
4467
|
return;
|
|
@@ -3965,8 +4550,11 @@ var modeSuggestion = {
|
|
|
3965
4550
|
};
|
|
3966
4551
|
function UserInput() {
|
|
3967
4552
|
const { userInputMode, focusedArea } = useChat();
|
|
3968
|
-
const [resetKey, setResetKey] =
|
|
3969
|
-
const [, setBlankLines] =
|
|
4553
|
+
const [resetKey, setResetKey] = useState12(0);
|
|
4554
|
+
const [, setBlankLines] = useState12(0);
|
|
4555
|
+
useListener("ClearUserInput", () => {
|
|
4556
|
+
setResetKey((prev) => prev + 1);
|
|
4557
|
+
}, []);
|
|
3970
4558
|
const borderColor = focusedArea === "input" ? "cyan" : "gray";
|
|
3971
4559
|
const placeholder = calculatePlaceholder(userInputMode);
|
|
3972
4560
|
const onSubmitResetKey = useCallback3((value) => {
|
|
@@ -4030,12 +4618,12 @@ function ChatContent() {
|
|
|
4030
4618
|
const { messages, isThinking, focusedArea, setFocusedArea } = useChat();
|
|
4031
4619
|
const size = useTerminalSize();
|
|
4032
4620
|
useMessageListener();
|
|
4033
|
-
const [activeTab, setActiveTab] =
|
|
4034
|
-
const [selectedIndex, setSelectedIndex] =
|
|
4621
|
+
const [activeTab, setActiveTab] = useState13("settings");
|
|
4622
|
+
const [selectedIndex, setSelectedIndex] = useState13(0);
|
|
4035
4623
|
const wrapCache = useRef2(
|
|
4036
4624
|
/* @__PURE__ */ new Map()
|
|
4037
4625
|
);
|
|
4038
|
-
|
|
4626
|
+
useInput4((input, key) => {
|
|
4039
4627
|
if (key.tab) {
|
|
4040
4628
|
const focusOrder = ["input", "timeline", "status"];
|
|
4041
4629
|
const currentIndex = focusOrder.indexOf(focusedArea);
|
|
@@ -4055,6 +4643,19 @@ function ChatContent() {
|
|
|
4055
4643
|
if (key.downArrow) {
|
|
4056
4644
|
setSelectedIndex((prev) => Math.min(Math.max(0, lineItems.length - 1), prev + 1));
|
|
4057
4645
|
}
|
|
4646
|
+
if (key.return) {
|
|
4647
|
+
const selected = lineItems[selectedIndex];
|
|
4648
|
+
if (selected && selected.type === "tool" && (selected.toolName === "apply_patch" || selected.toolName === "code_interpreter" || selected.toolName === "shell")) {
|
|
4649
|
+
const msg = messages.find((m) => m.id === selected.messageId);
|
|
4650
|
+
if (msg && msg.callId) {
|
|
4651
|
+
emitToListeners("OpenApprovalViewer", {
|
|
4652
|
+
type: selected.toolName,
|
|
4653
|
+
mode: "info",
|
|
4654
|
+
callId: msg.callId
|
|
4655
|
+
});
|
|
4656
|
+
}
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4058
4659
|
}
|
|
4059
4660
|
if (focusedArea === "status") {
|
|
4060
4661
|
if (key.leftArrow || key.rightArrow) {
|
|
@@ -4161,12 +4762,12 @@ function ChatContent() {
|
|
|
4161
4762
|
}
|
|
4162
4763
|
return acc;
|
|
4163
4764
|
}, [messages, availableTextWidth, labelWidthFor]);
|
|
4164
|
-
|
|
4765
|
+
useEffect8(() => {
|
|
4165
4766
|
if (lineItems.length > 0 && focusedArea !== "timeline") {
|
|
4166
4767
|
setSelectedIndex(lineItems.length - 1);
|
|
4167
4768
|
}
|
|
4168
4769
|
}, [lineItems.length, focusedArea]);
|
|
4169
|
-
|
|
4770
|
+
useEffect8(() => {
|
|
4170
4771
|
if (lineItems.length > 0) {
|
|
4171
4772
|
setSelectedIndex(lineItems.length - 1);
|
|
4172
4773
|
}
|
|
@@ -4311,10 +4912,246 @@ function ChatContent() {
|
|
|
4311
4912
|
] });
|
|
4312
4913
|
}
|
|
4313
4914
|
|
|
4915
|
+
// ink/components/DiffApprovalView.tsx
|
|
4916
|
+
import { Box as Box11, Text as Text11, useInput as useInput5 } from "ink";
|
|
4917
|
+
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
|
+
import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
4974
|
+
function DiffApprovalView() {
|
|
4975
|
+
const { payload } = useApproval();
|
|
4976
|
+
const size = useTerminalSize();
|
|
4977
|
+
const [scrollIndex, setScrollIndex] = useState15(0);
|
|
4978
|
+
const diffLines = useMemo12(() => {
|
|
4979
|
+
if (!payload?.diff) {
|
|
4980
|
+
return [];
|
|
4981
|
+
}
|
|
4982
|
+
return payload.diff.split("\n");
|
|
4983
|
+
}, [payload?.diff]);
|
|
4984
|
+
const wrappedLines = useMemo12(() => {
|
|
4985
|
+
const result = [];
|
|
4986
|
+
if (!payload) {
|
|
4987
|
+
return result;
|
|
4988
|
+
}
|
|
4989
|
+
if (payload.type === "delete_file") {
|
|
4990
|
+
result.push({ text: "Delete file: " + payload.path, color: "red" });
|
|
4991
|
+
return result;
|
|
4992
|
+
}
|
|
4993
|
+
if (payload.type === "code_interpreter" && payload.code) {
|
|
4994
|
+
const lines = payload.code.split("\n");
|
|
4995
|
+
for (const line of lines) {
|
|
4996
|
+
const wrapped = wrapText(line, size.columns - 4);
|
|
4997
|
+
for (const w of wrapped) {
|
|
4998
|
+
result.push({ text: w });
|
|
4999
|
+
}
|
|
5000
|
+
}
|
|
5001
|
+
return result;
|
|
5002
|
+
}
|
|
5003
|
+
if (payload.type === "shell" && payload.commands) {
|
|
5004
|
+
for (const cmd of payload.commands) {
|
|
5005
|
+
const wrapped = wrapText(`$ ${cmd}`, size.columns - 4);
|
|
5006
|
+
for (const w of wrapped) {
|
|
5007
|
+
result.push({ text: w, color: "yellow" });
|
|
5008
|
+
}
|
|
5009
|
+
}
|
|
5010
|
+
return result;
|
|
5011
|
+
}
|
|
5012
|
+
for (const line of diffLines) {
|
|
5013
|
+
let color;
|
|
5014
|
+
if (line.startsWith("+")) {
|
|
5015
|
+
color = "green";
|
|
5016
|
+
} else if (line.startsWith("-")) {
|
|
5017
|
+
color = "red";
|
|
5018
|
+
} else if (line.startsWith("@@")) {
|
|
5019
|
+
color = "cyan";
|
|
5020
|
+
}
|
|
5021
|
+
const wrapped = wrapText(line, size.columns - 4);
|
|
5022
|
+
for (const w of wrapped) {
|
|
5023
|
+
result.push({ text: w, color });
|
|
5024
|
+
}
|
|
5025
|
+
}
|
|
5026
|
+
return result;
|
|
5027
|
+
}, [diffLines, size.columns, payload]);
|
|
5028
|
+
const visibleHeight = size.rows - 6;
|
|
5029
|
+
useInput5((input, key) => {
|
|
5030
|
+
if (!payload) {
|
|
5031
|
+
return;
|
|
5032
|
+
}
|
|
5033
|
+
if (key.escape) {
|
|
5034
|
+
if (payload.mode === "ask") {
|
|
5035
|
+
emitToListeners("DenyCurrentApproval", void 0);
|
|
5036
|
+
}
|
|
5037
|
+
emitToListeners("CloseApprovalViewer", void 0);
|
|
5038
|
+
return;
|
|
5039
|
+
}
|
|
5040
|
+
if (key.return) {
|
|
5041
|
+
if (payload.mode === "ask") {
|
|
5042
|
+
const now = Date.now();
|
|
5043
|
+
if (payload.openedAt && now - payload.openedAt > 1e3) {
|
|
5044
|
+
emitToListeners("ApproveCurrentApproval", void 0);
|
|
5045
|
+
emitToListeners("CloseApprovalViewer", void 0);
|
|
5046
|
+
emitToListeners("ClearUserInput", void 0);
|
|
5047
|
+
}
|
|
5048
|
+
} else {
|
|
5049
|
+
emitToListeners("CloseApprovalViewer", void 0);
|
|
5050
|
+
}
|
|
5051
|
+
return;
|
|
5052
|
+
}
|
|
5053
|
+
if (payload.mode === "ask") {
|
|
5054
|
+
const now = Date.now();
|
|
5055
|
+
if (payload.openedAt && now - payload.openedAt > 1e3) {
|
|
5056
|
+
if (input === "y") {
|
|
5057
|
+
emitToListeners("ApproveCurrentApproval", void 0);
|
|
5058
|
+
emitToListeners("CloseApprovalViewer", void 0);
|
|
5059
|
+
emitToListeners("ClearUserInput", void 0);
|
|
5060
|
+
} else if (input === "n") {
|
|
5061
|
+
emitToListeners("DenyCurrentApproval", void 0);
|
|
5062
|
+
emitToListeners("CloseApprovalViewer", void 0);
|
|
5063
|
+
emitToListeners("ClearUserInput", void 0);
|
|
5064
|
+
}
|
|
5065
|
+
}
|
|
5066
|
+
}
|
|
5067
|
+
if (key.upArrow) {
|
|
5068
|
+
setScrollIndex((prev) => Math.max(0, prev - 1));
|
|
5069
|
+
}
|
|
5070
|
+
if (key.downArrow) {
|
|
5071
|
+
setScrollIndex((prev) => Math.min(Math.max(0, wrappedLines.length - visibleHeight), prev + 1));
|
|
5072
|
+
}
|
|
5073
|
+
if (key.pageUp) {
|
|
5074
|
+
setScrollIndex((prev) => Math.max(0, prev - visibleHeight));
|
|
5075
|
+
}
|
|
5076
|
+
if (key.pageDown) {
|
|
5077
|
+
setScrollIndex((prev) => Math.min(Math.max(0, wrappedLines.length - visibleHeight), prev + visibleHeight));
|
|
5078
|
+
}
|
|
5079
|
+
});
|
|
5080
|
+
if (!payload) {
|
|
5081
|
+
return null;
|
|
5082
|
+
}
|
|
5083
|
+
const canRespond = payload.mode === "ask" && payload.openedAt && Date.now() - payload.openedAt > 1e3;
|
|
5084
|
+
return /* @__PURE__ */ jsxs10(
|
|
5085
|
+
Box11,
|
|
5086
|
+
{
|
|
5087
|
+
position: "absolute",
|
|
5088
|
+
flexDirection: "column",
|
|
5089
|
+
width: size.columns,
|
|
5090
|
+
height: size.rows,
|
|
5091
|
+
backgroundColor: "black",
|
|
5092
|
+
borderStyle: "double",
|
|
5093
|
+
borderColor: "cyan",
|
|
5094
|
+
children: [
|
|
5095
|
+
/* @__PURE__ */ jsxs10(
|
|
5096
|
+
Box11,
|
|
5097
|
+
{
|
|
5098
|
+
paddingX: 1,
|
|
5099
|
+
borderStyle: "single",
|
|
5100
|
+
borderBottomColor: "gray",
|
|
5101
|
+
borderTop: false,
|
|
5102
|
+
borderLeft: false,
|
|
5103
|
+
borderRight: false,
|
|
5104
|
+
children: [
|
|
5105
|
+
/* @__PURE__ */ jsxs10(Text11, { bold: true, color: "cyan", children: [
|
|
5106
|
+
payload.mode === "ask" ? "APPROVE " : "VIEW ",
|
|
5107
|
+
payload.type.toUpperCase().replace("_", " ")
|
|
5108
|
+
] }),
|
|
5109
|
+
/* @__PURE__ */ jsx18(Text11, { color: "gray", children: "\u2502" }),
|
|
5110
|
+
payload.path && /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
5111
|
+
/* @__PURE__ */ jsxs10(Text11, { bold: true, children: [
|
|
5112
|
+
payload.type,
|
|
5113
|
+
":"
|
|
5114
|
+
] }),
|
|
5115
|
+
/* @__PURE__ */ jsx18(Text11, { children: payload.path })
|
|
5116
|
+
] })
|
|
5117
|
+
]
|
|
5118
|
+
}
|
|
5119
|
+
),
|
|
5120
|
+
/* @__PURE__ */ jsx18(Box11, { flexGrow: 1, flexDirection: "column", paddingX: 1, children: wrappedLines.length === 0 ? /* @__PURE__ */ jsx18(Text11, { italic: true, color: "gray", children: "No diff content." }) : wrappedLines.slice(scrollIndex, scrollIndex + visibleHeight).map((line, i) => /* @__PURE__ */ jsx18(Text11, { color: line.color || "white", children: line.text }, i)) }),
|
|
5121
|
+
/* @__PURE__ */ jsxs10(
|
|
5122
|
+
Box11,
|
|
5123
|
+
{
|
|
5124
|
+
paddingX: 1,
|
|
5125
|
+
borderStyle: "single",
|
|
5126
|
+
borderTopColor: "gray",
|
|
5127
|
+
borderBottom: false,
|
|
5128
|
+
borderLeft: false,
|
|
5129
|
+
borderRight: false,
|
|
5130
|
+
children: [
|
|
5131
|
+
payload.mode === "ask" ? /* @__PURE__ */ jsxs10(Box11, { flexGrow: 1, children: [
|
|
5132
|
+
/* @__PURE__ */ jsx18(Text11, { color: canRespond ? "green" : "gray", children: "[Enter/y] Approve" }),
|
|
5133
|
+
/* @__PURE__ */ jsx18(Text11, {}),
|
|
5134
|
+
/* @__PURE__ */ jsx18(Text11, { color: canRespond ? "red" : "gray", children: "[Esc/n] Deny" }),
|
|
5135
|
+
!canRespond && /* @__PURE__ */ jsx18(Text11, { color: "yellow", children: "(Wait 1s...)" })
|
|
5136
|
+
] }) : /* @__PURE__ */ jsx18(Box11, { flexGrow: 1, children: /* @__PURE__ */ jsx18(Text11, { color: "cyan", children: "[Enter/Esc] Close" }) }),
|
|
5137
|
+
/* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
5138
|
+
"Line ",
|
|
5139
|
+
scrollIndex + 1,
|
|
5140
|
+
"/",
|
|
5141
|
+
wrappedLines.length
|
|
5142
|
+
] })
|
|
5143
|
+
]
|
|
5144
|
+
}
|
|
5145
|
+
)
|
|
5146
|
+
]
|
|
5147
|
+
}
|
|
5148
|
+
);
|
|
5149
|
+
}
|
|
5150
|
+
|
|
4314
5151
|
// ink/configurationWizard/ConfigurationWizard.tsx
|
|
4315
|
-
import { Box as
|
|
5152
|
+
import { Box as Box18, useInput as useInput10 } from "ink";
|
|
4316
5153
|
import { Step, Stepper } from "ink-stepper";
|
|
4317
|
-
import { useState as
|
|
5154
|
+
import { useState as useState17 } from "react";
|
|
4318
5155
|
|
|
4319
5156
|
// utils/files/getEnvVarForProvider.ts
|
|
4320
5157
|
function getEnvVarForProvider(provider) {
|
|
@@ -4338,17 +5175,17 @@ function updateEnvKeyForProvider(provider, key) {
|
|
|
4338
5175
|
|
|
4339
5176
|
// ink/configurationWizard/ApiKeyStep.tsx
|
|
4340
5177
|
import { PasswordInput } from "@inkjs/ui";
|
|
4341
|
-
import { Box as
|
|
5178
|
+
import { Box as Box12, Text as Text12, useInput as useInput6 } from "ink";
|
|
4342
5179
|
import { useStepperInput } from "ink-stepper";
|
|
4343
|
-
import { useEffect as
|
|
4344
|
-
import { jsx as
|
|
5180
|
+
import { useEffect as useEffect9 } from "react";
|
|
5181
|
+
import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
4345
5182
|
function ApiKeyStep({ provider, onConfirm, onBack }) {
|
|
4346
5183
|
const { disableNavigation, enableNavigation } = useStepperInput();
|
|
4347
|
-
|
|
5184
|
+
useEffect9(() => {
|
|
4348
5185
|
disableNavigation();
|
|
4349
5186
|
return () => enableNavigation();
|
|
4350
5187
|
}, [disableNavigation, enableNavigation]);
|
|
4351
|
-
|
|
5188
|
+
useInput6((input, key) => {
|
|
4352
5189
|
if (key.escape) {
|
|
4353
5190
|
onBack();
|
|
4354
5191
|
}
|
|
@@ -4359,14 +5196,14 @@ function ApiKeyStep({ provider, onConfirm, onBack }) {
|
|
|
4359
5196
|
Google: "Get your key at: https://aistudio.google.com/app/apikey",
|
|
4360
5197
|
Ollama: ""
|
|
4361
5198
|
};
|
|
4362
|
-
return /* @__PURE__ */
|
|
4363
|
-
/* @__PURE__ */
|
|
5199
|
+
return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", children: [
|
|
5200
|
+
/* @__PURE__ */ jsxs11(Text12, { children: [
|
|
4364
5201
|
"Can you provide us with your ",
|
|
4365
5202
|
provider,
|
|
4366
5203
|
" API key?"
|
|
4367
5204
|
] }),
|
|
4368
|
-
/* @__PURE__ */
|
|
4369
|
-
/* @__PURE__ */
|
|
5205
|
+
/* @__PURE__ */ jsx19(Text12, { dimColor: true, children: instructions[provider] }),
|
|
5206
|
+
/* @__PURE__ */ jsx19(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx19(
|
|
4370
5207
|
PasswordInput,
|
|
4371
5208
|
{
|
|
4372
5209
|
onSubmit: (v) => {
|
|
@@ -4378,22 +5215,22 @@ function ApiKeyStep({ provider, onConfirm, onBack }) {
|
|
|
4378
5215
|
}
|
|
4379
5216
|
}
|
|
4380
5217
|
) }),
|
|
4381
|
-
/* @__PURE__ */
|
|
5218
|
+
/* @__PURE__ */ jsx19(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx19(Text12, { dimColor: true, children: "Press ESC to go back" }) })
|
|
4382
5219
|
] });
|
|
4383
5220
|
}
|
|
4384
5221
|
|
|
4385
5222
|
// ink/configurationWizard/ApiUrlStep.tsx
|
|
4386
|
-
import { Box as
|
|
5223
|
+
import { Box as Box13, Text as Text13, useInput as useInput7 } from "ink";
|
|
4387
5224
|
import { useStepperInput as useStepperInput2 } from "ink-stepper";
|
|
4388
|
-
import { useEffect as
|
|
4389
|
-
import { jsx as
|
|
5225
|
+
import { useEffect as useEffect10 } from "react";
|
|
5226
|
+
import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4390
5227
|
function ApiUrlStep({ provider, onConfirm, onBack }) {
|
|
4391
5228
|
const { disableNavigation, enableNavigation } = useStepperInput2();
|
|
4392
|
-
|
|
5229
|
+
useEffect10(() => {
|
|
4393
5230
|
disableNavigation();
|
|
4394
5231
|
return () => enableNavigation();
|
|
4395
5232
|
}, [disableNavigation, enableNavigation]);
|
|
4396
|
-
|
|
5233
|
+
useInput7((input, key) => {
|
|
4397
5234
|
if (key.escape) {
|
|
4398
5235
|
onBack();
|
|
4399
5236
|
}
|
|
@@ -4404,13 +5241,13 @@ function ApiUrlStep({ provider, onConfirm, onBack }) {
|
|
|
4404
5241
|
Google: "",
|
|
4405
5242
|
Ollama: "http://localhost:11434/api"
|
|
4406
5243
|
};
|
|
4407
|
-
return /* @__PURE__ */
|
|
4408
|
-
/* @__PURE__ */
|
|
5244
|
+
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", children: [
|
|
5245
|
+
/* @__PURE__ */ jsxs12(Text13, { children: [
|
|
4409
5246
|
"Where are you hosting ",
|
|
4410
5247
|
provider,
|
|
4411
5248
|
"?"
|
|
4412
5249
|
] }),
|
|
4413
|
-
/* @__PURE__ */
|
|
5250
|
+
/* @__PURE__ */ jsx20(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx20(
|
|
4414
5251
|
BlinkingTextInput,
|
|
4415
5252
|
{
|
|
4416
5253
|
placeholder: defaultApi[provider] || "",
|
|
@@ -4423,7 +5260,84 @@ function ApiUrlStep({ provider, onConfirm, onBack }) {
|
|
|
4423
5260
|
}
|
|
4424
5261
|
}
|
|
4425
5262
|
) }),
|
|
4426
|
-
/* @__PURE__ */
|
|
5263
|
+
/* @__PURE__ */ jsx20(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx20(Text13, { dimColor: true, children: "Press ESC to go back" }) })
|
|
5264
|
+
] });
|
|
5265
|
+
}
|
|
5266
|
+
|
|
5267
|
+
// ink/configurationWizard/EnvironmentSettingsStep.tsx
|
|
5268
|
+
import { MultiSelect } from "@inkjs/ui";
|
|
5269
|
+
import { Box as Box14, Text as Text14, useInput as useInput8 } from "ink";
|
|
5270
|
+
import { useStepperInput as useStepperInput3 } from "ink-stepper";
|
|
5271
|
+
import { useEffect as useEffect11 } from "react";
|
|
5272
|
+
import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
5273
|
+
var SETTINGS = [
|
|
5274
|
+
{
|
|
5275
|
+
label: "Save Harper agent memory locally",
|
|
5276
|
+
value: "HARPER_AGENT_SESSION",
|
|
5277
|
+
defaultValue: "./harper-agent-memory.json"
|
|
5278
|
+
},
|
|
5279
|
+
{
|
|
5280
|
+
label: "Automatically approve code interpreter execution",
|
|
5281
|
+
value: "HARPER_AGENT_AUTO_APPROVE_CODE_INTERPRETER",
|
|
5282
|
+
defaultValue: "1"
|
|
5283
|
+
},
|
|
5284
|
+
{
|
|
5285
|
+
label: "Automatically approve file patches",
|
|
5286
|
+
value: "HARPER_AGENT_AUTO_APPROVE_PATCHES",
|
|
5287
|
+
defaultValue: "1"
|
|
5288
|
+
},
|
|
5289
|
+
{
|
|
5290
|
+
label: "Automatically approve shell commands",
|
|
5291
|
+
value: "HARPER_AGENT_AUTO_APPROVE_SHELL",
|
|
5292
|
+
defaultValue: "1"
|
|
5293
|
+
},
|
|
5294
|
+
{
|
|
5295
|
+
label: "Use flex tier for lower costs when possible",
|
|
5296
|
+
value: "HARPER_AGENT_FLEX_TIER",
|
|
5297
|
+
defaultValue: "true"
|
|
5298
|
+
}
|
|
5299
|
+
];
|
|
5300
|
+
function EnvironmentSettingsStep({ onConfirm, onBack }) {
|
|
5301
|
+
const { disableNavigation, enableNavigation } = useStepperInput3();
|
|
5302
|
+
useEffect11(() => {
|
|
5303
|
+
disableNavigation();
|
|
5304
|
+
return () => enableNavigation();
|
|
5305
|
+
}, [disableNavigation, enableNavigation]);
|
|
5306
|
+
useInput8((_input, key) => {
|
|
5307
|
+
if (key.escape) {
|
|
5308
|
+
onBack();
|
|
5309
|
+
}
|
|
5310
|
+
});
|
|
5311
|
+
const options = SETTINGS.map((s) => ({
|
|
5312
|
+
label: s.label,
|
|
5313
|
+
value: s.value
|
|
5314
|
+
}));
|
|
5315
|
+
const defaultValues = SETTINGS.map((s) => s.value);
|
|
5316
|
+
const handleSubmit = (values) => {
|
|
5317
|
+
for (const setting of SETTINGS) {
|
|
5318
|
+
if (values.includes(setting.value)) {
|
|
5319
|
+
updateEnv(setting.value, setting.defaultValue);
|
|
5320
|
+
}
|
|
5321
|
+
}
|
|
5322
|
+
onConfirm();
|
|
5323
|
+
};
|
|
5324
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
|
|
5325
|
+
/* @__PURE__ */ jsx21(Text14, { children: "Additional Settings (all enabled by default):" }),
|
|
5326
|
+
/* @__PURE__ */ jsx21(
|
|
5327
|
+
MultiSelect,
|
|
5328
|
+
{
|
|
5329
|
+
options,
|
|
5330
|
+
defaultValue: defaultValues,
|
|
5331
|
+
onSubmit: handleSubmit
|
|
5332
|
+
}
|
|
5333
|
+
),
|
|
5334
|
+
/* @__PURE__ */ jsxs13(Text14, { color: "gray", children: [
|
|
5335
|
+
"Press ",
|
|
5336
|
+
"<space>",
|
|
5337
|
+
" to toggle, ",
|
|
5338
|
+
"<enter>",
|
|
5339
|
+
" to confirm."
|
|
5340
|
+
] })
|
|
4427
5341
|
] });
|
|
4428
5342
|
}
|
|
4429
5343
|
|
|
@@ -4443,23 +5357,23 @@ var compactorModelsByProvider = {
|
|
|
4443
5357
|
|
|
4444
5358
|
// ink/configurationWizard/ModelSelectionStep.tsx
|
|
4445
5359
|
import { Select } from "@inkjs/ui";
|
|
4446
|
-
import { Box as
|
|
4447
|
-
import { useStepperInput as
|
|
4448
|
-
import { useEffect as
|
|
4449
|
-
import { jsx as
|
|
5360
|
+
import { Box as Box15, Text as Text15, useInput as useInput9 } from "ink";
|
|
5361
|
+
import { useStepperInput as useStepperInput4 } from "ink-stepper";
|
|
5362
|
+
import { useEffect as useEffect12, useState as useState16 } from "react";
|
|
5363
|
+
import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
4450
5364
|
function ModelSelectionStep({
|
|
4451
5365
|
title,
|
|
4452
5366
|
models,
|
|
4453
5367
|
onConfirm,
|
|
4454
5368
|
onBack
|
|
4455
5369
|
}) {
|
|
4456
|
-
const { disableNavigation, enableNavigation } =
|
|
4457
|
-
const [isCustom, setIsCustom] =
|
|
4458
|
-
|
|
5370
|
+
const { disableNavigation, enableNavigation } = useStepperInput4();
|
|
5371
|
+
const [isCustom, setIsCustom] = useState16(false);
|
|
5372
|
+
useEffect12(() => {
|
|
4459
5373
|
disableNavigation();
|
|
4460
5374
|
return () => enableNavigation();
|
|
4461
5375
|
}, [isCustom, disableNavigation, enableNavigation]);
|
|
4462
|
-
|
|
5376
|
+
useInput9((input, key) => {
|
|
4463
5377
|
if (key.escape) {
|
|
4464
5378
|
if (isCustom) {
|
|
4465
5379
|
setIsCustom(false);
|
|
@@ -4469,12 +5383,12 @@ function ModelSelectionStep({
|
|
|
4469
5383
|
}
|
|
4470
5384
|
});
|
|
4471
5385
|
if (isCustom) {
|
|
4472
|
-
return /* @__PURE__ */
|
|
4473
|
-
/* @__PURE__ */
|
|
5386
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
|
|
5387
|
+
/* @__PURE__ */ jsxs14(Text15, { children: [
|
|
4474
5388
|
"Enter custom model name for: ",
|
|
4475
5389
|
title
|
|
4476
5390
|
] }),
|
|
4477
|
-
/* @__PURE__ */
|
|
5391
|
+
/* @__PURE__ */ jsx22(
|
|
4478
5392
|
BlinkingTextInput,
|
|
4479
5393
|
{
|
|
4480
5394
|
onSubmit: (v) => {
|
|
@@ -4486,12 +5400,12 @@ function ModelSelectionStep({
|
|
|
4486
5400
|
}
|
|
4487
5401
|
}
|
|
4488
5402
|
),
|
|
4489
|
-
/* @__PURE__ */
|
|
5403
|
+
/* @__PURE__ */ jsx22(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx22(Text15, { dimColor: true, children: "Press ESC to go back to list" }) })
|
|
4490
5404
|
] });
|
|
4491
5405
|
}
|
|
4492
|
-
return /* @__PURE__ */
|
|
4493
|
-
/* @__PURE__ */
|
|
4494
|
-
/* @__PURE__ */
|
|
5406
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
|
|
5407
|
+
/* @__PURE__ */ jsx22(Text15, { children: title }),
|
|
5408
|
+
/* @__PURE__ */ jsx22(
|
|
4495
5409
|
Select,
|
|
4496
5410
|
{
|
|
4497
5411
|
options: [
|
|
@@ -4507,15 +5421,15 @@ function ModelSelectionStep({
|
|
|
4507
5421
|
}
|
|
4508
5422
|
}
|
|
4509
5423
|
),
|
|
4510
|
-
/* @__PURE__ */
|
|
5424
|
+
/* @__PURE__ */ jsx22(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx22(Text15, { dimColor: true, children: "Press ESC to go back" }) })
|
|
4511
5425
|
] });
|
|
4512
5426
|
}
|
|
4513
5427
|
|
|
4514
5428
|
// ink/configurationWizard/ProviderStep.tsx
|
|
4515
5429
|
import { Select as Select2 } from "@inkjs/ui";
|
|
4516
|
-
import { Box as
|
|
4517
|
-
import { useStepperInput as
|
|
4518
|
-
import { useEffect as
|
|
5430
|
+
import { Box as Box16, Text as Text16 } from "ink";
|
|
5431
|
+
import { useStepperInput as useStepperInput5 } from "ink-stepper";
|
|
5432
|
+
import { useEffect as useEffect13 } from "react";
|
|
4519
5433
|
|
|
4520
5434
|
// ink/configurationWizard/providers.ts
|
|
4521
5435
|
var providers = [
|
|
@@ -4526,16 +5440,16 @@ var providers = [
|
|
|
4526
5440
|
];
|
|
4527
5441
|
|
|
4528
5442
|
// ink/configurationWizard/ProviderStep.tsx
|
|
4529
|
-
import { jsx as
|
|
5443
|
+
import { jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
4530
5444
|
function ProviderStep({ onConfirm }) {
|
|
4531
|
-
const { disableNavigation, enableNavigation } =
|
|
4532
|
-
|
|
5445
|
+
const { disableNavigation, enableNavigation } = useStepperInput5();
|
|
5446
|
+
useEffect13(() => {
|
|
4533
5447
|
disableNavigation();
|
|
4534
5448
|
return () => enableNavigation();
|
|
4535
5449
|
}, [disableNavigation, enableNavigation]);
|
|
4536
|
-
return /* @__PURE__ */
|
|
4537
|
-
/* @__PURE__ */
|
|
4538
|
-
/* @__PURE__ */
|
|
5450
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
5451
|
+
/* @__PURE__ */ jsx23(Text16, { children: "What model provider would you like to use today?" }),
|
|
5452
|
+
/* @__PURE__ */ jsx23(
|
|
4539
5453
|
Select2,
|
|
4540
5454
|
{
|
|
4541
5455
|
options: providers,
|
|
@@ -4546,9 +5460,9 @@ function ProviderStep({ onConfirm }) {
|
|
|
4546
5460
|
}
|
|
4547
5461
|
|
|
4548
5462
|
// ink/configurationWizard/StepperProgress.tsx
|
|
4549
|
-
import { Box as
|
|
4550
|
-
import { Fragment as
|
|
4551
|
-
import { jsx as
|
|
5463
|
+
import { Box as Box17, Text as Text17 } from "ink";
|
|
5464
|
+
import { Fragment as Fragment3 } from "react";
|
|
5465
|
+
import { jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
4552
5466
|
var markers = {
|
|
4553
5467
|
completed: " \u2713 ",
|
|
4554
5468
|
current: " \u25CF ",
|
|
@@ -4556,10 +5470,10 @@ var markers = {
|
|
|
4556
5470
|
};
|
|
4557
5471
|
var SEGMENT_WIDTH = 12;
|
|
4558
5472
|
function StepperProgress({ steps, currentStep }) {
|
|
4559
|
-
return /* @__PURE__ */
|
|
4560
|
-
/* @__PURE__ */
|
|
4561
|
-
return /* @__PURE__ */
|
|
4562
|
-
|
|
5473
|
+
return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", marginBottom: 1, children: [
|
|
5474
|
+
/* @__PURE__ */ jsx24(Box17, { children: steps.map((step) => {
|
|
5475
|
+
return /* @__PURE__ */ jsx24(Box17, { width: SEGMENT_WIDTH, justifyContent: "center", children: /* @__PURE__ */ jsx24(
|
|
5476
|
+
Text17,
|
|
4563
5477
|
{
|
|
4564
5478
|
color: step.completed ? "green" : step.current ? "cyan" : "gray",
|
|
4565
5479
|
bold: step.current,
|
|
@@ -4568,32 +5482,32 @@ function StepperProgress({ steps, currentStep }) {
|
|
|
4568
5482
|
}
|
|
4569
5483
|
) }, step.name);
|
|
4570
5484
|
}) }),
|
|
4571
|
-
/* @__PURE__ */
|
|
5485
|
+
/* @__PURE__ */ jsx24(Box17, { children: steps.map((step, idx) => {
|
|
4572
5486
|
const marker = step.completed ? markers.completed : step.current ? markers.current : markers.pending;
|
|
4573
5487
|
const beforeLineColor = step.completed || idx <= currentStep ? "green" : "gray";
|
|
4574
5488
|
const afterLineColor = step.completed ? "green" : "gray";
|
|
4575
5489
|
const markerColor = step.completed ? "green" : step.current ? "cyan" : "gray";
|
|
4576
|
-
return /* @__PURE__ */
|
|
4577
|
-
/* @__PURE__ */
|
|
4578
|
-
/* @__PURE__ */
|
|
4579
|
-
/* @__PURE__ */
|
|
5490
|
+
return /* @__PURE__ */ jsxs16(Fragment3, { children: [
|
|
5491
|
+
/* @__PURE__ */ jsx24(Text17, { color: beforeLineColor, children: "\u2501".repeat(SEGMENT_WIDTH / 2 - 2) }),
|
|
5492
|
+
/* @__PURE__ */ jsx24(Text17, { color: markerColor, bold: step.current, children: marker }),
|
|
5493
|
+
/* @__PURE__ */ jsx24(Text17, { color: afterLineColor, children: "\u2501".repeat(SEGMENT_WIDTH / 2 - 1) })
|
|
4580
5494
|
] }, step.name);
|
|
4581
5495
|
}) })
|
|
4582
5496
|
] });
|
|
4583
5497
|
}
|
|
4584
5498
|
|
|
4585
5499
|
// ink/configurationWizard/ConfigurationWizard.tsx
|
|
4586
|
-
import { jsx as
|
|
5500
|
+
import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
4587
5501
|
function ConfigurationWizard({ onComplete }) {
|
|
4588
|
-
const [provider, setProvider] =
|
|
4589
|
-
|
|
5502
|
+
const [provider, setProvider] = useState17("OpenAI");
|
|
5503
|
+
useInput10((input, key) => {
|
|
4590
5504
|
if (key.ctrl && input === "x") {
|
|
4591
5505
|
emitToListeners("ExitUI", void 0);
|
|
4592
5506
|
}
|
|
4593
5507
|
});
|
|
4594
5508
|
const models = modelsByProvider[provider];
|
|
4595
5509
|
const compactorModels = compactorModelsByProvider[provider];
|
|
4596
|
-
return /* @__PURE__ */
|
|
5510
|
+
return /* @__PURE__ */ jsx25(Box18, { flexDirection: "column", padding: 1, minHeight: 10, children: /* @__PURE__ */ jsxs17(
|
|
4597
5511
|
Stepper,
|
|
4598
5512
|
{
|
|
4599
5513
|
onComplete,
|
|
@@ -4601,7 +5515,7 @@ function ConfigurationWizard({ onComplete }) {
|
|
|
4601
5515
|
keyboardNav: true,
|
|
4602
5516
|
renderProgress: StepperProgress,
|
|
4603
5517
|
children: [
|
|
4604
|
-
/* @__PURE__ */
|
|
5518
|
+
/* @__PURE__ */ jsx25(Step, { name: "AI Provider", children: ({ goNext }) => /* @__PURE__ */ jsx25(
|
|
4605
5519
|
ProviderStep,
|
|
4606
5520
|
{
|
|
4607
5521
|
onConfirm: (p) => {
|
|
@@ -4610,7 +5524,7 @@ function ConfigurationWizard({ onComplete }) {
|
|
|
4610
5524
|
}
|
|
4611
5525
|
}
|
|
4612
5526
|
) }),
|
|
4613
|
-
/* @__PURE__ */
|
|
5527
|
+
/* @__PURE__ */ jsx25(Step, { name: provider !== "Ollama" ? "API Key" : "API", children: ({ goNext, goBack }) => provider !== "Ollama" ? /* @__PURE__ */ jsx25(
|
|
4614
5528
|
ApiKeyStep,
|
|
4615
5529
|
{
|
|
4616
5530
|
provider,
|
|
@@ -4620,7 +5534,7 @@ function ConfigurationWizard({ onComplete }) {
|
|
|
4620
5534
|
},
|
|
4621
5535
|
onBack: goBack
|
|
4622
5536
|
}
|
|
4623
|
-
) : /* @__PURE__ */
|
|
5537
|
+
) : /* @__PURE__ */ jsx25(
|
|
4624
5538
|
ApiUrlStep,
|
|
4625
5539
|
{
|
|
4626
5540
|
provider,
|
|
@@ -4631,7 +5545,7 @@ function ConfigurationWizard({ onComplete }) {
|
|
|
4631
5545
|
onBack: goBack
|
|
4632
5546
|
}
|
|
4633
5547
|
) }),
|
|
4634
|
-
/* @__PURE__ */
|
|
5548
|
+
/* @__PURE__ */ jsx25(Step, { name: "Model", children: ({ goNext, goBack }) => /* @__PURE__ */ jsx25(
|
|
4635
5549
|
ModelSelectionStep,
|
|
4636
5550
|
{
|
|
4637
5551
|
title: "What model would you like to use?",
|
|
@@ -4643,7 +5557,7 @@ function ConfigurationWizard({ onComplete }) {
|
|
|
4643
5557
|
onBack: goBack
|
|
4644
5558
|
}
|
|
4645
5559
|
) }),
|
|
4646
|
-
/* @__PURE__ */
|
|
5560
|
+
/* @__PURE__ */ jsx25(Step, { name: "Compactor", children: ({ goNext, goBack }) => /* @__PURE__ */ jsx25(
|
|
4647
5561
|
ModelSelectionStep,
|
|
4648
5562
|
{
|
|
4649
5563
|
title: "What model should we use for memory compaction?",
|
|
@@ -4654,6 +5568,15 @@ function ConfigurationWizard({ onComplete }) {
|
|
|
4654
5568
|
},
|
|
4655
5569
|
onBack: goBack
|
|
4656
5570
|
}
|
|
5571
|
+
) }),
|
|
5572
|
+
/* @__PURE__ */ jsx25(Step, { name: "Settings", children: ({ goNext, goBack }) => /* @__PURE__ */ jsx25(
|
|
5573
|
+
EnvironmentSettingsStep,
|
|
5574
|
+
{
|
|
5575
|
+
onConfirm: () => {
|
|
5576
|
+
goNext();
|
|
5577
|
+
},
|
|
5578
|
+
onBack: goBack
|
|
5579
|
+
}
|
|
4657
5580
|
) })
|
|
4658
5581
|
]
|
|
4659
5582
|
}
|
|
@@ -4661,22 +5584,25 @@ function ConfigurationWizard({ onComplete }) {
|
|
|
4661
5584
|
}
|
|
4662
5585
|
|
|
4663
5586
|
// ink/main.tsx
|
|
4664
|
-
import { jsx as
|
|
5587
|
+
import { jsx as jsx26, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
4665
5588
|
function bootstrapConfig(onComplete) {
|
|
4666
|
-
render(/* @__PURE__ */
|
|
5589
|
+
render(/* @__PURE__ */ jsx26(MainConfig, { onComplete }));
|
|
4667
5590
|
}
|
|
4668
5591
|
function MainConfig({ onComplete }) {
|
|
4669
5592
|
const { exit } = useApp();
|
|
4670
5593
|
useListener("ExitUI", () => exit(), [exit]);
|
|
4671
|
-
return /* @__PURE__ */
|
|
5594
|
+
return /* @__PURE__ */ jsx26(ConfigurationWizard, { onComplete });
|
|
4672
5595
|
}
|
|
4673
5596
|
function bootstrapMain() {
|
|
4674
|
-
render(/* @__PURE__ */
|
|
5597
|
+
render(/* @__PURE__ */ jsx26(MainChat, {}));
|
|
4675
5598
|
}
|
|
4676
5599
|
function MainChat() {
|
|
4677
5600
|
const { exit } = useApp();
|
|
4678
5601
|
useListener("ExitUI", () => exit(), [exit]);
|
|
4679
|
-
return /* @__PURE__ */
|
|
5602
|
+
return /* @__PURE__ */ jsx26(CostProvider, { children: /* @__PURE__ */ jsx26(PlanProvider, { children: /* @__PURE__ */ jsx26(ActionsProvider, { children: /* @__PURE__ */ jsx26(ApprovalProvider, { children: /* @__PURE__ */ jsx26(SettingsProvider, { children: /* @__PURE__ */ jsxs18(ChatProvider, { children: [
|
|
5603
|
+
/* @__PURE__ */ jsx26(ChatContent, {}),
|
|
5604
|
+
/* @__PURE__ */ jsx26(DiffApprovalView, {})
|
|
5605
|
+
] }) }) }) }) }) });
|
|
4680
5606
|
}
|
|
4681
5607
|
|
|
4682
5608
|
// lifecycle/parseArgs.ts
|
|
@@ -4687,12 +5613,12 @@ import chalk4 from "chalk";
|
|
|
4687
5613
|
|
|
4688
5614
|
// utils/package/getOwnPackageJson.ts
|
|
4689
5615
|
import { readFileSync as readFileSync6 } from "fs";
|
|
4690
|
-
import { join as
|
|
5616
|
+
import { join as join10 } from "path";
|
|
4691
5617
|
import { fileURLToPath } from "url";
|
|
4692
5618
|
var __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
4693
5619
|
function getOwnPackageJson() {
|
|
4694
5620
|
try {
|
|
4695
|
-
const packageContents = readFileSync6(
|
|
5621
|
+
const packageContents = readFileSync6(join10(__dirname, "../package.json"), "utf8");
|
|
4696
5622
|
return JSON.parse(packageContents);
|
|
4697
5623
|
} catch {
|
|
4698
5624
|
return { name: "@harperfast/agent", version: "0.0.0" };
|
|
@@ -4775,7 +5701,8 @@ function parseArgs() {
|
|
|
4775
5701
|
["compactionModel", ["--compaction-model", "-c", "compaction-model"]],
|
|
4776
5702
|
["sessionPath", ["--session", "-s", "session"]],
|
|
4777
5703
|
["maxTurns", ["--max-turns"]],
|
|
4778
|
-
["maxCost", ["--max-cost"]]
|
|
5704
|
+
["maxCost", ["--max-cost"]],
|
|
5705
|
+
["rateLimitThreshold", ["--rate-limit-threshold"]]
|
|
4779
5706
|
];
|
|
4780
5707
|
let handled = false;
|
|
4781
5708
|
for (const [key, prefixes] of flagPairs) {
|
|
@@ -4783,7 +5710,7 @@ function parseArgs() {
|
|
|
4783
5710
|
if (arg === prefix) {
|
|
4784
5711
|
if (args[i + 1]) {
|
|
4785
5712
|
const val = stripQuotes(args[++i]);
|
|
4786
|
-
if (key === "maxTurns" || key === "maxCost") {
|
|
5713
|
+
if (key === "maxTurns" || key === "maxCost" || key === "rateLimitThreshold") {
|
|
4787
5714
|
trackedState[key] = parseFloat(val);
|
|
4788
5715
|
} else {
|
|
4789
5716
|
trackedState[key] = val;
|
|
@@ -4793,7 +5720,7 @@ function parseArgs() {
|
|
|
4793
5720
|
break;
|
|
4794
5721
|
} else if (arg.startsWith(`${prefix}=`)) {
|
|
4795
5722
|
const val = stripQuotes(arg.slice(prefix.length + 1));
|
|
4796
|
-
if (key === "maxTurns" || key === "maxCost") {
|
|
5723
|
+
if (key === "maxTurns" || key === "maxCost" || key === "rateLimitThreshold") {
|
|
4797
5724
|
trackedState[key] = parseFloat(val);
|
|
4798
5725
|
} else {
|
|
4799
5726
|
trackedState[key] = val;
|
|
@@ -4811,6 +5738,8 @@ function parseArgs() {
|
|
|
4811
5738
|
}
|
|
4812
5739
|
if (arg === "--flex-tier") {
|
|
4813
5740
|
trackedState.useFlexTier = true;
|
|
5741
|
+
} else if (arg === "--no-monitor-rate-limits") {
|
|
5742
|
+
trackedState.monitorRateLimits = false;
|
|
4814
5743
|
}
|
|
4815
5744
|
}
|
|
4816
5745
|
if (!trackedState.model && process.env.HARPER_AGENT_MODEL) {
|
|
@@ -4828,6 +5757,12 @@ function parseArgs() {
|
|
|
4828
5757
|
if (process.env.HARPER_AGENT_MAX_COST) {
|
|
4829
5758
|
trackedState.maxCost = parseFloat(process.env.HARPER_AGENT_MAX_COST);
|
|
4830
5759
|
}
|
|
5760
|
+
if (process.env.HARPER_AGENT_RATE_LIMIT_THRESHOLD) {
|
|
5761
|
+
trackedState.rateLimitThreshold = parseFloat(process.env.HARPER_AGENT_RATE_LIMIT_THRESHOLD);
|
|
5762
|
+
}
|
|
5763
|
+
if (process.env.HARPER_AGENT_MONITOR_RATE_LIMITS === "false") {
|
|
5764
|
+
trackedState.monitorRateLimits = false;
|
|
5765
|
+
}
|
|
4831
5766
|
const sp = trackedState.sessionPath;
|
|
4832
5767
|
if (sp) {
|
|
4833
5768
|
trackedState.sessionPath = sp && !sp.startsWith("~") && !path8.isAbsolute(sp) ? path8.resolve(process.cwd(), sp) : sp;
|
|
@@ -4835,6 +5770,15 @@ function parseArgs() {
|
|
|
4835
5770
|
if (!trackedState.useFlexTier && isTrue(process.env.HARPER_AGENT_FLEX_TIER)) {
|
|
4836
5771
|
trackedState.useFlexTier = true;
|
|
4837
5772
|
}
|
|
5773
|
+
if (isTrue(process.env.HARPER_AGENT_AUTO_APPROVE_CODE_INTERPRETER)) {
|
|
5774
|
+
trackedState.autoApproveCodeInterpreter = true;
|
|
5775
|
+
}
|
|
5776
|
+
if (isTrue(process.env.HARPER_AGENT_AUTO_APPROVE_PATCHES)) {
|
|
5777
|
+
trackedState.autoApprovePatches = true;
|
|
5778
|
+
}
|
|
5779
|
+
if (isTrue(process.env.HARPER_AGENT_AUTO_APPROVE_SHELL)) {
|
|
5780
|
+
trackedState.autoApproveShell = true;
|
|
5781
|
+
}
|
|
4838
5782
|
if (!trackedState.model) {
|
|
4839
5783
|
if (process.env.ANTHROPIC_API_KEY) {
|
|
4840
5784
|
trackedState.model = "claude-3-7-sonnet-latest";
|
|
@@ -4865,6 +5809,159 @@ function parseArgs() {
|
|
|
4865
5809
|
}
|
|
4866
5810
|
}
|
|
4867
5811
|
|
|
5812
|
+
// utils/envLoader.ts
|
|
5813
|
+
import dotenv from "dotenv";
|
|
5814
|
+
import { existsSync as existsSync9 } from "fs";
|
|
5815
|
+
import { homedir as homedir4 } from "os";
|
|
5816
|
+
import { join as join11 } from "path";
|
|
5817
|
+
function loadEnv() {
|
|
5818
|
+
const topLevelEnvPath = join11(homedir4(), ".harper", "harper-agent-env");
|
|
5819
|
+
const localEnvPath = join11(process.cwd(), ".env");
|
|
5820
|
+
if (existsSync9(topLevelEnvPath)) {
|
|
5821
|
+
dotenv.config({ path: topLevelEnvPath, quiet: true });
|
|
5822
|
+
}
|
|
5823
|
+
if (existsSync9(localEnvPath)) {
|
|
5824
|
+
dotenv.config({ path: localEnvPath, override: true, quiet: true });
|
|
5825
|
+
}
|
|
5826
|
+
}
|
|
5827
|
+
|
|
5828
|
+
// utils/package/checkForUpdate.ts
|
|
5829
|
+
import { Select as Select3 } from "@inkjs/ui";
|
|
5830
|
+
import chalk5 from "chalk";
|
|
5831
|
+
import spawn2 from "cross-spawn";
|
|
5832
|
+
import { Box as Box19, render as render2, Text as Text18 } from "ink";
|
|
5833
|
+
import React21 from "react";
|
|
5834
|
+
|
|
5835
|
+
// utils/package/getLatestVersion.ts
|
|
5836
|
+
async function getLatestVersion(packageName) {
|
|
5837
|
+
try {
|
|
5838
|
+
const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
|
|
5839
|
+
signal: AbortSignal.timeout(1e3)
|
|
5840
|
+
// 1 second timeout
|
|
5841
|
+
});
|
|
5842
|
+
if (!response.ok) {
|
|
5843
|
+
return null;
|
|
5844
|
+
}
|
|
5845
|
+
const data = await response.json();
|
|
5846
|
+
return data.version;
|
|
5847
|
+
} catch {
|
|
5848
|
+
return null;
|
|
5849
|
+
}
|
|
5850
|
+
}
|
|
5851
|
+
|
|
5852
|
+
// utils/package/isVersionNewer.ts
|
|
5853
|
+
function isVersionNewer(latest, current) {
|
|
5854
|
+
const l = latest.split(".").map((x) => parseInt(x, 10));
|
|
5855
|
+
const c = current.split(".").map((x) => parseInt(x, 10));
|
|
5856
|
+
for (let i = 0; i < 3; i++) {
|
|
5857
|
+
let latestNumber = l[i];
|
|
5858
|
+
let currentNumber = c[i];
|
|
5859
|
+
if (latestNumber === void 0 || currentNumber === void 0 || isNaN(latestNumber) || isNaN(currentNumber)) {
|
|
5860
|
+
break;
|
|
5861
|
+
}
|
|
5862
|
+
if (latestNumber > currentNumber) {
|
|
5863
|
+
return true;
|
|
5864
|
+
}
|
|
5865
|
+
if (latestNumber < currentNumber) {
|
|
5866
|
+
return false;
|
|
5867
|
+
}
|
|
5868
|
+
}
|
|
5869
|
+
return false;
|
|
5870
|
+
}
|
|
5871
|
+
|
|
5872
|
+
// utils/package/checkForUpdate.ts
|
|
5873
|
+
async function checkForUpdate() {
|
|
5874
|
+
const pkg = getOwnPackageJson();
|
|
5875
|
+
const packageName = pkg.name;
|
|
5876
|
+
const packageVersion = pkg.version;
|
|
5877
|
+
if (process.env.HARPER_AGENT_SKIP_UPDATE) {
|
|
5878
|
+
return packageVersion;
|
|
5879
|
+
}
|
|
5880
|
+
try {
|
|
5881
|
+
const latestVersion = await getLatestVersion(packageName);
|
|
5882
|
+
if (latestVersion && isVersionNewer(latestVersion, packageVersion)) {
|
|
5883
|
+
const choice = await promptForUpdateChoice(packageName, packageVersion, latestVersion);
|
|
5884
|
+
if (choice === "later") {
|
|
5885
|
+
return packageVersion;
|
|
5886
|
+
}
|
|
5887
|
+
if (choice === "never") {
|
|
5888
|
+
updateEnv("HARPER_AGENT_SKIP_UPDATE", "1");
|
|
5889
|
+
return packageVersion;
|
|
5890
|
+
}
|
|
5891
|
+
let isGlobal = false;
|
|
5892
|
+
try {
|
|
5893
|
+
const globalRootResult = spawn2.sync("npm", ["root", "-g"], { encoding: "utf8" });
|
|
5894
|
+
const globalRoot = globalRootResult.stdout?.trim();
|
|
5895
|
+
if (globalRoot && process.argv[1] && process.argv[1].startsWith(globalRoot)) {
|
|
5896
|
+
isGlobal = true;
|
|
5897
|
+
}
|
|
5898
|
+
} catch {
|
|
5899
|
+
}
|
|
5900
|
+
if (isGlobal) {
|
|
5901
|
+
spawn2.sync("npm", ["install", "-g", `${packageName}@latest`], { stdio: "inherit" });
|
|
5902
|
+
const result2 = spawn2.sync("harper-agent", process.argv.slice(2), { stdio: "inherit" });
|
|
5903
|
+
process.exit(result2.status ?? 0);
|
|
5904
|
+
}
|
|
5905
|
+
const lsResult = spawn2.sync("npm", ["cache", "npx", "ls", packageName], { encoding: "utf8" });
|
|
5906
|
+
if (lsResult.stdout) {
|
|
5907
|
+
const keys = lsResult.stdout.split("\n").map((line) => line.trim()).filter((line) => line.includes(":")).filter((line) => {
|
|
5908
|
+
const [, pkgPart] = line.split(":");
|
|
5909
|
+
return pkgPart && pkgPart.trim().startsWith(`${packageName}@`);
|
|
5910
|
+
}).map((line) => line.split(":")[0].trim());
|
|
5911
|
+
if (keys.length > 0) {
|
|
5912
|
+
spawn2.sync("npm", ["cache", "npx", "rm", ...keys], { stdio: "inherit" });
|
|
5913
|
+
}
|
|
5914
|
+
}
|
|
5915
|
+
const result = spawn2.sync("npx", ["-y", `${packageName}@latest`, ...process.argv.slice(2)], { stdio: "inherit" });
|
|
5916
|
+
process.exit(result.status ?? 0);
|
|
5917
|
+
}
|
|
5918
|
+
} catch {
|
|
5919
|
+
}
|
|
5920
|
+
return packageVersion;
|
|
5921
|
+
}
|
|
5922
|
+
function promptForUpdateChoice(pkgName, currentVersion, latestVersion) {
|
|
5923
|
+
return new Promise((resolve2) => {
|
|
5924
|
+
const app = render2(
|
|
5925
|
+
React21.createElement(UpdatePrompt, {
|
|
5926
|
+
packageName: pkgName,
|
|
5927
|
+
currentVersion,
|
|
5928
|
+
latestVersion,
|
|
5929
|
+
onSelect: (c) => {
|
|
5930
|
+
resolve2(c);
|
|
5931
|
+
app.unmount();
|
|
5932
|
+
}
|
|
5933
|
+
})
|
|
5934
|
+
);
|
|
5935
|
+
});
|
|
5936
|
+
}
|
|
5937
|
+
function UpdatePrompt({ packageName, currentVersion, latestVersion, onSelect }) {
|
|
5938
|
+
const options = [
|
|
5939
|
+
{
|
|
5940
|
+
label: `Update right now (will run: npx -y @harperfast/agent@latest)`,
|
|
5941
|
+
value: "now"
|
|
5942
|
+
},
|
|
5943
|
+
{ label: "Update later", value: "later" },
|
|
5944
|
+
{ label: "Don\u2019t ask again", value: "never" }
|
|
5945
|
+
];
|
|
5946
|
+
return React21.createElement(
|
|
5947
|
+
Box19,
|
|
5948
|
+
{ flexDirection: "column", padding: 1 },
|
|
5949
|
+
React21.createElement(
|
|
5950
|
+
Text18,
|
|
5951
|
+
null,
|
|
5952
|
+
`${chalk5.yellow("Update available:")} ${chalk5.bold(packageName)} ${chalk5.dim(`v${currentVersion}`)} \u2192 ${chalk5.green(`v${latestVersion}`)}`
|
|
5953
|
+
),
|
|
5954
|
+
React21.createElement(
|
|
5955
|
+
Box19,
|
|
5956
|
+
{ marginTop: 1 },
|
|
5957
|
+
React21.createElement(Select3, {
|
|
5958
|
+
options,
|
|
5959
|
+
onChange: (v) => onSelect(v)
|
|
5960
|
+
})
|
|
5961
|
+
)
|
|
5962
|
+
);
|
|
5963
|
+
}
|
|
5964
|
+
|
|
4868
5965
|
// utils/shell/ensureApiKey.ts
|
|
4869
5966
|
function ensureApiKey() {
|
|
4870
5967
|
const models = [
|
|
@@ -4905,8 +6002,22 @@ async function getStdin() {
|
|
|
4905
6002
|
|
|
4906
6003
|
// agent.ts
|
|
4907
6004
|
(async function() {
|
|
6005
|
+
setupGlobalErrorHandlers();
|
|
6006
|
+
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
|
+
};
|
|
4908
6018
|
process.on("SIGINT", handleExit);
|
|
4909
6019
|
process.on("SIGTERM", handleExit);
|
|
6020
|
+
await checkForUpdate();
|
|
4910
6021
|
parseArgs();
|
|
4911
6022
|
if (!ensureApiKey()) {
|
|
4912
6023
|
await new Promise((resolve2) => {
|
|
@@ -4915,7 +6026,7 @@ async function getStdin() {
|
|
|
4915
6026
|
emitToListeners("ExitUI", void 0);
|
|
4916
6027
|
parseArgs();
|
|
4917
6028
|
if (!ensureApiKey()) {
|
|
4918
|
-
console.log(
|
|
6029
|
+
console.log(chalk6.red("No key provided. Exiting."));
|
|
4919
6030
|
process.exit(1);
|
|
4920
6031
|
}
|
|
4921
6032
|
}
|