naisys 1.3.0 → 1.4.0

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 CHANGED
@@ -75,6 +75,10 @@ dreamModel: claude3opus
75
75
  # defaults to the shellModel if omitted
76
76
  webModel: gemini-pro
77
77
 
78
+ # The model used by the 'genimg' command. If not defined then the genimg command is not available to the LLM
79
+ # Valid values: dalle2-256, dalle2-512, dalle2-1024, dalle3-1024, dalle3-1024-HD
80
+ imageModel: dalle3-1024
81
+
78
82
  # A system like prompt explaining the agent's role and responsibilities
79
83
  # You can use config variables in this string
80
84
  agentPrompt: |
@@ -161,10 +165,11 @@ initialCommands:
161
165
  - `comment "<note>"` - The LLM is directed to use this for 'thinking out loud' which avoids 'invalid command' errors
162
166
  - `endsession "<note>"` - Clear the context and start a new session.
163
167
  - The LLM is directed to track it's context size and to end the session with a note before running over the context limit
164
- - `pause <seconds>` - Can be used by the debug agent or the LLM to pause execution indefinitely, or until a new message is received from another agent, or for a set number of seconds
168
+ - `pause <seconds>` - Can be used by the debug agent or the LLM to pause execution for a set number of seconds
165
169
  - NAISYS apps
166
170
  - `llmail` - A context friendly 'mail system' used for agent to agent communication
167
171
  - `llmynx` - A context friendly wrapping on the lynx browser that can use a separate LLM to reduce the size of a large webpage into something that can fit into the LLM's context
172
+ - `genimg "<description>" <filepath>` - Generates an image with the given description, save at the specified path
168
173
 
169
174
  ## Running NAISYS from Source
170
175
 
@@ -188,3 +193,11 @@ initialCommands:
188
193
  - If you want to use NAISYS for a website
