0xkobold 0.1.0 → 0.3.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/HEARTBEAT.md +48 -32
- package/README.md +426 -157
- package/dist/package.json +5 -2
- package/dist/src/agent/bootstrap-loader.js +138 -0
- package/dist/src/agent/bootstrap-loader.js.map +1 -0
- package/dist/src/agent/context-pruning.js +279 -0
- package/dist/src/agent/context-pruning.js.map +1 -0
- package/dist/src/agent/dynamic-personality.js +211 -0
- package/dist/src/agent/dynamic-personality.js.map +1 -0
- package/dist/src/agent/embedded-runner.js +79 -0
- package/dist/src/agent/embedded-runner.js.map +1 -0
- package/dist/src/agent/index.js +16 -0
- package/dist/src/agent/index.js.map +1 -0
- package/dist/src/agent/system-prompt.js +84 -0
- package/dist/src/agent/system-prompt.js.map +1 -0
- package/dist/src/agent/task-router.js +194 -0
- package/dist/src/agent/task-router.js.map +1 -0
- package/dist/src/agent/tools/index.js +2 -0
- package/dist/src/agent/tools/index.js.map +1 -0
- package/dist/src/agent/tools/spawn-agent.js +200 -0
- package/dist/src/agent/tools/spawn-agent.js.map +1 -0
- package/dist/src/agent/types/definitions.js +317 -0
- package/dist/src/agent/types/definitions.js.map +1 -0
- package/dist/src/agent/types/index.js +2 -0
- package/dist/src/agent/types/index.js.map +1 -0
- package/dist/src/agent/user-profile.js +300 -0
- package/dist/src/agent/user-profile.js.map +1 -0
- package/dist/src/agents/task-router.js +194 -0
- package/dist/src/agents/task-router.js.map +1 -0
- package/dist/src/agents/tools/index.js +2 -0
- package/dist/src/agents/tools/index.js.map +1 -0
- package/dist/src/agents/tools/spawn-agent.js +200 -0
- package/dist/src/agents/tools/spawn-agent.js.map +1 -0
- package/dist/src/agents/types/definitions.js +317 -0
- package/dist/src/agents/types/definitions.js.map +1 -0
- package/dist/src/agents/types/index.js +2 -0
- package/dist/src/agents/types/index.js.map +1 -0
- package/dist/src/auth/device-auth.js +202 -0
- package/dist/src/auth/device-auth.js.map +1 -0
- package/dist/src/auth/index.js +3 -0
- package/dist/src/auth/index.js.map +1 -0
- package/dist/src/channels/index.js +8 -0
- package/dist/src/channels/index.js.map +1 -0
- package/dist/src/channels/slack/webhook.js +128 -0
- package/dist/src/channels/slack/webhook.js.map +1 -0
- package/dist/src/channels/telegram/bot.js +223 -0
- package/dist/src/channels/telegram/bot.js.map +1 -0
- package/dist/src/channels/whatsapp/integration.js +325 -0
- package/dist/src/channels/whatsapp/integration.js.map +1 -0
- package/dist/src/cli/commands/check.js +69 -0
- package/dist/src/cli/commands/check.js.map +1 -0
- package/dist/src/cli/commands/embedded.js +36 -0
- package/dist/src/cli/commands/embedded.js.map +1 -0
- package/dist/src/cli/commands/gateway.js +127 -166
- package/dist/src/cli/commands/gateway.js.map +1 -1
- package/dist/src/cli/commands/migrate.js +24 -0
- package/dist/src/cli/commands/migrate.js.map +1 -0
- package/dist/src/cli/commands/tailscale.js +81 -0
- package/dist/src/cli/commands/tailscale.js.map +1 -0
- package/dist/src/cli/commands/telegram.js +111 -0
- package/dist/src/cli/commands/telegram.js.map +1 -0
- package/dist/src/cli/commands/tui.js +40 -22
- package/dist/src/cli/commands/tui.js.map +1 -1
- package/dist/src/cli/commands/whatsapp.js +116 -0
- package/dist/src/cli/commands/whatsapp.js.map +1 -0
- package/dist/src/cli/program-fixed.js +1 -0
- package/dist/src/cli/program-fixed.js.map +1 -0
- package/dist/src/cli/program.js +30 -29
- package/dist/src/cli/program.js.map +1 -1
- package/dist/src/config/index.js +2 -9
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/config/manager.js +222 -0
- package/dist/src/config/manager.js.map +1 -0
- package/dist/src/documents/index.js +3 -0
- package/dist/src/documents/index.js.map +1 -0
- package/dist/src/documents/pdf.js +168 -0
- package/dist/src/documents/pdf.js.map +1 -0
- package/dist/src/extensions/core/agent-lifecycle-extension.js +323 -0
- package/dist/src/extensions/core/agent-lifecycle-extension.js.map +1 -0
- package/dist/src/extensions/core/agent-orchestrator-extension.js +642 -0
- package/dist/src/extensions/core/agent-orchestrator-extension.js.map +1 -0
- package/dist/src/extensions/core/agent-workspace-extension.js +174 -0
- package/dist/src/extensions/core/agent-workspace-extension.js.map +1 -0
- package/dist/src/extensions/core/autonomous-executor-extension.js +145 -0
- package/dist/src/extensions/core/autonomous-executor-extension.js.map +1 -0
- package/dist/src/extensions/core/autonomous-subagent-extension.js +333 -0
- package/dist/src/extensions/core/autonomous-subagent-extension.js.map +1 -0
- package/dist/src/extensions/core/deprecated/agent-lifecycle-extension.js +323 -0
- package/dist/src/extensions/core/deprecated/agent-lifecycle-extension.js.map +1 -0
- package/dist/src/extensions/core/deprecated/autonomous-subagent-extension.js +333 -0
- package/dist/src/extensions/core/deprecated/autonomous-subagent-extension.js.map +1 -0
- package/dist/src/extensions/core/deprecated/subagent-extension.js +647 -0
- package/dist/src/extensions/core/deprecated/subagent-extension.js.map +1 -0
- package/dist/src/extensions/core/mode-manager-extension.js +11 -0
- package/dist/src/extensions/core/mode-manager-extension.js.map +1 -1
- package/dist/src/extensions/core/persona-loader-extension.js +61 -129
- package/dist/src/extensions/core/persona-loader-extension.js.map +1 -1
- package/dist/src/extensions/core/websearch-enhanced-extension.js +705 -0
- package/dist/src/extensions/core/websearch-enhanced-extension.js.map +1 -0
- package/dist/src/extensions/core/websearch-v2-extension.js +303 -0
- package/dist/src/extensions/core/websearch-v2-extension.js.map +1 -0
- package/dist/src/gateway/client.js +318 -0
- package/dist/src/gateway/client.js.map +1 -0
- package/dist/src/gateway/discord-bot.js +185 -0
- package/dist/src/gateway/discord-bot.js.map +1 -0
- package/dist/src/gateway/index.js +10 -350
- package/dist/src/gateway/index.js.map +1 -1
- package/dist/src/gateway/server.js +325 -0
- package/dist/src/gateway/server.js.map +1 -0
- package/dist/src/gateway/websocket-server.js +142 -0
- package/dist/src/gateway/websocket-server.js.map +1 -0
- package/dist/src/heartbeat/checkin.js +185 -0
- package/dist/src/heartbeat/checkin.js.map +1 -0
- package/dist/src/heartbeat/index.js +5 -0
- package/dist/src/heartbeat/index.js.map +1 -0
- package/dist/src/heartbeat/notifications.js +216 -0
- package/dist/src/heartbeat/notifications.js.map +1 -0
- package/dist/src/heartbeat/scheduler.js +284 -0
- package/dist/src/heartbeat/scheduler.js.map +1 -0
- package/dist/src/index.js +21 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/infra/index.js +3 -0
- package/dist/src/infra/index.js.map +1 -0
- package/dist/src/infra/tailscale.js +163 -0
- package/dist/src/infra/tailscale.js.map +1 -0
- package/dist/src/media/audio.js +130 -0
- package/dist/src/media/audio.js.map +1 -0
- package/dist/src/media/index.js +4 -0
- package/dist/src/media/index.js.map +1 -0
- package/dist/src/media/vision.js +131 -0
- package/dist/src/media/vision.js.map +1 -0
- package/dist/src/migration/openclaw.js +498 -0
- package/dist/src/migration/openclaw.js.map +1 -0
- package/dist/src/mode/auto-detector.js +211 -0
- package/dist/src/mode/auto-detector.js.map +1 -0
- package/dist/src/mode/index.js +3 -0
- package/dist/src/mode/index.js.map +1 -0
- package/dist/src/mode/natural-switcher.js +123 -0
- package/dist/src/mode/natural-switcher.js.map +1 -0
- package/dist/src/sandbox/docker-runner.js +228 -0
- package/dist/src/sandbox/docker-runner.js.map +1 -0
- package/dist/src/sandbox/index.js +3 -0
- package/dist/src/sandbox/index.js.map +1 -0
- package/dist/src/skills/builtin/api-worker.js +88 -0
- package/dist/src/skills/builtin/api-worker.js.map +1 -0
- package/dist/src/skills/builtin/duplicate-detector.js +469 -0
- package/dist/src/skills/builtin/duplicate-detector.js.map +1 -0
- package/dist/src/skills/builtin/nextjs-worker.js +55 -0
- package/dist/src/skills/builtin/nextjs-worker.js.map +1 -0
- package/dist/src/skills/builtin/real-workers.js +166 -0
- package/dist/src/skills/builtin/real-workers.js.map +1 -0
- package/dist/src/skills/builtin/sql-worker.js +61 -0
- package/dist/src/skills/builtin/sql-worker.js.map +1 -0
- package/dist/src/skills/builtin/test-worker.js +79 -0
- package/dist/src/skills/builtin/test-worker.js.map +1 -0
- package/dist/src/skills/builtin/web-research.js +77 -0
- package/dist/src/skills/builtin/web-research.js.map +1 -0
- package/dist/src/skills/framework.js +250 -0
- package/dist/src/skills/framework.js.map +1 -0
- package/dist/src/skills/index.js +12 -10
- package/dist/src/skills/index.js.map +1 -1
- package/dist/src/streaming/block-streamer.js +172 -0
- package/dist/src/streaming/block-streamer.js.map +1 -0
- package/dist/src/streaming/index.js +2 -0
- package/dist/src/streaming/index.js.map +1 -0
- package/dist/src/workspace/index.js +2 -0
- package/dist/src/workspace/index.js.map +1 -0
- package/dist/src/workspace/manager.js +181 -0
- package/dist/src/workspace/manager.js.map +1 -0
- package/package.json +5 -2
package/dist/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "0xkobold",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Your digital familiar - a personal AI assistant that learns and evolves",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Your digital familiar - a personal AI assistant that learns and evolves (v0.3.0 - Multi-Channel, Security, Media Support)",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"types": "dist/src/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
@@ -38,12 +38,15 @@
|
|
|
38
38
|
"@mariozechner/pi-agent-core": "0.57.1",
|
|
39
39
|
"@mariozechner/pi-coding-agent": "0.57.1",
|
|
40
40
|
"@mariozechner/pi-tui": "0.57.1",
|
|
41
|
+
"@whiskeysockets/baileys": "^7.0.0-rc.9",
|
|
41
42
|
"ajv": "^8.18.0",
|
|
42
43
|
"blessed": "^0.1.81",
|
|
43
44
|
"commander": "^11.1.0",
|
|
44
45
|
"discord.js": "^14.25.1",
|
|
45
46
|
"glob": "^10.5.0",
|
|
46
47
|
"json5": "^2.2.3",
|
|
48
|
+
"node-telegram-bot-api": "^0.67.0",
|
|
49
|
+
"playwright": "^1.58.2",
|
|
47
50
|
"react": "^19.2.4",
|
|
48
51
|
"sharp": "^0.33.5",
|
|
49
52
|
"zod": "^3.25.76"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootstrap Loader - v0.2.0
|
|
3
|
+
*
|
|
4
|
+
* Loads bootstrap personality files (SOUL.md, IDENTITY.md, etc.)
|
|
5
|
+
* and prepares them for system prompt injection.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from "node:fs/promises";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import { existsSync } from "node:fs";
|
|
10
|
+
export const DEFAULT_BOOTSTRAP_FILES = [
|
|
11
|
+
"SOUL.md",
|
|
12
|
+
"IDENTITY.md",
|
|
13
|
+
"USER.md",
|
|
14
|
+
"AGENTS.md",
|
|
15
|
+
"TOOLS.md",
|
|
16
|
+
"HEARTBEAT.md",
|
|
17
|
+
"MEMORY.md",
|
|
18
|
+
];
|
|
19
|
+
const DEFAULT_CONFIG = {
|
|
20
|
+
maxFileSize: 20000,
|
|
21
|
+
maxTotalSize: 150000,
|
|
22
|
+
files: DEFAULT_BOOTSTRAP_FILES,
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Load bootstrap files from workspace
|
|
26
|
+
*/
|
|
27
|
+
export async function loadBootstrapFiles(workspaceDir, config = {}) {
|
|
28
|
+
const mergedConfig = { ...DEFAULT_CONFIG, ...config };
|
|
29
|
+
const results = [];
|
|
30
|
+
let totalSize = 0;
|
|
31
|
+
for (const filename of mergedConfig.files) {
|
|
32
|
+
const filePath = path.join(workspaceDir, filename);
|
|
33
|
+
if (!existsSync(filePath)) {
|
|
34
|
+
results.push({
|
|
35
|
+
name: filename,
|
|
36
|
+
content: ``,
|
|
37
|
+
exists: false,
|
|
38
|
+
size: 0,
|
|
39
|
+
});
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
44
|
+
// Truncate if too large
|
|
45
|
+
let finalContent = content;
|
|
46
|
+
if (content.length > mergedConfig.maxFileSize) {
|
|
47
|
+
finalContent = content.slice(0, mergedConfig.maxFileSize) +
|
|
48
|
+
"\n\n[... truncated, read file for full content]";
|
|
49
|
+
}
|
|
50
|
+
results.push({
|
|
51
|
+
name: filename,
|
|
52
|
+
content: finalContent,
|
|
53
|
+
exists: true,
|
|
54
|
+
size: finalContent.length,
|
|
55
|
+
});
|
|
56
|
+
totalSize += finalContent.length;
|
|
57
|
+
// Check total limit
|
|
58
|
+
if (totalSize > mergedConfig.maxTotalSize) {
|
|
59
|
+
console.warn(`[Bootstrap] Total size exceeded ${mergedConfig.maxTotalSize}, stopping`);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
results.push({
|
|
65
|
+
name: filename,
|
|
66
|
+
content: ``,
|
|
67
|
+
exists: false,
|
|
68
|
+
size: 0,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return results;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Format bootstrap files for system prompt
|
|
76
|
+
*/
|
|
77
|
+
export function formatBootstrapForPrompt(files) {
|
|
78
|
+
const existingFiles = files.filter(f => f.exists);
|
|
79
|
+
if (existingFiles.length === 0) {
|
|
80
|
+
return "<!-- No bootstrap context files present -->";
|
|
81
|
+
}
|
|
82
|
+
const sections = existingFiles.map(file => `<!-- ${file.name} (${file.size} chars) -->\n${file.content}`);
|
|
83
|
+
return [
|
|
84
|
+
"<!-- Bootstrap Context (auto-loaded on session start) -->",
|
|
85
|
+
...sections,
|
|
86
|
+
].join("\n\n---\n\n");
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Create default SOUL.md if missing
|
|
90
|
+
*/
|
|
91
|
+
export async function ensureDefaultBootstrap(workspaceDir) {
|
|
92
|
+
const soulPath = path.join(workspaceDir, "SOUL.md");
|
|
93
|
+
const identityPath = path.join(workspaceDir, "IDENTITY.md");
|
|
94
|
+
if (!existsSync(soulPath)) {
|
|
95
|
+
const defaultSoul = `# SOUL - Agent Personality
|
|
96
|
+
|
|
97
|
+
## Identity
|
|
98
|
+
**Name:** 0xKobold
|
|
99
|
+
**Role:** Your digital familiar
|
|
100
|
+
**Vibe:** Friendly, curious, and helpful
|
|
101
|
+
|
|
102
|
+
## Tone
|
|
103
|
+
- Style: Clear and conversational
|
|
104
|
+
- Formality: Casual but respectful
|
|
105
|
+
- Humor: Light and appropriate
|
|
106
|
+
|
|
107
|
+
## Core Values
|
|
108
|
+
- **Helpfulness:** Always assist the user
|
|
109
|
+
- **Honesty:** Be truthful about capabilities
|
|
110
|
+
- **Learning:** Improve from every interaction
|
|
111
|
+
|
|
112
|
+
## Guidelines
|
|
113
|
+
- Ask clarifying questions when needed
|
|
114
|
+
- Provide examples when helpful
|
|
115
|
+
- Admit when unsure or need more info
|
|
116
|
+
`;
|
|
117
|
+
await fs.writeFile(soulPath, defaultSoul);
|
|
118
|
+
console.log("[Bootstrap] Created default SOUL.md");
|
|
119
|
+
}
|
|
120
|
+
if (!existsSync(identityPath)) {
|
|
121
|
+
const defaultIdentity = `# IDENTITY
|
|
122
|
+
|
|
123
|
+
**Name:** 0xKobold
|
|
124
|
+
**Emoji:** 🐉
|
|
125
|
+
**Tagline:** Your digital familiar
|
|
126
|
+
**Role:** AI companion and coding assistant
|
|
127
|
+
|
|
128
|
+
## Tone
|
|
129
|
+
- Conversational and warm
|
|
130
|
+
- Uses emojis occasionally 🎉
|
|
131
|
+
- Celebrates wins with enthusiasm
|
|
132
|
+
- Gentle with mistakes
|
|
133
|
+
`;
|
|
134
|
+
await fs.writeFile(identityPath, defaultIdentity);
|
|
135
|
+
console.log("[Bootstrap] Created default IDENTITY.md");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=bootstrap-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap-loader.js","sourceRoot":"","sources":["../../../src/agent/bootstrap-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Pruning System - v0.2.0
|
|
3
|
+
*
|
|
4
|
+
* Automatic context management with token budget awareness.
|
|
5
|
+
* Part of Phase 1.3: Context Pruning
|
|
6
|
+
*/
|
|
7
|
+
export const DEFAULT_BUDGET = {
|
|
8
|
+
maxTokens: 128000, // Default for high-context models
|
|
9
|
+
warningThreshold: 80,
|
|
10
|
+
criticalThreshold: 95,
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Token estimation (rough approximation)
|
|
14
|
+
*/
|
|
15
|
+
export function estimateTokens(text) {
|
|
16
|
+
// ~4 chars per token on average
|
|
17
|
+
return Math.ceil(text.length / 4);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Calculate current token usage
|
|
21
|
+
*/
|
|
22
|
+
export function calculateTokenUsage(items) {
|
|
23
|
+
return items.reduce((sum, item) => sum + item.tokens, 0);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Check if pruning is needed
|
|
27
|
+
*/
|
|
28
|
+
export function shouldPrune(items, budget = DEFAULT_BUDGET) {
|
|
29
|
+
const usage = calculateTokenUsage(items);
|
|
30
|
+
const percentage = (usage / budget.maxTokens) * 100;
|
|
31
|
+
return {
|
|
32
|
+
needed: percentage >= budget.warningThreshold,
|
|
33
|
+
usage,
|
|
34
|
+
percentage,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Strategy: Remove oldest non-essential items first
|
|
39
|
+
*/
|
|
40
|
+
export const oldestFirstStrategy = {
|
|
41
|
+
name: 'oldest-first',
|
|
42
|
+
description: 'Remove oldest non-essential context items',
|
|
43
|
+
prune(items, budget) {
|
|
44
|
+
const essential = items.filter(i => i.isEssential);
|
|
45
|
+
const nonEssential = items.filter(i => !i.isEssential);
|
|
46
|
+
// Sort by timestamp (newest first, so oldest get pruned first)
|
|
47
|
+
const sorted = [...nonEssential].sort((a, b) => b.timestamp - a.timestamp);
|
|
48
|
+
const kept = [...essential];
|
|
49
|
+
const pruned = [];
|
|
50
|
+
const buffer = budget.maxTokens * (budget.warningThreshold / 100);
|
|
51
|
+
// Keep newest non-essential items until budget reached
|
|
52
|
+
for (const item of sorted) {
|
|
53
|
+
const projected = calculateTokenUsage(kept) + item.tokens;
|
|
54
|
+
if (projected <= buffer) {
|
|
55
|
+
kept.push(item);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
pruned.push(item);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
kept: kept.sort((a, b) => a.timestamp - b.timestamp),
|
|
63
|
+
pruned,
|
|
64
|
+
totalTokens: calculateTokenUsage(kept),
|
|
65
|
+
savedTokens: calculateTokenUsage(pruned),
|
|
66
|
+
strategy: this.name,
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Strategy: Remove lowest importance items first
|
|
72
|
+
*/
|
|
73
|
+
export const importanceStrategy = {
|
|
74
|
+
name: 'importance-based',
|
|
75
|
+
description: 'Remove least important context items first',
|
|
76
|
+
prune(items, budget) {
|
|
77
|
+
// Sort by importance (highest first, so lowest importance gets pruned first)
|
|
78
|
+
const sorted = [...items].sort((a, b) => b.importance - a.importance);
|
|
79
|
+
const kept = [];
|
|
80
|
+
const pruned = [];
|
|
81
|
+
const target = budget.maxTokens * (budget.warningThreshold / 100);
|
|
82
|
+
for (const item of sorted) {
|
|
83
|
+
if (item.isEssential) {
|
|
84
|
+
kept.push(item);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const projected = calculateTokenUsage(kept) + item.tokens;
|
|
88
|
+
if (projected <= target) {
|
|
89
|
+
kept.push(item);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
pruned.push(item);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
kept: kept.sort((a, b) => a.timestamp - b.timestamp),
|
|
97
|
+
pruned,
|
|
98
|
+
totalTokens: calculateTokenUsage(kept),
|
|
99
|
+
savedTokens: calculateTokenUsage(pruned),
|
|
100
|
+
strategy: this.name,
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Strategy: Smart compaction (summarize old content)
|
|
106
|
+
*/
|
|
107
|
+
export const smartCompactionStrategy = {
|
|
108
|
+
name: 'smart-compaction',
|
|
109
|
+
description: 'Summarize old content instead of removing',
|
|
110
|
+
prune(items, budget) {
|
|
111
|
+
const now = Date.now();
|
|
112
|
+
const fiveMinutes = 5 * 60 * 1000;
|
|
113
|
+
const essential = items.filter(i => i.isEssential || i.type === 'system');
|
|
114
|
+
const recent = items.filter(i => !i.isEssential &&
|
|
115
|
+
i.type !== 'system' &&
|
|
116
|
+
(now - i.timestamp) < fiveMinutes);
|
|
117
|
+
const old = items.filter(i => !i.isEssential &&
|
|
118
|
+
i.type !== 'system' &&
|
|
119
|
+
(now - i.timestamp) >= fiveMinutes);
|
|
120
|
+
const kept = [...essential, ...recent];
|
|
121
|
+
const summarized = [];
|
|
122
|
+
// Summarize old items
|
|
123
|
+
if (old.length > 0) {
|
|
124
|
+
const summaryContent = summarizeContext(old);
|
|
125
|
+
const summary = {
|
|
126
|
+
id: `summary-${Date.now()}`,
|
|
127
|
+
type: 'system',
|
|
128
|
+
content: summaryContent,
|
|
129
|
+
tokens: estimateTokens(summaryContent),
|
|
130
|
+
importance: 50,
|
|
131
|
+
timestamp: Date.now(),
|
|
132
|
+
};
|
|
133
|
+
summarized.push(summary);
|
|
134
|
+
}
|
|
135
|
+
// Check if we need further pruning
|
|
136
|
+
const totalKept = [...kept, ...summarized];
|
|
137
|
+
const usage = calculateTokenUsage(totalKept);
|
|
138
|
+
if (usage > budget.maxTokens * 0.8) {
|
|
139
|
+
// Fall back to oldest-first for remaining
|
|
140
|
+
const extraPruning = oldestFirstStrategy.prune(totalKept, budget);
|
|
141
|
+
return {
|
|
142
|
+
...extraPruning,
|
|
143
|
+
kept: [...extraPruning.kept, ...summarized],
|
|
144
|
+
pruned: [...extraPruning.pruned, ...old],
|
|
145
|
+
strategy: this.name,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
kept: [...kept, ...summarized],
|
|
150
|
+
pruned: old,
|
|
151
|
+
totalTokens: calculateTokenUsage([...kept, ...summarized]),
|
|
152
|
+
savedTokens: calculateTokenUsage(old) - calculateTokenUsage(summarized),
|
|
153
|
+
strategy: this.name,
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Summarize old context items
|
|
159
|
+
*/
|
|
160
|
+
function summarizeContext(items) {
|
|
161
|
+
const byType = {};
|
|
162
|
+
for (const item of items) {
|
|
163
|
+
if (!byType[item.type])
|
|
164
|
+
byType[item.type] = [];
|
|
165
|
+
byType[item.type].push(item);
|
|
166
|
+
}
|
|
167
|
+
const parts = ['[Context Summary: Previous conversation condensed]'];
|
|
168
|
+
if (byType['tool']) {
|
|
169
|
+
const toolCount = byType['tool'].length;
|
|
170
|
+
parts.push(`- Executed ${toolCount} tool calls`);
|
|
171
|
+
}
|
|
172
|
+
if (byType['user']) {
|
|
173
|
+
const userCount = byType['user'].length;
|
|
174
|
+
parts.push(`- ${userCount} user messages`);
|
|
175
|
+
}
|
|
176
|
+
if (byType['assistant']) {
|
|
177
|
+
const assistantCount = byType['assistant'].length;
|
|
178
|
+
parts.push(`- ${assistantCount} assistant responses`);
|
|
179
|
+
}
|
|
180
|
+
parts.push('Details available in full context if needed.');
|
|
181
|
+
return parts.join('\n');
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Prune context with specified strategy
|
|
185
|
+
*/
|
|
186
|
+
export function pruneContext(items, strategy, budget = DEFAULT_BUDGET) {
|
|
187
|
+
return strategy.prune(items, budget);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get recommended strategy based on context state
|
|
191
|
+
*/
|
|
192
|
+
export function recommendStrategy(items, budget = DEFAULT_BUDGET) {
|
|
193
|
+
const { percentage } = shouldPrune(items, budget);
|
|
194
|
+
if (percentage >= budget.criticalThreshold) {
|
|
195
|
+
return smartestPruningStrategy;
|
|
196
|
+
}
|
|
197
|
+
if (percentage >= budget.warningThreshold) {
|
|
198
|
+
return smartCompactionStrategy;
|
|
199
|
+
}
|
|
200
|
+
return importanceStrategy;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Auto-prune if needed
|
|
204
|
+
*/
|
|
205
|
+
export function autoPrune(items, budget = DEFAULT_BUDGET) {
|
|
206
|
+
const { needed, usage, percentage } = shouldPrune(items, budget);
|
|
207
|
+
if (!needed) {
|
|
208
|
+
return {
|
|
209
|
+
result: null,
|
|
210
|
+
wasPruned: false,
|
|
211
|
+
metrics: { before: usage, after: usage, percentage },
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
const strategy = recommendStrategy(items, budget);
|
|
215
|
+
const result = pruneContext(items, strategy, budget);
|
|
216
|
+
return {
|
|
217
|
+
result,
|
|
218
|
+
wasPruned: true,
|
|
219
|
+
metrics: {
|
|
220
|
+
before: usage,
|
|
221
|
+
after: result.totalTokens,
|
|
222
|
+
percentage: (result.totalTokens / budget.maxTokens) * 100,
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Advanced strategy: Smartest pruning (combines multiple techniques)
|
|
228
|
+
*/
|
|
229
|
+
export const smartestPruningStrategy = {
|
|
230
|
+
name: 'smartest',
|
|
231
|
+
description: 'Most aggressive pruning with multi-pass approach',
|
|
232
|
+
prune(items, budget) {
|
|
233
|
+
// First pass: remove duplicates and near-duplicates
|
|
234
|
+
const unique = removeDuplicates(items);
|
|
235
|
+
// Second pass: compact old items
|
|
236
|
+
const compacted = smartCompactionStrategy.prune(unique, budget);
|
|
237
|
+
// Third pass: importance-based if still over budget
|
|
238
|
+
if (compacted.totalTokens > budget.maxTokens * 0.95) {
|
|
239
|
+
return importanceStrategy.prune(compacted.kept, budget);
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
...compacted,
|
|
243
|
+
strategy: this.name,
|
|
244
|
+
};
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* Remove duplicate content
|
|
249
|
+
*/
|
|
250
|
+
function removeDuplicates(items) {
|
|
251
|
+
const seen = new Set();
|
|
252
|
+
const unique = [];
|
|
253
|
+
for (const item of items) {
|
|
254
|
+
// Simple hash of content
|
|
255
|
+
const hash = item.content.slice(0, 100);
|
|
256
|
+
if (!seen.has(hash) || item.isEssential || item.type === 'system') {
|
|
257
|
+
seen.add(hash);
|
|
258
|
+
unique.push(item);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return unique;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Context budget presets
|
|
265
|
+
*/
|
|
266
|
+
export const BUDGET_PRESETS = {
|
|
267
|
+
conservative: {
|
|
268
|
+
maxTokens: 32000,
|
|
269
|
+
warningThreshold: 70,
|
|
270
|
+
criticalThreshold: 85,
|
|
271
|
+
},
|
|
272
|
+
balanced: DEFAULT_BUDGET,
|
|
273
|
+
aggressive: {
|
|
274
|
+
maxTokens: 200000,
|
|
275
|
+
warningThreshold: 90,
|
|
276
|
+
criticalThreshold: 98,
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
//# sourceMappingURL=context-pruning.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-pruning.js","sourceRoot":"","sources":["../../../src/agent/context-pruning.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Personality System - v0.2.0
|
|
3
|
+
*
|
|
4
|
+
* Adapts the agent's persona based on interactions and user feedback.
|
|
5
|
+
* Part of the Persona System (Phase 1.2).
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from "node:fs/promises";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import { existsSync } from "node:fs";
|
|
10
|
+
const DEFAULT_TRAITS = {
|
|
11
|
+
warmth: 70,
|
|
12
|
+
formality: 40,
|
|
13
|
+
verbosity: 60,
|
|
14
|
+
humor: 30,
|
|
15
|
+
enthusiasm: 50,
|
|
16
|
+
};
|
|
17
|
+
const PERSONALITY_FILE = ".personality-state.json";
|
|
18
|
+
/**
|
|
19
|
+
* Load personality state
|
|
20
|
+
*/
|
|
21
|
+
export async function loadPersonalityState(workspaceDir) {
|
|
22
|
+
const filePath = path.join(workspaceDir, PERSONALITY_FILE);
|
|
23
|
+
if (!existsSync(filePath)) {
|
|
24
|
+
return {
|
|
25
|
+
traits: { ...DEFAULT_TRAITS },
|
|
26
|
+
baseTraits: { ...DEFAULT_TRAITS },
|
|
27
|
+
interactions: [],
|
|
28
|
+
adaptationLevel: 0,
|
|
29
|
+
lastAdapted: new Date().toISOString(),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
34
|
+
return JSON.parse(content);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return {
|
|
38
|
+
traits: { ...DEFAULT_TRAITS },
|
|
39
|
+
baseTraits: { ...DEFAULT_TRAITS },
|
|
40
|
+
interactions: [],
|
|
41
|
+
adaptationLevel: 0,
|
|
42
|
+
lastAdapted: new Date().toISOString(),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Save personality state
|
|
48
|
+
*/
|
|
49
|
+
export async function savePersonalityState(workspaceDir, state) {
|
|
50
|
+
const filePath = path.join(workspaceDir, PERSONALITY_FILE);
|
|
51
|
+
await fs.writeFile(filePath, JSON.stringify(state, null, 2), "utf-8");
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Record a user interaction
|
|
55
|
+
*/
|
|
56
|
+
export async function recordInteraction(workspaceDir, interaction) {
|
|
57
|
+
const state = await loadPersonalityState(workspaceDir);
|
|
58
|
+
state.interactions.push({
|
|
59
|
+
...interaction,
|
|
60
|
+
timestamp: new Date().toISOString(),
|
|
61
|
+
});
|
|
62
|
+
// Keep only last 100 interactions
|
|
63
|
+
if (state.interactions.length > 100) {
|
|
64
|
+
state.interactions = state.interactions.slice(-100);
|
|
65
|
+
}
|
|
66
|
+
await savePersonalityState(workspaceDir, state);
|
|
67
|
+
// Trigger adaptation periodically
|
|
68
|
+
if (state.interactions.length % 10 === 0) {
|
|
69
|
+
await adaptPersonality(workspaceDir);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Adapt personality based on recorded interactions
|
|
74
|
+
*/
|
|
75
|
+
export async function adaptPersonality(workspaceDir) {
|
|
76
|
+
const state = await loadPersonalityState(workspaceDir);
|
|
77
|
+
// Calculate new trait values based on interactions
|
|
78
|
+
const recent = state.interactions.slice(-20); // Last 20 interactions
|
|
79
|
+
if (recent.length < 5) {
|
|
80
|
+
return state; // Not enough data
|
|
81
|
+
}
|
|
82
|
+
// Analyze sentiment patterns
|
|
83
|
+
const positiveCount = recent.filter((i) => i.sentiment > 0).length;
|
|
84
|
+
const negativeCount = recent.filter((i) => i.sentiment < 0).length;
|
|
85
|
+
// Adjust traits based on patterns
|
|
86
|
+
if (negativeCount > positiveCount * 1.5) {
|
|
87
|
+
// More negative feedback - reduce enthusiasm, increase formality
|
|
88
|
+
state.traits.enthusiasm = clamp(state.traits.enthusiasm - 5);
|
|
89
|
+
state.traits.formality = clamp(state.traits.formality + 5);
|
|
90
|
+
}
|
|
91
|
+
else if (positiveCount > negativeCount * 1.5) {
|
|
92
|
+
// More positive feedback - can be more warm and enthusiastic
|
|
93
|
+
state.traits.warmth = clamp(state.traits.warmth + 3);
|
|
94
|
+
state.traits.enthusiasm = clamp(state.traits.enthusiasm + 3);
|
|
95
|
+
}
|
|
96
|
+
// Check for specific trait feedback
|
|
97
|
+
const traitFeedback = recent.filter((i) => i.trait);
|
|
98
|
+
for (const feedback of traitFeedback) {
|
|
99
|
+
if (feedback.trait && feedback.sentiment !== 0) {
|
|
100
|
+
const adjustment = feedback.sentiment * 5;
|
|
101
|
+
state.traits[feedback.trait] = clamp(state.traits[feedback.trait] + adjustment);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Calculate adaptation level
|
|
105
|
+
let totalDiff = 0;
|
|
106
|
+
for (const key of Object.keys(state.baseTraits)) {
|
|
107
|
+
totalDiff += Math.abs(state.traits[key] - state.baseTraits[key]);
|
|
108
|
+
}
|
|
109
|
+
state.adaptationLevel = clamp(totalDiff / 5); // Average diff across 5 traits
|
|
110
|
+
state.lastAdapted = new Date().toISOString();
|
|
111
|
+
await savePersonalityState(workspaceDir, state);
|
|
112
|
+
console.log(`[Personality] Adapted traits - Warmth: ${state.traits.warmth}, ` +
|
|
113
|
+
`Formality: ${state.traits.formality}, Enthusiasm: ${state.traits.enthusiasm}`);
|
|
114
|
+
return state;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get current persona based on adapted traits
|
|
118
|
+
*/
|
|
119
|
+
export async function getDynamicPersona(workspaceDir) {
|
|
120
|
+
const state = await loadPersonalityState(workspaceDir);
|
|
121
|
+
const parts = [];
|
|
122
|
+
// Tone description
|
|
123
|
+
const tones = [];
|
|
124
|
+
if (state.traits.warmth > 60)
|
|
125
|
+
tones.push("warm");
|
|
126
|
+
if (state.traits.warmth < 40)
|
|
127
|
+
tones.push("professional");
|
|
128
|
+
if (state.traits.formality > 60)
|
|
129
|
+
tones.push("formal");
|
|
130
|
+
if (state.traits.formality < 40)
|
|
131
|
+
tones.push("casual");
|
|
132
|
+
if (state.traits.enthusiasm > 60)
|
|
133
|
+
tones.push("enthusiastic");
|
|
134
|
+
if (state.traits.verbosity > 70)
|
|
135
|
+
tones.push("detailed");
|
|
136
|
+
if (state.traits.verbosity < 40)
|
|
137
|
+
tones.push("concise");
|
|
138
|
+
parts.push(`Tone: ${tones.join(", ") || "balanced"}`);
|
|
139
|
+
// Behavior instructions
|
|
140
|
+
if (state.traits.humor > 50) {
|
|
141
|
+
parts.push("Use light humor when appropriate");
|
|
142
|
+
}
|
|
143
|
+
if (state.traits.verbosity > 70) {
|
|
144
|
+
parts.push("Provide detailed explanations with examples");
|
|
145
|
+
}
|
|
146
|
+
else if (state.traits.verbosity < 40) {
|
|
147
|
+
parts.push("Be concise and direct");
|
|
148
|
+
}
|
|
149
|
+
if (state.traits.formality > 60) {
|
|
150
|
+
parts.push("Use formal language and structure");
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
parts.push("Use conversational language");
|
|
154
|
+
}
|
|
155
|
+
if (state.traits.enthusiasm > 60) {
|
|
156
|
+
parts.push("Show enthusiasm and encouragement");
|
|
157
|
+
}
|
|
158
|
+
// Adaptation note
|
|
159
|
+
if (state.adaptationLevel > 20) {
|
|
160
|
+
parts.push(`\n[Adapted ${state.adaptationLevel.toFixed(0)}% from base personality based on interactions]`);
|
|
161
|
+
}
|
|
162
|
+
return parts.join("\n");
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get personality insights
|
|
166
|
+
*/
|
|
167
|
+
export async function getPersonalityInsights(workspaceDir) {
|
|
168
|
+
const state = await loadPersonalityState(workspaceDir);
|
|
169
|
+
// Find top likes/dislikes from feedback
|
|
170
|
+
const likes = state.interactions
|
|
171
|
+
.filter((i) => i.type === "reaction" && i.sentiment > 0)
|
|
172
|
+
.map((i) => i.value);
|
|
173
|
+
const dislikes = state.interactions
|
|
174
|
+
.filter((i) => i.type === "reaction" && i.sentiment < 0)
|
|
175
|
+
.map((i) => i.value);
|
|
176
|
+
// Generate recommendation
|
|
177
|
+
let recommendation = "";
|
|
178
|
+
if (state.adaptationLevel < 10) {
|
|
179
|
+
recommendation = "Personality is close to base. Continue interacting to learn preferences.";
|
|
180
|
+
}
|
|
181
|
+
else if (state.traits.verbosity > 70 && state.traits.formality > 60) {
|
|
182
|
+
recommendation = "Current style is quite formal and detailed. Consider if user prefers more casual interactions.";
|
|
183
|
+
}
|
|
184
|
+
else if (state.traits.enthusiasm < 30) {
|
|
185
|
+
recommendation = "Low enthusiasm detected. User may prefer straightforward, minimal responses.";
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
recommendation = `Current personality adapted ${state.adaptationLevel.toFixed(0)}% from base. Matches observed preferences.`;
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
traits: state.traits,
|
|
192
|
+
adaptationLevel: state.adaptationLevel,
|
|
193
|
+
topLikes: [...new Set(likes)].slice(0, 5),
|
|
194
|
+
topDislikes: [...new Set(dislikes)].slice(0, 5),
|
|
195
|
+
recommendation,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Reset personality to base
|
|
200
|
+
*/
|
|
201
|
+
export async function resetPersonality(workspaceDir) {
|
|
202
|
+
const state = await loadPersonalityState(workspaceDir);
|
|
203
|
+
state.traits = { ...state.baseTraits };
|
|
204
|
+
state.adaptationLevel = 0;
|
|
205
|
+
state.lastAdapted = new Date().toISOString();
|
|
206
|
+
await savePersonalityState(workspaceDir, state);
|
|
207
|
+
}
|
|
208
|
+
function clamp(value, min = 0, max = 100) {
|
|
209
|
+
return Math.max(min, Math.min(max, value));
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=dynamic-personality.js.map
|