@jcheesepkg/nanobot 0.1.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 +139 -0
- package/dist/_virtual/_rolldown/runtime.mjs +40 -0
- package/dist/agent/context.d.mts +44 -0
- package/dist/agent/context.d.mts.map +1 -0
- package/dist/agent/context.mjs +153 -0
- package/dist/agent/context.mjs.map +1 -0
- package/dist/agent/loop.d.mts +56 -0
- package/dist/agent/loop.d.mts.map +1 -0
- package/dist/agent/loop.mjs +232 -0
- package/dist/agent/loop.mjs.map +1 -0
- package/dist/agent/memory.d.mts +30 -0
- package/dist/agent/memory.d.mts.map +1 -0
- package/dist/agent/memory.mjs +77 -0
- package/dist/agent/memory.mjs.map +1 -0
- package/dist/agent/skills.d.mts +43 -0
- package/dist/agent/skills.d.mts.map +1 -0
- package/dist/agent/skills.mjs +159 -0
- package/dist/agent/skills.mjs.map +1 -0
- package/dist/agent/subagent.d.mts +39 -0
- package/dist/agent/subagent.d.mts.map +1 -0
- package/dist/agent/subagent.mjs +167 -0
- package/dist/agent/subagent.mjs.map +1 -0
- package/dist/agent/tools/base.d.mts +16 -0
- package/dist/agent/tools/base.d.mts.map +1 -0
- package/dist/agent/tools/base.mjs +19 -0
- package/dist/agent/tools/base.mjs.map +1 -0
- package/dist/agent/tools/cron.d.mts +49 -0
- package/dist/agent/tools/cron.d.mts.map +1 -0
- package/dist/agent/tools/cron.mjs +101 -0
- package/dist/agent/tools/cron.mjs.map +1 -0
- package/dist/agent/tools/filesystem.d.mts +87 -0
- package/dist/agent/tools/filesystem.d.mts.map +1 -0
- package/dist/agent/tools/filesystem.mjs +145 -0
- package/dist/agent/tools/filesystem.mjs.map +1 -0
- package/dist/agent/tools/message.d.mts +44 -0
- package/dist/agent/tools/message.d.mts.map +1 -0
- package/dist/agent/tools/message.mjs +68 -0
- package/dist/agent/tools/message.mjs.map +1 -0
- package/dist/agent/tools/registry.d.mts +29 -0
- package/dist/agent/tools/registry.d.mts.map +1 -0
- package/dist/agent/tools/registry.mjs +49 -0
- package/dist/agent/tools/registry.mjs.map +1 -0
- package/dist/agent/tools/shell.d.mts +36 -0
- package/dist/agent/tools/shell.d.mts.map +1 -0
- package/dist/agent/tools/shell.mjs +73 -0
- package/dist/agent/tools/shell.mjs.map +1 -0
- package/dist/agent/tools/spawn.d.mts +35 -0
- package/dist/agent/tools/spawn.d.mts.map +1 -0
- package/dist/agent/tools/spawn.mjs +50 -0
- package/dist/agent/tools/spawn.mjs.map +1 -0
- package/dist/agent/tools/web.d.mts +63 -0
- package/dist/agent/tools/web.d.mts.map +1 -0
- package/dist/agent/tools/web.mjs +195 -0
- package/dist/agent/tools/web.mjs.map +1 -0
- package/dist/bus/events.d.mts +29 -0
- package/dist/bus/events.d.mts.map +1 -0
- package/dist/bus/events.mjs +30 -0
- package/dist/bus/events.mjs.map +1 -0
- package/dist/bus/queue.d.mts +38 -0
- package/dist/bus/queue.d.mts.map +1 -0
- package/dist/bus/queue.mjs +90 -0
- package/dist/bus/queue.mjs.map +1 -0
- package/dist/channels/base.d.mts +31 -0
- package/dist/channels/base.d.mts.map +1 -0
- package/dist/channels/base.mjs +49 -0
- package/dist/channels/base.mjs.map +1 -0
- package/dist/channels/manager.d.mts +30 -0
- package/dist/channels/manager.d.mts.map +1 -0
- package/dist/channels/manager.mjs +100 -0
- package/dist/channels/manager.mjs.map +1 -0
- package/dist/channels/telegram.d.mts +23 -0
- package/dist/channels/telegram.d.mts.map +1 -0
- package/dist/channels/telegram.mjs +161 -0
- package/dist/channels/telegram.mjs.map +1 -0
- package/dist/cli/index.d.mts +1 -0
- package/dist/cli/index.mjs +337 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/config/loader.d.mts +14 -0
- package/dist/config/loader.d.mts.map +1 -0
- package/dist/config/loader.mjs +40 -0
- package/dist/config/loader.mjs.map +1 -0
- package/dist/config/schema.d.mts +724 -0
- package/dist/config/schema.d.mts.map +1 -0
- package/dist/config/schema.mjs +123 -0
- package/dist/config/schema.mjs.map +1 -0
- package/dist/cron/service.d.mts +42 -0
- package/dist/cron/service.d.mts.map +1 -0
- package/dist/cron/service.mjs +256 -0
- package/dist/cron/service.mjs.map +1 -0
- package/dist/cron/types.d.mts +55 -0
- package/dist/cron/types.d.mts.map +1 -0
- package/dist/cron/types.mjs +30 -0
- package/dist/cron/types.mjs.map +1 -0
- package/dist/heartbeat/service.d.mts +29 -0
- package/dist/heartbeat/service.d.mts.map +1 -0
- package/dist/heartbeat/service.mjs +96 -0
- package/dist/heartbeat/service.mjs.map +1 -0
- package/dist/index.d.mts +23 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +23 -0
- package/dist/index.mjs.map +1 -0
- package/dist/providers/base.d.mts +65 -0
- package/dist/providers/base.d.mts.map +1 -0
- package/dist/providers/base.mjs +1 -0
- package/dist/providers/openai-provider.d.mts +27 -0
- package/dist/providers/openai-provider.d.mts.map +1 -0
- package/dist/providers/openai-provider.mjs +67 -0
- package/dist/providers/openai-provider.mjs.map +1 -0
- package/dist/providers/transcription.d.mts +14 -0
- package/dist/providers/transcription.d.mts.map +1 -0
- package/dist/providers/transcription.mjs +46 -0
- package/dist/providers/transcription.mjs.map +1 -0
- package/dist/session/manager.d.mts +56 -0
- package/dist/session/manager.d.mts.map +1 -0
- package/dist/session/manager.mjs +144 -0
- package/dist/session/manager.mjs.map +1 -0
- package/dist/utils/helpers.d.mts +26 -0
- package/dist/utils/helpers.d.mts.map +1 -0
- package/dist/utils/helpers.mjs +60 -0
- package/dist/utils/helpers.mjs.map +1 -0
- package/dist/utils/which.d.mts +6 -0
- package/dist/utils/which.d.mts.map +1 -0
- package/dist/utils/which.mjs +20 -0
- package/dist/utils/which.mjs.map +1 -0
- package/package.json +45 -0
- package/skills/cron/SKILL.md +40 -0
- package/skills/github/SKILL.md +48 -0
- package/skills/skill-creator/SKILL.md +73 -0
- package/skills/summarize/SKILL.md +47 -0
- package/skills/weather/SKILL.md +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# nanobot
|
|
2
|
+
|
|
3
|
+
Lightweight AI assistant -- TypeScript port of [HKUDS/nanobot](https://github.com/HKUDS/nanobot).
|
|
4
|
+
|
|
5
|
+
Zero native dependencies. Runs in Node.js or WebContainer.
|
|
6
|
+
|
|
7
|
+
## Quickstart
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
npm run build
|
|
12
|
+
node dist/cli/index.mjs onboard
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Add your API key to `~/.nanobot/config.json`, then:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
node dist/cli/index.mjs agent -m "Hello!"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Architecture
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
src/
|
|
25
|
+
agent/ Core agent loop, context builder, memory, skills, subagents
|
|
26
|
+
tools/ Tool implementations (filesystem, shell, web, message, spawn, cron)
|
|
27
|
+
bus/ Async message bus (inbound/outbound queues)
|
|
28
|
+
channels/ Chat platform integrations (Telegram via grammY)
|
|
29
|
+
cli/ Commander-based CLI
|
|
30
|
+
config/ Zod schemas + JSON config loader
|
|
31
|
+
cron/ Timer-based job scheduler
|
|
32
|
+
heartbeat/ Periodic HEARTBEAT.md checker
|
|
33
|
+
providers/ LLM providers (OpenAI SDK -- works with OpenRouter, Anthropic, etc.)
|
|
34
|
+
session/ JSONL conversation persistence
|
|
35
|
+
utils/ Path helpers, binary finder
|
|
36
|
+
skills/ Bundled skill definitions (cron, github, summarize, weather, skill-creator)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### How it works
|
|
40
|
+
|
|
41
|
+
1. Messages arrive via the **bus** (from Telegram, CLI, cron, or heartbeat)
|
|
42
|
+
2. The **agent loop** builds context (system prompt + history + memory + skills)
|
|
43
|
+
3. The **LLM provider** generates a response (with optional tool calls)
|
|
44
|
+
4. **Tools** execute (filesystem, shell, web search, etc.) and results feed back to the LLM
|
|
45
|
+
5. Final response is routed back through the bus to the originating channel
|
|
46
|
+
|
|
47
|
+
## CLI Commands
|
|
48
|
+
|
|
49
|
+
| Command | Description |
|
|
50
|
+
|---------|-------------|
|
|
51
|
+
| `nanobot onboard` | Initialize config and workspace |
|
|
52
|
+
| `nanobot gateway` | Start the full gateway (agent + channels + cron + heartbeat) |
|
|
53
|
+
| `nanobot agent -m "..."` | Send a single message to the agent |
|
|
54
|
+
| `nanobot agent` | Interactive chat mode |
|
|
55
|
+
| `nanobot channels status` | Show channel configuration |
|
|
56
|
+
| `nanobot cron list` | List scheduled jobs |
|
|
57
|
+
| `nanobot cron add` | Add a scheduled job |
|
|
58
|
+
| `nanobot cron remove <id>` | Remove a scheduled job |
|
|
59
|
+
| `nanobot status` | Show config and API key status |
|
|
60
|
+
|
|
61
|
+
## Scripts
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm run build # Build with tsdown
|
|
65
|
+
npm run dev # Run CLI via tsx (no build needed)
|
|
66
|
+
npm run typecheck # Type-check with tsc --noEmit
|
|
67
|
+
npm run start # Run built CLI
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Dependencies
|
|
71
|
+
|
|
72
|
+
| Package | Purpose |
|
|
73
|
+
|---------|---------|
|
|
74
|
+
| `openai` | LLM provider (OpenAI-compatible API) |
|
|
75
|
+
| `zod` | Config schema validation |
|
|
76
|
+
| `commander` | CLI framework |
|
|
77
|
+
| `grammy` | Telegram bot (grammY) |
|
|
78
|
+
| `cron-parser` | Cron expression parsing |
|
|
79
|
+
|
|
80
|
+
## Configuration
|
|
81
|
+
|
|
82
|
+
Config lives at `~/.nanobot/config.json`. Supports multiple providers:
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"providers": {
|
|
87
|
+
"openrouter": { "apiKey": "sk-or-..." },
|
|
88
|
+
"anthropic": { "apiKey": "sk-ant-..." },
|
|
89
|
+
"openai": { "apiKey": "sk-..." }
|
|
90
|
+
},
|
|
91
|
+
"agents": {
|
|
92
|
+
"defaults": {
|
|
93
|
+
"model": "anthropic/claude-sonnet-4-20250514",
|
|
94
|
+
"maxTokens": 8192,
|
|
95
|
+
"maxToolIterations": 20
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"channels": {
|
|
99
|
+
"telegram": {
|
|
100
|
+
"enabled": true,
|
|
101
|
+
"token": "123456:ABC..."
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Programmatic usage
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { AgentLoop, MessageBus, OpenAIProvider, loadConfig } from "nanobot";
|
|
111
|
+
|
|
112
|
+
const config = loadConfig();
|
|
113
|
+
const bus = new MessageBus();
|
|
114
|
+
const provider = new OpenAIProvider({
|
|
115
|
+
apiKey: "sk-...",
|
|
116
|
+
defaultModel: "anthropic/claude-sonnet-4-20250514",
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const agent = new AgentLoop({
|
|
120
|
+
bus,
|
|
121
|
+
provider,
|
|
122
|
+
workspace: "/path/to/workspace",
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const response = await agent.processDirect("What time is it?");
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Differences from Python nanobot
|
|
129
|
+
|
|
130
|
+
- WhatsApp and Feishu channels removed (Telegram only)
|
|
131
|
+
- `litellm` replaced with OpenAI SDK
|
|
132
|
+
- `pydantic` replaced with `zod`
|
|
133
|
+
- `typer`/`rich` replaced with `commander`
|
|
134
|
+
- `python-telegram-bot` replaced with `grammy`
|
|
135
|
+
- No native dependencies -- suitable for WebContainer deployment
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
MIT
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
//#region \0rolldown/runtime.js
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
9
|
+
var __exportAll = (all, no_symbols) => {
|
|
10
|
+
let target = {};
|
|
11
|
+
for (var name in all) {
|
|
12
|
+
__defProp(target, name, {
|
|
13
|
+
get: all[name],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
if (!no_symbols) {
|
|
18
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
19
|
+
}
|
|
20
|
+
return target;
|
|
21
|
+
};
|
|
22
|
+
var __copyProps = (to, from, except, desc) => {
|
|
23
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
24
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
25
|
+
key = keys[i];
|
|
26
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
27
|
+
__defProp(to, key, {
|
|
28
|
+
get: ((k) => from[k]).bind(null, key),
|
|
29
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return to;
|
|
35
|
+
};
|
|
36
|
+
var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
37
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
export { __esmMin, __exportAll, __require, __toCommonJS };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ChatMessage } from "../providers/base.mjs";
|
|
2
|
+
import { MemoryStore } from "./memory.mjs";
|
|
3
|
+
import { SkillsLoader } from "./skills.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/agent/context.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Builds the context (system prompt + messages) for the agent.
|
|
8
|
+
*/
|
|
9
|
+
declare class ContextBuilder {
|
|
10
|
+
private workspace;
|
|
11
|
+
readonly memory: MemoryStore;
|
|
12
|
+
readonly skills: SkillsLoader;
|
|
13
|
+
constructor(workspace: string);
|
|
14
|
+
/** Build the system prompt from bootstrap files, memory, and skills. */
|
|
15
|
+
buildSystemPrompt(): string;
|
|
16
|
+
private getIdentity;
|
|
17
|
+
private loadBootstrapFiles;
|
|
18
|
+
/** Build the complete message list for an LLM call. */
|
|
19
|
+
buildMessages(params: {
|
|
20
|
+
history: Array<{
|
|
21
|
+
role: string;
|
|
22
|
+
content: string;
|
|
23
|
+
}>;
|
|
24
|
+
currentMessage: string;
|
|
25
|
+
media?: string[];
|
|
26
|
+
channel?: string;
|
|
27
|
+
chatId?: string;
|
|
28
|
+
}): ChatMessage[];
|
|
29
|
+
private buildUserContent;
|
|
30
|
+
/** Add a tool result to the message list. */
|
|
31
|
+
addToolResult(messages: ChatMessage[], toolCallId: string, toolName: string, result: string): ChatMessage[];
|
|
32
|
+
/** Add an assistant message to the message list. */
|
|
33
|
+
addAssistantMessage(messages: ChatMessage[], content: string | null, toolCalls?: Array<{
|
|
34
|
+
id: string;
|
|
35
|
+
type: "function";
|
|
36
|
+
function: {
|
|
37
|
+
name: string;
|
|
38
|
+
arguments: string;
|
|
39
|
+
};
|
|
40
|
+
}>): ChatMessage[];
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { ContextBuilder };
|
|
44
|
+
//# sourceMappingURL=context.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.mts","names":[],"sources":["../../src/agent/context.ts"],"mappings":";;;;;;;AAiBA;cAAa,cAAA;EAAA,QACH,SAAA;EAAA,SACC,MAAA,EAAQ,WAAA;EAAA,SACR,MAAA,EAAQ,YAAA;cAEL,SAAA;EA8FR;EAvFJ,iBAAA,CAAA;EAAA,QAqCQ,WAAA;EAAA,QA+BA,kBAAA;EAuGM;EA1Fd,aAAA,CAAc,MAAA;IACZ,OAAA,EAAS,KAAA;MAAQ,IAAA;MAAc,OAAA;IAAA;IAC/B,cAAA;IACA,KAAA;IACA,OAAA;IACA,MAAA;EAAA,IACE,WAAA;EAAA,QA4BI,gBAAA;EA9EA;EAmHR,aAAA,CACE,QAAA,EAAU,WAAA,IACV,UAAA,UACA,QAAA,UACA,MAAA,WACC,WAAA;EA5EH;EAuFA,mBAAA,CACE,QAAA,EAAU,WAAA,IACV,OAAA,iBACA,SAAA,GAAY,KAAA;IACV,EAAA;IACA,IAAA;IACA,QAAA;MAAY,IAAA;MAAc,SAAA;IAAA;EAAA,KAE3B,WAAA;AAAA"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { MemoryStore } from "./memory.mjs";
|
|
2
|
+
import { SkillsLoader } from "./skills.mjs";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
//#region src/agent/context.ts
|
|
7
|
+
const BOOTSTRAP_FILES = [
|
|
8
|
+
"AGENTS.md",
|
|
9
|
+
"SOUL.md",
|
|
10
|
+
"USER.md",
|
|
11
|
+
"TOOLS.md",
|
|
12
|
+
"IDENTITY.md"
|
|
13
|
+
];
|
|
14
|
+
/**
|
|
15
|
+
* Builds the context (system prompt + messages) for the agent.
|
|
16
|
+
*/
|
|
17
|
+
var ContextBuilder = class {
|
|
18
|
+
workspace;
|
|
19
|
+
memory;
|
|
20
|
+
skills;
|
|
21
|
+
constructor(workspace) {
|
|
22
|
+
this.workspace = workspace;
|
|
23
|
+
this.memory = new MemoryStore(workspace);
|
|
24
|
+
this.skills = new SkillsLoader(workspace);
|
|
25
|
+
}
|
|
26
|
+
/** Build the system prompt from bootstrap files, memory, and skills. */
|
|
27
|
+
buildSystemPrompt() {
|
|
28
|
+
const parts = [];
|
|
29
|
+
parts.push(this.getIdentity());
|
|
30
|
+
const bootstrap = this.loadBootstrapFiles();
|
|
31
|
+
if (bootstrap) parts.push(bootstrap);
|
|
32
|
+
const memory = this.memory.getMemoryContext();
|
|
33
|
+
if (memory) parts.push(`# Memory\n\n${memory}`);
|
|
34
|
+
const alwaysSkills = this.skills.getAlwaysSkills();
|
|
35
|
+
if (alwaysSkills.length > 0) {
|
|
36
|
+
const alwaysContent = this.skills.loadSkillsForContext(alwaysSkills);
|
|
37
|
+
if (alwaysContent) parts.push(`# Active Skills\n\n${alwaysContent}`);
|
|
38
|
+
}
|
|
39
|
+
const skillsSummary = this.skills.buildSkillsSummary();
|
|
40
|
+
if (skillsSummary) parts.push("# Skills\n\nThe following skills extend your capabilities. To use a skill, read its SKILL.md file using the read_file tool.\nSkills with available=\"false\" need dependencies installed first.\n\n" + skillsSummary);
|
|
41
|
+
return parts.join("\n\n---\n\n");
|
|
42
|
+
}
|
|
43
|
+
getIdentity() {
|
|
44
|
+
const now = /* @__PURE__ */ new Date();
|
|
45
|
+
return `# nanobot
|
|
46
|
+
|
|
47
|
+
You are nanobot, a helpful AI assistant. You have access to tools that allow you to:
|
|
48
|
+
- Read, write, and edit files
|
|
49
|
+
- Execute shell commands
|
|
50
|
+
- Search the web and fetch web pages
|
|
51
|
+
- Send messages to users on chat channels
|
|
52
|
+
- Spawn subagents for complex background tasks
|
|
53
|
+
|
|
54
|
+
## Current Time
|
|
55
|
+
${now.toISOString().slice(0, 16).replace("T", " ")} (${now.toLocaleDateString("en-US", { weekday: "long" })})
|
|
56
|
+
|
|
57
|
+
## Workspace
|
|
58
|
+
Your workspace is at: ${this.workspace}
|
|
59
|
+
- Memory files: ${this.workspace}/memory/MEMORY.md
|
|
60
|
+
- Daily notes: ${this.workspace}/memory/YYYY-MM-DD.md
|
|
61
|
+
- Custom skills: ${this.workspace}/skills/{skill-name}/SKILL.md
|
|
62
|
+
|
|
63
|
+
IMPORTANT: When responding to direct questions or conversations, reply directly with your text response.
|
|
64
|
+
Only use the 'message' tool when you need to send a message to a specific chat channel.
|
|
65
|
+
For normal conversation, just respond with text - do not call the message tool.
|
|
66
|
+
|
|
67
|
+
Always be helpful, accurate, and concise. When using tools, explain what you're doing.
|
|
68
|
+
When remembering something, write to ${this.workspace}/memory/MEMORY.md`;
|
|
69
|
+
}
|
|
70
|
+
loadBootstrapFiles() {
|
|
71
|
+
const parts = [];
|
|
72
|
+
for (const filename of BOOTSTRAP_FILES) {
|
|
73
|
+
const filePath = join(this.workspace, filename);
|
|
74
|
+
if (existsSync(filePath)) {
|
|
75
|
+
const content = readFileSync(filePath, "utf-8");
|
|
76
|
+
parts.push(`## ${filename}\n\n${content}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return parts.join("\n\n");
|
|
80
|
+
}
|
|
81
|
+
/** Build the complete message list for an LLM call. */
|
|
82
|
+
buildMessages(params) {
|
|
83
|
+
const messages = [];
|
|
84
|
+
let systemPrompt = this.buildSystemPrompt();
|
|
85
|
+
if (params.channel && params.chatId) systemPrompt += `\n\n## Current Session\nChannel: ${params.channel}\nChat ID: ${params.chatId}`;
|
|
86
|
+
messages.push({
|
|
87
|
+
role: "system",
|
|
88
|
+
content: systemPrompt
|
|
89
|
+
});
|
|
90
|
+
for (const msg of params.history) messages.push({
|
|
91
|
+
role: msg.role,
|
|
92
|
+
content: msg.content
|
|
93
|
+
});
|
|
94
|
+
const userContent = this.buildUserContent(params.currentMessage, params.media);
|
|
95
|
+
messages.push({
|
|
96
|
+
role: "user",
|
|
97
|
+
content: userContent
|
|
98
|
+
});
|
|
99
|
+
return messages;
|
|
100
|
+
}
|
|
101
|
+
buildUserContent(text, media) {
|
|
102
|
+
if (!media || media.length === 0) return text;
|
|
103
|
+
const images = [];
|
|
104
|
+
for (const filePath of media) {
|
|
105
|
+
if (!existsSync(filePath)) continue;
|
|
106
|
+
try {
|
|
107
|
+
const data = readFileSync(filePath);
|
|
108
|
+
const mime = {
|
|
109
|
+
jpg: "image/jpeg",
|
|
110
|
+
jpeg: "image/jpeg",
|
|
111
|
+
png: "image/png",
|
|
112
|
+
gif: "image/gif",
|
|
113
|
+
webp: "image/webp"
|
|
114
|
+
}[filePath.split(".").pop()?.toLowerCase() ?? ""];
|
|
115
|
+
if (!mime) continue;
|
|
116
|
+
const b64 = data.toString("base64");
|
|
117
|
+
images.push({
|
|
118
|
+
type: "image_url",
|
|
119
|
+
image_url: { url: `data:${mime};base64,${b64}` }
|
|
120
|
+
});
|
|
121
|
+
} catch {}
|
|
122
|
+
}
|
|
123
|
+
if (images.length === 0) return text;
|
|
124
|
+
return [...images, {
|
|
125
|
+
type: "text",
|
|
126
|
+
text
|
|
127
|
+
}];
|
|
128
|
+
}
|
|
129
|
+
/** Add a tool result to the message list. */
|
|
130
|
+
addToolResult(messages, toolCallId, toolName, result) {
|
|
131
|
+
messages.push({
|
|
132
|
+
role: "tool",
|
|
133
|
+
tool_call_id: toolCallId,
|
|
134
|
+
name: toolName,
|
|
135
|
+
content: result
|
|
136
|
+
});
|
|
137
|
+
return messages;
|
|
138
|
+
}
|
|
139
|
+
/** Add an assistant message to the message list. */
|
|
140
|
+
addAssistantMessage(messages, content, toolCalls) {
|
|
141
|
+
const msg = {
|
|
142
|
+
role: "assistant",
|
|
143
|
+
content: content ?? ""
|
|
144
|
+
};
|
|
145
|
+
if (toolCalls) msg.tool_calls = toolCalls;
|
|
146
|
+
messages.push(msg);
|
|
147
|
+
return messages;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
//#endregion
|
|
152
|
+
export { ContextBuilder };
|
|
153
|
+
//# sourceMappingURL=context.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.mjs","names":[],"sources":["../../src/agent/context.ts"],"sourcesContent":["import { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ChatMessage, ContentPart } from \"../providers/base.js\";\nimport { MemoryStore } from \"./memory.js\";\nimport { SkillsLoader } from \"./skills.js\";\n\nconst BOOTSTRAP_FILES = [\n \"AGENTS.md\",\n \"SOUL.md\",\n \"USER.md\",\n \"TOOLS.md\",\n \"IDENTITY.md\",\n];\n\n/**\n * Builds the context (system prompt + messages) for the agent.\n */\nexport class ContextBuilder {\n private workspace: string;\n readonly memory: MemoryStore;\n readonly skills: SkillsLoader;\n\n constructor(workspace: string) {\n this.workspace = workspace;\n this.memory = new MemoryStore(workspace);\n this.skills = new SkillsLoader(workspace);\n }\n\n /** Build the system prompt from bootstrap files, memory, and skills. */\n buildSystemPrompt(): string {\n const parts: string[] = [];\n\n // Core identity\n parts.push(this.getIdentity());\n\n // Bootstrap files\n const bootstrap = this.loadBootstrapFiles();\n if (bootstrap) parts.push(bootstrap);\n\n // Memory context\n const memory = this.memory.getMemoryContext();\n if (memory) parts.push(`# Memory\\n\\n${memory}`);\n\n // Always-loaded skills\n const alwaysSkills = this.skills.getAlwaysSkills();\n if (alwaysSkills.length > 0) {\n const alwaysContent = this.skills.loadSkillsForContext(alwaysSkills);\n if (alwaysContent) {\n parts.push(`# Active Skills\\n\\n${alwaysContent}`);\n }\n }\n\n // Available skills summary\n const skillsSummary = this.skills.buildSkillsSummary();\n if (skillsSummary) {\n parts.push(\n `# Skills\\n\\n` +\n `The following skills extend your capabilities. To use a skill, read its SKILL.md file using the read_file tool.\\n` +\n `Skills with available=\"false\" need dependencies installed first.\\n\\n` +\n skillsSummary,\n );\n }\n\n return parts.join(\"\\n\\n---\\n\\n\");\n }\n\n private getIdentity(): string {\n const now = new Date();\n const dateStr = now.toISOString().slice(0, 16).replace(\"T\", \" \");\n const dayName = now.toLocaleDateString(\"en-US\", { weekday: \"long\" });\n\n return `# nanobot\n\nYou are nanobot, a helpful AI assistant. You have access to tools that allow you to:\n- Read, write, and edit files\n- Execute shell commands\n- Search the web and fetch web pages\n- Send messages to users on chat channels\n- Spawn subagents for complex background tasks\n\n## Current Time\n${dateStr} (${dayName})\n\n## Workspace\nYour workspace is at: ${this.workspace}\n- Memory files: ${this.workspace}/memory/MEMORY.md\n- Daily notes: ${this.workspace}/memory/YYYY-MM-DD.md\n- Custom skills: ${this.workspace}/skills/{skill-name}/SKILL.md\n\nIMPORTANT: When responding to direct questions or conversations, reply directly with your text response.\nOnly use the 'message' tool when you need to send a message to a specific chat channel.\nFor normal conversation, just respond with text - do not call the message tool.\n\nAlways be helpful, accurate, and concise. When using tools, explain what you're doing.\nWhen remembering something, write to ${this.workspace}/memory/MEMORY.md`;\n }\n\n private loadBootstrapFiles(): string {\n const parts: string[] = [];\n for (const filename of BOOTSTRAP_FILES) {\n const filePath = join(this.workspace, filename);\n if (existsSync(filePath)) {\n const content = readFileSync(filePath, \"utf-8\");\n parts.push(`## ${filename}\\n\\n${content}`);\n }\n }\n return parts.join(\"\\n\\n\");\n }\n\n /** Build the complete message list for an LLM call. */\n buildMessages(params: {\n history: Array<{ role: string; content: string }>;\n currentMessage: string;\n media?: string[];\n channel?: string;\n chatId?: string;\n }): ChatMessage[] {\n const messages: ChatMessage[] = [];\n\n // System prompt\n let systemPrompt = this.buildSystemPrompt();\n if (params.channel && params.chatId) {\n systemPrompt += `\\n\\n## Current Session\\nChannel: ${params.channel}\\nChat ID: ${params.chatId}`;\n }\n messages.push({ role: \"system\", content: systemPrompt });\n\n // History\n for (const msg of params.history) {\n messages.push({\n role: msg.role as \"user\" | \"assistant\",\n content: msg.content,\n });\n }\n\n // Current message (with optional image attachments)\n const userContent = this.buildUserContent(\n params.currentMessage,\n params.media,\n );\n messages.push({ role: \"user\", content: userContent });\n\n return messages;\n }\n\n private buildUserContent(\n text: string,\n media?: string[],\n ): string | ContentPart[] {\n if (!media || media.length === 0) return text;\n\n const images: ContentPart[] = [];\n for (const filePath of media) {\n if (!existsSync(filePath)) continue;\n try {\n const data = readFileSync(filePath);\n const ext = filePath.split(\".\").pop()?.toLowerCase() ?? \"\";\n const mimeMap: Record<string, string> = {\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n png: \"image/png\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n };\n const mime = mimeMap[ext];\n if (!mime) continue;\n\n const b64 = data.toString(\"base64\");\n images.push({\n type: \"image_url\",\n image_url: { url: `data:${mime};base64,${b64}` },\n });\n } catch {\n // skip unreadable files\n }\n }\n\n if (images.length === 0) return text;\n return [...images, { type: \"text\", text }];\n }\n\n /** Add a tool result to the message list. */\n addToolResult(\n messages: ChatMessage[],\n toolCallId: string,\n toolName: string,\n result: string,\n ): ChatMessage[] {\n messages.push({\n role: \"tool\",\n tool_call_id: toolCallId,\n name: toolName,\n content: result,\n });\n return messages;\n }\n\n /** Add an assistant message to the message list. */\n addAssistantMessage(\n messages: ChatMessage[],\n content: string | null,\n toolCalls?: Array<{\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n }>,\n ): ChatMessage[] {\n const msg: ChatMessage = {\n role: \"assistant\",\n content: content ?? \"\",\n };\n if (toolCalls) {\n msg.tool_calls = toolCalls;\n }\n messages.push(msg);\n return messages;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { LLMProvider } from "../providers/base.mjs";
|
|
2
|
+
import { ContextBuilder } from "./context.mjs";
|
|
3
|
+
import { MessageBus } from "../bus/queue.mjs";
|
|
4
|
+
import { ToolRegistry } from "./tools/registry.mjs";
|
|
5
|
+
import { ExecToolConfig } from "../config/schema.mjs";
|
|
6
|
+
import { SubagentManager } from "./subagent.mjs";
|
|
7
|
+
import { SessionManager } from "../session/manager.mjs";
|
|
8
|
+
import { CronService } from "../cron/service.mjs";
|
|
9
|
+
|
|
10
|
+
//#region src/agent/loop.d.ts
|
|
11
|
+
/**
|
|
12
|
+
* The agent loop: core processing engine.
|
|
13
|
+
*
|
|
14
|
+
* 1. Receives messages from the bus
|
|
15
|
+
* 2. Builds context with history, memory, skills
|
|
16
|
+
* 3. Calls the LLM
|
|
17
|
+
* 4. Executes tool calls
|
|
18
|
+
* 5. Sends responses back
|
|
19
|
+
*/
|
|
20
|
+
declare class AgentLoop {
|
|
21
|
+
private bus;
|
|
22
|
+
private provider;
|
|
23
|
+
private workspace;
|
|
24
|
+
private model;
|
|
25
|
+
private maxIterations;
|
|
26
|
+
readonly context: ContextBuilder;
|
|
27
|
+
readonly sessions: SessionManager;
|
|
28
|
+
readonly tools: ToolRegistry;
|
|
29
|
+
readonly subagents: SubagentManager;
|
|
30
|
+
private _running;
|
|
31
|
+
constructor(params: {
|
|
32
|
+
bus: MessageBus;
|
|
33
|
+
provider: LLMProvider;
|
|
34
|
+
workspace: string;
|
|
35
|
+
model?: string;
|
|
36
|
+
maxIterations?: number;
|
|
37
|
+
braveApiKey?: string;
|
|
38
|
+
execConfig?: ExecToolConfig;
|
|
39
|
+
cronService?: CronService;
|
|
40
|
+
});
|
|
41
|
+
private registerDefaultTools;
|
|
42
|
+
/** Run the agent loop, processing messages from the bus. */
|
|
43
|
+
run(): Promise<void>;
|
|
44
|
+
/** Stop the agent loop. */
|
|
45
|
+
stop(): void;
|
|
46
|
+
/** Process a single inbound message. */
|
|
47
|
+
private processMessage;
|
|
48
|
+
private processSystemMessage;
|
|
49
|
+
private runAgentLoop;
|
|
50
|
+
private updateToolContexts;
|
|
51
|
+
/** Process a message directly (for CLI or cron usage). */
|
|
52
|
+
processDirect(content: string, sessionKey?: string, channel?: string, chatId?: string): Promise<string>;
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { AgentLoop };
|
|
56
|
+
//# sourceMappingURL=loop.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop.d.mts","names":[],"sources":["../../src/agent/loop.ts"],"mappings":";;;;;;;;;;;;AAkCA;;;;;;;cAAa,SAAA;EAAA,QACH,GAAA;EAAA,QACA,QAAA;EAAA,QACA,SAAA;EAAA,QACA,KAAA;EAAA,QACA,aAAA;EAAA,SAEC,OAAA,EAAS,cAAA;EAAA,SACT,QAAA,EAAU,cAAA;EAAA,SACV,KAAA,EAAO,YAAA;EAAA,SACP,SAAA,EAAW,eAAA;EAAA,QAEZ,QAAA;cAEI,MAAA;IACV,GAAA,EAAK,UAAA;IACL,QAAA,EAAU,WAAA;IACV,SAAA;IACA,KAAA;IACA,aAAA;IACA,WAAA;IACA,UAAA,GAAa,cAAA;IACb,WAAA,GAAc,WAAA;EAAA;EAAA,QA4BR,oBAAA;;EAyCF,GAAA,CAAA,GAAO,OAAA;EA5EN;EA0GP,IAAA,CAAA;EAzGY;EAAA,QA+GE,cAAA;EAAA,QAwCA,oBAAA;EAAA,QA0CA,YAAA;EAAA,QA0CN,kBAAA;EAtON;EAwPI,aAAA,CACJ,OAAA,UACA,UAAA,WACA,OAAA,WACA,MAAA,YACC,OAAA;AAAA"}
|