189
194
  - Install a local web server, for example [XAMPP](https://www.apachefriends.org/) on Windows
190
195
  - Start the server and put the URL in the `.env` file
196
+
197
+ ## Changelog
198
+
199
+ - 1.4: `genimg` command for generating images
200
+ - 1.3: Post-session 'dreaming' as well as a mail 'blackout' period
201
+ - 1.2: Created stand-in shell commands for custom Naisys commands
202
+ - 1.1: Added command protection settings to prevent unwanted writes
203
+ - 1.0: Initial release
package/bin/genimg ADDED
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ echo "'genimg' cannot be used with other commands on the same prompt."
package/bin/naisys CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
2
 
3
- # Make sure to enable this script for execution with `chmod +x runteam.sh`
3
+ # Make sure to enable this script for execution with `chmod +x naisys`
4
4
 
5
5
  # Check if an argument is provided
6
6
  if [ $# -eq 0 ]; then
@@ -0,0 +1,97 @@
1
+ import * as fs from "fs";
2
+ import OpenAI from "openai";
3
+ import path from "path";
4
+ import sharp from "sharp";
5
+ import * as config from "../config.js";
6
+ import * as costTracker from "../llm/costTracker.js";
7
+ import * as output from "../utils/output.js";
8
+ import { unixToHostPath } from "../utils/utilities.js";
9
+ /** genimg "<description>" <filepath>: Generate an image with the description and save it to the file path */
10
+ export async function handleCommand(args) {
11
+ // genimg sholdn't even be presented as an available command unless it is defined in the config
12
+ if (!config.agent.imageModel) {
13
+ throw "Agent config: Error, 'imageModel' is not defined";
14
+ }
15
+ const description = args.split('"')[1].trim();
16
+ const filepath = args.split('"')[2].trim();
17
+ if (!description) {
18
+ throw "Error: Description is required";
19
+ }
20
+ if (!filepath) {
21
+ throw "Error: Filepath is required";
22
+ }
23
+ // Check directory exists
24
+ const hostpath = unixToHostPath(filepath);
25
+ const dirname = path.dirname(hostpath);
26
+ if (!fs.existsSync(dirname)) {
27
+ throw `Error: Directory does not exist`;
28
+ }
29
+ output.comment(`Generating image with ${config.agent.imageModel}...`);
30
+ const openai = new OpenAI();
31
+ const model = getImageModel(config.agent.imageModel);
32
+ const response = await openai.images.generate({
33
+ prompt: description,
34
+ model: model.name,
35
+ size: model.size,
36
+ quality: model.quality,
37
+ response_format: "b64_json",
38
+ });
39
+ // save to filepath
40
+ const base64Image = response.data[0].b64_json;
41
+ if (!base64Image) {
42
+ throw 'Error: "b64_json" not found in response';
43
+ }
44
+ // Convert the base64 string to a buffer
45
+ const imageBuffer = Buffer.from(base64Image, "base64");
46
+ // Use sharp to convert the buffer and save it as a JPG file
47
+ //const currentPath = unixToHostPath(await shellWrapper.getCurrentPath());
48
+ //const filepath = path.join(currentPath, filename);
49
+ const fileExtension = path.extname(filepath).substring(1);
50
+ await sharp(imageBuffer)
51
+ .toFormat(fileExtension)
52
+ .toFile(hostpath);
53
+ // Record the cost
54
+ await costTracker.recordCost(model.cost, "genimg", model.name);
55
+ return "Image generated and saved to " + filepath;
56
+ }
57
+ const imageModels = [
58
+ {
59
+ key: "dalle3-1024-HD",
60
+ name: "dall-e-3",
61
+ size: "1024x1024",
62
+ quality: "hd",
63
+ cost: 0.08,
64
+ },
65
+ {
66
+ key: "dalle3-1024",
67
+ name: "dall-e-3",
68
+ size: "1024x1024",
69
+ cost: 0.04,
70
+ },
71
+ {
72
+ key: "dalle2-1024",
73
+ name: "dall-e-2",
74
+ size: "1024x1024",
75
+ cost: 0.02,
76
+ },
77
+ {
78
+ key: "dalle2-512",
79
+ name: "dall-e-2",
80
+ size: "512x512",
81
+ cost: 0.018,
82
+ },
83
+ {
84
+ key: "dalle2-256",
85
+ name: "dall-e-2",
86
+ size: "256x256",
87
+ cost: 0.016,
88
+ },
89
+ ];
90
+ function getImageModel(key) {
91
+ const model = imageModels.find((m) => m.key === key);
92
+ if (!model) {
93
+ throw `Error, image model not found: ${key}`;
94
+ }
95
+ return model;
96
+ }
97
+ //# sourceMappingURL=genimg.js.map
@@ -3,8 +3,8 @@ import table from "text-table";
3
3
  import * as config from "../config.js";
4
4
  import * as dbUtils from "../utils/dbUtils.js";
5
5
  import * as utilities from "../utils/utilities.js";
6
- import { naisysToHostPath } from "../utils/utilities.js";
7
- const _dbFilePath = naisysToHostPath(`${config.naisysFolder}/lib/llmail.db`);
6
+ import { unixToHostPath } from "../utils/utilities.js";
7
+ const _dbFilePath = unixToHostPath(`${config.naisysFolder}/lib/llmail.db`);
8
8
  let _myUserId = -1;
9
9
  /** Threading is not currently used so this doesn't matter */
10
10
  const _threadTokenMax = config.mailMessageTokenMax * 5;
@@ -69,6 +69,7 @@ async function init() {
69
69
  }
70
70
  });
71
71
  }
