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.
Files changed (170) hide show
  1. package/HEARTBEAT.md +48 -32
  2. package/README.md +426 -157
  3. package/dist/package.json +5 -2
  4. package/dist/src/agent/bootstrap-loader.js +138 -0
  5. package/dist/src/agent/bootstrap-loader.js.map +1 -0
  6. package/dist/src/agent/context-pruning.js +279 -0
  7. package/dist/src/agent/context-pruning.js.map +1 -0
  8. package/dist/src/agent/dynamic-personality.js +211 -0
  9. package/dist/src/agent/dynamic-personality.js.map +1 -0
  10. package/dist/src/agent/embedded-runner.js +79 -0
  11. package/dist/src/agent/embedded-runner.js.map +1 -0
  12. package/dist/src/agent/index.js +16 -0
  13. package/dist/src/agent/index.js.map +1 -0
  14. package/dist/src/agent/system-prompt.js +84 -0
  15. package/dist/src/agent/system-prompt.js.map +1 -0
  16. package/dist/src/agent/task-router.js +194 -0
  17. package/dist/src/agent/task-router.js.map +1 -0
  18. package/dist/src/agent/tools/index.js +2 -0
  19. package/dist/src/agent/tools/index.js.map +1 -0
  20. package/dist/src/agent/tools/spawn-agent.js +200 -0
  21. package/dist/src/agent/tools/spawn-agent.js.map +1 -0
  22. package/dist/src/agent/types/definitions.js +317 -0
  23. package/dist/src/agent/types/definitions.js.map +1 -0
  24. package/dist/src/agent/types/index.js +2 -0
  25. package/dist/src/agent/types/index.js.map +1 -0
  26. package/dist/src/agent/user-profile.js +300 -0
  27. package/dist/src/agent/user-profile.js.map +1 -0
  28. package/dist/src/agents/task-router.js +194 -0
  29. package/dist/src/agents/task-router.js.map +1 -0
  30. package/dist/src/agents/tools/index.js +2 -0
  31. package/dist/src/agents/tools/index.js.map +1 -0
  32. package/dist/src/agents/tools/spawn-agent.js +200 -0
  33. package/dist/src/agents/tools/spawn-agent.js.map +1 -0
  34. package/dist/src/agents/types/definitions.js +317 -0
  35. package/dist/src/agents/types/definitions.js.map +1 -0
  36. package/dist/src/agents/types/index.js +2 -0
  37. package/dist/src/agents/types/index.js.map +1 -0
  38. package/dist/src/auth/device-auth.js +202 -0
  39. package/dist/src/auth/device-auth.js.map +1 -0
  40. package/dist/src/auth/index.js +3 -0
  41. package/dist/src/auth/index.js.map +1 -0
  42. package/dist/src/channels/index.js +8 -0
  43. package/dist/src/channels/index.js.map +1 -0
  44. package/dist/src/channels/slack/webhook.js +128 -0
  45. package/dist/src/channels/slack/webhook.js.map +1 -0
  46. package/dist/src/channels/telegram/bot.js +223 -0
  47. package/dist/src/channels/telegram/bot.js.map +1 -0
  48. package/dist/src/channels/whatsapp/integration.js +325 -0
  49. package/dist/src/channels/whatsapp/integration.js.map +1 -0
  50. package/dist/src/cli/commands/check.js +69 -0
  51. package/dist/src/cli/commands/check.js.map +1 -0
  52. package/dist/src/cli/commands/embedded.js +36 -0
  53. package/dist/src/cli/commands/embedded.js.map +1 -0
  54. package/dist/src/cli/commands/gateway.js +127 -166
  55. package/dist/src/cli/commands/gateway.js.map +1 -1
  56. package/dist/src/cli/commands/migrate.js +24 -0
  57. package/dist/src/cli/commands/migrate.js.map +1 -0
  58. package/dist/src/cli/commands/tailscale.js +81 -0
  59. package/dist/src/cli/commands/tailscale.js.map +1 -0
  60. package/dist/src/cli/commands/telegram.js +111 -0
  61. package/dist/src/cli/commands/telegram.js.map +1 -0
  62. package/dist/src/cli/commands/tui.js +40 -22
  63. package/dist/src/cli/commands/tui.js.map +1 -1
  64. package/dist/src/cli/commands/whatsapp.js +116 -0
  65. package/dist/src/cli/commands/whatsapp.js.map +1 -0
  66. package/dist/src/cli/program-fixed.js +1 -0
  67. package/dist/src/cli/program-fixed.js.map +1 -0
  68. package/dist/src/cli/program.js +30 -29
  69. package/dist/src/cli/program.js.map +1 -1
  70. package/dist/src/config/index.js +2 -9
  71. package/dist/src/config/index.js.map +1 -1
  72. package/dist/src/config/manager.js +222 -0
  73. package/dist/src/config/manager.js.map +1 -0
  74. package/dist/src/documents/index.js +3 -0
  75. package/dist/src/documents/index.js.map +1 -0
  76. package/dist/src/documents/pdf.js +168 -0
  77. package/dist/src/documents/pdf.js.map +1 -0
  78. package/dist/src/extensions/core/agent-lifecycle-extension.js +323 -0
  79. package/dist/src/extensions/core/agent-lifecycle-extension.js.map +1 -0
  80. package/dist/src/extensions/core/agent-orchestrator-extension.js +642 -0
  81. package/dist/src/extensions/core/agent-orchestrator-extension.js.map +1 -0
  82. package/dist/src/extensions/core/agent-workspace-extension.js +174 -0
  83. package/dist/src/extensions/core/agent-workspace-extension.js.map +1 -0
  84. package/dist/src/extensions/core/autonomous-executor-extension.js +145 -0
  85. package/dist/src/extensions/core/autonomous-executor-extension.js.map +1 -0
  86. package/dist/src/extensions/core/autonomous-subagent-extension.js +333 -0
  87. package/dist/src/extensions/core/autonomous-subagent-extension.js.map +1 -0
  88. package/dist/src/extensions/core/deprecated/agent-lifecycle-extension.js +323 -0
  89. package/dist/src/extensions/core/deprecated/agent-lifecycle-extension.js.map +1 -0
  90. package/dist/src/extensions/core/deprecated/autonomous-subagent-extension.js +333 -0
  91. package/dist/src/extensions/core/deprecated/autonomous-subagent-extension.js.map +1 -0
  92. package/dist/src/extensions/core/deprecated/subagent-extension.js +647 -0
  93. package/dist/src/extensions/core/deprecated/subagent-extension.js.map +1 -0
  94. package/dist/src/extensions/core/mode-manager-extension.js +11 -0
  95. package/dist/src/extensions/core/mode-manager-extension.js.map +1 -1
  96. package/dist/src/extensions/core/persona-loader-extension.js +61 -129
  97. package/dist/src/extensions/core/persona-loader-extension.js.map +1 -1
  98. package/dist/src/extensions/core/websearch-enhanced-extension.js +705 -0
  99. package/dist/src/extensions/core/websearch-enhanced-extension.js.map +1 -0
  100. package/dist/src/extensions/core/websearch-v2-extension.js +303 -0
  101. package/dist/src/extensions/core/websearch-v2-extension.js.map +1 -0
  102. package/dist/src/gateway/client.js +318 -0
  103. package/dist/src/gateway/client.js.map +1 -0
  104. package/dist/src/gateway/discord-bot.js +185 -0
  105. package/dist/src/gateway/discord-bot.js.map +1 -0
  106. package/dist/src/gateway/index.js +10 -350
  107. package/dist/src/gateway/index.js.map +1 -1
  108. package/dist/src/gateway/server.js +325 -0
  109. package/dist/src/gateway/server.js.map +1 -0
  110. package/dist/src/gateway/websocket-server.js +142 -0
  111. package/dist/src/gateway/websocket-server.js.map +1 -0
  112. package/dist/src/heartbeat/checkin.js +185 -0
  113. package/dist/src/heartbeat/checkin.js.map +1 -0
  114. package/dist/src/heartbeat/index.js +5 -0
  115. package/dist/src/heartbeat/index.js.map +1 -0
  116. package/dist/src/heartbeat/notifications.js +216 -0
  117. package/dist/src/heartbeat/notifications.js.map +1 -0
  118. package/dist/src/heartbeat/scheduler.js +284 -0
  119. package/dist/src/heartbeat/scheduler.js.map +1 -0
  120. package/dist/src/index.js +21 -3
  121. package/dist/src/index.js.map +1 -1
  122. package/dist/src/infra/index.js +3 -0
  123. package/dist/src/infra/index.js.map +1 -0
  124. package/dist/src/infra/tailscale.js +163 -0
  125. package/dist/src/infra/tailscale.js.map +1 -0
  126. package/dist/src/media/audio.js +130 -0
  127. package/dist/src/media/audio.js.map +1 -0
  128. package/dist/src/media/index.js +4 -0
  129. package/dist/src/media/index.js.map +1 -0
  130. package/dist/src/media/vision.js +131 -0
  131. package/dist/src/media/vision.js.map +1 -0
  132. package/dist/src/migration/openclaw.js +498 -0
  133. package/dist/src/migration/openclaw.js.map +1 -0
  134. package/dist/src/mode/auto-detector.js +211 -0
  135. package/dist/src/mode/auto-detector.js.map +1 -0
  136. package/dist/src/mode/index.js +3 -0
  137. package/dist/src/mode/index.js.map +1 -0
  138. package/dist/src/mode/natural-switcher.js +123 -0
  139. package/dist/src/mode/natural-switcher.js.map +1 -0
  140. package/dist/src/sandbox/docker-runner.js +228 -0
  141. package/dist/src/sandbox/docker-runner.js.map +1 -0
  142. package/dist/src/sandbox/index.js +3 -0
  143. package/dist/src/sandbox/index.js.map +1 -0
  144. package/dist/src/skills/builtin/api-worker.js +88 -0
  145. package/dist/src/skills/builtin/api-worker.js.map +1 -0
  146. package/dist/src/skills/builtin/duplicate-detector.js +469 -0
  147. package/dist/src/skills/builtin/duplicate-detector.js.map +1 -0
  148. package/dist/src/skills/builtin/nextjs-worker.js +55 -0
  149. package/dist/src/skills/builtin/nextjs-worker.js.map +1 -0
  150. package/dist/src/skills/builtin/real-workers.js +166 -0
  151. package/dist/src/skills/builtin/real-workers.js.map +1 -0
  152. package/dist/src/skills/builtin/sql-worker.js +61 -0
  153. package/dist/src/skills/builtin/sql-worker.js.map +1 -0
  154. package/dist/src/skills/builtin/test-worker.js +79 -0
  155. package/dist/src/skills/builtin/test-worker.js.map +1 -0
  156. package/dist/src/skills/builtin/web-research.js +77 -0
  157. package/dist/src/skills/builtin/web-research.js.map +1 -0
  158. package/dist/src/skills/framework.js +250 -0
  159. package/dist/src/skills/framework.js.map +1 -0
  160. package/dist/src/skills/index.js +12 -10
  161. package/dist/src/skills/index.js.map +1 -1
  162. package/dist/src/streaming/block-streamer.js +172 -0
  163. package/dist/src/streaming/block-streamer.js.map +1 -0
  164. package/dist/src/streaming/index.js +2 -0
  165. package/dist/src/streaming/index.js.map +1 -0
  166. package/dist/src/workspace/index.js +2 -0
  167. package/dist/src/workspace/index.js.map +1 -0
  168. package/dist/src/workspace/manager.js +181 -0
  169. package/dist/src/workspace/manager.js.map +1 -0
  170. package/package.json +5 -2
package/dist/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "0xkobold",
3
- "version": "0.1.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