@harperfast/agent 0.16.1 → 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 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 existsSync6, readFileSync as readFileSync4 } from "fs";
878
+ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
879
879
  import { mkdir, rm, writeFile } from "fs/promises";
880
- import path4 from "path";
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(path4.dirname(targetPath), { recursive: true });
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
- return { status: "completed", output: `Created ${operation.path}` };
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 (!existsSync6(targetPath)) {
1079
+ if (!existsSync7(targetPath)) {
1018
1080
  return { status: "failed", output: "Error: file not found at path " + targetPath };
1019
1081
  }
1020
- const original = readFileSync4(targetPath, "utf8");
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
- return { status: "completed", output: `Updated ${operation.path}` };
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(path4.dirname(targetPath), { recursive: true });
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
- return { status: "completed", output: `Overwrote ${operation.path}` };
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 (!existsSync6(targetPath)) {
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(path9, type) {
1168
+ async function requiredSkillForOperation(path10, type) {
1093
1169
  if (type === "delete_file") {
1094
1170
  return null;
1095
1171
  }
1096
- const p = normalizedPath(path9);
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")) {
@@ -1214,9 +1290,9 @@ import { z as z12 } from "zod";
1214
1290
  var ToolParameters11 = z12.object({
1215
1291
  path: z12.string().describe("Directory to switch into. Can be absolute or relative to current workspace.")
1216
1292
  });
1217
- async function execute12({ path: path9 }) {
1293
+ async function execute12({ path: path10 }) {
1218
1294
  try {
1219
- const target = resolvePath(trackedState.cwd, path9);
1295
+ const target = resolvePath(trackedState.cwd, path10);
1220
1296
  const stat = statSync(target);
1221
1297
  if (!stat.isDirectory()) {
1222
1298
  return `Path is not a directory: ${target}`;
@@ -1317,7 +1393,7 @@ var findTool = tool14({
1317
1393
  // tools/files/readDirTool.ts
1318
1394
  import { tool as tool15 } from "@openai/agents";
1319
1395
  import { readdir } from "fs/promises";
1320
- import path5 from "path";
1396
+ import path6 from "path";
1321
1397
  import { z as z15 } from "zod";
1322
1398
  var ToolParameters14 = z15.object({
1323
1399
  directoryName: z15.string().describe("The name of the directory to read.")
@@ -1330,7 +1406,7 @@ var readDirTool = tool15({
1330
1406
  try {
1331
1407
  const resolvedPath = resolvePath(trackedState.cwd, directoryName);
1332
1408
  const files = await readdir(resolvedPath, "utf8");
1333
- return files.filter((file) => !isIgnored(path5.join(resolvedPath, file)));
1409
+ return files.filter((file) => !isIgnored(path6.join(resolvedPath, file)));
1334
1410
  } catch (error) {
1335
1411
  return `Error reading directory: ${error}`;
1336
1412
  }
@@ -1386,7 +1462,7 @@ var readFileTool = tool16({
1386
1462
  import { tool as tool17 } from "@openai/agents";
1387
1463
  import { exec } from "child_process";
1388
1464
  import { unlink, writeFile as writeFile2 } from "fs/promises";
1389
- import path6 from "path";
1465
+ import path7 from "path";
1390
1466
  import { promisify as promisify3 } from "util";
1391
1467
  import { z as z17 } from "zod";
1392
1468
  var execAsync = promisify3(exec);
@@ -1429,7 +1505,7 @@ async function needsApproval2(runContext, parameters, callId) {
1429
1505
  async function execute14({ code, language }) {
1430
1506
  const extension = language === "javascript" ? "js" : "py";
1431
1507
  const interpreter = language === "javascript" ? "node" : "python3";
1432
- const tempFile = path6.join(trackedState.cwd, `.temp_code_${Date.now()}.${extension}`);
1508
+ const tempFile = path7.join(trackedState.cwd, `.temp_code_${Date.now()}.${extension}`);
1433
1509
  try {
1434
1510
  await writeFile2(tempFile, code, "utf8");
1435
1511
  const { stdout, stderr } = await execAsync(`${interpreter} ${tempFile}`);
@@ -1488,7 +1564,7 @@ import { tool as tool19 } from "@openai/agents";
1488
1564
  import { z as z19 } from "zod";
1489
1565
 
1490
1566
  // utils/files/updateEnv.ts
1491
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync } from "fs";
1567
+ import { existsSync as existsSync8, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync } from "fs";
1492
1568
  import { homedir as homedir3 } from "os";
1493
1569
  import { dirname as dirname4, join as join7 } from "path";
1494
1570
  function updateEnv(key, value) {
@@ -1496,14 +1572,14 @@ function updateEnv(key, value) {
1496
1572
  process.env[key] = normalizedValue;
1497
1573
  const topLevelEnvPath = join7(homedir3(), ".harper", "harper-agent-env");
1498
1574
  const localEnvPath = join7(trackedState.cwd, ".env");
1499
- const envPath = existsSync7(topLevelEnvPath) || !existsSync7(localEnvPath) ? topLevelEnvPath : localEnvPath;
1575
+ const envPath = existsSync8(topLevelEnvPath) || !existsSync8(localEnvPath) ? topLevelEnvPath : localEnvPath;
1500
1576
  const dir = dirname4(envPath);
1501
- if (!existsSync7(dir)) {
1577
+ if (!existsSync8(dir)) {
1502
1578
  mkdirSync2(dir, { recursive: true });
1503
1579
  }
1504
1580
  let envContent = "";
1505
- if (existsSync7(envPath)) {
1506
- envContent = readFileSync5(envPath, "utf8");
1581
+ if (existsSync8(envPath)) {
1582
+ envContent = readFileSync6(envPath, "utf8");
1507
1583
  }
1508
1584
  const regex = new RegExp(`^${key}=.*`, "m");
1509
1585
  if (regex.test(envContent)) {
@@ -2023,7 +2099,7 @@ var checkHarperStatusTool = tool30({
2023
2099
  // tools/harper/createNewHarperApplicationTool.ts
2024
2100
  import { tool as tool31 } from "@openai/agents";
2025
2101
  import { execSync as execSync3 } from "child_process";
2026
- import path7 from "path";
2102
+ import path8 from "path";
2027
2103
  import { z as z31 } from "zod";
2028
2104
 
2029
2105
  // utils/package/buildHarperCreateCommand.ts
@@ -2082,8 +2158,8 @@ async function execute16({ directoryName, template }) {
2082
2158
  const currentCwd = trackedState.cwd;
2083
2159
  const resolvedPath = resolvePath(currentCwd, directoryName);
2084
2160
  const isCurrentDir = resolvedPath === currentCwd;
2085
- const executionCwd = isCurrentDir ? resolvedPath : path7.dirname(resolvedPath);
2086
- const appName = isCurrentDir ? "." : path7.basename(resolvedPath);
2161
+ const executionCwd = isCurrentDir ? resolvedPath : path8.dirname(resolvedPath);
2162
+ const appName = isCurrentDir ? "." : path8.basename(resolvedPath);
2087
2163
  try {
2088
2164
  const pm = pickPreferredPackageManager();
2089
2165
  const { cmd, label } = buildCreateCommand(pm, appName, template);
@@ -2244,14 +2320,14 @@ var hitHarperAPITool = tool35({
2244
2320
  }
2245
2321
  return false;
2246
2322
  },
2247
- async execute({ path: path9 = "/openapi", port, method = "GET", body }) {
2323
+ async execute({ path: path10 = "/openapi", port, method = "GET", body }) {
2248
2324
  try {
2249
2325
  const effectivePort = port ?? (harperProcess.running ? harperProcess.httpPort : void 0);
2250
2326
  if (!effectivePort) {
2251
2327
  return `Error: No Harper application is currently running and no port was specified.`;
2252
2328
  }
2253
2329
  const response = await fetch(
2254
- `http://localhost:${effectivePort}${path9.startsWith("/") ? "" : "/"}${path9}`,
2330
+ `http://localhost:${effectivePort}${path10.startsWith("/") ? "" : "/"}${path10}`,
2255
2331
  {
2256
2332
  method,
2257
2333
  headers: body ? { "Content-Type": "application/json" } : {},
@@ -2291,7 +2367,7 @@ var readHarperLogsTool = tool36({
2291
2367
 
2292
2368
  // tools/harper/startHarperTool.ts
2293
2369
  import { tool as tool37 } from "@openai/agents";
2294
- import { existsSync as existsSync8 } from "fs";
2370
+ import { existsSync as existsSync9 } from "fs";
2295
2371
  import { basename, resolve } from "path";
2296
2372
  import { z as z37 } from "zod";
2297
2373
 
@@ -2318,7 +2394,7 @@ var startHarperTool = tool37({
2318
2394
  try {
2319
2395
  let effectiveDirectory = directoryName;
2320
2396
  const candidatePath = resolve(process.cwd(), directoryName);
2321
- if (!existsSync8(candidatePath)) {
2397
+ if (!existsSync9(candidatePath)) {
2322
2398
  const cwd = process.cwd();
2323
2399
  if (basename(cwd) === directoryName) {
2324
2400
  effectiveDirectory = cwd;
@@ -2646,7 +2722,7 @@ Stack: ${String(err.stack).split("\n").slice(0, 8).join("\n")}` : "";
2646
2722
 
2647
2723
  // utils/sessions/DiskSession.ts
2648
2724
  import { MemorySession } from "@openai/agents";
2649
- import { existsSync as existsSync9 } from "fs";
2725
+ import { existsSync as existsSync10 } from "fs";
2650
2726
  import { mkdir as mkdir2, readFile as readFile5, rename, writeFile as writeFile3 } from "fs/promises";
2651
2727
  import { dirname as dirname8 } from "path";
2652
2728
  var DiskSession = class extends MemorySession {
@@ -2697,7 +2773,7 @@ var DiskSession = class extends MemorySession {
2697
2773
  }
2698
2774
  }
2699
2775
  async loadStorage() {
2700
- if (existsSync9(this.filePath)) {
2776
+ if (existsSync10(this.filePath)) {
2701
2777
  try {
2702
2778
  const data = await readFile5(this.filePath, "utf-8");
2703
2779
  const parsed = JSON.parse(data);
@@ -2715,7 +2791,7 @@ var DiskSession = class extends MemorySession {
2715
2791
  const storage = await this.loadStorage();
2716
2792
  update(storage);
2717
2793
  const dir = dirname8(this.filePath);
2718
- if (!existsSync9(dir)) {
2794
+ if (!existsSync10(dir)) {
2719
2795
  await mkdir2(dir, { recursive: true });
2720
2796
  }
2721
2797
  const data = JSON.stringify(storage, null, 2);
@@ -4947,7 +5023,7 @@ function PlanView() {
4947
5023
 
4948
5024
  // ink/components/SettingsView.tsx
4949
5025
  import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
4950
- import path8 from "path";
5026
+ import path9 from "path";
4951
5027
  import { useEffect as useEffect6, useMemo as useMemo9, useState as useState11 } from "react";
4952
5028
 
4953
5029
  // ink/contexts/SettingsContext.tsx
@@ -5089,8 +5165,8 @@ function SettingsView({ isDense = false }) {
5089
5165
  if (!sessionPath) {
5090
5166
  return null;
5091
5167
  }
5092
- const relative = path8.relative(cwd, sessionPath);
5093
- if (!relative.startsWith("..") && !path8.isAbsolute(relative)) {
5168
+ const relative = path9.relative(cwd, sessionPath);
5169
+ if (!relative.startsWith("..") && !path9.isAbsolute(relative)) {
5094
5170
  return `./${relative}`;
5095
5171
  }
5096
5172
  return sessionPath;
@@ -6500,13 +6576,13 @@ function warnAndPersistRedirect(original, envKey, replacement, reason) {
6500
6576
  import chalk5 from "chalk";
6501
6577
 
6502
6578
  // utils/package/getOwnPackageJson.ts
6503
- import { readFileSync as readFileSync6 } from "fs";
6579
+ import { readFileSync as readFileSync7 } from "fs";
6504
6580
  import { join as join11 } from "path";
6505
- import { fileURLToPath } from "url";
6506
- var __dirname = fileURLToPath(new URL(".", import.meta.url));
6581
+ import { fileURLToPath as fileURLToPath2 } from "url";
6582
+ var __dirname2 = fileURLToPath2(new URL(".", import.meta.url));
6507
6583
  function getOwnPackageJson() {
6508
6584
  try {
6509
- const packageContents = readFileSync6(join11(__dirname, "../package.json"), "utf8");
6585
+ const packageContents = readFileSync7(join11(__dirname2, "../package.json"), "utf8");
6510
6586
  return JSON.parse(packageContents);
6511
6587
  } catch {
6512
6588
  return { name: "@harperfast/agent", version: "0.0.0" };
@@ -6710,16 +6786,16 @@ function parseArgs() {
6710
6786
 
6711
6787
  // utils/envLoader.ts
6712
6788
  import dotenv from "dotenv";
6713
- import { existsSync as existsSync10 } from "fs";
6789
+ import { existsSync as existsSync11 } from "fs";
6714
6790
  import { homedir as homedir4 } from "os";
6715
6791
  import { join as join12 } from "path";
6716
6792
  function loadEnv() {
6717
6793
  const topLevelEnvPath = join12(homedir4(), ".harper", "harper-agent-env");
6718
6794
  const localEnvPath = join12(process.cwd(), ".env");
6719
- if (existsSync10(topLevelEnvPath)) {
6795
+ if (existsSync11(topLevelEnvPath)) {
6720
6796
  dotenv.config({ path: topLevelEnvPath, quiet: true });
6721
6797
  }
6722
- if (existsSync10(localEnvPath)) {
6798
+ if (existsSync11(localEnvPath)) {
6723
6799
  dotenv.config({ path: localEnvPath, override: true, quiet: true });
6724
6800
  }
6725
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.1",
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",