72
+ export const waitingForMailMessage = "Waiting for new mail messages...";
72
73
  export async function handleCommand(args) {
73
74
  const argParams = args.split(" ");
74
75
  if (!argParams[0]) {
@@ -85,6 +86,7 @@ export async function handleCommand(args) {
85
86
  return `llmail <command>
86
87
  users: Get list of users on the system
87
88
  send "<users>" "subject" "message": Send a message. ${config.mailMessageTokenMax} token max.
89
+ wait: Pause the session until a new mail message is received
88
90
 
89
91
  * Attachments are not supported, use file paths to refence files in emails as all users are on the same machine`;
90
92
  }
@@ -93,6 +95,7 @@ export async function handleCommand(args) {
93
95
  no params: List all active threads
94
96
  users: Get list of users on the system
95
97
  send "<users>" "subject" "message": Send a new mail, starting a new thread
98
+ wait: Pause the session until a new mail message is received
96
99
  read <id>: Read a thread
97
100
  reply <id> <message>: Reply to a thread
98
101
  adduser <id> <username>: Add a user to thread with id
@@ -106,6 +109,9 @@ export async function handleCommand(args) {
106
109
  const message = newParams[5];
107
110
  return await newThread(usernames, subject, message);
108
111
  }
112
+ case "wait": {
113
+ return waitingForMailMessage;
114
+ }
109
115
  case "read": {
110
116
  const threadId = parseInt(argParams[1]);
111
117
  return await readThread(threadId);
@@ -1,4 +1,5 @@
1
1
  import chalk from "chalk";
2
+ import * as genimg from "../apps/genimg.js";
2
3
  import * as llmail from "../apps/llmail.js";
3
4
  import * as llmynx from "../apps/llmynx.js";
4
5
  import * as config from "../config.js";
@@ -90,15 +91,17 @@ export async function processCommand(prompt, consoleInput) {
90
91
  }
91
92
  break;
92
93
  }
93
- // With no argument, in debug mode, pause will pause forever,
94
- // in LLM mode it will pause until a message is receieved
95
- // Don't want the llm to hang itself, but it still can if it's the only agent or if all the agents pause..
96
- // The setting only lasts for the next command, next loop it uses the agent default
97
94
  case "pause": {
95
+ const pauseSeconds = cmdArgs ? parseInt(cmdArgs) : 0;
96
+ // Don't allow the LLM to hang itself
97
+ if (inputMode.current === InputMode.LLM && !pauseSeconds) {
98
+ await contextManager.append("Puase command requires a number of seconds to pause for");
99
+ break;
100
+ }
98
101
  return {
99
102
  nextCommandAction: NextCommandAction.Continue,
100
- pauseSeconds: cmdArgs ? parseInt(cmdArgs) : 0,
101
- wakeOnMessage: inputMode.current === InputMode.LLM,
103
+ pauseSeconds,
104
+ wakeOnMessage: false, // llmail has a 'wait' command that is useful in multi-agent situations
102
105
  };
103
106
  }
104
107
  case "cost": {
@@ -114,6 +117,18 @@ export async function processCommand(prompt, consoleInput) {
114
117
  case "llmail": {
115
118
  const mailResponse = await llmail.handleCommand(cmdArgs);
116
119
  await contextManager.append(mailResponse);
120
+ if (mailResponse == llmail.waitingForMailMessage) {
121
+ return {
122
+ nextCommandAction: NextCommandAction.Continue,
123
+ pauseSeconds: 0,
124
+ wakeOnMessage: true,
125
+ };
126
+ }
127
+ break;
128
+ }
129
+ case "genimg": {
130
+ const genimgResponse = await genimg.handleCommand(cmdArgs);
131
+ await contextManager.append(genimgResponse);
117
132
  break;
118
133
  }
119
134
  case "context":
@@ -190,7 +205,8 @@ async function splitMultipleInputCommands(nextInput) {
190
205
  }
191
206
  }
192
207
  // If the LLM forgets the quote on the comment, treat it as a single line comment
193
- else if (newLinePos > 0 && nextInput.startsWith("comment ")) {
208
+ else if (newLinePos > 0 &&
209
+ (nextInput.startsWith("comment ") || nextInput.startsWith("genimg "))) {
194
210
  input = nextInput.slice(0, newLinePos);
195
211
  nextInput = nextInput.slice(newLinePos).trim();
196
212
  }
@@ -40,7 +40,9 @@ export async function run() {
40
40
  await contextManager.append(latestDream);
41
41
  }
42
42
  for (const initialCommand of config.agent.initialCommands) {
43
- await commandHandler.processCommand(await promptBuilder.getPrompt(0, false), config.resolveConfigVars(initialCommand));
43
+ const prompt = await promptBuilder.getPrompt(0, false);
44
+ await contextManager.append(prompt, ContentSource.ConsolePrompt);
45
+ await commandHandler.processCommand(prompt, config.resolveConfigVars(initialCommand));
44
46
  }
45
47
  inputMode.toggle(InputMode.Debug);
46
48
  let pauseSeconds = config.agent.debugPauseSeconds;
@@ -141,12 +143,22 @@ async function handleErrorAndSwitchToDebugMode(e, llmErrorCount, addToContext) {
141
143
  wakeOnMessage,
142
144
  };
143
145
  }
146
+ let mailBlackoutCountdown = 0;
144
147
  async function checkNewMailNotification() {
148
+ let supressMail = false;
149
+ if (mailBlackoutCountdown > 0) {
150
+ mailBlackoutCountdown--;
151
+ supressMail = true;
152
+ }
145
153
  // Check for unread threads
146
154
  const unreadThreads = await llmail.getUnreadThreads();
147
155
  if (!unreadThreads.length) {
148
156
  return;
149
157
  }
158
+ if (supressMail) {
159
+ await output.commentAndLog(`New mail notifications blackout in effect. ${mailBlackoutCountdown} cycles remaining.`);
160
+ return;
161
+ }
150
162
  // Get the new messages for each thread
151
163
  const newMessages = [];
152
164
  for (const { threadId, newMsgId } of unreadThreads) {
@@ -166,10 +178,11 @@ async function checkNewMailNotification() {
166
178
  for (const unreadThread of unreadThreads) {
167
179
  await llmail.markAsRead(unreadThread.threadId);
168
180
  }
181
+ mailBlackoutCountdown = config.mailBlackoutCycles;
169
182
  }
170
183
  else if (llmail.simpleMode) {
171
184
  await contextManager.append(`You have new mail, but not enough context to read them.\n` +
172
- `Finish up what you're doing. After you 'endsession' and the context resets, you will be able to read them.`, ContentSource.Console);
185
+ `After you 'endsession' and the context resets, you will be able to read them.`, ContentSource.Console);
173
186
  }
174
187
  // LLM will in many cases end the session here, when the new session starts
175
188
  // this code will run again, and show a full preview of the messages
@@ -47,6 +47,9 @@ export async function handleCommand(input) {
47
47
  if (outputLimitExceeded) {
48
48
  await contextManager.append(`\nThe shell command generated too much output (${tokenCount} tokens). Only 2,000 tokens worth are shown above.`);
49
49
  }
50
+ if (text.endsWith(": command not found")) {
51
+ await contextManager.append("Please enter a valid Linux or NAISYS command after the prompt. Use the 'comment' command for thoughts.");
52
+ }
50
53
  }
51
54
  response.hasErrors = output.hasErrors;
52
55
  return response;
@@ -3,7 +3,7 @@ import * as fs from "fs";
3
3
  import * as os from "os";
4
4
  import * as config from "../config.js";
5
5
  import * as output from "../utils/output.js";
6
- import { naisysToHostPath } from "../utils/utilities.js";
6
+ import { unixToHostPath } from "../utils/utilities.js";
7
7
  var ShellEvent;
8
8
  (function (ShellEvent) {
9
9
  ShellEvent["Ouptput"] = "stdout";
@@ -57,13 +57,13 @@ function errorIfNotEmpty(response) {
57
57
  output.error(response.value);
58
58
  }
59
59
  }
60
- export function processOutput(dataStr, eventType) {
60
+ function processOutput(dataStr, eventType) {
61
61
  if (!_resolveCurrentCommand) {
62
62
  output.comment(eventType + " without handler: " + dataStr);
63
63
  return;
64
64
  }
65
65
  if (eventType === ShellEvent.Exit) {
66
- output.error("SHELL EXITED. PID: " + (_process === null || _process === void 0 ? void 0 : _process.pid) + " CODE: " + dataStr);
66
+ output.error("SHELL EXITED. PID: " + _process?.pid + " CODE: " + dataStr);
67
67
  }
68
68
  else {
69
69
  //_log += "OUTPUT: " + dataStr;
@@ -127,7 +127,7 @@ export async function executeCommand(command) {
127
127
  _resolveCurrentCommand = resolve;
128
128
  const commandWithDelimiter = `${command.trim()}\necho "${_commandDelimiter} LINE:\${LINENO}"\n`;
129
129
  //_log += "INPUT: " + commandWithDelimiter;
130
- _process === null || _process === void 0 ? void 0 : _process.stdin.write(commandWithDelimiter);
130
+ _process?.stdin.write(commandWithDelimiter);
131
131
  // If no response, kill and reset the shell, often hanging on some unescaped input
132
132
  _currentCommandTimeout = setTimeout(resetShell, config.shellCommmandTimeoutSeconds * 1000);
133
133
  });
@@ -136,8 +136,8 @@ function resetShell() {
136
136
  if (!_resolveCurrentCommand) {
137
137
  return;
138
138
  }
139
- _process === null || _process === void 0 ? void 0 : _process.kill();
140
- output.error("SHELL TIMEMOUT/KILLED. PID: " + (_process === null || _process === void 0 ? void 0 : _process.pid));
139
+ _process?.kill();
140
+ output.error("SHELL TIMEMOUT/KILLED. PID: " + _process?.pid);
141
141
  const outputWithError = _commandOutput.trim() +
142
142
  `\nError: Command timed out after ${config.shellCommmandTimeoutSeconds} seconds.`;
143
143
  resetProcess();
@@ -163,7 +163,7 @@ function resetCommand() {
163
163
  }
164
164
  function resetProcess() {
165
165
  resetCommand();
166
- _process === null || _process === void 0 ? void 0 : _process.removeAllListeners();
166
+ _process?.removeAllListeners();
167
167
  _process = undefined;
168
168
  }
169
169
  /** Wraps multi line commands in a script to make it easier to diagnose the source of errors based on line number
@@ -176,7 +176,7 @@ set -e
176
176
  cd ${_currentPath}
177
177
  ${command.trim()}`;
178
178
  // create/writewrite file
179
- fs.writeFileSync(naisysToHostPath(scriptPath), scriptContent);
179
+ fs.writeFileSync(unixToHostPath(scriptPath), scriptContent);
180
180
  // `Path` is set to the ./bin folder because custom NAISYS commands that follow shell commands will be handled by the shell, which will fail
181
181
  // so we need to remind the LLM that 'naisys commands cannot be used with other commands on the same prompt'
182
182
  // `source` will run the script in the current shell, so any change directories in the script will persist in the current shell
package/dist/config.js CHANGED
@@ -1,17 +1,24 @@
1
1
  import { program } from "commander";
2
2
  import dotenv from "dotenv";
3
3
  import * as fs from "fs";
4
+ import { readFile } from "fs/promises";
4
5
  import yaml from "js-yaml";
6
+ import path from "path";
7
+ import { fileURLToPath } from "url";
5
8
  import { CommandProtection } from "./utils/enums.js";
6
- import { valueFromString } from "./utils/utilities.js";
9
+ import { hostToUnixPath, valueFromString } from "./utils/utilities.js";
7
10
  program.argument("<agent-path>", "Path to agent configuration file").parse();
8
11
  dotenv.config();
9
12
  /** The system name that shows after the @ in the command prompt */
10
13
  export const hostname = "naisys";
11
- export const shellOutputTokenMax = 2500; // Limits the size of files that can be read/wrote
12
- export const shellCommmandTimeoutSeconds = 15; // The number of seconds NAISYS will wait for a shell command to complete
13
- export const webTokenMax = 2500;
14
+ /** Limits the size of files that can be read/wrote */
15
+ export const shellOutputTokenMax = 3000; //
16
+ /** The number of seconds NAISYS will wait for a shell command to complete */
17
+ export const shellCommmandTimeoutSeconds = 15;
18
+ export const webTokenMax = 3000;
14
19
  export const mailMessageTokenMax = 400;
20
+ /** Used to prevent the agent from constantly responding to mail and not getting any work done */
21
+ export const mailBlackoutCycles = 3;
15
22
  /* .env is used for global configs across naisys, while agent configs are for the specific agent */
16
23
  export const naisysFolder = getEnv("NAISYS_FOLDER", true);
17
24
  export const websiteFolder = getEnv("WEBSITE_FOLDER");
@@ -22,8 +29,9 @@ export const googleApiKey = getEnv("GOOGLE_API_KEY");
22
29
  export const anthropicApiKey = getEnv("ANTHROPIC_API_KEY");
23
30
  export const agent = loadAgentConfig();
24
31
  function loadAgentConfig() {
25
- const agentPath = program.args[0];
26
- const config = yaml.load(fs.readFileSync(agentPath, "utf8"));
32
+ const config = yaml.load(fs.readFileSync(program.args[0], "utf8"));
33
+ config.path = hostToUnixPath(path.resolve(program.args[0]));
34
+ config.directory = config.path.substring(0, config.path.lastIndexOf("/"));
27
35
  // throw if any property is undefined
28
36
  for (const key of [
29
37
  "username",
@@ -65,11 +73,14 @@ export const binPath = getBinPath();
65
73
  * otherwise need to rip it from the package ourselves relative to where this file is located */
66
74
  async function getVersion() {
67
75
  try {
68
- const packageJsonPath = new URL("../package.json", import.meta.url);
69
- const packageJson = await import(packageJsonPath.href, {
70
- assert: { type: "json" },
71
- });
72
- return packageJson.default.version;
76
+ /* Removed for compatibility with https://bundlephobia.com/package/naisys
77
+ const packageJson = await import(packageJsonUrl.href, {
78
+ assert: { type: "json" },
79
+ });*/
80
+ const packageJsonUrl = new URL("../package.json", import.meta.url);
81
+ const packageJsonPath = fileURLToPath(packageJsonUrl);
82
+ const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
83
+ return packageJson.version;
73
84
  }
74
85
  catch (e) {
75
86
  return "0.1";
@@ -18,6 +18,11 @@ export function getSystemMessage() {
18
18
  if (_cachedSystemMessage) {
19
19
  return _cachedSystemMessage;
20
20
  }
21
+ let genImgCmd = "";
22
+ if (config.agent.imageModel) {
23
+ genImgCmd = `
24
+ genimg "<description>" <filepath>: Generate an image with the description and save it to the given filename`;
25
+ }
21
26
  // Fill out the templates in the agent prompt and stick it to the front of the system message
22
27
  // A lot of the stipulations in here are to prevent common LLM mistakes
23
28
  // Like we can't jump between standard and special commands in a single prompt, which the LLM will try to do if not warned
@@ -31,6 +36,7 @@ Don't try to guess the output of commands. Don't put commands in \`\`\` blocks.
31
36
  For example when you run 'cat' or 'ls', don't write what you think the output will be. Let the system do that.
32
37
  Your role is that of the user. The system will provide responses and next command prompt. Don't output your own command prompt.
33
38
  Be careful when writing files through the command prompt with cat. Make sure to close and escape quotes properly.
39
+ Don't blindly overwrite existing files without reading them first.
34
40
 
35
41
  NAISYS ${config.packageVersion} Shell
36
42
  Welcome back ${config.agent.username}!
@@ -43,9 +49,9 @@ LINUX Commands:
43
49
  Do not input notes after the prompt. Only valid commands.
44
50
  NAISYS Commands: (cannot be used with other commands on the same prompt)
45
51
  llmail: A local mail system for communicating with your team
46
- llmynx: A context optimized web browser. Enter 'llmynx help' to learn how to use it
52
+ llmynx: A context optimized web browser. Enter 'llmynx help' to learn how to use it${genImgCmd}
47
53
  comment "<thought>": Any non-command output like thinking out loud, prefix with the 'comment' command
48
- pause <seconds>: Pause for <seconds> or indeterminite if no argument is provided. Auto wake up on new mail message
54
+ pause <seconds>: Pause for <seconds>
49
55
  endsession "<note>": Ends this session, clears the console log and context.
50
56
  The note should help you find your bearings in the next session.
51
57
  The note should contain your next goal, and important things should you remember.
@@ -1,7 +1,7 @@
1
1
  import * as config from "../config.js";
2
2
  import * as dbUtils from "../utils/dbUtils.js";
3
- import { naisysToHostPath } from "../utils/utilities.js";
4
- const _dbFilePath = naisysToHostPath(`${config.naisysFolder}/lib/costs.db`);
3
+ import { unixToHostPath } from "../utils/utilities.js";
4
+ const _dbFilePath = unixToHostPath(`${config.naisysFolder}/lib/costs.db`);
5
5
  await init();
6
6
  async function init() {
7
7
  const newDbCreated = await dbUtils.initDatabase(_dbFilePath);
@@ -1,11 +1,11 @@
1
1
  import * as config from "../config.js";
2
2
  import * as dbUtils from "../utils/dbUtils.js";
3
3
  import * as output from "../utils/output.js";
4
- import { naisysToHostPath } from "../utils/utilities.js";
4
+ import { unixToHostPath } from "../utils/utilities.js";
5
5
  import * as contextManager from "./contextManager.js";
6
6
  import { LlmRole } from "./llmDtos.js";
7
7
  import * as llmService from "./llmService.js";
8
- const _dbFilePath = naisysToHostPath(`${config.naisysFolder}/lib/dream.db`);
8
+ const _dbFilePath = unixToHostPath(`${config.naisysFolder}/lib/dream.db`);
9
9
  await init();
10
10
  async function init() {
11
11
  const newDbCreated = await dbUtils.initDatabase(_dbFilePath);
@@ -27,7 +27,7 @@ export async function goodmorning() {
27
27
  FROM DreamLog
28
28
  WHERE username = ?
29
29
  ORDER BY date DESC LIMIT 1`, config.agent.username);
30
- return row === null || row === void 0 ? void 0 : row.dream;
30
+ return row?.dream;
31
31
  });
32
32
  }
33
33
  export async function goodnight() {
@@ -39,8 +39,10 @@ export async function goodnight() {
39
39
  async function runDreamSequence() {
40
40
  const systemMessage = `You are ${config.agent.username}'s unconcious sleep process. You compile all ${config.agent.username}'s
41
41
  thoughts during the day and reduce them down to important things to remember - references, plans, project structure, schemas,
42
- file locations, urls, and more. You are the sleep process, and you are the most important process. Using your results,
43
- when ${config.agent.username} wakes up they'll know exactly what to do and how to do it.`;
42
+ file locations, urls, and more. You don't need to summarize what happened today, or what to do in the far future, just focus on the
43
+ near term. Check what happened during the day for inconsistencies, things to fix and/or check tomorrow. You are the sleep process,
44
+ and you are the most important process. Using your results, when ${config.agent.username} wakes up they'll know exactly what to do
45
+ and how to do it with minimal time spent scanning existing work because you've laid everything out so well.`;
44
46
  const allTheThings = contextManager.messages.map((m) => m.content).join("\n");
45
47
  return await llmService.query(config.agent.dreamModel, systemMessage, [
46
48
  {
@@ -68,7 +68,6 @@ async function sendWithOpenAiCompatible(modelKey, systemMessage, context, source
68
68
  return chatResponse.choices[0].message.content || "";
69
69
  }
70
70
  async function sendWithGoogle(modelKey, systemMessage, context, source) {
71
- var _a;
72
71
  if (!config.googleApiKey) {
73
72
  throw "Error, googleApiKey is not defined";
74
73
  }
@@ -116,7 +115,7 @@ async function sendWithGoogle(modelKey, systemMessage, context, source) {
116
115
  },
117
116
  });
118
117
  const result = await chat.sendMessage(lastMessage.content);
119
- if ((_a = result.response.promptFeedback) === null || _a === void 0 ? void 0 : _a.blockReason) {
118
+ if (result.response.promptFeedback?.blockReason) {
120
119
  throw `Google API Request Blocked, ${result.response.promptFeedback.blockReason}`;
121
120
  }
122
121
  const responseText = result.response.text();
@@ -131,7 +130,6 @@ async function sendWithGoogle(modelKey, systemMessage, context, source) {
131
130
  return responseText;
132
131
  }
133
132
  async function sendWithAnthropic(modelKey, systemMessage, context, source) {
134
- var _a;
135
133
  const model = getLLModel(modelKey);
136
134
  if (!config.anthropicApiKey) {
137
135
  throw "Error, anthropicApiKey is not defined";
@@ -171,6 +169,6 @@ async function sendWithAnthropic(modelKey, systemMessage, context, source) {
171
169
  else {
172
170
  throw "Error, no usage data returned from Anthropic API.";
173
171
  }
174
- return ((_a = msgResponse.content.find((c) => c.type == "text")) === null || _a === void 0 ? void 0 : _a.text) || "";
172
+ return msgResponse.content.find((c) => c.type == "text")?.text || "";
175
173
  }
176
174
  //# sourceMappingURL=llmService.js.map
@@ -3,10 +3,10 @@ import * as fs from "fs";
3
3
  import * as config from "../config.js";
4
4
  import { LlmRole } from "../llm/llmDtos.js";
5
5
  import * as dbUtils from "./dbUtils.js";
6
- import { ensureFileDirExists, naisysToHostPath } from "./utilities.js";
7
- const _dbFilePath = naisysToHostPath(`${config.naisysFolder}/lib/log.db`);
8
- const _combinedLogFilePath = naisysToHostPath(`${config.websiteFolder || config.naisysFolder}/logs/combined-log.html`);
9
- const _userLogFilePath = naisysToHostPath(`${config.websiteFolder || config.naisysFolder}/logs/${config.agent.username}-log.html`);
6
+ import { ensureFileDirExists, unixToHostPath } from "./utilities.js";
7
+ const _dbFilePath = unixToHostPath(`${config.naisysFolder}/lib/log.db`);
8
+ const _combinedLogFilePath = unixToHostPath(`${config.websiteFolder || config.naisysFolder}/logs/combined-log.html`);
9
+ const _userLogFilePath = unixToHostPath(`${config.websiteFolder || config.naisysFolder}/logs/${config.agent.username}-log.html`);
10
10
  await init();
11
11
  async function init() {
12
12
  initLogFile(_combinedLogFilePath);
@@ -1,17 +1,26 @@
1
1
  import * as fs from "fs";
2
2
  import * as os from "os";
3
+ import path from "path";
3
4
  import { get_encoding } from "tiktoken";
4
- /** Take a NAISYS path and convert to something that can be
5
+ /** Take a unix path and convert to something that can be
5
6
  * opened by the host with the native fs library and such */
6
- export function naisysToHostPath(filePath) {
7
- if (os.platform() === "win32" && filePath.startsWith("/mnt/")) {
8
- // "/mnt/c/" -> "c:/"
9
- filePath = filePath.substring(5);
10
- return filePath[0] + ":" + filePath.substring(1);
7
+ export function unixToHostPath(unixPath) {
8
+ const match = unixPath.match(/^\/mnt\/([a-zA-Z])\//);
9
+ if (os.platform() === "win32" && match) {
10
+ // Replace '/mnt/c/' with 'C:/' and convert forward slashes to backslashes
11
+ return unixPath
12
+ .replace(`/mnt/${match[1]}/`, `${match[1].toLowerCase()}:\\`)
13
+ .replace(/\//g, "\\");
11
14
  }
12
- else {
13
- return filePath;
15
+ return unixPath;
16
+ }
17
+ export function hostToUnixPath(hostPath) {
18
+ const match = hostPath.match(/^([a-zA-Z]):\\(.*)/);
19
+ if (os.platform() === "win32" && match) {
20
+ // Replace 'C:\' with '/mnt/c/' and convert backslashes to forward slashes
21
+ return `/mnt/${match[1].toLowerCase()}/${match[2].replace(/\\/g, "/")}`;
14
22
  }
23
+ return hostPath; // Return the original path if it doesn't match the pattern
15
24
  }
16
25
  export function valueFromString(obj, path, defaultValue) {
17
26
  if (!path) {
@@ -20,7 +29,7 @@ export function valueFromString(obj, path, defaultValue) {
20
29
  const keys = path.split(".");
21
30
  let result = obj;
22
31
  for (const key of keys) {
23
- result = result === null || result === void 0 ? void 0 : result[key];
32
+ result = result?.[key];
24
33
  if (result === undefined) {
25
34
  return defaultValue;
26
35
  }
@@ -32,9 +41,9 @@ export function getTokenCount(text) {
32
41
  return _gpt2encoding.encode(text).length;
33
42
  }
34
43
  export function ensureFileDirExists(filePath) {
35
- const dir = filePath.split("/").slice(0, -1).join("/");
36
- if (!fs.existsSync(dir)) {
37
- fs.mkdirSync(dir, { recursive: true });
44
+ const dirname = path.dirname(filePath);
45
+ if (!fs.existsSync(dirname)) {
46
+ fs.mkdirSync(dirname, { recursive: true });
38
47
  }
39
48
  }
40
49
  export function trimChars(text, charList) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "naisys",
3
3
  "description": "Node.js Autonomous Intelligence System",
4
- "version": "1.3.0",
4
+ "version": "1.4.0",
5
5
  "type": "module",
6
6
  "main": "dist/naisys.js",
7
7
  "preferGlobal": true,
@@ -9,10 +9,9 @@
9
9
  "naisys": "bin/naisys"
10
10
  },
11
11
  "scripts": {
12
- "compile/run/attachable": "tsc && node --inspect dist/naisys.js ./agents/webdev-fansite.yaml",
13
- "agent:dev": "node dist/naisys.js ./agents/3-team-dev-db-content/dev.yaml",
14
- "agent:db": "node dist/naisys.js ./agents/3-team-dev-db-content/db.yaml",
15
- "agent:content": "node dist/naisys.js ./agents/3-team-dev-db-content/content.yaml",
12
+ "compile/run/attachable": "tsc && node --inspect dist/naisys.js ./agents/mud/mud.yaml",
13
+ "agent:claude": "node dist/naisys.js ./agents/mud/battle-claude.yaml",
14
+ "agent:gpt": "node dist/naisys.js ./agents/mud/battle-gpt.yaml",
16
15
  "clean": "rm -rf dist",
17
16
  "compile": "tsc",
18
17
  "eslint": "npx eslint --rulesdir eslint-rules src",
@@ -22,7 +21,7 @@
22
21
  "detect-cycles": "madge --circular dist",
23
22
  "updates:check": "npm-check-updates",
24
23
  "updates:apply": "npm-check-updates -u && npm install",
25
- "npm:publish:dryrun": "npm run clean && npm install && npm run compile && npm publish --dry-run",
24
+ "npm:publish:dryrun": "npm run clean && npm ci && npm run compile && npm publish --dry-run",
26
25
  "postinstall": "chmod +x ./bin/*"
27
26
  },
28
27
  "repository": {
@@ -61,10 +60,11 @@
61
60
  "dotenv": "16.4.5",
62
61
  "escape-html": "1.0.3",
63
62
  "js-yaml": "4.1.0",
64
- "openai": "4.29.0",
63
+ "openai": "4.29.1",
64
+ "sharp": "0.33.2",
65
65
  "sqlite": "5.1.1",
66
66
  "sqlite3": "5.1.7",
67
67
  "text-table": "0.2.0",
68
68
  "tiktoken": "1.0.13"
69
69
  }
70
- }
70
+ }