@harperfast/agent 0.13.0 → 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/agent.js +167 -74
- package/package.json +13 -13
- package/dist/BrowserWebSocketTransport-YCNS4FS4.js +0 -7
- package/dist/LaunchOptions-Y7D2IMM3.js +0 -9
- package/dist/NodeWebSocketTransport-TOVQQIXD.js +0 -8
- package/dist/bidi-UGABXYIW.js +0 -18794
- package/dist/chunk-23SGXNI6.js +0 -244
- package/dist/chunk-2ESYSVXG.js +0 -48
- package/dist/chunk-435G4BZK.js +0 -210767
- package/dist/chunk-FCLITLWE.js +0 -775
- package/dist/chunk-MGX7MDP2.js +0 -15
- package/dist/chunk-PG3SGAEX.js +0 -40
- package/dist/chunk-SACY2WFT.js +0 -12205
- package/dist/chunk-SLTU5TTQ.js +0 -31483
- package/dist/chunk-VD4EVG4H.js +0 -3692
- package/dist/chunk-YLJAHQTP.js +0 -30
- package/dist/chunk-ZDS7NDTW.js +0 -1661
- package/dist/extract-zip-N66P73SM.js +0 -1499
- package/dist/helpers-4S4VJSDS.js +0 -17
- package/dist/main-J53CTMGG.js +0 -60
- package/dist/puppeteer-KULXOV7T.js +0 -15064
- package/dist/src-GTRPEVYZ.js +0 -5
- package/dist/tar-fs-ETYYVPLS.js +0 -2562
- package/dist/typescript-XXE3DJ5D.js +0 -5
- package/dist/yargs-CJ75NAFN.js +0 -3230
package/README.md
CHANGED
|
@@ -56,6 +56,18 @@ Harper: What do you want to do together today?
|
|
|
56
56
|
>
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
### Non-interactive: pipe an initial prompt
|
|
60
|
+
|
|
61
|
+
You can pass an initial chat dump via stdin. This runs a one-shot interaction and exits after responding:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
cat somePrompt.md | harper-agent
|
|
65
|
+
# or
|
|
66
|
+
harper-agent < somePrompt.md
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
In this mode, the initial greeting question is suppressed, and the agent processes the provided prompt immediately.
|
|
70
|
+
|
|
59
71
|
## Model Selection
|
|
60
72
|
|
|
61
73
|
By default, `harper-agent` uses OpenAI. You can switch to other models using the `--model` (or `-m`) flag:
|
package/dist/agent.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./chunk-2ESYSVXG.js";
|
|
3
2
|
|
|
4
3
|
// agent.ts
|
|
5
4
|
import "dotenv/config";
|
|
@@ -93,6 +92,17 @@ var CostTracker = class {
|
|
|
93
92
|
this.totalCompactionCost = 0;
|
|
94
93
|
this.hasUnknownPrices = false;
|
|
95
94
|
}
|
|
95
|
+
getTotalCost() {
|
|
96
|
+
return this.totalCost + this.totalCompactionCost;
|
|
97
|
+
}
|
|
98
|
+
getEstimatedTotalCost(currentTurnUsage, model, compactionModel) {
|
|
99
|
+
const { turnCost, compactionCost } = this.calculateUsageCosts(
|
|
100
|
+
model,
|
|
101
|
+
currentTurnUsage,
|
|
102
|
+
compactionModel
|
|
103
|
+
);
|
|
104
|
+
return this.getTotalCost() + turnCost + compactionCost;
|
|
105
|
+
}
|
|
96
106
|
recordTurn(model, usage, compactionModel) {
|
|
97
107
|
const { turnCost, compactionCost, unknownPrices } = this.calculateUsageCosts(model, usage, compactionModel);
|
|
98
108
|
this.totalInputTokens += usage.inputTokens;
|
|
@@ -316,6 +326,8 @@ var trackedState = {
|
|
|
316
326
|
useFlexTier: false,
|
|
317
327
|
disableSpinner: false,
|
|
318
328
|
enableInterruptions: true,
|
|
329
|
+
maxTurns: 30,
|
|
330
|
+
maxCost: null,
|
|
319
331
|
session: null
|
|
320
332
|
};
|
|
321
333
|
|
|
@@ -517,6 +529,12 @@ ${chalk3.bold("OPTIONS")}
|
|
|
517
529
|
Can also be set via HARPER_AGENT_COMPACTION_MODEL environment variable.
|
|
518
530
|
-s, --session Specify a path to a SQLite database file to persist the chat session.
|
|
519
531
|
Can also be set via HARPER_AGENT_SESSION environment variable.
|
|
532
|
+
--max-turns Specify the maximum number of turns for the agent run.
|
|
533
|
+
In task-driven mode, this defaults to unlimited.
|
|
534
|
+
Can also be set via HARPER_AGENT_MAX_TURNS environment variable.
|
|
535
|
+
--max-cost Specify the maximum cost (in USD) for the agent run.
|
|
536
|
+
If exceeded, the agent will exit with a non-zero code.
|
|
537
|
+
Can also be set via HARPER_AGENT_MAX_COST environment variable.
|
|
520
538
|
--flex-tier Force the use of the flex service tier for lower costs but potentially
|
|
521
539
|
more errors under high system load.
|
|
522
540
|
Can also be set via HARPER_AGENT_FLEX_TIER=true environment variable.
|
|
@@ -584,19 +602,31 @@ function parseArgs() {
|
|
|
584
602
|
const flagPairs = [
|
|
585
603
|
["model", ["--model", "-m", "model"]],
|
|
586
604
|
["compactionModel", ["--compaction-model", "-c", "compaction-model"]],
|
|
587
|
-
["sessionPath", ["--session", "-s", "session"]]
|
|
605
|
+
["sessionPath", ["--session", "-s", "session"]],
|
|
606
|
+
["maxTurns", ["--max-turns"]],
|
|
607
|
+
["maxCost", ["--max-cost"]]
|
|
588
608
|
];
|
|
589
609
|
let handled = false;
|
|
590
610
|
for (const [key, prefixes] of flagPairs) {
|
|
591
611
|
for (const prefix of prefixes) {
|
|
592
612
|
if (arg === prefix) {
|
|
593
613
|
if (args[i + 1]) {
|
|
594
|
-
|
|
614
|
+
const val = stripQuotes(args[++i]);
|
|
615
|
+
if (key === "maxTurns" || key === "maxCost") {
|
|
616
|
+
trackedState[key] = parseFloat(val);
|
|
617
|
+
} else {
|
|
618
|
+
trackedState[key] = val;
|
|
619
|
+
}
|
|
595
620
|
}
|
|
596
621
|
handled = true;
|
|
597
622
|
break;
|
|
598
623
|
} else if (arg.startsWith(`${prefix}=`)) {
|
|
599
|
-
|
|
624
|
+
const val = stripQuotes(arg.slice(prefix.length + 1));
|
|
625
|
+
if (key === "maxTurns" || key === "maxCost") {
|
|
626
|
+
trackedState[key] = parseFloat(val);
|
|
627
|
+
} else {
|
|
628
|
+
trackedState[key] = val;
|
|
629
|
+
}
|
|
600
630
|
handled = true;
|
|
601
631
|
break;
|
|
602
632
|
}
|
|
@@ -632,6 +662,12 @@ function parseArgs() {
|
|
|
632
662
|
if (!trackedState.sessionPath && process.env.HARPER_AGENT_SESSION) {
|
|
633
663
|
trackedState.sessionPath = process.env.HARPER_AGENT_SESSION;
|
|
634
664
|
}
|
|
665
|
+
if (process.env.HARPER_AGENT_MAX_TURNS) {
|
|
666
|
+
trackedState.maxTurns = parseFloat(process.env.HARPER_AGENT_MAX_TURNS);
|
|
667
|
+
}
|
|
668
|
+
if (process.env.HARPER_AGENT_MAX_COST) {
|
|
669
|
+
trackedState.maxCost = parseFloat(process.env.HARPER_AGENT_MAX_COST);
|
|
670
|
+
}
|
|
635
671
|
const sp = trackedState.sessionPath;
|
|
636
672
|
if (sp) {
|
|
637
673
|
trackedState.sessionPath = sp && !sp.startsWith("~") && !path.isAbsolute(sp) ? path.resolve(process.cwd(), sp) : sp;
|
|
@@ -715,7 +751,7 @@ async function getBrowser() {
|
|
|
715
751
|
if (!browser) {
|
|
716
752
|
let puppeteer;
|
|
717
753
|
try {
|
|
718
|
-
puppeteer = await import("
|
|
754
|
+
puppeteer = await import("puppeteer");
|
|
719
755
|
} catch {
|
|
720
756
|
throw new Error(
|
|
721
757
|
"Puppeteer is not installed. Browser tools require puppeteer. Please install it with `npm install puppeteer`."
|
|
@@ -1035,7 +1071,7 @@ var ToolParameters10 = z10.object({
|
|
|
1035
1071
|
)
|
|
1036
1072
|
});
|
|
1037
1073
|
var getHarperSkillTool = tool10({
|
|
1038
|
-
name: "
|
|
1074
|
+
name: "get_harper_skill",
|
|
1039
1075
|
description: getSkillsDescription(),
|
|
1040
1076
|
parameters: ToolParameters10,
|
|
1041
1077
|
execute: execute10
|
|
@@ -1232,12 +1268,12 @@ async function requiredSkillForOperation(path8, type) {
|
|
|
1232
1268
|
}
|
|
1233
1269
|
const p = normalizedPath(path8);
|
|
1234
1270
|
const read = await getSkillsRead();
|
|
1235
|
-
if (p.includes("/resources/") || p.startsWith("resources/")) {
|
|
1236
|
-
if (!read.includes("
|
|
1237
|
-
return pickExistingSkill(["
|
|
1271
|
+
if (p.includes("/resources/") || p.startsWith("resources/") || p.endsWith("resources.ts") || p.endsWith("resources.js")) {
|
|
1272
|
+
if (!read.includes("automatic-apis")) {
|
|
1273
|
+
return pickExistingSkill(["automatic-apis"]);
|
|
1238
1274
|
}
|
|
1239
1275
|
}
|
|
1240
|
-
if (p.
|
|
1276
|
+
if (p.endsWith(".graphql")) {
|
|
1241
1277
|
if (!read.includes("adding-tables-with-schemas")) {
|
|
1242
1278
|
return pickExistingSkill(["adding-tables-with-schemas"]);
|
|
1243
1279
|
}
|
|
@@ -1276,40 +1312,42 @@ ${chalk7.bold.bgYellow.black(" Apply patch approval required: ")}`);
|
|
|
1276
1312
|
return false;
|
|
1277
1313
|
}
|
|
1278
1314
|
}
|
|
1315
|
+
async function execute11(operation) {
|
|
1316
|
+
try {
|
|
1317
|
+
const needed = await requiredSkillForOperation(operation.path, operation.type);
|
|
1318
|
+
if (needed) {
|
|
1319
|
+
const content = await execute10({ skill: needed });
|
|
1320
|
+
console.log(`Understanding ${needed} is necessary before applying this patch.`);
|
|
1321
|
+
return { status: "failed, skill guarded", output: content };
|
|
1322
|
+
}
|
|
1323
|
+
switch (operation.type) {
|
|
1324
|
+
case "create_file":
|
|
1325
|
+
if (!operation.diff) {
|
|
1326
|
+
return { status: "failed", output: "Error: diff is required for create_file" };
|
|
1327
|
+
}
|
|
1328
|
+
return await editor.createFile(operation);
|
|
1329
|
+
case "update_file":
|
|
1330
|
+
if (!operation.diff) {
|
|
1331
|
+
return { status: "failed", output: "Error: diff is required for update_file" };
|
|
1332
|
+
}
|
|
1333
|
+
return await editor.updateFile(operation);
|
|
1334
|
+
case "delete_file":
|
|
1335
|
+
return await editor.deleteFile(operation);
|
|
1336
|
+
default:
|
|
1337
|
+
return { status: "failed", output: `Error: Unknown operation type: ${operation.type}` };
|
|
1338
|
+
}
|
|
1339
|
+
} catch (err) {
|
|
1340
|
+
console.error("hit unexpected error in apply patch tool", err);
|
|
1341
|
+
return { status: "failed", output: `apply_patch threw: ${String(err)}` };
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1279
1344
|
function createApplyPatchTool() {
|
|
1280
1345
|
return tool11({
|
|
1281
1346
|
name: "apply_patch",
|
|
1282
1347
|
description: "Applies a patch (create, update, or delete a file) to the workspace.",
|
|
1283
1348
|
parameters: ApplyPatchParameters,
|
|
1284
1349
|
needsApproval,
|
|
1285
|
-
execute:
|
|
1286
|
-
try {
|
|
1287
|
-
const needed = await requiredSkillForOperation(operation.path, operation.type);
|
|
1288
|
-
if (needed) {
|
|
1289
|
-
const content = await execute10({ skill: needed });
|
|
1290
|
-
return { status: "completed", output: content };
|
|
1291
|
-
}
|
|
1292
|
-
switch (operation.type) {
|
|
1293
|
-
case "create_file":
|
|
1294
|
-
if (!operation.diff) {
|
|
1295
|
-
return { status: "failed", output: "Error: diff is required for create_file" };
|
|
1296
|
-
}
|
|
1297
|
-
return await editor.createFile(operation);
|
|
1298
|
-
case "update_file":
|
|
1299
|
-
if (!operation.diff) {
|
|
1300
|
-
return { status: "failed", output: "Error: diff is required for update_file" };
|
|
1301
|
-
}
|
|
1302
|
-
return await editor.updateFile(operation);
|
|
1303
|
-
case "delete_file":
|
|
1304
|
-
return await editor.deleteFile(operation);
|
|
1305
|
-
default:
|
|
1306
|
-
return { status: "failed", output: `Error: Unknown operation type: ${operation.type}` };
|
|
1307
|
-
}
|
|
1308
|
-
} catch (err) {
|
|
1309
|
-
console.error("hit unexpected error in apply patch tool", err);
|
|
1310
|
-
return { status: "failed", output: `apply_patch threw: ${String(err)}` };
|
|
1311
|
-
}
|
|
1312
|
-
}
|
|
1350
|
+
execute: execute11
|
|
1313
1351
|
});
|
|
1314
1352
|
}
|
|
1315
1353
|
|
|
@@ -1320,7 +1358,7 @@ import { z as z12 } from "zod";
|
|
|
1320
1358
|
var ToolParameters11 = z12.object({
|
|
1321
1359
|
path: z12.string().describe("Directory to switch into. Can be absolute or relative to current workspace.")
|
|
1322
1360
|
});
|
|
1323
|
-
async function
|
|
1361
|
+
async function execute12({ path: path8 }) {
|
|
1324
1362
|
try {
|
|
1325
1363
|
const target = resolvePath(trackedState.cwd, path8);
|
|
1326
1364
|
const stat = statSync(target);
|
|
@@ -1348,10 +1386,10 @@ I strongly suggest you use these newfound skills!`;
|
|
|
1348
1386
|
}
|
|
1349
1387
|
}
|
|
1350
1388
|
var changeCwdTool = tool12({
|
|
1351
|
-
name: "
|
|
1389
|
+
name: "change_cwd",
|
|
1352
1390
|
description: "Changes the current working directory for subsequent tools. Accepts absolute or relative paths.",
|
|
1353
1391
|
parameters: ToolParameters11,
|
|
1354
|
-
execute:
|
|
1392
|
+
execute: execute12
|
|
1355
1393
|
});
|
|
1356
1394
|
|
|
1357
1395
|
// tools/files/egrepTool.ts
|
|
@@ -1428,7 +1466,7 @@ var ToolParameters14 = z15.object({
|
|
|
1428
1466
|
directoryName: z15.string().describe("The name of the directory to read.")
|
|
1429
1467
|
});
|
|
1430
1468
|
var readDirTool = tool15({
|
|
1431
|
-
name: "
|
|
1469
|
+
name: "read_dir",
|
|
1432
1470
|
description: "Lists the files in a directory.",
|
|
1433
1471
|
parameters: ToolParameters14,
|
|
1434
1472
|
async execute({ directoryName }) {
|
|
@@ -1458,7 +1496,7 @@ var IMAGE_EXTENSIONS = {
|
|
|
1458
1496
|
".webp": "image/webp",
|
|
1459
1497
|
".bmp": "image/bmp"
|
|
1460
1498
|
};
|
|
1461
|
-
async function
|
|
1499
|
+
async function execute13({ fileName }) {
|
|
1462
1500
|
try {
|
|
1463
1501
|
const normalized = String(fileName).replace(/\\/g, "/");
|
|
1464
1502
|
const m = normalized.match(/(?:^|\/)skills\/([A-Za-z0-9_-]+)\.md(?:$|[?#])/);
|
|
@@ -1481,10 +1519,10 @@ async function execute12({ fileName }) {
|
|
|
1481
1519
|
}
|
|
1482
1520
|
}
|
|
1483
1521
|
var readFileTool = tool16({
|
|
1484
|
-
name: "
|
|
1522
|
+
name: "read_file",
|
|
1485
1523
|
description: "Reads the contents of a specified file. If the file is an image, it returns a structured image object.",
|
|
1486
1524
|
parameters: ToolParameters15,
|
|
1487
|
-
execute:
|
|
1525
|
+
execute: execute13
|
|
1488
1526
|
});
|
|
1489
1527
|
|
|
1490
1528
|
// tools/general/codeInterpreterTool.ts
|
|
@@ -1506,7 +1544,7 @@ var codeInterpreterTool = tool17({
|
|
|
1506
1544
|
name: "code_interpreter",
|
|
1507
1545
|
description: "Executes Python or JavaScript code in a local environment. This is useful for data analysis, complex calculations, and more. All code will be executed in the current workspace.",
|
|
1508
1546
|
parameters: CodeInterpreterParameters,
|
|
1509
|
-
execute:
|
|
1547
|
+
execute: execute14,
|
|
1510
1548
|
needsApproval: needsApproval2
|
|
1511
1549
|
});
|
|
1512
1550
|
async function needsApproval2(runContext, { code, language }, callId) {
|
|
@@ -1528,7 +1566,7 @@ ${chalk8.bold.bgYellow.black(` Code interpreter (${language}) approval required:
|
|
|
1528
1566
|
}
|
|
1529
1567
|
return !autoApproved;
|
|
1530
1568
|
}
|
|
1531
|
-
async function
|
|
1569
|
+
async function execute14({ code, language }) {
|
|
1532
1570
|
const extension = language === "javascript" ? "js" : "py";
|
|
1533
1571
|
const interpreter = language === "javascript" ? "node" : "python3";
|
|
1534
1572
|
const tempFile = path6.join(trackedState.cwd, `.temp_code_${Date.now()}.${extension}`);
|
|
@@ -1582,7 +1620,7 @@ var SetInterpreterAutoApproveParameters = z18.object({
|
|
|
1582
1620
|
autoApprove: z18.boolean()
|
|
1583
1621
|
});
|
|
1584
1622
|
var setInterpreterAutoApproveTool = tool18({
|
|
1585
|
-
name: "
|
|
1623
|
+
name: "set_interpreter_auto_approve",
|
|
1586
1624
|
description: "Enable or disable automatic approval for code interpreter by setting HARPER_AGENT_AUTO_APPROVE_CODE_INTERPRETER=1 or 0 in .env and current process.",
|
|
1587
1625
|
parameters: SetInterpreterAutoApproveParameters,
|
|
1588
1626
|
needsApproval: async (_runContext, { autoApprove }) => {
|
|
@@ -1610,7 +1648,7 @@ var SetPatchAutoApproveParameters = z19.object({
|
|
|
1610
1648
|
autoApprove: z19.boolean()
|
|
1611
1649
|
});
|
|
1612
1650
|
var setPatchAutoApproveTool = tool19({
|
|
1613
|
-
name: "
|
|
1651
|
+
name: "set_patch_auto_approve",
|
|
1614
1652
|
description: "Enable or disable automatic approval for patch commands by setting HARPER_AGENT_AUTO_APPROVE_PATCHES=1 or 0 in .env and current process.",
|
|
1615
1653
|
parameters: SetPatchAutoApproveParameters,
|
|
1616
1654
|
needsApproval: async (_runContext, { autoApprove }) => {
|
|
@@ -1638,7 +1676,7 @@ var SetShellAutoApproveParameters = z20.object({
|
|
|
1638
1676
|
autoApprove: z20.boolean()
|
|
1639
1677
|
});
|
|
1640
1678
|
var setShellAutoApproveTool = tool20({
|
|
1641
|
-
name: "
|
|
1679
|
+
name: "set_shell_auto_approve",
|
|
1642
1680
|
description: "Enable or disable automatic approval for shell commands by setting HARPER_AGENT_AUTO_APPROVE_SHELL=1 or 0 in .env and current process.",
|
|
1643
1681
|
parameters: SetShellAutoApproveParameters,
|
|
1644
1682
|
needsApproval: async (_runContext, { autoApprove }) => {
|
|
@@ -1779,8 +1817,8 @@ var ShellParameters = z21.object({
|
|
|
1779
1817
|
});
|
|
1780
1818
|
var shell = new LocalShell();
|
|
1781
1819
|
var shellTool = tool21({
|
|
1782
|
-
name: "
|
|
1783
|
-
description: "Executes shell commands.",
|
|
1820
|
+
name: "shell",
|
|
1821
|
+
description: "Executes shell commands. Only use when we do not have a better tool.",
|
|
1784
1822
|
parameters: ShellParameters,
|
|
1785
1823
|
execute: async ({ commands }) => {
|
|
1786
1824
|
const result = await shell.run({ commands });
|
|
@@ -1844,7 +1882,7 @@ var GitAddParameters = z22.object({
|
|
|
1844
1882
|
files: z22.array(z22.string()).describe("The files to add. If not provided, all changes will be added.")
|
|
1845
1883
|
});
|
|
1846
1884
|
var gitAddTool = tool22({
|
|
1847
|
-
name: "
|
|
1885
|
+
name: "git_add",
|
|
1848
1886
|
description: "Add file contents to the index.",
|
|
1849
1887
|
parameters: GitAddParameters,
|
|
1850
1888
|
async execute({ files }) {
|
|
@@ -1874,7 +1912,7 @@ var GitBranchParameters = z23.object({
|
|
|
1874
1912
|
create: z23.boolean().optional().default(false).describe("Whether to create a new branch.")
|
|
1875
1913
|
});
|
|
1876
1914
|
var gitBranchTool = tool23({
|
|
1877
|
-
name: "
|
|
1915
|
+
name: "git_branch",
|
|
1878
1916
|
description: "Create or switch to a git branch.",
|
|
1879
1917
|
parameters: GitBranchParameters,
|
|
1880
1918
|
needsApproval: true,
|
|
@@ -1902,7 +1940,7 @@ var GitCommitParameters = z24.object({
|
|
|
1902
1940
|
)
|
|
1903
1941
|
});
|
|
1904
1942
|
var gitCommitTool = tool24({
|
|
1905
|
-
name: "
|
|
1943
|
+
name: "git_commit",
|
|
1906
1944
|
description: "Commit changes to the repository.",
|
|
1907
1945
|
parameters: GitCommitParameters,
|
|
1908
1946
|
async execute({ message, addAll }) {
|
|
@@ -1927,7 +1965,7 @@ var GitLogParameters = z25.object({
|
|
|
1927
1965
|
oneline: z25.boolean().optional().default(true).describe("Whether to show log in oneline format.")
|
|
1928
1966
|
});
|
|
1929
1967
|
var gitLogTool = tool25({
|
|
1930
|
-
name: "
|
|
1968
|
+
name: "git_log",
|
|
1931
1969
|
description: "Show commit logs.",
|
|
1932
1970
|
parameters: GitLogParameters,
|
|
1933
1971
|
async execute({ count, oneline }) {
|
|
@@ -1956,7 +1994,7 @@ var GitStashParameters = z26.object({
|
|
|
1956
1994
|
message: z26.string().describe("A message for the stash change.")
|
|
1957
1995
|
});
|
|
1958
1996
|
var gitStashTool = tool26({
|
|
1959
|
-
name: "
|
|
1997
|
+
name: "git_stash",
|
|
1960
1998
|
description: "Stash changes or apply a stash.",
|
|
1961
1999
|
parameters: GitStashParameters,
|
|
1962
2000
|
async execute({ action, message }) {
|
|
@@ -1986,7 +2024,7 @@ var GitStatusParameters = z27.object({
|
|
|
1986
2024
|
short: z27.boolean().optional().default(false).describe("Whether to show the status in short format.")
|
|
1987
2025
|
});
|
|
1988
2026
|
var gitStatusTool = tool27({
|
|
1989
|
-
name: "
|
|
2027
|
+
name: "git_status",
|
|
1990
2028
|
description: "Show the working tree status.",
|
|
1991
2029
|
parameters: GitStatusParameters,
|
|
1992
2030
|
async execute({ short }) {
|
|
@@ -2015,7 +2053,7 @@ var GitWorkspaceParameters = z28.object({
|
|
|
2015
2053
|
createBranch: z28.boolean().optional().default(false).describe("Whether to create a new branch for this workspace.")
|
|
2016
2054
|
});
|
|
2017
2055
|
var gitWorkspaceTool = tool28({
|
|
2018
|
-
name: "
|
|
2056
|
+
name: "git_workspace",
|
|
2019
2057
|
description: "Create a new workspace (git worktree) for parallel work.",
|
|
2020
2058
|
parameters: GitWorkspaceParameters,
|
|
2021
2059
|
async execute({ path: workspacePath, branchName, createBranch }) {
|
|
@@ -2035,7 +2073,7 @@ import { tool as tool29 } from "@openai/agents";
|
|
|
2035
2073
|
import { z as z29 } from "zod";
|
|
2036
2074
|
var ToolParameters16 = z29.object({});
|
|
2037
2075
|
var checkHarperStatusTool = tool29({
|
|
2038
|
-
name: "
|
|
2076
|
+
name: "check_harper_status",
|
|
2039
2077
|
description: "Checks if a Harper application is currently running.",
|
|
2040
2078
|
parameters: ToolParameters16,
|
|
2041
2079
|
async execute() {
|
|
@@ -2105,7 +2143,7 @@ var ToolParameters17 = z30.object({
|
|
|
2105
2143
|
directoryName: z30.string().describe("The name of the directory to create the application in."),
|
|
2106
2144
|
template: z30.enum(["vanilla-ts", "vanilla", "react-ts", "react"]).optional().describe("The template to use for the new application. Defaults to vanilla-ts.").default("vanilla-ts")
|
|
2107
2145
|
});
|
|
2108
|
-
async function
|
|
2146
|
+
async function execute15({ directoryName, template }) {
|
|
2109
2147
|
const currentCwd = trackedState.cwd;
|
|
2110
2148
|
const resolvedPath = resolvePath(currentCwd, directoryName);
|
|
2111
2149
|
const isCurrentDir = resolvedPath === currentCwd;
|
|
@@ -2122,7 +2160,7 @@ async function execute14({ directoryName, template }) {
|
|
|
2122
2160
|
});
|
|
2123
2161
|
console.log(`Initializing new Git repository in ${resolvedPath}...`);
|
|
2124
2162
|
execSync3("git init", { cwd: resolvedPath, stdio: "ignore" });
|
|
2125
|
-
const switchedDir = await
|
|
2163
|
+
const switchedDir = await execute12({ path: resolvedPath });
|
|
2126
2164
|
return `Successfully created a new Harper application in '${resolvedPath}' using template '${template}' with a matching Git repository initialized. Use the readDir and readFile tools to inspect the contents of the application. ${switchedDir}.`;
|
|
2127
2165
|
} catch (error) {
|
|
2128
2166
|
let errorMsg = `Error creating new Harper application: ${error.message}`;
|
|
@@ -2136,10 +2174,10 @@ ${error.stdout}`;
|
|
|
2136
2174
|
}
|
|
2137
2175
|
}
|
|
2138
2176
|
var createNewHarperApplicationTool = tool30({
|
|
2139
|
-
name: "
|
|
2177
|
+
name: "create_new_harper_application",
|
|
2140
2178
|
description: "Creates a new Harper application using the best available package manager (yarn/pnpm/bun/deno, falling back to npm).",
|
|
2141
2179
|
parameters: ToolParameters17,
|
|
2142
|
-
execute:
|
|
2180
|
+
execute: execute15
|
|
2143
2181
|
});
|
|
2144
2182
|
|
|
2145
2183
|
// tools/harper/getHarperConfigSchemaTool.ts
|
|
@@ -2154,7 +2192,7 @@ var ToolParameters18 = z31.object({
|
|
|
2154
2192
|
)
|
|
2155
2193
|
});
|
|
2156
2194
|
var getHarperConfigSchemaTool = tool31({
|
|
2157
|
-
name: "
|
|
2195
|
+
name: "get_harper_config_schema",
|
|
2158
2196
|
description: "Returns the JSON schema for HarperDB configuration files (either app or root), which describes the config.yaml or harperdb-config.yaml files.",
|
|
2159
2197
|
parameters: ToolParameters18,
|
|
2160
2198
|
async execute({ schemaType }) {
|
|
@@ -2190,7 +2228,7 @@ var ToolParameters19 = z32.object({
|
|
|
2190
2228
|
).default("ResourceInterfaceV2")
|
|
2191
2229
|
});
|
|
2192
2230
|
var getHarperResourceInterfaceTool = tool32({
|
|
2193
|
-
name: "
|
|
2231
|
+
name: "get_harper_resource_interface",
|
|
2194
2232
|
description: "Reads HarperDB resource interface and class definitions (like ResourceInterfaceV2.d.ts) to understand how resources and tables are structured.",
|
|
2195
2233
|
parameters: ToolParameters19,
|
|
2196
2234
|
async execute({ resourceFile }) {
|
|
@@ -2217,7 +2255,7 @@ import { dirname as dirname4, join as join10 } from "path";
|
|
|
2217
2255
|
import { z as z33 } from "zod";
|
|
2218
2256
|
var ToolParameters20 = z33.object({});
|
|
2219
2257
|
var getHarperSchemaGraphQLTool = tool33({
|
|
2220
|
-
name: "
|
|
2258
|
+
name: "get_harper_schema_graphql",
|
|
2221
2259
|
description: "Returns the GraphQL schema for HarperDB schema files, which define the structure of HarperDB database tables.",
|
|
2222
2260
|
parameters: ToolParameters20,
|
|
2223
2261
|
async execute() {
|
|
@@ -2249,11 +2287,11 @@ var ToolParameters21 = z34.object({
|
|
|
2249
2287
|
body: z34.string().optional().default("").describe("An optional JSON string body to send along with the request.")
|
|
2250
2288
|
});
|
|
2251
2289
|
var hitHarperAPITool = tool34({
|
|
2252
|
-
name: "
|
|
2290
|
+
name: "hit_harper_api",
|
|
2253
2291
|
description: "Performs a request against the running Harper API. Use /openapi to look up Harper APIs.",
|
|
2254
2292
|
parameters: ToolParameters21,
|
|
2255
2293
|
needsApproval: async (runContext, input, callId) => {
|
|
2256
|
-
if (callId && runContext.isToolApproved({ toolName: "
|
|
2294
|
+
if (callId && runContext.isToolApproved({ toolName: "hit_harper_api", callId })) {
|
|
2257
2295
|
return false;
|
|
2258
2296
|
}
|
|
2259
2297
|
if (input.method === "DELETE") {
|
|
@@ -2293,7 +2331,7 @@ import { tool as tool35 } from "@openai/agents";
|
|
|
2293
2331
|
import { z as z35 } from "zod";
|
|
2294
2332
|
var ToolParameters22 = z35.object({});
|
|
2295
2333
|
var readHarperLogsTool = tool35({
|
|
2296
|
-
name: "
|
|
2334
|
+
name: "read_harper_logs",
|
|
2297
2335
|
description: "Reads the most recent console logs of a started Harper app and clears them so that subsequent reads will only show new logs.",
|
|
2298
2336
|
parameters: ToolParameters22,
|
|
2299
2337
|
async execute() {
|
|
@@ -2325,7 +2363,7 @@ var ToolParameters23 = z36.object({
|
|
|
2325
2363
|
directoryName: z36.string().describe("The name of the directory that the Harper app is in.")
|
|
2326
2364
|
});
|
|
2327
2365
|
var startHarperTool = tool36({
|
|
2328
|
-
name: "
|
|
2366
|
+
name: "start_harper",
|
|
2329
2367
|
description: "Starts a Harper app background process, allowing you to observe the app in action (by readHarperLogsTool, hitHarperAPITool, etc).",
|
|
2330
2368
|
parameters: ToolParameters23,
|
|
2331
2369
|
async execute({ directoryName }) {
|
|
@@ -2360,7 +2398,7 @@ import { tool as tool37 } from "@openai/agents";
|
|
|
2360
2398
|
import { z as z37 } from "zod";
|
|
2361
2399
|
var ToolParameters24 = z37.object({});
|
|
2362
2400
|
var stopHarperTool = tool37({
|
|
2363
|
-
name: "
|
|
2401
|
+
name: "stop_harper",
|
|
2364
2402
|
description: "Stops all previously started Harper app background process.",
|
|
2365
2403
|
parameters: ToolParameters24,
|
|
2366
2404
|
async execute() {
|
|
@@ -2672,6 +2710,19 @@ async function ensureApiKey() {
|
|
|
2672
2710
|
}
|
|
2673
2711
|
}
|
|
2674
2712
|
|
|
2713
|
+
// utils/shell/getStdin.ts
|
|
2714
|
+
async function getStdin() {
|
|
2715
|
+
if (process.stdin.isTTY) {
|
|
2716
|
+
return "";
|
|
2717
|
+
}
|
|
2718
|
+
let result = "";
|
|
2719
|
+
process.stdin.setEncoding("utf8");
|
|
2720
|
+
for await (const chunk of process.stdin) {
|
|
2721
|
+
result += chunk;
|
|
2722
|
+
}
|
|
2723
|
+
return result.trim();
|
|
2724
|
+
}
|
|
2725
|
+
|
|
2675
2726
|
// utils/sessions/createSession.ts
|
|
2676
2727
|
import { MemorySession as MemorySession3 } from "@openai/agents";
|
|
2677
2728
|
|
|
@@ -3116,6 +3167,7 @@ async function main() {
|
|
|
3116
3167
|
parseArgs();
|
|
3117
3168
|
await ensureApiKey();
|
|
3118
3169
|
sayHi();
|
|
3170
|
+
const stdinPrompt = await getStdin();
|
|
3119
3171
|
const agent = trackedState.agent = new Agent2({
|
|
3120
3172
|
name: "Harper App Development Assistant",
|
|
3121
3173
|
model: isOpenAIModel(trackedState.model) ? trackedState.model : getModel(trackedState.model),
|
|
@@ -3124,12 +3176,20 @@ async function main() {
|
|
|
3124
3176
|
tools: createTools()
|
|
3125
3177
|
});
|
|
3126
3178
|
const session = trackedState.session = createSession(trackedState.sessionPath);
|
|
3179
|
+
let firstIteration = true;
|
|
3127
3180
|
while (true) {
|
|
3128
3181
|
let task = "";
|
|
3129
3182
|
let lastToolCallInfo = null;
|
|
3130
3183
|
trackedState.controller = new AbortController();
|
|
3131
3184
|
if (!trackedState.approvalState) {
|
|
3132
|
-
|
|
3185
|
+
if (firstIteration && stdinPrompt) {
|
|
3186
|
+
task = stdinPrompt;
|
|
3187
|
+
console.log(`${chalk13.bold(">")} ${task}
|
|
3188
|
+
`);
|
|
3189
|
+
} else {
|
|
3190
|
+
task = await askQuestion("> ");
|
|
3191
|
+
}
|
|
3192
|
+
firstIteration = false;
|
|
3133
3193
|
if (!task) {
|
|
3134
3194
|
trackedState.emptyLines += 1;
|
|
3135
3195
|
if (trackedState.emptyLines >= 2) {
|
|
@@ -3146,7 +3206,7 @@ async function main() {
|
|
|
3146
3206
|
session,
|
|
3147
3207
|
stream: true,
|
|
3148
3208
|
signal: trackedState.controller.signal,
|
|
3149
|
-
maxTurns:
|
|
3209
|
+
maxTurns: trackedState.maxTurns
|
|
3150
3210
|
});
|
|
3151
3211
|
trackedState.approvalState = null;
|
|
3152
3212
|
let hasStartedResponse = false;
|
|
@@ -3223,6 +3283,26 @@ ${chalk13.yellow("\u{1F6E0}\uFE0F")} ${chalk13.cyan(name)}${chalk13.dim(display
|
|
|
3223
3283
|
trackedState.model || "gpt-5.2",
|
|
3224
3284
|
trackedState.compactionModel || "gpt-4o-mini"
|
|
3225
3285
|
);
|
|
3286
|
+
if (trackedState.maxCost !== null) {
|
|
3287
|
+
const estimatedTotalCost = costTracker.getEstimatedTotalCost(
|
|
3288
|
+
stream.state.usage,
|
|
3289
|
+
trackedState.model || "gpt-5.2",
|
|
3290
|
+
trackedState.compactionModel || "gpt-4o-mini"
|
|
3291
|
+
);
|
|
3292
|
+
if (estimatedTotalCost > trackedState.maxCost) {
|
|
3293
|
+
spinner.stop();
|
|
3294
|
+
console.log(
|
|
3295
|
+
chalk13.red(
|
|
3296
|
+
`Cost limit exceeded: $${estimatedTotalCost.toFixed(4)} > $${trackedState.maxCost.toFixed(4)}`
|
|
3297
|
+
)
|
|
3298
|
+
);
|
|
3299
|
+
if (trackedState.controller) {
|
|
3300
|
+
trackedState.controller.abort();
|
|
3301
|
+
}
|
|
3302
|
+
process.exitCode = 1;
|
|
3303
|
+
return handleExit();
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3226
3306
|
}
|
|
3227
3307
|
if (stream.interruptions?.length) {
|
|
3228
3308
|
for (const interruption of stream.interruptions) {
|
|
@@ -3251,6 +3331,19 @@ ${chalk13.yellow("\u{1F6E0}\uFE0F")} ${chalk13.cyan(name)}${chalk13.dim(display
|
|
|
3251
3331
|
stream.state.usage,
|
|
3252
3332
|
trackedState.compactionModel || "gpt-4o-mini"
|
|
3253
3333
|
);
|
|
3334
|
+
if (trackedState.maxCost !== null && costTracker.getTotalCost() > trackedState.maxCost) {
|
|
3335
|
+
spinner.stop();
|
|
3336
|
+
console.log(
|
|
3337
|
+
chalk13.red(
|
|
3338
|
+
`Cost limit exceeded: $${costTracker.getTotalCost().toFixed(4)} > $${trackedState.maxCost.toFixed(4)}`
|
|
3339
|
+
)
|
|
3340
|
+
);
|
|
3341
|
+
process.exitCode = 1;
|
|
3342
|
+
return handleExit();
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
if (stdinPrompt) {
|
|
3346
|
+
return handleExit();
|
|
3254
3347
|
}
|
|
3255
3348
|
} catch (error) {
|
|
3256
3349
|
spinner.stop();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@harperfast/agent",
|
|
3
3
|
"description": "AI to help you with Harper app management",
|
|
4
|
-
"version": "0.13.
|
|
4
|
+
"version": "0.13.2",
|
|
5
5
|
"main": "dist/agent.js",
|
|
6
6
|
"repository": "github:HarperFast/harper-agent",
|
|
7
7
|
"bugs": {
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
},
|
|
10
10
|
"homepage": "https://github.com/harperfast",
|
|
11
11
|
"scripts": {
|
|
12
|
-
"dev": "tsup agent.ts --format esm --clean --dts --watch",
|
|
12
|
+
"dev": "tsup agent.ts --format esm --clean --dts --watch --external puppeteer",
|
|
13
13
|
"link": "npm run build && npm link",
|
|
14
|
-
"build": "tsup agent.ts --format esm --clean --dts",
|
|
14
|
+
"build": "tsup agent.ts --format esm --clean --dts --external puppeteer",
|
|
15
15
|
"commitlint": "commitlint --edit",
|
|
16
16
|
"start": "node ./dist/agent.js",
|
|
17
17
|
"lint": "oxlint --format stylish .",
|
|
@@ -42,17 +42,17 @@
|
|
|
42
42
|
"license": "None",
|
|
43
43
|
"type": "module",
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@ai-sdk/anthropic": "^3.0.
|
|
46
|
-
"@ai-sdk/google": "^3.0.
|
|
47
|
-
"@ai-sdk/openai": "^3.0.
|
|
48
|
-
"@openai/agents": "^0.4.
|
|
49
|
-
"@openai/agents-extensions": "^0.4.
|
|
50
|
-
"ai": "^6.0.
|
|
45
|
+
"@ai-sdk/anthropic": "^3.0.41",
|
|
46
|
+
"@ai-sdk/google": "^3.0.24",
|
|
47
|
+
"@ai-sdk/openai": "^3.0.26",
|
|
48
|
+
"@openai/agents": "^0.4.6",
|
|
49
|
+
"@openai/agents-extensions": "^0.4.6",
|
|
50
|
+
"ai": "^6.0.79",
|
|
51
51
|
"chalk": "^5.6.2",
|
|
52
|
-
"create-harper": "^0.12.
|
|
52
|
+
"create-harper": "^0.12.4",
|
|
53
53
|
"cross-spawn": "^7.0.6",
|
|
54
|
-
"dotenv": "^17.2.
|
|
55
|
-
"ollama-ai-provider-v2": "^3.0
|
|
54
|
+
"dotenv": "^17.2.4",
|
|
55
|
+
"ollama-ai-provider-v2": "^3.3.0",
|
|
56
56
|
"zod": "^4.3.6"
|
|
57
57
|
},
|
|
58
58
|
"optionalDependencies": {
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
73
73
|
"dprint": "^0.51.1",
|
|
74
74
|
"express": "^5.2.1",
|
|
75
|
-
"harperdb": "^4.7.
|
|
75
|
+
"harperdb": "^4.7.19",
|
|
76
76
|
"hono": "^4.11.9",
|
|
77
77
|
"husky": "^9.1.7",
|
|
78
78
|
"oxlint": "^1.43.0",
|