@aman_asmuei/aman-agent 0.17.0 → 0.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +526 -151
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -232,7 +232,7 @@ function toAnthropicMessages(messages) {
|
|
|
232
232
|
function createAnthropicClient(apiKey, model) {
|
|
233
233
|
const client = new Anthropic({ apiKey });
|
|
234
234
|
return {
|
|
235
|
-
async chat(systemPrompt, messages, onChunk, tools) {
|
|
235
|
+
async chat(systemPrompt, messages, onChunk, tools, options) {
|
|
236
236
|
const anthropicMessages = toAnthropicMessages(messages);
|
|
237
237
|
const hasTools = tools && tools.length > 0;
|
|
238
238
|
try {
|
|
@@ -242,7 +242,7 @@ function createAnthropicClient(apiKey, model) {
|
|
|
242
242
|
let currentBlockIndex = -1;
|
|
243
243
|
const createParams = {
|
|
244
244
|
model,
|
|
245
|
-
max_tokens: 8192,
|
|
245
|
+
max_tokens: options?.maxOutputTokens ?? 8192,
|
|
246
246
|
system: systemPrompt,
|
|
247
247
|
messages: anthropicMessages,
|
|
248
248
|
stream: true
|
|
@@ -330,7 +330,9 @@ function createAnthropicClient(apiKey, model) {
|
|
|
330
330
|
|
|
331
331
|
// src/llm/openai.ts
|
|
332
332
|
import OpenAI from "openai";
|
|
333
|
-
|
|
333
|
+
|
|
334
|
+
// src/llm/openai-compat.ts
|
|
335
|
+
function toOpenAICompatibleMessages(systemPrompt, messages) {
|
|
334
336
|
const result = [
|
|
335
337
|
{ role: "system", content: systemPrompt }
|
|
336
338
|
];
|
|
@@ -398,18 +400,20 @@ function toOpenAIMessages(systemPrompt, messages) {
|
|
|
398
400
|
}
|
|
399
401
|
return result;
|
|
400
402
|
}
|
|
403
|
+
|
|
404
|
+
// src/llm/openai.ts
|
|
401
405
|
function createOpenAIClient(apiKey, model) {
|
|
402
406
|
const client = new OpenAI({ apiKey });
|
|
403
407
|
return {
|
|
404
|
-
async chat(systemPrompt, messages, onChunk, tools) {
|
|
405
|
-
const openaiMessages =
|
|
408
|
+
async chat(systemPrompt, messages, onChunk, tools, options) {
|
|
409
|
+
const openaiMessages = toOpenAICompatibleMessages(systemPrompt, messages);
|
|
406
410
|
const hasTools = tools && tools.length > 0;
|
|
407
411
|
try {
|
|
408
412
|
let fullText = "";
|
|
409
413
|
const toolCallAccumulators = /* @__PURE__ */ new Map();
|
|
410
414
|
const createParams = {
|
|
411
415
|
model,
|
|
412
|
-
max_tokens: 8192,
|
|
416
|
+
max_tokens: options?.maxOutputTokens ?? 8192,
|
|
413
417
|
messages: openaiMessages,
|
|
414
418
|
stream: true
|
|
415
419
|
};
|
|
@@ -495,74 +499,6 @@ function createOpenAIClient(apiKey, model) {
|
|
|
495
499
|
|
|
496
500
|
// src/llm/ollama.ts
|
|
497
501
|
import OpenAI2 from "openai";
|
|
498
|
-
function toOllamaMessages(systemPrompt, messages) {
|
|
499
|
-
const result = [
|
|
500
|
-
{ role: "system", content: systemPrompt }
|
|
501
|
-
];
|
|
502
|
-
for (const m of messages) {
|
|
503
|
-
if (typeof m.content === "string") {
|
|
504
|
-
result.push({
|
|
505
|
-
role: m.role,
|
|
506
|
-
content: m.content
|
|
507
|
-
});
|
|
508
|
-
} else if (m.role === "assistant") {
|
|
509
|
-
const textParts = m.content.filter((b) => b.type === "text");
|
|
510
|
-
const toolUseParts = m.content.filter((b) => b.type === "tool_use");
|
|
511
|
-
const text2 = textParts.map((b) => "text" in b ? b.text : "").join("");
|
|
512
|
-
if (toolUseParts.length > 0) {
|
|
513
|
-
result.push({
|
|
514
|
-
role: "assistant",
|
|
515
|
-
content: text2 || null,
|
|
516
|
-
tool_calls: toolUseParts.map((b) => ({
|
|
517
|
-
id: "id" in b ? b.id : "",
|
|
518
|
-
type: "function",
|
|
519
|
-
function: {
|
|
520
|
-
name: "name" in b ? b.name : "",
|
|
521
|
-
arguments: JSON.stringify("input" in b ? b.input : {})
|
|
522
|
-
}
|
|
523
|
-
}))
|
|
524
|
-
});
|
|
525
|
-
} else {
|
|
526
|
-
result.push({ role: "assistant", content: text2 });
|
|
527
|
-
}
|
|
528
|
-
} else if (m.role === "user") {
|
|
529
|
-
const toolResults = m.content.filter((b) => b.type === "tool_result");
|
|
530
|
-
if (toolResults.length > 0) {
|
|
531
|
-
for (const tr of toolResults) {
|
|
532
|
-
if (tr.type === "tool_result") {
|
|
533
|
-
result.push({
|
|
534
|
-
role: "tool",
|
|
535
|
-
tool_call_id: tr.tool_use_id,
|
|
536
|
-
content: tr.content
|
|
537
|
-
});
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
} else {
|
|
541
|
-
const hasImages = m.content.some((b) => b.type === "image");
|
|
542
|
-
if (hasImages) {
|
|
543
|
-
const parts = [];
|
|
544
|
-
for (const b of m.content) {
|
|
545
|
-
if (b.type === "text") {
|
|
546
|
-
parts.push({ type: "text", text: b.text });
|
|
547
|
-
} else if (b.type === "image") {
|
|
548
|
-
parts.push({
|
|
549
|
-
type: "image_url",
|
|
550
|
-
image_url: {
|
|
551
|
-
url: `data:${b.source.media_type};base64,${b.source.data}`
|
|
552
|
-
}
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
result.push({ role: "user", content: parts });
|
|
557
|
-
} else {
|
|
558
|
-
const text2 = m.content.map((b) => "text" in b ? b.text : "").join("");
|
|
559
|
-
result.push({ role: "user", content: text2 });
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
return result;
|
|
565
|
-
}
|
|
566
502
|
function createOllamaClient(model, baseURL) {
|
|
567
503
|
const client = new OpenAI2({
|
|
568
504
|
baseURL: baseURL || "http://localhost:11434/v1",
|
|
@@ -570,15 +506,15 @@ function createOllamaClient(model, baseURL) {
|
|
|
570
506
|
// Ollama doesn't require a real key
|
|
571
507
|
});
|
|
572
508
|
return {
|
|
573
|
-
async chat(systemPrompt, messages, onChunk, tools) {
|
|
574
|
-
const ollamaMessages =
|
|
509
|
+
async chat(systemPrompt, messages, onChunk, tools, options) {
|
|
510
|
+
const ollamaMessages = toOpenAICompatibleMessages(systemPrompt, messages);
|
|
575
511
|
const hasTools = tools && tools.length > 0;
|
|
576
512
|
try {
|
|
577
513
|
let fullText = "";
|
|
578
514
|
const toolCallAccumulators = /* @__PURE__ */ new Map();
|
|
579
515
|
const createParams = {
|
|
580
516
|
model,
|
|
581
|
-
max_tokens:
|
|
517
|
+
max_tokens: options?.maxOutputTokens ?? 4096,
|
|
582
518
|
messages: ollamaMessages,
|
|
583
519
|
stream: true
|
|
584
520
|
};
|
|
@@ -730,12 +666,18 @@ async function withRetry(fn, options) {
|
|
|
730
666
|
}
|
|
731
667
|
|
|
732
668
|
// src/mcp/client.ts
|
|
669
|
+
var TOOL_CALL_TIMEOUT_MS = 3e4;
|
|
733
670
|
var McpManager = class {
|
|
734
671
|
connections = [];
|
|
735
672
|
tools = [];
|
|
736
|
-
async connect(name, command, args) {
|
|
673
|
+
async connect(name, command, args, env) {
|
|
737
674
|
try {
|
|
738
|
-
const transport = new StdioClientTransport({
|
|
675
|
+
const transport = new StdioClientTransport({
|
|
676
|
+
command,
|
|
677
|
+
args,
|
|
678
|
+
stderr: "pipe",
|
|
679
|
+
env: env ? env : void 0
|
|
680
|
+
});
|
|
739
681
|
const client = new Client({
|
|
740
682
|
name: `aman-agent-${name}`,
|
|
741
683
|
version: "0.1.0"
|
|
@@ -746,9 +688,16 @@ var McpManager = class {
|
|
|
746
688
|
log.debug("mcp", `[${name} stderr] ${chunk.toString().trim()}`);
|
|
747
689
|
});
|
|
748
690
|
}
|
|
749
|
-
this.connections.push({ name, client, transport });
|
|
691
|
+
this.connections.push({ name, client, transport, connectParams: { command, args, env } });
|
|
750
692
|
const toolsResult = await client.listTools();
|
|
751
693
|
for (const tool of toolsResult.tools) {
|
|
694
|
+
const existing = this.tools.find((t) => t.name === tool.name);
|
|
695
|
+
if (existing) {
|
|
696
|
+
log.warn(
|
|
697
|
+
"mcp",
|
|
698
|
+
`Warning: tool "${tool.name}" from server "${name}" shadows existing tool from "${existing.serverName}"`
|
|
699
|
+
);
|
|
700
|
+
}
|
|
752
701
|
this.tools.push({
|
|
753
702
|
name: tool.name,
|
|
754
703
|
description: tool.description || "",
|
|
@@ -771,7 +720,15 @@ var McpManager = class {
|
|
|
771
720
|
if (!conn) return `Error: server ${tool.serverName} not connected`;
|
|
772
721
|
try {
|
|
773
722
|
const result = await withRetry(
|
|
774
|
-
() =>
|
|
723
|
+
() => Promise.race([
|
|
724
|
+
conn.client.callTool({ name: toolName, arguments: args }),
|
|
725
|
+
new Promise(
|
|
726
|
+
(_, reject) => setTimeout(
|
|
727
|
+
() => reject(new Error(`Tool ${toolName} timed out after 30s`)),
|
|
728
|
+
TOOL_CALL_TIMEOUT_MS
|
|
729
|
+
)
|
|
730
|
+
)
|
|
731
|
+
]),
|
|
775
732
|
{ maxAttempts: 2, baseDelay: 500, retryable: (err) => err.message.includes("ETIMEDOUT") || err.message.includes("timeout") }
|
|
776
733
|
);
|
|
777
734
|
if (result.content && Array.isArray(result.content)) {
|
|
@@ -782,6 +739,23 @@ var McpManager = class {
|
|
|
782
739
|
return `Error calling ${toolName}: ${error instanceof Error ? error.message : String(error)}`;
|
|
783
740
|
}
|
|
784
741
|
}
|
|
742
|
+
async reconnect(name) {
|
|
743
|
+
const connIndex = this.connections.findIndex((c) => c.name === name);
|
|
744
|
+
if (connIndex === -1) {
|
|
745
|
+
log.error("mcp", `Cannot reconnect: no connection found for "${name}"`);
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
const conn = this.connections[connIndex];
|
|
749
|
+
const { command, args, env } = conn.connectParams;
|
|
750
|
+
try {
|
|
751
|
+
await conn.client.close();
|
|
752
|
+
} catch (err) {
|
|
753
|
+
log.debug("mcp", `Error closing old connection for ${name}`, err);
|
|
754
|
+
}
|
|
755
|
+
this.connections.splice(connIndex, 1);
|
|
756
|
+
this.tools = this.tools.filter((t) => t.serverName !== name);
|
|
757
|
+
await this.connect(name, command, args, env);
|
|
758
|
+
}
|
|
785
759
|
async disconnect() {
|
|
786
760
|
for (const conn of this.connections) {
|
|
787
761
|
try {
|
|
@@ -878,7 +852,10 @@ import {
|
|
|
878
852
|
consolidateMemories,
|
|
879
853
|
cosineSimilarity,
|
|
880
854
|
preloadEmbeddings,
|
|
881
|
-
buildVectorIndex
|
|
855
|
+
buildVectorIndex,
|
|
856
|
+
recallMemories,
|
|
857
|
+
generateEmbedding,
|
|
858
|
+
getVectorIndex
|
|
882
859
|
} from "@aman_asmuei/amem-core";
|
|
883
860
|
import path5 from "path";
|
|
884
861
|
import os5 from "os";
|
|
@@ -890,7 +867,27 @@ async function initMemory(project) {
|
|
|
890
867
|
const amemDir = process.env.AMEM_DIR ?? path5.join(os5.homedir(), ".amem");
|
|
891
868
|
if (!fs5.existsSync(amemDir)) fs5.mkdirSync(amemDir, { recursive: true });
|
|
892
869
|
const dbPath = process.env.AMEM_DB ?? path5.join(amemDir, "memory.db");
|
|
893
|
-
|
|
870
|
+
try {
|
|
871
|
+
db = createDatabase(dbPath);
|
|
872
|
+
} catch (err) {
|
|
873
|
+
const backupPath = `${dbPath}.corrupt.${Date.now()}`;
|
|
874
|
+
try {
|
|
875
|
+
if (fs5.existsSync(dbPath)) {
|
|
876
|
+
fs5.renameSync(dbPath, backupPath);
|
|
877
|
+
if (fs5.existsSync(`${dbPath}-wal`)) fs5.unlinkSync(`${dbPath}-wal`);
|
|
878
|
+
if (fs5.existsSync(`${dbPath}-shm`)) fs5.unlinkSync(`${dbPath}-shm`);
|
|
879
|
+
console.error(`[amem] Database corrupted \u2014 backed up to ${backupPath}`);
|
|
880
|
+
console.error("[amem] Creating fresh database. Previous memories are in the backup file.");
|
|
881
|
+
db = createDatabase(dbPath);
|
|
882
|
+
} else {
|
|
883
|
+
throw err;
|
|
884
|
+
}
|
|
885
|
+
} catch {
|
|
886
|
+
console.error(`[amem] Failed to initialize memory: ${err instanceof Error ? err.message : String(err)}`);
|
|
887
|
+
console.error(`[amem] Try deleting ${amemDir} to reset: rm -rf ${amemDir}`);
|
|
888
|
+
throw err;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
894
891
|
currentProject = project ?? "global";
|
|
895
892
|
preloadEmbeddings();
|
|
896
893
|
setTimeout(() => {
|
|
@@ -933,16 +930,86 @@ function memoryLog(sessionId, role, content) {
|
|
|
933
930
|
});
|
|
934
931
|
}
|
|
935
932
|
function reminderCheck() {
|
|
936
|
-
|
|
933
|
+
const all = getDb().checkReminders();
|
|
934
|
+
return all.filter((r) => r.scope === "global" || r.scope === currentProject);
|
|
935
|
+
}
|
|
936
|
+
async function memoryForget(opts) {
|
|
937
|
+
const db2 = getDb();
|
|
938
|
+
if (opts.id) {
|
|
939
|
+
const fullId = db2.resolveId(opts.id) ?? opts.id;
|
|
940
|
+
const memory = db2.getById(fullId);
|
|
941
|
+
if (!memory) return { deleted: 0, message: `Memory ${opts.id} not found.` };
|
|
942
|
+
db2.deleteMemory(fullId);
|
|
943
|
+
const vecIdx = getVectorIndex();
|
|
944
|
+
if (vecIdx) vecIdx.remove(fullId);
|
|
945
|
+
return { deleted: 1, message: `Deleted: "${memory.content}" (${memory.type})` };
|
|
946
|
+
}
|
|
947
|
+
if (opts.type) {
|
|
948
|
+
const all = db2.getAllForProject(currentProject);
|
|
949
|
+
const matches = all.filter((m) => m.type === opts.type);
|
|
950
|
+
if (matches.length === 0) return { deleted: 0, message: `No memories of type "${opts.type}" found.` };
|
|
951
|
+
const vecIdx = getVectorIndex();
|
|
952
|
+
for (const m of matches) {
|
|
953
|
+
db2.deleteMemory(m.id);
|
|
954
|
+
if (vecIdx) vecIdx.remove(m.id);
|
|
955
|
+
}
|
|
956
|
+
return { deleted: matches.length, message: `Deleted ${matches.length} "${opts.type}" memories.` };
|
|
957
|
+
}
|
|
958
|
+
if (opts.query) {
|
|
959
|
+
const queryEmbedding = await generateEmbedding(opts.query);
|
|
960
|
+
const matches = recallMemories(db2, { query: opts.query, queryEmbedding, limit: 50, minConfidence: 0, scope: currentProject });
|
|
961
|
+
if (matches.length === 0) return { deleted: 0, message: `No memories found matching "${opts.query}".` };
|
|
962
|
+
const vecIdx = getVectorIndex();
|
|
963
|
+
for (const m of matches) {
|
|
964
|
+
db2.deleteMemory(m.id);
|
|
965
|
+
if (vecIdx) vecIdx.remove(m.id);
|
|
966
|
+
}
|
|
967
|
+
return { deleted: matches.length, message: `Deleted ${matches.length} memories matching "${opts.query}".` };
|
|
968
|
+
}
|
|
969
|
+
return { deleted: 0, message: "Provide an id, type, or query to forget." };
|
|
970
|
+
}
|
|
971
|
+
var memoryConfig = {};
|
|
972
|
+
function setMemoryConfig(config) {
|
|
973
|
+
memoryConfig = config;
|
|
974
|
+
}
|
|
975
|
+
function getMaxRecallTokens() {
|
|
976
|
+
return memoryConfig.maxRecallTokens ?? 1500;
|
|
937
977
|
}
|
|
938
978
|
function memoryConsolidate(dryRun = false) {
|
|
939
979
|
return consolidateMemories(getDb(), cosineSimilarity, {
|
|
940
980
|
dryRun,
|
|
941
|
-
maxStaleDays: 90,
|
|
942
|
-
minConfidence: 0.3,
|
|
943
|
-
minAccessCount: 0
|
|
981
|
+
maxStaleDays: memoryConfig.maxStaleDays ?? 90,
|
|
982
|
+
minConfidence: memoryConfig.minConfidence ?? 0.3,
|
|
983
|
+
minAccessCount: memoryConfig.minAccessCount ?? 0
|
|
944
984
|
});
|
|
945
985
|
}
|
|
986
|
+
function isMemoryInitialized() {
|
|
987
|
+
return db !== null;
|
|
988
|
+
}
|
|
989
|
+
function memoryStats() {
|
|
990
|
+
return getDb().getStats();
|
|
991
|
+
}
|
|
992
|
+
function memoryExport() {
|
|
993
|
+
return getDb().getAllForProject(currentProject);
|
|
994
|
+
}
|
|
995
|
+
function memorySince(hours) {
|
|
996
|
+
const since = Date.now() - hours * 60 * 60 * 1e3;
|
|
997
|
+
const all = getDb().getMemoriesSince(since);
|
|
998
|
+
return all.filter((m) => m.scope === "global" || m.scope === currentProject);
|
|
999
|
+
}
|
|
1000
|
+
function memorySearch(query, limit) {
|
|
1001
|
+
return getDb().fullTextSearch(query, limit, currentProject);
|
|
1002
|
+
}
|
|
1003
|
+
function reminderSet(content, dueAt) {
|
|
1004
|
+
return getDb().insertReminder(content, dueAt ?? null, currentProject);
|
|
1005
|
+
}
|
|
1006
|
+
function reminderList(includeCompleted) {
|
|
1007
|
+
return getDb().listReminders(includeCompleted, currentProject);
|
|
1008
|
+
}
|
|
1009
|
+
function reminderComplete(id) {
|
|
1010
|
+
const fullId = getDb().resolveReminderId(id) ?? id;
|
|
1011
|
+
return getDb().completeReminder(fullId);
|
|
1012
|
+
}
|
|
946
1013
|
|
|
947
1014
|
// src/profile-templates.ts
|
|
948
1015
|
import fs6 from "fs";
|
|
@@ -1664,7 +1731,8 @@ function parseCommand(input) {
|
|
|
1664
1731
|
const trimmed = input.trim();
|
|
1665
1732
|
const parts = trimmed.split(/\s+/);
|
|
1666
1733
|
const base = parts[0].toLowerCase().replace(/^\//, "");
|
|
1667
|
-
|
|
1734
|
+
let action = parts.length > 1 ? parts[1].toLowerCase() : void 0;
|
|
1735
|
+
if (action === "--help" || action === "-h") action = "help";
|
|
1668
1736
|
const args = parts.slice(2);
|
|
1669
1737
|
return { base, action, args };
|
|
1670
1738
|
}
|
|
@@ -1702,7 +1770,14 @@ async function handleIdentityCommand(action, args, ctx) {
|
|
|
1702
1770
|
const output = await mcpWrite(ctx, "identity", "identity_update_section", { section, content });
|
|
1703
1771
|
return { handled: true, output };
|
|
1704
1772
|
}
|
|
1705
|
-
|
|
1773
|
+
if (action === "help") {
|
|
1774
|
+
return { handled: true, output: [
|
|
1775
|
+
pc3.bold("Identity commands:"),
|
|
1776
|
+
` ${pc3.cyan("/identity")} View current identity`,
|
|
1777
|
+
` ${pc3.cyan("/identity update")} <section> Update a section`
|
|
1778
|
+
].join("\n") };
|
|
1779
|
+
}
|
|
1780
|
+
return { handled: true, output: pc3.yellow(`Unknown action: /identity ${action}. Try /identity --help`) };
|
|
1706
1781
|
}
|
|
1707
1782
|
async function handleRulesCommand(action, args, ctx) {
|
|
1708
1783
|
const home2 = os9.homedir();
|
|
@@ -1733,7 +1808,16 @@ async function handleRulesCommand(action, args, ctx) {
|
|
|
1733
1808
|
const output = await mcpWrite(ctx, "rules", "rules_toggle", { category: args[0], index: parseInt(args[1], 10) });
|
|
1734
1809
|
return { handled: true, output };
|
|
1735
1810
|
}
|
|
1736
|
-
|
|
1811
|
+
if (action === "help") {
|
|
1812
|
+
return { handled: true, output: [
|
|
1813
|
+
pc3.bold("Rules commands:"),
|
|
1814
|
+
` ${pc3.cyan("/rules")} View current rules`,
|
|
1815
|
+
` ${pc3.cyan("/rules add")} <category> <text> Add a rule`,
|
|
1816
|
+
` ${pc3.cyan("/rules remove")} <category> <idx> Remove a rule`,
|
|
1817
|
+
` ${pc3.cyan("/rules toggle")} <category> <idx> Toggle a rule`
|
|
1818
|
+
].join("\n") };
|
|
1819
|
+
}
|
|
1820
|
+
return { handled: true, output: pc3.yellow(`Unknown action: /rules ${action}. Try /rules --help`) };
|
|
1737
1821
|
}
|
|
1738
1822
|
async function handleWorkflowsCommand(action, args, ctx) {
|
|
1739
1823
|
const home2 = os9.homedir();
|
|
@@ -1755,7 +1839,15 @@ async function handleWorkflowsCommand(action, args, ctx) {
|
|
|
1755
1839
|
const output = await mcpWrite(ctx, "workflows", "workflow_remove", { name: args.join(" ") });
|
|
1756
1840
|
return { handled: true, output };
|
|
1757
1841
|
}
|
|
1758
|
-
|
|
1842
|
+
if (action === "help") {
|
|
1843
|
+
return { handled: true, output: [
|
|
1844
|
+
pc3.bold("Workflow commands:"),
|
|
1845
|
+
` ${pc3.cyan("/workflows")} View current workflows`,
|
|
1846
|
+
` ${pc3.cyan("/workflows add")} <name> Add a workflow`,
|
|
1847
|
+
` ${pc3.cyan("/workflows remove")} <name> Remove a workflow`
|
|
1848
|
+
].join("\n") };
|
|
1849
|
+
}
|
|
1850
|
+
return { handled: true, output: pc3.yellow(`Unknown action: /workflows ${action}. Try /workflows --help`) };
|
|
1759
1851
|
}
|
|
1760
1852
|
var AKIT_REGISTRY = [
|
|
1761
1853
|
{ name: "web-search", description: "Search the web for current information", category: "search", mcp: { package: "@anthropic/web-search", command: "npx", args: ["-y", "@anthropic/web-search"] } },
|
|
@@ -1977,7 +2069,15 @@ async function handleSkillsCommand(action, args, ctx) {
|
|
|
1977
2069
|
const output = await mcpWrite(ctx, "skills", "skill_uninstall", { name: args.join(" ") });
|
|
1978
2070
|
return { handled: true, output };
|
|
1979
2071
|
}
|
|
1980
|
-
|
|
2072
|
+
if (action === "help") {
|
|
2073
|
+
return { handled: true, output: [
|
|
2074
|
+
pc3.bold("Skills commands:"),
|
|
2075
|
+
` ${pc3.cyan("/skills")} View installed skills`,
|
|
2076
|
+
` ${pc3.cyan("/skills install")} <name> Install a skill`,
|
|
2077
|
+
` ${pc3.cyan("/skills uninstall")} <name> Uninstall a skill`
|
|
2078
|
+
].join("\n") };
|
|
2079
|
+
}
|
|
2080
|
+
return { handled: true, output: pc3.yellow(`Unknown action: /skills ${action}. Try /skills --help`) };
|
|
1981
2081
|
}
|
|
1982
2082
|
async function handleEvalCommand(action, args, ctx) {
|
|
1983
2083
|
const home2 = os9.homedir();
|
|
@@ -2007,7 +2107,7 @@ async function handleMemoryCommand(action, args, ctx) {
|
|
|
2007
2107
|
return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
|
|
2008
2108
|
}
|
|
2009
2109
|
}
|
|
2010
|
-
if (action && !["search", "clear", "timeline"].includes(action)) {
|
|
2110
|
+
if (action && !["search", "clear", "timeline", "stats", "export", "since", "fts", "help"].includes(action)) {
|
|
2011
2111
|
try {
|
|
2012
2112
|
const topic = [action, ...args].join(" ");
|
|
2013
2113
|
const result = await memoryContext(topic);
|
|
@@ -2033,10 +2133,18 @@ async function handleMemoryCommand(action, args, ctx) {
|
|
|
2033
2133
|
}
|
|
2034
2134
|
if (action === "clear") {
|
|
2035
2135
|
if (args.length < 1) {
|
|
2036
|
-
return { handled: true, output: pc3.yellow("Usage: /memory clear <
|
|
2136
|
+
return { handled: true, output: pc3.yellow("Usage: /memory clear <query> \u2014 delete memories matching a search query\n /memory clear --type <type> \u2014 delete all memories of a type (correction|decision|pattern|preference|topology|fact)") };
|
|
2137
|
+
}
|
|
2138
|
+
try {
|
|
2139
|
+
if (args[0] === "--type" && args[1]) {
|
|
2140
|
+
const result2 = await memoryForget({ type: args[1] });
|
|
2141
|
+
return { handled: true, output: result2.deleted > 0 ? pc3.green(result2.message) : pc3.dim(result2.message) };
|
|
2142
|
+
}
|
|
2143
|
+
const result = await memoryForget({ query: args.join(" ") });
|
|
2144
|
+
return { handled: true, output: result.deleted > 0 ? pc3.green(result.message) : pc3.dim(result.message) };
|
|
2145
|
+
} catch (err) {
|
|
2146
|
+
return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
|
|
2037
2147
|
}
|
|
2038
|
-
const output = await mcpWrite(ctx, "memory", "memory_forget", { category: args[0] });
|
|
2039
|
-
return { handled: true, output };
|
|
2040
2148
|
}
|
|
2041
2149
|
if (action === "timeline") {
|
|
2042
2150
|
try {
|
|
@@ -2082,11 +2190,115 @@ async function handleMemoryCommand(action, args, ctx) {
|
|
|
2082
2190
|
return { handled: true, output: pc3.red("Failed to retrieve memory timeline.") };
|
|
2083
2191
|
}
|
|
2084
2192
|
}
|
|
2085
|
-
|
|
2193
|
+
if (action === "stats") {
|
|
2194
|
+
try {
|
|
2195
|
+
const stats = memoryStats();
|
|
2196
|
+
const lines = [pc3.bold("Memory Statistics:"), ""];
|
|
2197
|
+
lines.push(` Total memories: ${pc3.bold(String(stats.total))}`);
|
|
2198
|
+
if (Object.keys(stats.byType).length > 0) {
|
|
2199
|
+
lines.push("");
|
|
2200
|
+
lines.push(` ${pc3.dim("By type:")}`);
|
|
2201
|
+
for (const [type, count] of Object.entries(stats.byType)) {
|
|
2202
|
+
lines.push(` ${type.padEnd(16)} ${count}`);
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
return { handled: true, output: lines.join("\n") };
|
|
2206
|
+
} catch (err) {
|
|
2207
|
+
return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
if (action === "export") {
|
|
2211
|
+
try {
|
|
2212
|
+
const format = args[0] === "json" ? "json" : "markdown";
|
|
2213
|
+
const memories = memoryExport();
|
|
2214
|
+
if (memories.length === 0) {
|
|
2215
|
+
return { handled: true, output: pc3.dim("No memories to export.") };
|
|
2216
|
+
}
|
|
2217
|
+
if (format === "json") {
|
|
2218
|
+
const jsonOut = memories.map((m) => ({ id: m.id, type: m.type, content: m.content, tags: m.tags, confidence: m.confidence, createdAt: m.createdAt, tier: m.tier }));
|
|
2219
|
+
return { handled: true, output: JSON.stringify(jsonOut, null, 2) };
|
|
2220
|
+
}
|
|
2221
|
+
const lines = [`# Memory Export (${memories.length} memories)`, ""];
|
|
2222
|
+
for (const m of memories) {
|
|
2223
|
+
const date = new Date(m.createdAt).toLocaleDateString();
|
|
2224
|
+
const tags = m.tags.length > 0 ? ` [${m.tags.map((t) => `#${t}`).join(", ")}]` : "";
|
|
2225
|
+
lines.push(`- **[${m.type}]** ${m.content}${tags} ${pc3.dim(`(${date}, ${Math.round(m.confidence * 100)}%)`)}`);
|
|
2226
|
+
}
|
|
2227
|
+
return { handled: true, output: lines.join("\n") };
|
|
2228
|
+
} catch (err) {
|
|
2229
|
+
return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
if (action === "since") {
|
|
2233
|
+
try {
|
|
2234
|
+
let hours = 24;
|
|
2235
|
+
if (args[0]) {
|
|
2236
|
+
const match = args[0].match(/^(\d+)(h|d|w)$/);
|
|
2237
|
+
if (match) {
|
|
2238
|
+
const value = parseInt(match[1], 10);
|
|
2239
|
+
const unit = match[2];
|
|
2240
|
+
if (unit === "h") hours = value;
|
|
2241
|
+
else if (unit === "d") hours = value * 24;
|
|
2242
|
+
else if (unit === "w") hours = value * 24 * 7;
|
|
2243
|
+
} else {
|
|
2244
|
+
return { handled: true, output: pc3.yellow("Usage: /memory since <Nh|Nd|Nw> (e.g., 24h, 7d, 1w)") };
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
const memories = memorySince(hours);
|
|
2248
|
+
if (memories.length === 0) {
|
|
2249
|
+
return { handled: true, output: pc3.dim(`No memories in the last ${args[0] || "24h"}.`) };
|
|
2250
|
+
}
|
|
2251
|
+
const lines = [pc3.bold(`Memories since ${args[0] || "24h"} (${memories.length}):`), ""];
|
|
2252
|
+
for (const m of memories) {
|
|
2253
|
+
const age = Math.round((Date.now() - m.createdAt) / 36e5);
|
|
2254
|
+
const ageStr = age < 1 ? "<1h ago" : `${age}h ago`;
|
|
2255
|
+
lines.push(` ${pc3.dim(ageStr.padEnd(10))} [${m.type}] ${m.content}`);
|
|
2256
|
+
}
|
|
2257
|
+
return { handled: true, output: lines.join("\n") };
|
|
2258
|
+
} catch (err) {
|
|
2259
|
+
return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
if (action === "fts") {
|
|
2263
|
+
if (args.length < 1) {
|
|
2264
|
+
return { handled: true, output: pc3.yellow("Usage: /memory fts <query...> \u2014 full-text search") };
|
|
2265
|
+
}
|
|
2266
|
+
try {
|
|
2267
|
+
const query = args.join(" ");
|
|
2268
|
+
const results = memorySearch(query, 20);
|
|
2269
|
+
if (results.length === 0) {
|
|
2270
|
+
return { handled: true, output: pc3.dim(`No results for full-text search: "${query}".`) };
|
|
2271
|
+
}
|
|
2272
|
+
const lines = [pc3.bold(`FTS results for "${query}" (${results.length}):`), ""];
|
|
2273
|
+
for (const m of results) {
|
|
2274
|
+
const tags = m.tags.length > 0 ? ` ${pc3.dim(m.tags.map((t) => `#${t}`).join(" "))}` : "";
|
|
2275
|
+
lines.push(` [${m.type}] ${m.content}${tags}`);
|
|
2276
|
+
}
|
|
2277
|
+
return { handled: true, output: lines.join("\n") };
|
|
2278
|
+
} catch (err) {
|
|
2279
|
+
return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
if (action === "help") {
|
|
2283
|
+
return { handled: true, output: [
|
|
2284
|
+
pc3.bold("Memory commands:"),
|
|
2285
|
+
` ${pc3.cyan("/memory")} View recent context`,
|
|
2286
|
+
` ${pc3.cyan("/memory")} <topic> Context for a topic`,
|
|
2287
|
+
` ${pc3.cyan("/memory search")} <query> Search memories (semantic)`,
|
|
2288
|
+
` ${pc3.cyan("/memory fts")} <query> Full-text search (FTS5)`,
|
|
2289
|
+
` ${pc3.cyan("/memory since")} <Nh|Nd|Nw> Memories from time window`,
|
|
2290
|
+
` ${pc3.cyan("/memory stats")} Show memory statistics`,
|
|
2291
|
+
` ${pc3.cyan("/memory export")} [json] Export all memories`,
|
|
2292
|
+
` ${pc3.cyan("/memory timeline")} View memory timeline`,
|
|
2293
|
+
` ${pc3.cyan("/memory clear")} <query> Delete matching memories`,
|
|
2294
|
+
` ${pc3.cyan("/memory clear --type")} <type> Delete all of a type`
|
|
2295
|
+
].join("\n") };
|
|
2296
|
+
}
|
|
2297
|
+
return { handled: true, output: pc3.yellow(`Unknown action: /memory ${action}. Try /memory --help`) };
|
|
2086
2298
|
}
|
|
2087
2299
|
function handleStatusCommand(ctx) {
|
|
2088
2300
|
const mcpToolCount = ctx.mcpManager ? ctx.mcpManager.getTools().length : 0;
|
|
2089
|
-
const amemConnected =
|
|
2301
|
+
const amemConnected = isMemoryInitialized();
|
|
2090
2302
|
const status = getEcosystemStatus(mcpToolCount, amemConnected);
|
|
2091
2303
|
const lines = [pc3.bold("Aman Ecosystem Dashboard"), ""];
|
|
2092
2304
|
for (const layer of status.layers) {
|
|
@@ -2102,7 +2314,7 @@ function handleStatusCommand(ctx) {
|
|
|
2102
2314
|
}
|
|
2103
2315
|
function handleDoctorCommand(ctx) {
|
|
2104
2316
|
const mcpToolCount = ctx.mcpManager ? ctx.mcpManager.getTools().length : 0;
|
|
2105
|
-
const amemConnected =
|
|
2317
|
+
const amemConnected = isMemoryInitialized();
|
|
2106
2318
|
const status = getEcosystemStatus(mcpToolCount, amemConnected);
|
|
2107
2319
|
const lines = [pc3.bold("Aman Health Check"), ""];
|
|
2108
2320
|
let healthy = 0;
|
|
@@ -2136,7 +2348,7 @@ function handleDoctorCommand(ctx) {
|
|
|
2136
2348
|
}
|
|
2137
2349
|
lines.push(` ${status.amemConnected ? pc3.green("\u2713") : pc3.red("\u2717")} ${"Memory".padEnd(12)} ${status.amemConnected ? pc3.green("connected") : pc3.red("not connected")}`);
|
|
2138
2350
|
if (!status.amemConnected) {
|
|
2139
|
-
lines.push(` ${pc3.dim("\u2192 Fix:
|
|
2351
|
+
lines.push(` ${pc3.dim("\u2192 Fix: restart aman-agent (memory initializes automatically)")}`);
|
|
2140
2352
|
fixes++;
|
|
2141
2353
|
} else {
|
|
2142
2354
|
healthy++;
|
|
@@ -2158,7 +2370,8 @@ function handleHelp() {
|
|
|
2158
2370
|
` ${pc3.cyan("/akit")} Manage tools [add|remove <tool>]`,
|
|
2159
2371
|
` ${pc3.cyan("/skills")} View skills [install|uninstall ...]`,
|
|
2160
2372
|
` ${pc3.cyan("/eval")} View evaluation [milestone ...]`,
|
|
2161
|
-
` ${pc3.cyan("/memory")} View recent memories [search|clear|timeline]`,
|
|
2373
|
+
` ${pc3.cyan("/memory")} View recent memories [search|fts|since|stats|export|clear|timeline]`,
|
|
2374
|
+
` ${pc3.cyan("/reminder")} Manage reminders [set|check|done]`,
|
|
2162
2375
|
` ${pc3.cyan("/status")} Ecosystem dashboard`,
|
|
2163
2376
|
` ${pc3.cyan("/doctor")} Health check all layers`,
|
|
2164
2377
|
` ${pc3.cyan("/decisions")} View decision log [<project>]`,
|
|
@@ -2166,8 +2379,12 @@ function handleHelp() {
|
|
|
2166
2379
|
` ${pc3.cyan("/debug")} Show debug log`,
|
|
2167
2380
|
` ${pc3.cyan("/save")} Save conversation to memory`,
|
|
2168
2381
|
` ${pc3.cyan("/model")} Show current LLM model`,
|
|
2382
|
+
` ${pc3.cyan("/plan")} Manage multi-step plans`,
|
|
2383
|
+
` ${pc3.cyan("/profile")} Switch agent profiles`,
|
|
2384
|
+
` ${pc3.cyan("/delegate")} Delegate tasks to sub-agents`,
|
|
2385
|
+
` ${pc3.cyan("/team")} Manage agent teams`,
|
|
2169
2386
|
` ${pc3.cyan("/update")} Check for updates`,
|
|
2170
|
-
` ${pc3.cyan("/
|
|
2387
|
+
` ${pc3.cyan("/reset")} Full reset [all|memory|config|identity|rules]`,
|
|
2171
2388
|
` ${pc3.cyan("/clear")} Clear conversation history`,
|
|
2172
2389
|
` ${pc3.cyan("/quit")} Exit`
|
|
2173
2390
|
].join("\n")
|
|
@@ -2176,27 +2393,62 @@ function handleHelp() {
|
|
|
2176
2393
|
function handleSave() {
|
|
2177
2394
|
return { handled: true, saveConversation: true };
|
|
2178
2395
|
}
|
|
2179
|
-
function
|
|
2180
|
-
const
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2396
|
+
function handleReset(action) {
|
|
2397
|
+
const dirs = {
|
|
2398
|
+
config: path9.join(os9.homedir(), ".aman-agent"),
|
|
2399
|
+
memory: path9.join(os9.homedir(), ".amem"),
|
|
2400
|
+
identity: path9.join(os9.homedir(), ".acore"),
|
|
2401
|
+
rules: path9.join(os9.homedir(), ".arules")
|
|
2402
|
+
};
|
|
2403
|
+
if (action === "help" || !action) {
|
|
2404
|
+
return {
|
|
2405
|
+
handled: true,
|
|
2406
|
+
output: [
|
|
2407
|
+
pc3.bold("Reset options:"),
|
|
2408
|
+
` ${pc3.cyan("/reset all")} Full reset \u2014 config, memory, identity, rules`,
|
|
2409
|
+
` ${pc3.cyan("/reset memory")} Clear all memories only`,
|
|
2410
|
+
` ${pc3.cyan("/reset config")} Reset LLM config only`,
|
|
2411
|
+
` ${pc3.cyan("/reset identity")} Reset persona/identity only`,
|
|
2412
|
+
` ${pc3.cyan("/reset rules")} Reset guardrails only`,
|
|
2413
|
+
"",
|
|
2414
|
+
pc3.dim("Directories:"),
|
|
2415
|
+
...Object.entries(dirs).map(([k, v]) => ` ${k}: ${pc3.dim(v)}`)
|
|
2416
|
+
].join("\n")
|
|
2417
|
+
};
|
|
2418
|
+
}
|
|
2419
|
+
const targets = action === "all" ? ["config", "memory", "identity", "rules"] : [action];
|
|
2420
|
+
if (!targets.every((t) => t in dirs)) {
|
|
2421
|
+
return { handled: true, output: pc3.red(`Unknown target: ${action}. Use /reset help`) };
|
|
2422
|
+
}
|
|
2423
|
+
const removed = [];
|
|
2424
|
+
for (const target of targets) {
|
|
2425
|
+
const dir = dirs[target];
|
|
2426
|
+
if (fs9.existsSync(dir)) {
|
|
2427
|
+
fs9.rmSync(dir, { recursive: true, force: true });
|
|
2428
|
+
removed.push(target);
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
if (targets.includes("config")) {
|
|
2432
|
+
const configDir = dirs.config;
|
|
2433
|
+
fs9.mkdirSync(configDir, { recursive: true });
|
|
2434
|
+
fs9.writeFileSync(path9.join(configDir, ".reconfig"), "", "utf-8");
|
|
2435
|
+
}
|
|
2436
|
+
if (removed.length === 0) {
|
|
2437
|
+
return { handled: true, output: pc3.dim("Nothing to reset \u2014 directories don't exist.") };
|
|
2184
2438
|
}
|
|
2185
|
-
fs9.mkdirSync(configDir, { recursive: true });
|
|
2186
|
-
fs9.writeFileSync(path9.join(configDir, ".reconfig"), "", "utf-8");
|
|
2187
2439
|
return {
|
|
2188
2440
|
handled: true,
|
|
2189
2441
|
quit: true,
|
|
2190
2442
|
output: [
|
|
2191
|
-
pc3.green(
|
|
2192
|
-
"
|
|
2443
|
+
pc3.green(`Reset complete: ${removed.join(", ")}`),
|
|
2444
|
+
"Restart aman-agent to begin fresh."
|
|
2193
2445
|
].join("\n")
|
|
2194
2446
|
};
|
|
2195
2447
|
}
|
|
2196
2448
|
function handleUpdate() {
|
|
2197
2449
|
try {
|
|
2198
2450
|
const current = execFileSync("npm", ["view", "@aman_asmuei/aman-agent", "version"], { encoding: "utf-8" }).trim();
|
|
2199
|
-
const local =
|
|
2451
|
+
const local = true ? "0.17.2" : "unknown";
|
|
2200
2452
|
if (current === local) {
|
|
2201
2453
|
return { handled: true, output: `${pc3.green("Up to date")} \u2014 v${local}` };
|
|
2202
2454
|
}
|
|
@@ -2634,6 +2886,86 @@ function handlePlanCommand(action, args) {
|
|
|
2634
2886
|
return { handled: true, output: pc3.yellow(`Unknown plan action: ${action}. Try /plan help`) };
|
|
2635
2887
|
}
|
|
2636
2888
|
}
|
|
2889
|
+
async function handleReminderCommand(action, args) {
|
|
2890
|
+
if (!action || action === "list") {
|
|
2891
|
+
try {
|
|
2892
|
+
const reminders = reminderList();
|
|
2893
|
+
if (reminders.length === 0) return { handled: true, output: pc3.dim("No reminders.") };
|
|
2894
|
+
const lines = [pc3.bold(`Reminders (${reminders.length}):`), ""];
|
|
2895
|
+
for (const r of reminders) {
|
|
2896
|
+
const status = r.completed ? pc3.green("[done]") : pc3.yellow("[todo]");
|
|
2897
|
+
const due = r.dueAt ? ` ${pc3.dim(`due: ${new Date(r.dueAt).toLocaleString()}`)}` : "";
|
|
2898
|
+
lines.push(` ${status} ${r.content}${due} ${pc3.dim(`(${r.id.slice(0, 8)})`)}`);
|
|
2899
|
+
}
|
|
2900
|
+
return { handled: true, output: lines.join("\n") };
|
|
2901
|
+
} catch (err) {
|
|
2902
|
+
return { handled: true, output: pc3.red(`Reminder error: ${err instanceof Error ? err.message : String(err)}`) };
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
if (action === "set" || action === "add") {
|
|
2906
|
+
if (args.length === 0) return { handled: true, output: pc3.yellow("Usage: /reminder set <text> [--due <time>]\n Time formats: 1h, 2d, 1w, or ISO date (2026-04-10)") };
|
|
2907
|
+
let dueAt;
|
|
2908
|
+
const dueIdx = args.indexOf("--due");
|
|
2909
|
+
let contentArgs = args;
|
|
2910
|
+
if (dueIdx >= 0 && args[dueIdx + 1]) {
|
|
2911
|
+
const dueStr = args[dueIdx + 1];
|
|
2912
|
+
contentArgs = [...args.slice(0, dueIdx), ...args.slice(dueIdx + 2)];
|
|
2913
|
+
const relMatch = dueStr.match(/^(\d+)(h|d|w)$/);
|
|
2914
|
+
if (relMatch) {
|
|
2915
|
+
const num = parseInt(relMatch[1], 10);
|
|
2916
|
+
const unit = relMatch[2];
|
|
2917
|
+
const ms = unit === "h" ? num * 36e5 : unit === "d" ? num * 864e5 : num * 6048e5;
|
|
2918
|
+
dueAt = Date.now() + ms;
|
|
2919
|
+
} else {
|
|
2920
|
+
const parsed = Date.parse(dueStr);
|
|
2921
|
+
if (!isNaN(parsed)) dueAt = parsed;
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
const content = contentArgs.join(" ");
|
|
2925
|
+
if (!content) return { handled: true, output: pc3.yellow("Usage: /reminder set <text> [--due <time>]") };
|
|
2926
|
+
try {
|
|
2927
|
+
const id = reminderSet(content, dueAt);
|
|
2928
|
+
const dueInfo = dueAt ? ` (due: ${new Date(dueAt).toLocaleDateString()})` : "";
|
|
2929
|
+
return { handled: true, output: pc3.green(`Reminder set: "${content}"${dueInfo} (ID: ${id.slice(0, 8)})`) };
|
|
2930
|
+
} catch (err) {
|
|
2931
|
+
return { handled: true, output: pc3.red(`Reminder error: ${err instanceof Error ? err.message : String(err)}`) };
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
if (action === "done" || action === "complete") {
|
|
2935
|
+
if (!args[0]) return { handled: true, output: pc3.yellow("Usage: /reminder done <id>") };
|
|
2936
|
+
try {
|
|
2937
|
+
const result = reminderComplete(args[0]);
|
|
2938
|
+
return { handled: true, output: result ? pc3.green("Reminder completed.") : pc3.yellow("Reminder not found.") };
|
|
2939
|
+
} catch (err) {
|
|
2940
|
+
return { handled: true, output: pc3.red(`Reminder error: ${err instanceof Error ? err.message : String(err)}`) };
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
if (action === "check") {
|
|
2944
|
+
try {
|
|
2945
|
+
const reminders = reminderCheck();
|
|
2946
|
+
if (reminders.length === 0) return { handled: true, output: pc3.dim("No pending reminders.") };
|
|
2947
|
+
const lines = [pc3.bold("Pending Reminders:"), ""];
|
|
2948
|
+
for (const r of reminders) {
|
|
2949
|
+
const icon = r.status === "overdue" ? pc3.red("!!!") : r.status === "today" ? pc3.yellow("(!)") : pc3.dim("( )");
|
|
2950
|
+
const due = r.dueAt ? ` ${pc3.dim(`due: ${new Date(r.dueAt).toLocaleString()}`)}` : "";
|
|
2951
|
+
lines.push(` ${icon} ${r.content}${due} ${pc3.dim(`[${r.status}]`)}`);
|
|
2952
|
+
}
|
|
2953
|
+
return { handled: true, output: lines.join("\n") };
|
|
2954
|
+
} catch (err) {
|
|
2955
|
+
return { handled: true, output: pc3.red(`Reminder error: ${err instanceof Error ? err.message : String(err)}`) };
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
if (action === "help") {
|
|
2959
|
+
return { handled: true, output: [
|
|
2960
|
+
pc3.bold("Reminder commands:"),
|
|
2961
|
+
` ${pc3.cyan("/reminder")} List all reminders`,
|
|
2962
|
+
` ${pc3.cyan("/reminder set")} <text> Create a reminder [--due 1h|2d|1w|date]`,
|
|
2963
|
+
` ${pc3.cyan("/reminder check")} Show overdue/upcoming`,
|
|
2964
|
+
` ${pc3.cyan("/reminder done")} <id> Mark as completed`
|
|
2965
|
+
].join("\n") };
|
|
2966
|
+
}
|
|
2967
|
+
return { handled: true, output: pc3.yellow(`Unknown action: /reminder ${action}. Try /reminder --help`) };
|
|
2968
|
+
}
|
|
2637
2969
|
var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
2638
2970
|
"quit",
|
|
2639
2971
|
"exit",
|
|
@@ -2655,8 +2987,8 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
2655
2987
|
"decisions",
|
|
2656
2988
|
"export",
|
|
2657
2989
|
"debug",
|
|
2658
|
-
"
|
|
2659
|
-
"
|
|
2990
|
+
"reset",
|
|
2991
|
+
"reminder",
|
|
2660
2992
|
"update",
|
|
2661
2993
|
"upgrade",
|
|
2662
2994
|
"plan",
|
|
@@ -2707,9 +3039,8 @@ async function handleCommand(input, ctx) {
|
|
|
2707
3039
|
return handleExportCommand();
|
|
2708
3040
|
case "debug":
|
|
2709
3041
|
return handleDebugCommand();
|
|
2710
|
-
case "
|
|
2711
|
-
|
|
2712
|
-
return handleReconfig();
|
|
3042
|
+
case "reset":
|
|
3043
|
+
return handleReset(action);
|
|
2713
3044
|
case "plan":
|
|
2714
3045
|
return handlePlanCommand(action, args);
|
|
2715
3046
|
case "profile":
|
|
@@ -2718,6 +3049,8 @@ async function handleCommand(input, ctx) {
|
|
|
2718
3049
|
return handleDelegateCommand(action, args, ctx);
|
|
2719
3050
|
case "team":
|
|
2720
3051
|
return handleTeamCommand(action, args, ctx);
|
|
3052
|
+
case "reminder":
|
|
3053
|
+
return handleReminderCommand(action, args);
|
|
2721
3054
|
case "update":
|
|
2722
3055
|
case "upgrade":
|
|
2723
3056
|
return handleUpdate();
|
|
@@ -2933,14 +3266,18 @@ async function onSessionStart(ctx) {
|
|
|
2933
3266
|
let firstRun = false;
|
|
2934
3267
|
let resumeTopic;
|
|
2935
3268
|
const visibleReminders = [];
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
3269
|
+
if (!isMemoryInitialized()) {
|
|
3270
|
+
firstRun = false;
|
|
3271
|
+
} else {
|
|
3272
|
+
try {
|
|
3273
|
+
isHookCall = true;
|
|
3274
|
+
const recallResult = await memoryRecall("*", { limit: 1 });
|
|
3275
|
+
firstRun = recallResult.total === 0;
|
|
3276
|
+
} catch {
|
|
3277
|
+
firstRun = true;
|
|
3278
|
+
} finally {
|
|
3279
|
+
isHookCall = false;
|
|
3280
|
+
}
|
|
2944
3281
|
}
|
|
2945
3282
|
if (firstRun) {
|
|
2946
3283
|
contextInjection = `<first-session>
|
|
@@ -3685,8 +4022,8 @@ Rules:
|
|
|
3685
4022
|
- Be conservative \u2014 90% of turns produce nothing worth storing
|
|
3686
4023
|
- Return ONLY the JSON array, no other text`;
|
|
3687
4024
|
function shouldExtract(assistantResponse, turnsSinceLastExtraction, lastExtractionCount) {
|
|
3688
|
-
if (lastExtractionCount > 0) return true;
|
|
3689
4025
|
if (assistantResponse.length < MIN_RESPONSE_LENGTH) return false;
|
|
4026
|
+
if (lastExtractionCount > 0 && turnsSinceLastExtraction >= 1) return true;
|
|
3690
4027
|
if (turnsSinceLastExtraction < MIN_TURNS_BETWEEN_EMPTY) return false;
|
|
3691
4028
|
return true;
|
|
3692
4029
|
}
|
|
@@ -3914,7 +4251,7 @@ var BackgroundTaskManager = class {
|
|
|
3914
4251
|
// src/errors.ts
|
|
3915
4252
|
var ERROR_MAPPINGS = [
|
|
3916
4253
|
{ pattern: /rate.?limit|429/i, message: "Rate limited. I'll retry automatically." },
|
|
3917
|
-
{ pattern: /401|unauthorized/i, message: "API key invalid. Run /
|
|
4254
|
+
{ pattern: /401|unauthorized/i, message: "API key invalid. Run /reset config to fix." },
|
|
3918
4255
|
{ pattern: /403|forbidden/i, message: "API key doesn't have access to this model. Try a different model with --model." },
|
|
3919
4256
|
{ pattern: /fetch failed|network/i, message: "Network error. Check your internet connection." },
|
|
3920
4257
|
{ pattern: /ECONNREFUSED/i, message: "Can't reach the API. Are you behind a proxy or firewall?" },
|
|
@@ -4001,13 +4338,20 @@ async function recallForMessage(input) {
|
|
|
4001
4338
|
return null;
|
|
4002
4339
|
}
|
|
4003
4340
|
const tokenEstimate = result.tokenEstimate ?? Math.round(result.text.split(/\s+/).filter(Boolean).length * 1.3);
|
|
4341
|
+
const MAX_MEMORY_TOKENS = getMaxRecallTokens();
|
|
4342
|
+
let memoryText = result.text;
|
|
4343
|
+
if (tokenEstimate > MAX_MEMORY_TOKENS) {
|
|
4344
|
+
const maxChars = MAX_MEMORY_TOKENS * 4;
|
|
4345
|
+
memoryText = memoryText.slice(0, maxChars) + "\n[... memory truncated to fit token budget]";
|
|
4346
|
+
log.debug("agent", `memory recall truncated from ~${tokenEstimate} to ~${MAX_MEMORY_TOKENS} tokens`);
|
|
4347
|
+
}
|
|
4004
4348
|
return {
|
|
4005
4349
|
text: `
|
|
4006
4350
|
|
|
4007
4351
|
<relevant-memories>
|
|
4008
|
-
${
|
|
4352
|
+
${memoryText}
|
|
4009
4353
|
</relevant-memories>`,
|
|
4010
|
-
tokenEstimate
|
|
4354
|
+
tokenEstimate: Math.min(tokenEstimate, MAX_MEMORY_TOKENS)
|
|
4011
4355
|
};
|
|
4012
4356
|
} catch (err) {
|
|
4013
4357
|
log.debug("agent", "memory recall failed", err);
|
|
@@ -4063,7 +4407,7 @@ async function runAgent(client, systemPrompt, aiName, model, tools, mcpManager,
|
|
|
4063
4407
|
shownHints: loadShownHints(),
|
|
4064
4408
|
hintShownThisSession: false
|
|
4065
4409
|
};
|
|
4066
|
-
const isRetryable2 = (err) => err.message.includes("Rate limit") || err.message.includes("rate limit") || err.message.includes("ECONNRESET") || err.message.includes("ETIMEDOUT") || err.message.includes("fetch failed");
|
|
4410
|
+
const isRetryable2 = (err) => err.message.includes("Rate limit") || err.message.includes("rate limit") || err.message.includes("ECONNRESET") || err.message.includes("ETIMEDOUT") || err.message.includes("fetch failed") || err.message.includes("socket hang up") || err.message.includes("network socket disconnected") || err.message.includes("ENOTFOUND") || err.message.includes("EAI_AGAIN");
|
|
4067
4411
|
let responseBuffer = "";
|
|
4068
4412
|
const onChunkHandler = (chunk) => {
|
|
4069
4413
|
if (chunk.type === "text" && chunk.text) {
|
|
@@ -4481,7 +4825,18 @@ ${converted}
|
|
|
4481
4825
|
{ maxAttempts: 3, baseDelay: 1e3, retryable: isRetryable2 }
|
|
4482
4826
|
);
|
|
4483
4827
|
messages.push(response.message);
|
|
4828
|
+
const MAX_TOOL_TURNS = 20;
|
|
4829
|
+
let toolTurnCount = 0;
|
|
4484
4830
|
while (response.toolUses.length > 0 && mcpManager) {
|
|
4831
|
+
toolTurnCount++;
|
|
4832
|
+
if (toolTurnCount > MAX_TOOL_TURNS) {
|
|
4833
|
+
messages.push({
|
|
4834
|
+
role: "assistant",
|
|
4835
|
+
content: "Tool execution limit reached (20). Breaking to prevent infinite loop."
|
|
4836
|
+
});
|
|
4837
|
+
console.log(pc6.yellow("\n Tool execution limit reached (20). Breaking to prevent infinite loop."));
|
|
4838
|
+
break;
|
|
4839
|
+
}
|
|
4485
4840
|
const toolResults = await Promise.all(
|
|
4486
4841
|
response.toolUses.map(async (toolUse) => {
|
|
4487
4842
|
if (hooksConfig) {
|
|
@@ -4796,10 +5151,22 @@ function bootstrapEcosystem() {
|
|
|
4796
5151
|
"- Respect the user's preferences stored in memory"
|
|
4797
5152
|
].join("\n"), "utf-8");
|
|
4798
5153
|
}
|
|
5154
|
+
const flowDir = path14.join(home2, ".aflow");
|
|
5155
|
+
const flowPath = path14.join(flowDir, "flow.md");
|
|
5156
|
+
if (!fs14.existsSync(flowPath)) {
|
|
5157
|
+
fs14.mkdirSync(flowDir, { recursive: true });
|
|
5158
|
+
fs14.writeFileSync(flowPath, "# Workflows\n\n_No workflows defined yet. Use /workflows add to create one._\n", "utf-8");
|
|
5159
|
+
}
|
|
5160
|
+
const skillDir = path14.join(home2, ".askill");
|
|
5161
|
+
const skillPath = path14.join(skillDir, "skills.md");
|
|
5162
|
+
if (!fs14.existsSync(skillPath)) {
|
|
5163
|
+
fs14.mkdirSync(skillDir, { recursive: true });
|
|
5164
|
+
fs14.writeFileSync(skillPath, "# Skills\n\n_No skills installed yet. Use /skills install to add domain expertise._\n", "utf-8");
|
|
5165
|
+
}
|
|
4799
5166
|
return true;
|
|
4800
5167
|
}
|
|
4801
5168
|
var program = new Command();
|
|
4802
|
-
program.name("aman-agent").description("Your AI companion, running locally").version("0.
|
|
5169
|
+
program.name("aman-agent").description("Your AI companion, running locally").version("0.17.2").option("--model <model>", "Override LLM model").option("--budget <tokens>", "Token budget for system prompt (default: 8000)", parseInt).option("--profile <name>", "Use a specific agent profile (e.g., coder, writer, researcher)").action(async (options) => {
|
|
4803
5170
|
p2.intro(pc7.bold("aman agent") + pc7.dim(" \u2014 your AI companion"));
|
|
4804
5171
|
let config = loadConfig();
|
|
4805
5172
|
if (!config) {
|
|
@@ -4808,7 +5175,7 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
|
|
|
4808
5175
|
config = detected;
|
|
4809
5176
|
const providerLabel = detected.provider === "anthropic" ? "Anthropic API key" : detected.provider === "openai" ? "OpenAI API key" : "Ollama";
|
|
4810
5177
|
p2.log.success(`Auto-detected ${providerLabel}. Using ${pc7.bold(detected.model)}.`);
|
|
4811
|
-
p2.log.info(pc7.dim("Change anytime with /
|
|
5178
|
+
p2.log.info(pc7.dim("Change anytime with /reset config"));
|
|
4812
5179
|
saveConfig(config);
|
|
4813
5180
|
} else {
|
|
4814
5181
|
p2.log.info("First-time setup \u2014 configure your LLM connection.");
|
|
@@ -4926,39 +5293,47 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
|
|
|
4926
5293
|
}
|
|
4927
5294
|
}
|
|
4928
5295
|
const aiName = getProfileAiName(profile);
|
|
4929
|
-
|
|
5296
|
+
if (config.memory) setMemoryConfig(config.memory);
|
|
5297
|
+
try {
|
|
5298
|
+
await initMemory();
|
|
5299
|
+
} catch (err) {
|
|
5300
|
+
p2.log.warning(`Memory initialization failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5301
|
+
}
|
|
5302
|
+
if (isMemoryInitialized()) {
|
|
5303
|
+
const memSpinner = p2.spinner();
|
|
5304
|
+
memSpinner.start("Consolidating memory");
|
|
5305
|
+
try {
|
|
5306
|
+
const report = memoryConsolidate();
|
|
5307
|
+
memSpinner.stop("Memory consolidated");
|
|
5308
|
+
if (report.merged > 0 || report.pruned > 0 || report.promoted > 0) {
|
|
5309
|
+
p2.log.info(
|
|
5310
|
+
`Memory health: ${report.healthScore ?? "?"}% ` + pc7.dim(`(merged ${report.merged}, pruned ${report.pruned}, promoted ${report.promoted})`)
|
|
5311
|
+
);
|
|
5312
|
+
}
|
|
5313
|
+
} catch {
|
|
5314
|
+
memSpinner.stop("Memory consolidation skipped");
|
|
5315
|
+
}
|
|
5316
|
+
}
|
|
4930
5317
|
const mcpManager = new McpManager();
|
|
4931
5318
|
const mcpSpinner = p2.spinner();
|
|
4932
5319
|
mcpSpinner.start("Connecting to MCP servers");
|
|
4933
|
-
|
|
5320
|
+
const connections = [
|
|
5321
|
+
mcpManager.connect("aman", "npx", ["-y", "@aman_asmuei/aman-mcp"])
|
|
5322
|
+
];
|
|
4934
5323
|
if (config.mcpServers) {
|
|
4935
5324
|
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
|
|
4936
5325
|
if (name === "aman" || name === "amem") continue;
|
|
4937
|
-
|
|
5326
|
+
connections.push(mcpManager.connect(name, serverConfig.command, serverConfig.args, serverConfig.env));
|
|
4938
5327
|
}
|
|
4939
5328
|
}
|
|
5329
|
+
await Promise.all(connections);
|
|
4940
5330
|
const mcpTools = mcpManager.getTools();
|
|
4941
5331
|
mcpSpinner.stop("MCP connected");
|
|
4942
5332
|
if (mcpTools.length > 0) {
|
|
4943
5333
|
p2.log.success(`${mcpTools.length} MCP tools available`);
|
|
4944
|
-
{
|
|
4945
|
-
const memSpinner = p2.spinner();
|
|
4946
|
-
memSpinner.start("Consolidating memory");
|
|
4947
|
-
try {
|
|
4948
|
-
const report = memoryConsolidate();
|
|
4949
|
-
memSpinner.stop("Memory consolidated");
|
|
4950
|
-
if (report.merged > 0 || report.pruned > 0 || report.promoted > 0) {
|
|
4951
|
-
p2.log.info(
|
|
4952
|
-
`Memory health: ${report.healthScore ?? "?"}% ` + pc7.dim(`(merged ${report.merged}, pruned ${report.pruned}, promoted ${report.promoted})`)
|
|
4953
|
-
);
|
|
4954
|
-
}
|
|
4955
|
-
} catch {
|
|
4956
|
-
memSpinner.stop("Memory consolidation skipped");
|
|
4957
|
-
}
|
|
4958
|
-
}
|
|
4959
5334
|
} else {
|
|
4960
5335
|
p2.log.info(
|
|
4961
|
-
"No MCP tools connected (install aman-mcp
|
|
5336
|
+
"No MCP tools connected (install aman-mcp for tool support)"
|
|
4962
5337
|
);
|
|
4963
5338
|
}
|
|
4964
5339
|
const toolDefs = mcpTools.map((t) => ({
|