@harperfast/agent 0.16.0 → 0.16.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/dist/agent.js +152 -52
- package/dist/schema.graphql +245 -0
- package/package.json +3 -2
package/dist/agent.js
CHANGED
|
@@ -875,9 +875,9 @@ async function execute10({ skill }) {
|
|
|
875
875
|
|
|
876
876
|
// tools/files/workspaceEditor.ts
|
|
877
877
|
import { applyDiff } from "@openai/agents";
|
|
878
|
-
import { existsSync as
|
|
878
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
|
|
879
879
|
import { mkdir, rm, writeFile } from "fs/promises";
|
|
880
|
-
import
|
|
880
|
+
import path5 from "path";
|
|
881
881
|
|
|
882
882
|
// utils/files/normalizeDiff.ts
|
|
883
883
|
function normalizeDiff(diff) {
|
|
@@ -993,6 +993,61 @@ function resolvePath(root, relativePath) {
|
|
|
993
993
|
return resolved;
|
|
994
994
|
}
|
|
995
995
|
|
|
996
|
+
// utils/files/validateGraphQL.ts
|
|
997
|
+
import { buildSchema, extendSchema, parse, validateSchema } from "graphql";
|
|
998
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
|
|
999
|
+
import path4 from "path";
|
|
1000
|
+
import { fileURLToPath } from "url";
|
|
1001
|
+
var __dirname = path4.dirname(fileURLToPath(import.meta.url));
|
|
1002
|
+
var cachedSchema = null;
|
|
1003
|
+
function getHarperSchema(root) {
|
|
1004
|
+
if (cachedSchema) {
|
|
1005
|
+
return cachedSchema;
|
|
1006
|
+
}
|
|
1007
|
+
const paths = [
|
|
1008
|
+
path4.join(root, "node_modules", "harperdb", "schema.graphql"),
|
|
1009
|
+
path4.join(__dirname, "schema.graphql"),
|
|
1010
|
+
path4.join(__dirname, "..", "schema.graphql"),
|
|
1011
|
+
path4.join(__dirname, "..", "..", "schema.graphql")
|
|
1012
|
+
];
|
|
1013
|
+
for (const schemaPath of paths) {
|
|
1014
|
+
if (existsSync6(schemaPath)) {
|
|
1015
|
+
try {
|
|
1016
|
+
const schemaSource = readFileSync4(schemaPath, "utf8");
|
|
1017
|
+
cachedSchema = buildSchema(schemaSource, { assumeValidSDL: true });
|
|
1018
|
+
return cachedSchema;
|
|
1019
|
+
} catch (e) {
|
|
1020
|
+
console.error(`Error parsing HarperDB schema at ${schemaPath}:`, e);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
return null;
|
|
1025
|
+
}
|
|
1026
|
+
function validateGraphQL(content, filePath, root) {
|
|
1027
|
+
if (!filePath.endsWith(".graphql")) {
|
|
1028
|
+
return null;
|
|
1029
|
+
}
|
|
1030
|
+
try {
|
|
1031
|
+
const document2 = parse(content);
|
|
1032
|
+
const schema = getHarperSchema(root);
|
|
1033
|
+
if (schema) {
|
|
1034
|
+
const extendedSchema = extendSchema(schema, document2, { assumeValidSDL: false });
|
|
1035
|
+
const errors = validateSchema(extendedSchema).filter(
|
|
1036
|
+
(e) => !e.message.includes("Query root type must be provided")
|
|
1037
|
+
);
|
|
1038
|
+
if (errors.length > 0) {
|
|
1039
|
+
return `GraphQL validation error in ${filePath}:
|
|
1040
|
+
${errors.map((e) => e.message).join("\n")}`;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
return null;
|
|
1044
|
+
} catch (err) {
|
|
1045
|
+
const message = err.message || String(err);
|
|
1046
|
+
const type = message.includes("Syntax Error") ? "syntax" : "validation";
|
|
1047
|
+
return `GraphQL ${type} error in ${filePath}: ${message}`;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
996
1051
|
// tools/files/workspaceEditor.ts
|
|
997
1052
|
var WorkspaceEditor = class {
|
|
998
1053
|
root;
|
|
@@ -1002,11 +1057,18 @@ var WorkspaceEditor = class {
|
|
|
1002
1057
|
async createFile(operation) {
|
|
1003
1058
|
try {
|
|
1004
1059
|
const targetPath = resolvePath(this.root(), operation.path);
|
|
1005
|
-
await mkdir(
|
|
1060
|
+
await mkdir(path5.dirname(targetPath), { recursive: true });
|
|
1006
1061
|
const normalizedDiff = normalizeDiff(operation.diff);
|
|
1007
1062
|
const content = applyDiff("", normalizedDiff, "create");
|
|
1008
1063
|
await writeFile(targetPath, content, "utf8");
|
|
1009
|
-
|
|
1064
|
+
let output = `Created ${operation.path}`;
|
|
1065
|
+
const validationError = validateGraphQL(content, operation.path, this.root());
|
|
1066
|
+
if (validationError) {
|
|
1067
|
+
output += `
|
|
1068
|
+
|
|
1069
|
+
${validationError}`;
|
|
1070
|
+
}
|
|
1071
|
+
return { status: "completed", output };
|
|
1010
1072
|
} catch (err) {
|
|
1011
1073
|
return { status: "failed", output: `Error creating ${operation.path}: ${String(err)}` };
|
|
1012
1074
|
}
|
|
@@ -1014,14 +1076,21 @@ var WorkspaceEditor = class {
|
|
|
1014
1076
|
async updateFile(operation) {
|
|
1015
1077
|
try {
|
|
1016
1078
|
const targetPath = resolvePath(this.root(), operation.path);
|
|
1017
|
-
if (!
|
|
1079
|
+
if (!existsSync7(targetPath)) {
|
|
1018
1080
|
return { status: "failed", output: "Error: file not found at path " + targetPath };
|
|
1019
1081
|
}
|
|
1020
|
-
const original =
|
|
1082
|
+
const original = readFileSync5(targetPath, "utf8");
|
|
1021
1083
|
const normalizedDiff = normalizeDiff(operation.diff);
|
|
1022
1084
|
const patched = applyDiff(original, normalizedDiff);
|
|
1023
1085
|
await writeFile(targetPath, patched, "utf8");
|
|
1024
|
-
|
|
1086
|
+
let output = `Updated ${operation.path}`;
|
|
1087
|
+
const validationError = validateGraphQL(patched, operation.path, this.root());
|
|
1088
|
+
if (validationError) {
|
|
1089
|
+
output += `
|
|
1090
|
+
|
|
1091
|
+
${validationError}`;
|
|
1092
|
+
}
|
|
1093
|
+
return { status: "completed", output };
|
|
1025
1094
|
} catch (err) {
|
|
1026
1095
|
return { status: "failed", output: `Error updating ${operation.path}: ${String(err)}` };
|
|
1027
1096
|
}
|
|
@@ -1029,7 +1098,7 @@ var WorkspaceEditor = class {
|
|
|
1029
1098
|
async overwriteFile(operation) {
|
|
1030
1099
|
try {
|
|
1031
1100
|
const targetPath = resolvePath(this.root(), operation.path);
|
|
1032
|
-
await mkdir(
|
|
1101
|
+
await mkdir(path5.dirname(targetPath), { recursive: true });
|
|
1033
1102
|
const normalizedInput = normalizeDiff(operation.diff);
|
|
1034
1103
|
const lines = normalizedInput.split(/\r?\n/);
|
|
1035
1104
|
const hasDiffMarkers = lines.some((line) => line.startsWith("+") || line.startsWith("-"));
|
|
@@ -1040,7 +1109,14 @@ var WorkspaceEditor = class {
|
|
|
1040
1109
|
finalContent = normalizedInput;
|
|
1041
1110
|
}
|
|
1042
1111
|
await writeFile(targetPath, finalContent, "utf8");
|
|
1043
|
-
|
|
1112
|
+
let output = `Overwrote ${operation.path}`;
|
|
1113
|
+
const validationError = validateGraphQL(finalContent, operation.path, this.root());
|
|
1114
|
+
if (validationError) {
|
|
1115
|
+
output += `
|
|
1116
|
+
|
|
1117
|
+
${validationError}`;
|
|
1118
|
+
}
|
|
1119
|
+
return { status: "completed", output };
|
|
1044
1120
|
} catch (err) {
|
|
1045
1121
|
return { status: "failed", output: `Error overwriting ${operation.path}: ${String(err)}` };
|
|
1046
1122
|
}
|
|
@@ -1048,7 +1124,7 @@ var WorkspaceEditor = class {
|
|
|
1048
1124
|
async deleteFile(operation) {
|
|
1049
1125
|
try {
|
|
1050
1126
|
const targetPath = resolvePath(this.root(), operation.path);
|
|
1051
|
-
if (!
|
|
1127
|
+
if (!existsSync7(targetPath)) {
|
|
1052
1128
|
return { status: "failed", output: "Error: file not found at path " + targetPath };
|
|
1053
1129
|
}
|
|
1054
1130
|
await rm(targetPath, { force: true });
|
|
@@ -1089,11 +1165,11 @@ function pickExistingSkill(candidates) {
|
|
|
1089
1165
|
}
|
|
1090
1166
|
return null;
|
|
1091
1167
|
}
|
|
1092
|
-
async function requiredSkillForOperation(
|
|
1168
|
+
async function requiredSkillForOperation(path10, type) {
|
|
1093
1169
|
if (type === "delete_file") {
|
|
1094
1170
|
return null;
|
|
1095
1171
|
}
|
|
1096
|
-
const p = normalizedPath(
|
|
1172
|
+
const p = normalizedPath(path10);
|
|
1097
1173
|
const read = await getSkillsRead();
|
|
1098
1174
|
if (p.includes("/resources/") || p.startsWith("resources/") || p.endsWith("resources.ts") || p.endsWith("resources.js")) {
|
|
1099
1175
|
if (!read.includes("automatic-apis")) {
|
|
@@ -1148,7 +1224,23 @@ async function execute11(operation) {
|
|
|
1148
1224
|
if (needed) {
|
|
1149
1225
|
const content = await execute10({ skill: needed });
|
|
1150
1226
|
console.error(`Understanding ${needed} is necessary before applying this patch.`);
|
|
1151
|
-
return {
|
|
1227
|
+
return {
|
|
1228
|
+
status: "skill_required",
|
|
1229
|
+
output: `apply_patch blocked: you must understand the "${needed}" skill before writing to "${operation.path}".
|
|
1230
|
+
|
|
1231
|
+
REQUIRED NEXT STEP: Call apply_patch again with the EXACT same arguments:
|
|
1232
|
+
type="${operation.type}", path="${operation.path}", diff=<same diff as before>
|
|
1233
|
+
|
|
1234
|
+
PROHIBITED \u2014 do NOT attempt any of the following to work around this:
|
|
1235
|
+
- Use the shell tool to write or create files
|
|
1236
|
+
- Call set_patch_auto_approve or set_shell_auto_approve
|
|
1237
|
+
- Use any other workaround
|
|
1238
|
+
|
|
1239
|
+
Read the skill content below, then retry apply_patch immediately.
|
|
1240
|
+
|
|
1241
|
+
--- Skill: ${needed} ---
|
|
1242
|
+
${content}`
|
|
1243
|
+
};
|
|
1152
1244
|
}
|
|
1153
1245
|
switch (operation.type) {
|
|
1154
1246
|
case "create_file":
|
|
@@ -1179,7 +1271,12 @@ async function execute11(operation) {
|
|
|
1179
1271
|
function createApplyPatchTool() {
|
|
1180
1272
|
return tool11({
|
|
1181
1273
|
name: "apply_patch",
|
|
1182
|
-
description:
|
|
1274
|
+
description: `Applies a patch (create, update, or delete a file) to the workspace.
|
|
1275
|
+
|
|
1276
|
+
SKILL GUARD: Some file paths require reading a specific Harper skill before writing. If this tool returns status "skill_required", you MUST:
|
|
1277
|
+
1. Read the skill content included in the response carefully.
|
|
1278
|
+
2. Call apply_patch again with the EXACT same type, path, and diff arguments.
|
|
1279
|
+
Do NOT use shell, set_patch_auto_approve, set_shell_auto_approve, or any other workaround to bypass this requirement.`,
|
|
1183
1280
|
parameters: ApplyPatchParameters,
|
|
1184
1281
|
needsApproval,
|
|
1185
1282
|
execute: execute11
|
|
@@ -1193,9 +1290,9 @@ import { z as z12 } from "zod";
|
|
|
1193
1290
|
var ToolParameters11 = z12.object({
|
|
1194
1291
|
path: z12.string().describe("Directory to switch into. Can be absolute or relative to current workspace.")
|
|
1195
1292
|
});
|
|
1196
|
-
async function execute12({ path:
|
|
1293
|
+
async function execute12({ path: path10 }) {
|
|
1197
1294
|
try {
|
|
1198
|
-
const target = resolvePath(trackedState.cwd,
|
|
1295
|
+
const target = resolvePath(trackedState.cwd, path10);
|
|
1199
1296
|
const stat = statSync(target);
|
|
1200
1297
|
if (!stat.isDirectory()) {
|
|
1201
1298
|
return `Path is not a directory: ${target}`;
|
|
@@ -1296,7 +1393,7 @@ var findTool = tool14({
|
|
|
1296
1393
|
// tools/files/readDirTool.ts
|
|
1297
1394
|
import { tool as tool15 } from "@openai/agents";
|
|
1298
1395
|
import { readdir } from "fs/promises";
|
|
1299
|
-
import
|
|
1396
|
+
import path6 from "path";
|
|
1300
1397
|
import { z as z15 } from "zod";
|
|
1301
1398
|
var ToolParameters14 = z15.object({
|
|
1302
1399
|
directoryName: z15.string().describe("The name of the directory to read.")
|
|
@@ -1309,7 +1406,7 @@ var readDirTool = tool15({
|
|
|
1309
1406
|
try {
|
|
1310
1407
|
const resolvedPath = resolvePath(trackedState.cwd, directoryName);
|
|
1311
1408
|
const files = await readdir(resolvedPath, "utf8");
|
|
1312
|
-
return files.filter((file) => !isIgnored(
|
|
1409
|
+
return files.filter((file) => !isIgnored(path6.join(resolvedPath, file)));
|
|
1313
1410
|
} catch (error) {
|
|
1314
1411
|
return `Error reading directory: ${error}`;
|
|
1315
1412
|
}
|
|
@@ -1365,7 +1462,7 @@ var readFileTool = tool16({
|
|
|
1365
1462
|
import { tool as tool17 } from "@openai/agents";
|
|
1366
1463
|
import { exec } from "child_process";
|
|
1367
1464
|
import { unlink, writeFile as writeFile2 } from "fs/promises";
|
|
1368
|
-
import
|
|
1465
|
+
import path7 from "path";
|
|
1369
1466
|
import { promisify as promisify3 } from "util";
|
|
1370
1467
|
import { z as z17 } from "zod";
|
|
1371
1468
|
var execAsync = promisify3(exec);
|
|
@@ -1408,7 +1505,7 @@ async function needsApproval2(runContext, parameters, callId) {
|
|
|
1408
1505
|
async function execute14({ code, language }) {
|
|
1409
1506
|
const extension = language === "javascript" ? "js" : "py";
|
|
1410
1507
|
const interpreter = language === "javascript" ? "node" : "python3";
|
|
1411
|
-
const tempFile =
|
|
1508
|
+
const tempFile = path7.join(trackedState.cwd, `.temp_code_${Date.now()}.${extension}`);
|
|
1412
1509
|
try {
|
|
1413
1510
|
await writeFile2(tempFile, code, "utf8");
|
|
1414
1511
|
const { stdout, stderr } = await execAsync(`${interpreter} ${tempFile}`);
|
|
@@ -1467,7 +1564,7 @@ import { tool as tool19 } from "@openai/agents";
|
|
|
1467
1564
|
import { z as z19 } from "zod";
|
|
1468
1565
|
|
|
1469
1566
|
// utils/files/updateEnv.ts
|
|
1470
|
-
import { existsSync as
|
|
1567
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync } from "fs";
|
|
1471
1568
|
import { homedir as homedir3 } from "os";
|
|
1472
1569
|
import { dirname as dirname4, join as join7 } from "path";
|
|
1473
1570
|
function updateEnv(key, value) {
|
|
@@ -1475,14 +1572,14 @@ function updateEnv(key, value) {
|
|
|
1475
1572
|
process.env[key] = normalizedValue;
|
|
1476
1573
|
const topLevelEnvPath = join7(homedir3(), ".harper", "harper-agent-env");
|
|
1477
1574
|
const localEnvPath = join7(trackedState.cwd, ".env");
|
|
1478
|
-
const envPath =
|
|
1575
|
+
const envPath = existsSync8(topLevelEnvPath) || !existsSync8(localEnvPath) ? topLevelEnvPath : localEnvPath;
|
|
1479
1576
|
const dir = dirname4(envPath);
|
|
1480
|
-
if (!
|
|
1577
|
+
if (!existsSync8(dir)) {
|
|
1481
1578
|
mkdirSync2(dir, { recursive: true });
|
|
1482
1579
|
}
|
|
1483
1580
|
let envContent = "";
|
|
1484
|
-
if (
|
|
1485
|
-
envContent =
|
|
1581
|
+
if (existsSync8(envPath)) {
|
|
1582
|
+
envContent = readFileSync6(envPath, "utf8");
|
|
1486
1583
|
}
|
|
1487
1584
|
const regex = new RegExp(`^${key}=.*`, "m");
|
|
1488
1585
|
if (regex.test(envContent)) {
|
|
@@ -2002,7 +2099,7 @@ var checkHarperStatusTool = tool30({
|
|
|
2002
2099
|
// tools/harper/createNewHarperApplicationTool.ts
|
|
2003
2100
|
import { tool as tool31 } from "@openai/agents";
|
|
2004
2101
|
import { execSync as execSync3 } from "child_process";
|
|
2005
|
-
import
|
|
2102
|
+
import path8 from "path";
|
|
2006
2103
|
import { z as z31 } from "zod";
|
|
2007
2104
|
|
|
2008
2105
|
// utils/package/buildHarperCreateCommand.ts
|
|
@@ -2061,8 +2158,8 @@ async function execute16({ directoryName, template }) {
|
|
|
2061
2158
|
const currentCwd = trackedState.cwd;
|
|
2062
2159
|
const resolvedPath = resolvePath(currentCwd, directoryName);
|
|
2063
2160
|
const isCurrentDir = resolvedPath === currentCwd;
|
|
2064
|
-
const executionCwd = isCurrentDir ? resolvedPath :
|
|
2065
|
-
const appName = isCurrentDir ? "." :
|
|
2161
|
+
const executionCwd = isCurrentDir ? resolvedPath : path8.dirname(resolvedPath);
|
|
2162
|
+
const appName = isCurrentDir ? "." : path8.basename(resolvedPath);
|
|
2066
2163
|
try {
|
|
2067
2164
|
const pm = pickPreferredPackageManager();
|
|
2068
2165
|
const { cmd, label } = buildCreateCommand(pm, appName, template);
|
|
@@ -2223,14 +2320,14 @@ var hitHarperAPITool = tool35({
|
|
|
2223
2320
|
}
|
|
2224
2321
|
return false;
|
|
2225
2322
|
},
|
|
2226
|
-
async execute({ path:
|
|
2323
|
+
async execute({ path: path10 = "/openapi", port, method = "GET", body }) {
|
|
2227
2324
|
try {
|
|
2228
2325
|
const effectivePort = port ?? (harperProcess.running ? harperProcess.httpPort : void 0);
|
|
2229
2326
|
if (!effectivePort) {
|
|
2230
2327
|
return `Error: No Harper application is currently running and no port was specified.`;
|
|
2231
2328
|
}
|
|
2232
2329
|
const response = await fetch(
|
|
2233
|
-
`http://localhost:${effectivePort}${
|
|
2330
|
+
`http://localhost:${effectivePort}${path10.startsWith("/") ? "" : "/"}${path10}`,
|
|
2234
2331
|
{
|
|
2235
2332
|
method,
|
|
2236
2333
|
headers: body ? { "Content-Type": "application/json" } : {},
|
|
@@ -2270,7 +2367,7 @@ var readHarperLogsTool = tool36({
|
|
|
2270
2367
|
|
|
2271
2368
|
// tools/harper/startHarperTool.ts
|
|
2272
2369
|
import { tool as tool37 } from "@openai/agents";
|
|
2273
|
-
import { existsSync as
|
|
2370
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2274
2371
|
import { basename, resolve } from "path";
|
|
2275
2372
|
import { z as z37 } from "zod";
|
|
2276
2373
|
|
|
@@ -2297,7 +2394,7 @@ var startHarperTool = tool37({
|
|
|
2297
2394
|
try {
|
|
2298
2395
|
let effectiveDirectory = directoryName;
|
|
2299
2396
|
const candidatePath = resolve(process.cwd(), directoryName);
|
|
2300
|
-
if (!
|
|
2397
|
+
if (!existsSync9(candidatePath)) {
|
|
2301
2398
|
const cwd = process.cwd();
|
|
2302
2399
|
if (basename(cwd) === directoryName) {
|
|
2303
2400
|
effectiveDirectory = cwd;
|
|
@@ -2625,7 +2722,7 @@ Stack: ${String(err.stack).split("\n").slice(0, 8).join("\n")}` : "";
|
|
|
2625
2722
|
|
|
2626
2723
|
// utils/sessions/DiskSession.ts
|
|
2627
2724
|
import { MemorySession } from "@openai/agents";
|
|
2628
|
-
import { existsSync as
|
|
2725
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2629
2726
|
import { mkdir as mkdir2, readFile as readFile5, rename, writeFile as writeFile3 } from "fs/promises";
|
|
2630
2727
|
import { dirname as dirname8 } from "path";
|
|
2631
2728
|
var DiskSession = class extends MemorySession {
|
|
@@ -2676,7 +2773,7 @@ var DiskSession = class extends MemorySession {
|
|
|
2676
2773
|
}
|
|
2677
2774
|
}
|
|
2678
2775
|
async loadStorage() {
|
|
2679
|
-
if (
|
|
2776
|
+
if (existsSync10(this.filePath)) {
|
|
2680
2777
|
try {
|
|
2681
2778
|
const data = await readFile5(this.filePath, "utf-8");
|
|
2682
2779
|
const parsed = JSON.parse(data);
|
|
@@ -2694,7 +2791,7 @@ var DiskSession = class extends MemorySession {
|
|
|
2694
2791
|
const storage = await this.loadStorage();
|
|
2695
2792
|
update(storage);
|
|
2696
2793
|
const dir = dirname8(this.filePath);
|
|
2697
|
-
if (!
|
|
2794
|
+
if (!existsSync10(dir)) {
|
|
2698
2795
|
await mkdir2(dir, { recursive: true });
|
|
2699
2796
|
}
|
|
2700
2797
|
const data = JSON.stringify(storage, null, 2);
|
|
@@ -3810,7 +3907,7 @@ async function runAgentForOnePass(agent, session, input, controller, isPrompt) {
|
|
|
3810
3907
|
}
|
|
3811
3908
|
|
|
3812
3909
|
// agent/AgentManager.ts
|
|
3813
|
-
var AgentManager = class {
|
|
3910
|
+
var AgentManager = class _AgentManager {
|
|
3814
3911
|
isInitialized = false;
|
|
3815
3912
|
controller = null;
|
|
3816
3913
|
queuedUserInputs = [];
|
|
@@ -3818,17 +3915,20 @@ var AgentManager = class {
|
|
|
3818
3915
|
agent = null;
|
|
3819
3916
|
session = null;
|
|
3820
3917
|
initialMessages = [];
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
return;
|
|
3824
|
-
}
|
|
3825
|
-
this.agent = new Agent3({
|
|
3918
|
+
static instantiateAgent(tools) {
|
|
3919
|
+
return new Agent3({
|
|
3826
3920
|
name: "Harper Agent",
|
|
3827
3921
|
model: isOpenAIModel(trackedState.model) ? trackedState.model : getModel(trackedState.model),
|
|
3828
3922
|
modelSettings: getModelSettings(trackedState.model),
|
|
3829
3923
|
instructions: readAgentSkillsRoot() || defaultInstructions(),
|
|
3830
|
-
tools
|
|
3924
|
+
tools
|
|
3831
3925
|
});
|
|
3926
|
+
}
|
|
3927
|
+
async initialize() {
|
|
3928
|
+
if (this.isInitialized) {
|
|
3929
|
+
return;
|
|
3930
|
+
}
|
|
3931
|
+
this.agent = _AgentManager.instantiateAgent(createTools());
|
|
3832
3932
|
this.session = createSession(trackedState.sessionPath);
|
|
3833
3933
|
try {
|
|
3834
3934
|
const plan = await this.session?.getPlanState?.();
|
|
@@ -4923,7 +5023,7 @@ function PlanView() {
|
|
|
4923
5023
|
|
|
4924
5024
|
// ink/components/SettingsView.tsx
|
|
4925
5025
|
import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
|
|
4926
|
-
import
|
|
5026
|
+
import path9 from "path";
|
|
4927
5027
|
import { useEffect as useEffect6, useMemo as useMemo9, useState as useState11 } from "react";
|
|
4928
5028
|
|
|
4929
5029
|
// ink/contexts/SettingsContext.tsx
|
|
@@ -5065,8 +5165,8 @@ function SettingsView({ isDense = false }) {
|
|
|
5065
5165
|
if (!sessionPath) {
|
|
5066
5166
|
return null;
|
|
5067
5167
|
}
|
|
5068
|
-
const relative =
|
|
5069
|
-
if (!relative.startsWith("..") && !
|
|
5168
|
+
const relative = path9.relative(cwd, sessionPath);
|
|
5169
|
+
if (!relative.startsWith("..") && !path9.isAbsolute(relative)) {
|
|
5070
5170
|
return `./${relative}`;
|
|
5071
5171
|
}
|
|
5072
5172
|
return sessionPath;
|
|
@@ -6476,13 +6576,13 @@ function warnAndPersistRedirect(original, envKey, replacement, reason) {
|
|
|
6476
6576
|
import chalk5 from "chalk";
|
|
6477
6577
|
|
|
6478
6578
|
// utils/package/getOwnPackageJson.ts
|
|
6479
|
-
import { readFileSync as
|
|
6579
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
6480
6580
|
import { join as join11 } from "path";
|
|
6481
|
-
import { fileURLToPath } from "url";
|
|
6482
|
-
var
|
|
6581
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6582
|
+
var __dirname2 = fileURLToPath2(new URL(".", import.meta.url));
|
|
6483
6583
|
function getOwnPackageJson() {
|
|
6484
6584
|
try {
|
|
6485
|
-
const packageContents =
|
|
6585
|
+
const packageContents = readFileSync7(join11(__dirname2, "../package.json"), "utf8");
|
|
6486
6586
|
return JSON.parse(packageContents);
|
|
6487
6587
|
} catch {
|
|
6488
6588
|
return { name: "@harperfast/agent", version: "0.0.0" };
|
|
@@ -6686,16 +6786,16 @@ function parseArgs() {
|
|
|
6686
6786
|
|
|
6687
6787
|
// utils/envLoader.ts
|
|
6688
6788
|
import dotenv from "dotenv";
|
|
6689
|
-
import { existsSync as
|
|
6789
|
+
import { existsSync as existsSync11 } from "fs";
|
|
6690
6790
|
import { homedir as homedir4 } from "os";
|
|
6691
6791
|
import { join as join12 } from "path";
|
|
6692
6792
|
function loadEnv() {
|
|
6693
6793
|
const topLevelEnvPath = join12(homedir4(), ".harper", "harper-agent-env");
|
|
6694
6794
|
const localEnvPath = join12(process.cwd(), ".env");
|
|
6695
|
-
if (
|
|
6795
|
+
if (existsSync11(topLevelEnvPath)) {
|
|
6696
6796
|
dotenv.config({ path: topLevelEnvPath, quiet: true });
|
|
6697
6797
|
}
|
|
6698
|
-
if (
|
|
6798
|
+
if (existsSync11(localEnvPath)) {
|
|
6699
6799
|
dotenv.config({ path: localEnvPath, override: true, quiet: true });
|
|
6700
6800
|
}
|
|
6701
6801
|
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A flexible JSON-like value.
|
|
3
|
+
Accepts objects, arrays, strings, numbers, booleans, or null.
|
|
4
|
+
Useful for schemaless or mixed content.
|
|
5
|
+
"""
|
|
6
|
+
scalar Any
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
Arbitrary-precision integer for values larger than 32-bit/64-bit ranges.
|
|
10
|
+
Input may be provided as a JSON number or string; tooling may return it as a
|
|
11
|
+
string to avoid precision loss. Use `Long` for 64-bit integers when possible.
|
|
12
|
+
"""
|
|
13
|
+
scalar BigInt
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Binary large object. Represents binary data such as images or files.
|
|
17
|
+
Typically encoded as Base64 in JSON.
|
|
18
|
+
"""
|
|
19
|
+
scalar Blob
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
true or false
|
|
23
|
+
"""
|
|
24
|
+
scalar Boolean
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
Raw bytes. Typically encoded/transported as Base64 in JSON.
|
|
28
|
+
Use when you need deterministic binary data that is not a large file.
|
|
29
|
+
"""
|
|
30
|
+
scalar Bytes
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
Date/time scalar. Recommended format: RFC 3339/ISO-8601 (e.g.,
|
|
34
|
+
"2025-12-02T19:44:00Z"). Represents an absolute timestamp.
|
|
35
|
+
"""
|
|
36
|
+
scalar Date
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
A signed double-precision floating-point value
|
|
40
|
+
"""
|
|
41
|
+
scalar Float
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
ID (serialized as a String): A unique identifier that's often used to refetch an
|
|
45
|
+
object or as the key for a cache. Although it's serialized as a String, an ID is
|
|
46
|
+
not intended to be human‐readable.
|
|
47
|
+
"""
|
|
48
|
+
scalar ID
|
|
49
|
+
|
|
50
|
+
"""
|
|
51
|
+
A signed 32‐bit integer
|
|
52
|
+
"""
|
|
53
|
+
scalar Int
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
64-bit signed integer. Some clients may serialize values as strings to
|
|
57
|
+
preserve precision across environments.
|
|
58
|
+
"""
|
|
59
|
+
scalar Long
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
A UTF‐8 character sequence
|
|
63
|
+
"""
|
|
64
|
+
scalar String
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
Attach to an object type to persist it as a table.
|
|
68
|
+
|
|
69
|
+
Example:
|
|
70
|
+
```
|
|
71
|
+
type Post @table(table: "posts", database: "blog") {
|
|
72
|
+
id: ID @primaryKey
|
|
73
|
+
title: String @indexed(type: "fulltext")
|
|
74
|
+
body: String
|
|
75
|
+
createdAt: Date @createdTime
|
|
76
|
+
updatedAt: Date @updatedTime
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
"""
|
|
80
|
+
directive @table(
|
|
81
|
+
"""
|
|
82
|
+
Explicit table name. If omitted, a sensible default derived from the
|
|
83
|
+
type name will be used.
|
|
84
|
+
"""
|
|
85
|
+
table: String
|
|
86
|
+
"""
|
|
87
|
+
Logical database/namespace to place this table in.
|
|
88
|
+
"""
|
|
89
|
+
database: String
|
|
90
|
+
"""
|
|
91
|
+
Default time-to-live (TTL) for records in seconds. Use a positive value to
|
|
92
|
+
enable automatic expiration; omit or set to 0 to disable.
|
|
93
|
+
"""
|
|
94
|
+
expiration: Int
|
|
95
|
+
"""
|
|
96
|
+
Enable auditing for create/update/delete operations.
|
|
97
|
+
"""
|
|
98
|
+
audit: Boolean
|
|
99
|
+
"""
|
|
100
|
+
The amount of time after expiration before a record can be evicted (defaults to zero).
|
|
101
|
+
"""
|
|
102
|
+
eviction: Int
|
|
103
|
+
"""
|
|
104
|
+
The interval for scanning for expired records (defaults to one quarter of the
|
|
105
|
+
total of expiration and eviction).
|
|
106
|
+
"""
|
|
107
|
+
scanInterval: Int
|
|
108
|
+
"""
|
|
109
|
+
By default, all tables within a replicated database will be replicated. Transactions
|
|
110
|
+
are replicated atomically, which may involve data across multiple tables. However,
|
|
111
|
+
you can also configure replication for individual tables, and disable and exclude
|
|
112
|
+
replication for specific tables in a database by setting replicate to false in the
|
|
113
|
+
table definition.
|
|
114
|
+
"""
|
|
115
|
+
replicate: Boolean
|
|
116
|
+
) on OBJECT
|
|
117
|
+
|
|
118
|
+
"""
|
|
119
|
+
Expose the table via the REST API. When applied to a `@table` type, routes are
|
|
120
|
+
generated using the type name or the provided alias.
|
|
121
|
+
"""
|
|
122
|
+
directive @export(
|
|
123
|
+
"""
|
|
124
|
+
Optional alias to use for REST endpoints. If omitted, the type/table name is
|
|
125
|
+
used.
|
|
126
|
+
"""
|
|
127
|
+
name: String
|
|
128
|
+
"""
|
|
129
|
+
REST support is, by default, turned on for any exported resource. You may specify
|
|
130
|
+
false to disable this automatic API support.
|
|
131
|
+
"""
|
|
132
|
+
rest: Boolean
|
|
133
|
+
"""
|
|
134
|
+
MQTT support is, by default, turned on for any exported resource. You may specify
|
|
135
|
+
false to disable this automatic API support.
|
|
136
|
+
"""
|
|
137
|
+
mqtt: Boolean
|
|
138
|
+
) on OBJECT
|
|
139
|
+
"""
|
|
140
|
+
As a NoSQL database, HarperDB supports heterogeneous records (also referred to as
|
|
141
|
+
documents), so you can freely specify additional properties on any record. If you
|
|
142
|
+
do want to restrict the records to only defined properties, you can always do that
|
|
143
|
+
by adding the sealed directive.
|
|
144
|
+
"""
|
|
145
|
+
directive @sealed on OBJECT
|
|
146
|
+
|
|
147
|
+
"""
|
|
148
|
+
Marks the primary key field for the table. The value must be unique per record
|
|
149
|
+
and is used for lookups, updates, and relationships.
|
|
150
|
+
"""
|
|
151
|
+
directive @primaryKey on FIELD_DEFINITION
|
|
152
|
+
|
|
153
|
+
"""
|
|
154
|
+
Allows enumeration over a computed field, causing it to be included in serialized responses. (Non-computed fields are always enumerable, and don't need to be flagged.)
|
|
155
|
+
"""
|
|
156
|
+
directive @enumerable on FIELD_DEFINITION
|
|
157
|
+
|
|
158
|
+
"""
|
|
159
|
+
Flags the field as containing the expiration time of the entry.
|
|
160
|
+
"""
|
|
161
|
+
directive @expiresAt on FIELD_DEFINITION
|
|
162
|
+
|
|
163
|
+
"""
|
|
164
|
+
Permit access to the field based on the named roles, only.
|
|
165
|
+
"""
|
|
166
|
+
directive @allow(role: String) on FIELD_DEFINITION
|
|
167
|
+
|
|
168
|
+
"""
|
|
169
|
+
Create an index for the annotated field. Supports traditional and vector/ANN
|
|
170
|
+
index types.
|
|
171
|
+
"""
|
|
172
|
+
directive @indexed(
|
|
173
|
+
"""
|
|
174
|
+
Optional index type, e.g. "HNSW"
|
|
175
|
+
"""
|
|
176
|
+
type: String
|
|
177
|
+
"""
|
|
178
|
+
Distance metric for vector indexes (e.g., "euclidean", "cosine").
|
|
179
|
+
Ignored for non-vector index types.
|
|
180
|
+
"""
|
|
181
|
+
distance: String
|
|
182
|
+
"""
|
|
183
|
+
Construction effort/recall parameter (HNSW). Higher values improve recall at
|
|
184
|
+
the cost of build time and memory.
|
|
185
|
+
"""
|
|
186
|
+
efConstruction: Int
|
|
187
|
+
"""
|
|
188
|
+
Maximum number of bi-directional connections per node (HNSW).
|
|
189
|
+
Typical range: 4–64.
|
|
190
|
+
"""
|
|
191
|
+
M: Int
|
|
192
|
+
"""
|
|
193
|
+
Routing optimization level for search graphs (implementation-specific).
|
|
194
|
+
"""
|
|
195
|
+
optimizeRouting: Int
|
|
196
|
+
"""
|
|
197
|
+
Additional multiplier for graph links (implementation-specific).
|
|
198
|
+
"""
|
|
199
|
+
mL: Int
|
|
200
|
+
"""
|
|
201
|
+
Search-time effort/recall parameter used during construction (implementation-
|
|
202
|
+
specific). Larger values typically yield better accuracy.
|
|
203
|
+
"""
|
|
204
|
+
efConstructionSearch: Int
|
|
205
|
+
) on FIELD_DEFINITION
|
|
206
|
+
|
|
207
|
+
"""
|
|
208
|
+
Define a derived field whose value is computed from other fields.
|
|
209
|
+
Useful for denormalized or presentation-friendly data.
|
|
210
|
+
"""
|
|
211
|
+
directive @computed(
|
|
212
|
+
"""
|
|
213
|
+
Computation expression or reference describing how to derive this field from
|
|
214
|
+
other fields (engine-specific syntax).
|
|
215
|
+
"""
|
|
216
|
+
from: String
|
|
217
|
+
"""
|
|
218
|
+
Increment when the computation changes to trigger recomputation.
|
|
219
|
+
"""
|
|
220
|
+
version: Int
|
|
221
|
+
) on FIELD_DEFINITION
|
|
222
|
+
"""
|
|
223
|
+
Automatically sets this field to the record's creation timestamp. The server
|
|
224
|
+
assigns the value on insert.
|
|
225
|
+
"""
|
|
226
|
+
directive @createdTime on FIELD_DEFINITION
|
|
227
|
+
"""
|
|
228
|
+
Automatically updates this field to the current timestamp whenever the record
|
|
229
|
+
is updated.
|
|
230
|
+
"""
|
|
231
|
+
directive @updatedTime on FIELD_DEFINITION
|
|
232
|
+
"""
|
|
233
|
+
Declares a relationship to another record or records.
|
|
234
|
+
Use on fields that should resolve to related entities by ID.
|
|
235
|
+
"""
|
|
236
|
+
directive @relationship(
|
|
237
|
+
"""
|
|
238
|
+
Name of field in THIS table containing foreign key(s) (for one-to-many or many-to-many).
|
|
239
|
+
"""
|
|
240
|
+
from: String
|
|
241
|
+
"""
|
|
242
|
+
Name of field in OTHER table containing foreign key (for one-to-one or many-to-one).
|
|
243
|
+
"""
|
|
244
|
+
to: String
|
|
245
|
+
) on FIELD_DEFINITION
|
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.16.
|
|
4
|
+
"version": "0.16.2",
|
|
5
5
|
"main": "dist/agent.js",
|
|
6
6
|
"repository": "github:HarperFast/harper-agent",
|
|
7
7
|
"bugs": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"scripts": {
|
|
12
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 --external puppeteer",
|
|
14
|
+
"build": "tsup agent.ts --format esm --clean --dts --external puppeteer && cp node_modules/harperdb/schema.graphql dist/",
|
|
15
15
|
"commitlint": "commitlint --edit",
|
|
16
16
|
"start": "node ./dist/agent.js",
|
|
17
17
|
"lint": "oxlint --format stylish .",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"chalk": "^5.6.2",
|
|
55
55
|
"cross-spawn": "^7.0.6",
|
|
56
56
|
"dotenv": "^17.2.4",
|
|
57
|
+
"graphql": "^16.13.1",
|
|
57
58
|
"ink": "^6.7.0",
|
|
58
59
|
"ink-stepper": "^0.2.1",
|
|
59
60
|
"ink-task-list": "^2.0.0",
|