@askexenow/exe-os 0.9.270 → 0.9.272
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/active-agent-BDYXURXQ.js +26 -0
- package/dist/active-agent-YWBGAKGU.js +25 -0
- package/dist/agentic-ontology-56VHSVS3.js +25 -0
- package/dist/backfill-metadata-G46ABBVR.js +597 -0
- package/dist/backfill-metadata-VAV27KJK.js +597 -0
- package/dist/behaviors-USUTDXVA.js +25 -0
- package/dist/bin/agentic-ontology-backfill.js +5 -5
- package/dist/bin/agentic-reflection-backfill.js +6 -6
- package/dist/bin/agentic-semantic-label.js +5 -5
- package/dist/bin/backfill-conversations.js +4 -4
- package/dist/bin/backfill-responses.js +4 -4
- package/dist/bin/backfill-vectors.js +5 -5
- package/dist/bin/bulk-sync-postgres.js +6 -6
- package/dist/bin/cc-doctor.js +4 -4
- package/dist/bin/cleanup-stale-review-tasks.js +10 -10
- package/dist/bin/cli.js +16 -16
- package/dist/bin/exe-agent-config.js +3 -3
- package/dist/bin/exe-agent.js +4 -4
- package/dist/bin/exe-assign.js +5 -5
- package/dist/bin/exe-boot.js +17 -17
- package/dist/bin/exe-call.js +4 -4
- package/dist/bin/exe-cloud.js +4 -4
- package/dist/bin/exe-dispatch.js +10 -10
- package/dist/bin/exe-doctor.js +1 -1
- package/dist/bin/exe-export-behaviors.js +7 -7
- package/dist/bin/exe-forget.js +6 -6
- package/dist/bin/exe-gateway.js +7 -7
- package/dist/bin/exe-healthcheck.js +4 -4
- package/dist/bin/exe-heartbeat.js +10 -10
- package/dist/bin/exe-kill.js +13 -13
- package/dist/bin/exe-launch-agent.js +37 -19
- package/dist/bin/exe-new-employee.js +6 -6
- package/dist/bin/exe-pending-messages.js +11 -11
- package/dist/bin/exe-pending-notifications.js +10 -10
- package/dist/bin/exe-pending-reviews.js +10 -10
- package/dist/bin/exe-rename.js +4 -4
- package/dist/bin/exe-review.js +12 -12
- package/dist/bin/exe-search.js +5 -5
- package/dist/bin/exe-session-cleanup.js +15 -15
- package/dist/bin/exe-settings.js +5 -5
- package/dist/bin/exe-start-codex.js +11 -11
- package/dist/bin/exe-start-opencode.js +8 -8
- package/dist/bin/exe-status.js +11 -11
- package/dist/bin/exe-team.js +3 -3
- package/dist/bin/git-sweep.js +11 -11
- package/dist/bin/graph-backfill.js +4 -4
- package/dist/bin/graph-export.js +5 -5
- package/dist/bin/import-history.js +7 -7
- package/dist/bin/install.js +6 -6
- package/dist/bin/intercom-check.js +4 -4
- package/dist/bin/mcp-sessions.js +2 -2
- package/dist/bin/orchestration-metrics.js +4 -4
- package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
- package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
- package/dist/bin/scan-tasks.js +10 -10
- package/dist/bin/setup.js +1 -1
- package/dist/bin/shard-migrate.js +4 -4
- package/dist/bin/stack-update.js +10 -3
- package/dist/bin/vps-health-gate.js +1 -1
- package/dist/capacity-monitor-IFVRCIM7.js +49 -0
- package/dist/capacity-monitor-Q47GBDSY.js +49 -0
- package/dist/catchup-brief-RP4QHXNT.js +151 -0
- package/dist/catchup-brief-TKA6TEK4.js +151 -0
- package/dist/chunk-23KJ2LXY.js +58 -0
- package/dist/chunk-2NQQP3FF.js +630 -0
- package/dist/chunk-3FU5I3KV.js +526 -0
- package/dist/chunk-46IEEKPU.js +13696 -0
- package/dist/chunk-57UAFTO2.js +3958 -0
- package/dist/chunk-5AS622MM.js +3958 -0
- package/dist/chunk-62DEE65H.js +371 -0
- package/dist/chunk-64T6DFSS.js +447 -0
- package/dist/chunk-6BWDP63Z.js +197 -0
- package/dist/chunk-6GPYL7TX.js +214 -0
- package/dist/chunk-6KWLUVFL.js +54 -0
- package/dist/chunk-6N5ISWBF.js +1148 -0
- package/dist/chunk-6OD7PVMC.js +333 -0
- package/dist/chunk-7ET5CYTD.js +382 -0
- package/dist/chunk-7IWLKR6N.js +76 -0
- package/dist/chunk-7OEUOJL5.js +1021 -0
- package/dist/chunk-AIRJTKDK.js +204 -0
- package/dist/chunk-ATJ3NXDP.js +244 -0
- package/dist/chunk-B5IS7LE4.js +128 -0
- package/dist/chunk-BKINEQVI.js +244 -0
- package/dist/chunk-BOJV6NI3.js +128 -0
- package/dist/chunk-BPHWI6N2.js +284 -0
- package/dist/chunk-BXCQWWJP.js +185 -0
- package/dist/chunk-BZ6K7AY3.js +50 -0
- package/dist/chunk-C54KIFLS.js +214 -0
- package/dist/chunk-C6OYEJJI.js +260 -0
- package/dist/chunk-CHBHR5W6.js +3556 -0
- package/dist/chunk-D2T3272U.js +171 -0
- package/dist/chunk-DCHEIVGT.js +221 -0
- package/dist/chunk-DHIBLMSP.js +30 -0
- package/dist/chunk-E2AF2WYY.js +346 -0
- package/dist/chunk-E2KZEZZW.js +1090 -0
- package/dist/chunk-E4KWB4WM.js +348 -0
- package/dist/chunk-ENU7URWK.js +1073 -0
- package/dist/chunk-EZ7KAZMC.js +132 -0
- package/dist/chunk-F4FSSHR4.js +1073 -0
- package/dist/chunk-FPXU56FG.js +346 -0
- package/dist/chunk-FY7HHR5I.js +128 -0
- package/dist/chunk-G2S2UMU4.js +159 -0
- package/dist/chunk-G33BHQCO.js +70 -0
- package/dist/chunk-GJQTL7RX.js +127 -0
- package/dist/chunk-GLCKDEM2.js +97 -0
- package/dist/chunk-GMA34SXV.js +240 -0
- package/dist/chunk-GVAVEBYR.js +2091 -0
- package/dist/chunk-HOSJTLBQ.js +513 -0
- package/dist/chunk-IBMTSEZC.js +230 -0
- package/dist/chunk-IC6HVAS3.js +56 -0
- package/dist/chunk-IDFJNO44.js +1051 -0
- package/dist/chunk-ILFJMEY5.js +97 -0
- package/dist/chunk-ISQAOSL3.js +1921 -0
- package/dist/chunk-J6V2DCZK.js +382 -0
- package/dist/chunk-JP4CLFLR.js +1148 -0
- package/dist/chunk-JTIOZHWG.js +58 -0
- package/dist/chunk-KDICWAYV.js +1345 -0
- package/dist/chunk-KOBIB6WG.js +159 -0
- package/dist/chunk-KQFDDQB6.js +13696 -0
- package/dist/chunk-KZNSOHCB.js +280 -0
- package/dist/chunk-LVMBYP3C.js +171 -0
- package/dist/chunk-M2WQW5NC.js +227 -0
- package/dist/chunk-MY6SP5NZ.js +551 -0
- package/dist/chunk-N2ACW2ZG.js +363 -0
- package/dist/chunk-NSMJDATI.js +495 -0
- package/dist/chunk-NSQ5JE23.js +1090 -0
- package/dist/chunk-NZL567WG.js +81 -0
- package/dist/chunk-O5OMH6LI.js +244 -0
- package/dist/chunk-O7YO7E2G.js +1512 -0
- package/dist/chunk-OBUV3W7L.js +163 -0
- package/dist/chunk-OLDS7LJN.js +495 -0
- package/dist/chunk-OO2I22RX.js +38 -0
- package/dist/chunk-OPUUT33V.js +447 -0
- package/dist/chunk-OT3VMTKB.js +50 -0
- package/dist/chunk-P6RVIOVA.js +157 -0
- package/dist/chunk-PUA5564C.js +210 -0
- package/dist/chunk-PUQLKLQX.js +731 -0
- package/dist/chunk-QROKS65G.js +76 -0
- package/dist/chunk-R54I2N2T.js +818 -0
- package/dist/chunk-RCFYQHUP.js +818 -0
- package/dist/chunk-RJTND4YS.js +284 -0
- package/dist/chunk-SBLHQMMZ.js +81 -0
- package/dist/chunk-SG2ANG5C.js +123 -0
- package/dist/chunk-SVFNKSZV.js +333 -0
- package/dist/chunk-TAQT2DC7.js +330 -0
- package/dist/chunk-TB7HFW7M.js +127 -0
- package/dist/chunk-UUKDAIH2.js +731 -0
- package/dist/chunk-V6VEFEEH.js +1345 -0
- package/dist/chunk-VIO2ALGH.js +290 -0
- package/dist/chunk-VKCUSNJW.js +377 -0
- package/dist/chunk-VRPPJFIQ.js +1921 -0
- package/dist/chunk-WP3PVBBP.js +204 -0
- package/dist/chunk-WQEUY7DC.js +129 -0
- package/dist/chunk-WXMXUKCA.js +262 -0
- package/dist/chunk-X2WBH2IO.js +297 -0
- package/dist/chunk-X33TSJNO.js +394 -0
- package/dist/chunk-X7MMI2UI.js +89 -0
- package/dist/chunk-XG3BQZIK.js +85 -0
- package/dist/chunk-XIKBIAOS.js +75 -0
- package/dist/chunk-XPEB545Q.js +54 -0
- package/dist/chunk-XWH2MLWS.js +330 -0
- package/dist/chunk-YH7V73XW.js +89 -0
- package/dist/chunk-YMLM5D65.js +135 -0
- package/dist/chunk-YNJPRQ6J.js +377 -0
- package/dist/chunk-YSNEHBI6.js +551 -0
- package/dist/chunk-ZD6BMW2K.js +33 -0
- package/dist/chunk-ZKG5IYCG.js +668 -0
- package/dist/chunk-ZU4K7ZNX.js +197 -0
- package/dist/co-activation-HZMJC34P.js +72 -0
- package/dist/co-occurrence-AVYXRV4L.js +74 -0
- package/dist/core-memory-NPJCVUMF.js +110 -0
- package/dist/core-memory-OKGXL33Z.js +110 -0
- package/dist/crdt-sync-ZCH55JNR.js +33 -0
- package/dist/crm-webhook-6OMVUUGR.js +10 -0
- package/dist/crm-webhook-UCWF3XDB.js +10 -0
- package/dist/cto-delegation-gate-JFZFZGC2.js +206 -0
- package/dist/cto-delegation-gate-K32M4GVM.js +206 -0
- package/dist/daemon-orchestration-2Q7BYOHC.js +135 -0
- package/dist/daemon-orchestration-4RJ2CZJL.js +135 -0
- package/dist/db-backup-5GA2YFDX.js +33 -0
- package/dist/dreaming-I6KXO6E2.js +32 -0
- package/dist/dreaming-NJBK5ILR.js +32 -0
- package/dist/exe-drift-VSMIMHL4.js +68 -0
- package/dist/exe-export-DVHHIA6Y.js +73 -0
- package/dist/exe-export-GIVQDENS.js +73 -0
- package/dist/exe-import-7N46LSMQ.js +76 -0
- package/dist/exe-import-FINYUV5T.js +76 -0
- package/dist/exe-key-H45JY44F.js +579 -0
- package/dist/exe-key-MAEQGTB7.js +579 -0
- package/dist/exe-snapshot-3TEM3BFD.js +164 -0
- package/dist/exe-snapshot-HECGUHL3.js +164 -0
- package/dist/fast-db-init-3CNTADVO.js +7 -0
- package/dist/fast-db-init-HXCS2AP5.js +7 -0
- package/dist/gateway/index.js +8 -8
- package/dist/git-staleness-YCEBBIVK.js +110 -0
- package/dist/git-task-sweep-H34STRNT.js +40 -0
- package/dist/git-task-sweep-YL7NLDCK.js +40 -0
- package/dist/global-procedures-IHZM6C2K.js +20 -0
- package/dist/graph-auto-extract-RZQ3MHP2.js +162 -0
- package/dist/hooks/bug-report-worker.js +12 -12
- package/dist/hooks/codex-stop-task-finalizer.js +12 -12
- package/dist/hooks/commit-complete.js +12 -12
- package/dist/hooks/error-recall.js +6 -6
- package/dist/hooks/exe-heartbeat-hook.js +3 -3
- package/dist/hooks/ingest.js +6 -6
- package/dist/hooks/instructions-loaded.js +4 -4
- package/dist/hooks/manifest.json +19 -19
- package/dist/hooks/notification.js +4 -4
- package/dist/hooks/post-compact.js +11 -11
- package/dist/hooks/post-tool-combined.js +5 -5
- package/dist/hooks/pre-compact.js +12 -12
- package/dist/hooks/pre-tool-use.js +15 -15
- package/dist/hooks/prompt-submit.js +21 -21
- package/dist/hooks/session-end.js +16 -16
- package/dist/hooks/session-start.js +10 -10
- package/dist/hooks/stop.js +15 -15
- package/dist/hooks/subagent-stop.js +11 -11
- package/dist/hooks/summary-worker.js +15 -15
- package/dist/index.js +18 -18
- package/dist/installer-4EW5ZDGD.js +296 -0
- package/dist/installer-B2JTQO55.js +38 -0
- package/dist/installer-MIL352T7.js +342 -0
- package/dist/lib/agent-config.js +9 -3
- package/dist/lib/cloud-sync.js +4 -4
- package/dist/lib/consolidation.js +5 -5
- package/dist/lib/database.js +2 -2
- package/dist/lib/db.js +2 -2
- package/dist/lib/employee-templates.js +4 -4
- package/dist/lib/employees.js +2 -2
- package/dist/lib/exe-daemon.js +34 -34
- package/dist/lib/hybrid-search.js +5 -5
- package/dist/lib/identity.js +2 -2
- package/dist/lib/messaging.js +10 -10
- package/dist/lib/reminders.js +3 -3
- package/dist/lib/schedules.js +5 -5
- package/dist/lib/session-registry.js +4 -4
- package/dist/lib/skill-learning.js +4 -4
- package/dist/lib/store.js +4 -4
- package/dist/lib/task-router.js +3 -3
- package/dist/lib/tasks.js +11 -11
- package/dist/lib/tmux-routing.js +9 -9
- package/dist/lib/token-spend.js +3 -3
- package/dist/mcp/register-tools.js +54 -54
- package/dist/mcp/server.js +55 -55
- package/dist/mcp/tools/complete-reminder.js +4 -4
- package/dist/mcp/tools/create-reminder.js +4 -4
- package/dist/mcp/tools/create-task.js +13 -13
- package/dist/mcp/tools/deactivate-behavior.js +5 -5
- package/dist/mcp/tools/list-reminders.js +4 -4
- package/dist/mcp/tools/list-tasks.js +13 -13
- package/dist/mcp/tools/send-message.js +12 -12
- package/dist/mcp/tools/update-task.js +12 -12
- package/dist/mcp-http-config-OJQR246S.js +27 -0
- package/dist/memory-cards-IPULSQFA.js +174 -0
- package/dist/memory-graph-extractor-3TZZOKHY.js +17 -0
- package/dist/memory-poisoning-defense-SGUGR5YJ.js +225 -0
- package/dist/memory-reflection-H3WGCEM6.js +238 -0
- package/dist/notifications-VWPO6NJF.js +45 -0
- package/dist/notifications-WCSRQN2V.js +45 -0
- package/dist/orchestration-events-O5PSDEIO.js +25 -0
- package/dist/orchestrator-RAPEJUOI.js +33 -0
- package/dist/orchestrator-XPG6LJAI.js +33 -0
- package/dist/pipeline-router-5NT6FUC3.js +13 -0
- package/dist/pipeline-router-KSUXONDT.js +13 -0
- package/dist/plan-limits-53NXLNDQ.js +26 -0
- package/dist/project-boot-ITN3FZMM.js +299 -0
- package/dist/projection-worker-27XX5M2W.js +964 -0
- package/dist/reranker-GU7L2PJX.js +19 -0
- package/dist/reranker-TZEXIJAN.js +19 -0
- package/dist/review-polling-FA2J2Q5O.js +124 -0
- package/dist/review-polling-MLS4BQ3N.js +124 -0
- package/dist/runtime/index.js +12 -12
- package/dist/session-events-WWGF3B2N.js +36 -0
- package/dist/session-events-ZHXXAH6B.js +36 -0
- package/dist/session-kill-telemetry-O4TJHHOZ.js +29 -0
- package/dist/session-scope-CQXB7VMH.js +86 -0
- package/dist/session-scope-HHUMJYF6.js +86 -0
- package/dist/setup-wizard-UM2RHSBJ.js +12 -0
- package/dist/skill-refinement-MJPOHYD5.js +157 -0
- package/dist/skill-refinement-NVUBRK22.js +157 -0
- package/dist/stack-release-2KSOYDIV.js +712 -0
- package/dist/stack-release-BAPCXMXW.js +713 -0
- package/dist/stack-release-W4TWTEZP.js +731 -0
- package/dist/stack-update-QQA64STQ.js +52 -0
- package/dist/steward-gate-VLE7OCKO.js +13 -0
- package/dist/task-enforcement-FUHDL6UR.js +391 -0
- package/dist/task-enforcement-QL3K4N3F.js +391 -0
- package/dist/task-scope-TZYMB634.js +35 -0
- package/dist/task-scope-ZVLUBS4C.js +35 -0
- package/dist/tasks-crud-4MSLJWXE.js +77 -0
- package/dist/tasks-crud-HIPXKRKX.js +77 -0
- package/dist/tasks-notify-7JBUNE7R.js +38 -0
- package/dist/tasks-notify-UPIJ3L4O.js +38 -0
- package/dist/tasks-review-5SJSFTUB.js +47 -0
- package/dist/tasks-review-JHSYBR5I.js +47 -0
- package/dist/telemetry-upload-BSGOXGUP.js +739 -0
- package/dist/telemetry-upload-LTX3C5HZ.js +739 -0
- package/dist/token-budget-2CDWQU3Q.js +84 -0
- package/dist/tool-telemetry-7YS7EN7B.js +17 -0
- package/dist/tui/App.js +17 -17
- package/dist/tui-data-GDGBOS6G.js +258 -0
- package/dist/tui-data-VXF2RBVM.js +258 -0
- package/dist/wiki-acl-MJIMXRQV.js +111 -0
- package/dist/worker-gate-WQGTZOSM.js +21 -0
- package/dist/worker-gate-X2YDTKTL.js +21 -0
- package/dist/workflow-engine-CYXRZXBM.js +28 -0
- package/dist/workflow-engine-KMLAXVA4.js +28 -0
- package/dist/worktree-NLSKVRNC.js +26 -0
- package/dist/worktree-sweep-44TMEPLE.js +19 -0
- package/package.json +1 -1
- package/release-notes.json +26 -40
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import {
|
|
2
|
+
storeBehavior
|
|
3
|
+
} from "./chunk-O5OMH6LI.js";
|
|
4
|
+
import {
|
|
5
|
+
getClient
|
|
6
|
+
} from "./chunk-CHBHR5W6.js";
|
|
7
|
+
import {
|
|
8
|
+
loadConfig
|
|
9
|
+
} from "./chunk-VXIMSRTO.js";
|
|
10
|
+
|
|
11
|
+
// src/lib/skill-learning.ts
|
|
12
|
+
import crypto from "crypto";
|
|
13
|
+
var DEFAULT_SKILL_THRESHOLD = 3;
|
|
14
|
+
async function extractTrajectory(taskId, agentId) {
|
|
15
|
+
const client = getClient();
|
|
16
|
+
const result = await client.execute({
|
|
17
|
+
sql: `SELECT tool_name, raw_text
|
|
18
|
+
FROM memories
|
|
19
|
+
WHERE task_id = ? AND agent_id = ?
|
|
20
|
+
ORDER BY timestamp ASC`,
|
|
21
|
+
args: [taskId, agentId]
|
|
22
|
+
});
|
|
23
|
+
if (result.rows.length === 0) return [];
|
|
24
|
+
const rawTools = result.rows.map((r) => {
|
|
25
|
+
const toolName = String(r.tool_name);
|
|
26
|
+
if (toolName === "Bash") {
|
|
27
|
+
const text = String(r.raw_text);
|
|
28
|
+
const cmdMatch = text.match(/(?:command|Command).*?[:\s]+"?(\w+)/);
|
|
29
|
+
return cmdMatch ? `Bash:${cmdMatch[1]}` : "Bash";
|
|
30
|
+
}
|
|
31
|
+
return toolName;
|
|
32
|
+
});
|
|
33
|
+
const signature = [];
|
|
34
|
+
for (const tool of rawTools) {
|
|
35
|
+
if (signature.length === 0 || signature[signature.length - 1] !== tool) {
|
|
36
|
+
signature.push(tool);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return signature;
|
|
40
|
+
}
|
|
41
|
+
function hashSignature(signature) {
|
|
42
|
+
return crypto.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
43
|
+
}
|
|
44
|
+
async function storeTrajectory(opts) {
|
|
45
|
+
const client = getClient();
|
|
46
|
+
const id = crypto.randomUUID();
|
|
47
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
48
|
+
const signatureHash = hashSignature(opts.signature);
|
|
49
|
+
await client.execute({
|
|
50
|
+
sql: `INSERT INTO trajectories (id, task_id, agent_id, project_name, task_title, signature, signature_hash, tool_count, created_at)
|
|
51
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
52
|
+
args: [
|
|
53
|
+
id,
|
|
54
|
+
opts.taskId,
|
|
55
|
+
opts.agentId,
|
|
56
|
+
opts.projectName,
|
|
57
|
+
opts.taskTitle,
|
|
58
|
+
JSON.stringify(opts.signature),
|
|
59
|
+
signatureHash,
|
|
60
|
+
opts.signature.length,
|
|
61
|
+
now
|
|
62
|
+
]
|
|
63
|
+
});
|
|
64
|
+
return id;
|
|
65
|
+
}
|
|
66
|
+
async function findSimilarTrajectories(signature, threshold = DEFAULT_SKILL_THRESHOLD) {
|
|
67
|
+
const client = getClient();
|
|
68
|
+
const hash = hashSignature(signature);
|
|
69
|
+
const result = await client.execute({
|
|
70
|
+
sql: `SELECT id, task_id, agent_id, project_name, task_title, signature, signature_hash, tool_count, skill_id, created_at
|
|
71
|
+
FROM trajectories
|
|
72
|
+
WHERE signature_hash = ?
|
|
73
|
+
ORDER BY created_at DESC
|
|
74
|
+
LIMIT 20`,
|
|
75
|
+
args: [hash]
|
|
76
|
+
});
|
|
77
|
+
const mapRow = (r) => ({
|
|
78
|
+
id: String(r.id),
|
|
79
|
+
taskId: String(r.task_id),
|
|
80
|
+
agentId: String(r.agent_id),
|
|
81
|
+
projectName: String(r.project_name),
|
|
82
|
+
taskTitle: String(r.task_title),
|
|
83
|
+
signature: JSON.parse(String(r.signature)),
|
|
84
|
+
signatureHash: String(r.signature_hash),
|
|
85
|
+
toolCount: Number(r.tool_count),
|
|
86
|
+
skillId: r.skill_id ? String(r.skill_id) : null,
|
|
87
|
+
createdAt: String(r.created_at)
|
|
88
|
+
});
|
|
89
|
+
const matches = result.rows.map(mapRow);
|
|
90
|
+
if (matches.length >= threshold) return matches;
|
|
91
|
+
const nearResult = await client.execute({
|
|
92
|
+
sql: `SELECT id, task_id, agent_id, project_name, task_title, signature, signature_hash, tool_count, skill_id, created_at
|
|
93
|
+
FROM trajectories
|
|
94
|
+
WHERE tool_count BETWEEN ? AND ?
|
|
95
|
+
AND signature_hash != ?
|
|
96
|
+
ORDER BY created_at DESC
|
|
97
|
+
LIMIT 50`,
|
|
98
|
+
args: [
|
|
99
|
+
Math.max(1, signature.length - 3),
|
|
100
|
+
signature.length + 3,
|
|
101
|
+
hash
|
|
102
|
+
]
|
|
103
|
+
});
|
|
104
|
+
for (const r of nearResult.rows) {
|
|
105
|
+
const candidateSig = JSON.parse(String(r.signature));
|
|
106
|
+
if (editDistance(signature, candidateSig) <= 2) {
|
|
107
|
+
matches.push(mapRow(r));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return matches;
|
|
111
|
+
}
|
|
112
|
+
async function captureTrajectory(opts) {
|
|
113
|
+
const signature = await extractTrajectory(opts.taskId, opts.agentId);
|
|
114
|
+
if (signature.length < 3) {
|
|
115
|
+
return { trajectoryId: "", similarCount: 0, similar: [] };
|
|
116
|
+
}
|
|
117
|
+
const trajectoryId = await storeTrajectory({
|
|
118
|
+
taskId: opts.taskId,
|
|
119
|
+
agentId: opts.agentId,
|
|
120
|
+
projectName: opts.projectName,
|
|
121
|
+
taskTitle: opts.taskTitle,
|
|
122
|
+
signature
|
|
123
|
+
});
|
|
124
|
+
const similar = await findSimilarTrajectories(
|
|
125
|
+
signature,
|
|
126
|
+
opts.skillThreshold ?? DEFAULT_SKILL_THRESHOLD
|
|
127
|
+
);
|
|
128
|
+
return { trajectoryId, similarCount: similar.length, similar };
|
|
129
|
+
}
|
|
130
|
+
function buildExtractionPrompt(trajectories) {
|
|
131
|
+
const items = trajectories.map((t, i) => {
|
|
132
|
+
const sig = t.signature.join(" \u2192 ");
|
|
133
|
+
return `Task ${i + 1}: "${t.taskTitle}" (${t.agentId}, ${t.projectName}) \u2014 ${t.toolCount} tool calls
|
|
134
|
+
Signature: ${sig}`;
|
|
135
|
+
}).join("\n\n");
|
|
136
|
+
return `You are analyzing ${trajectories.length} completed tasks that followed similar procedures:
|
|
137
|
+
|
|
138
|
+
${items}
|
|
139
|
+
|
|
140
|
+
Extract the reusable procedure. Format your response EXACTLY like this:
|
|
141
|
+
|
|
142
|
+
SKILL: {name \u2014 short, descriptive}
|
|
143
|
+
TRIGGER: {when to use this \u2014 one sentence}
|
|
144
|
+
STEPS:
|
|
145
|
+
1. ...
|
|
146
|
+
2. ...
|
|
147
|
+
PITFALLS: {common mistakes to avoid}
|
|
148
|
+
|
|
149
|
+
Be specific and actionable. Include tool names, file patterns, and concrete commands where applicable.`;
|
|
150
|
+
}
|
|
151
|
+
async function extractSkill(trajectories, model) {
|
|
152
|
+
if (trajectories.length === 0) return null;
|
|
153
|
+
const config = await loadConfig();
|
|
154
|
+
const skillModel = model ?? config.skillModel;
|
|
155
|
+
const Anthropic = (await import("@anthropic-ai/sdk")).default;
|
|
156
|
+
const client = new Anthropic();
|
|
157
|
+
const prompt = buildExtractionPrompt(trajectories);
|
|
158
|
+
const response = await client.messages.create({
|
|
159
|
+
model: skillModel,
|
|
160
|
+
max_tokens: 500,
|
|
161
|
+
messages: [{ role: "user", content: prompt }]
|
|
162
|
+
});
|
|
163
|
+
const textBlock = response.content.find((b) => b.type === "text");
|
|
164
|
+
const skillText = textBlock?.text;
|
|
165
|
+
if (!skillText) return null;
|
|
166
|
+
const agentId = trajectories[0].agentId;
|
|
167
|
+
const projectName = trajectories[0].projectName;
|
|
168
|
+
const skillId = await storeBehavior({
|
|
169
|
+
agentId,
|
|
170
|
+
content: skillText,
|
|
171
|
+
domain: "skill",
|
|
172
|
+
projectName
|
|
173
|
+
});
|
|
174
|
+
const dbClient = getClient();
|
|
175
|
+
for (const t of trajectories) {
|
|
176
|
+
await dbClient.execute({
|
|
177
|
+
sql: "UPDATE trajectories SET skill_id = ? WHERE id = ?",
|
|
178
|
+
args: [skillId, t.id]
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
process.stderr.write(
|
|
182
|
+
`[skill-learning] Skill extracted from ${trajectories.length} trajectories \u2192 behavior ${skillId}
|
|
183
|
+
`
|
|
184
|
+
);
|
|
185
|
+
return skillId;
|
|
186
|
+
}
|
|
187
|
+
async function captureAndLearn(opts) {
|
|
188
|
+
try {
|
|
189
|
+
const config = await loadConfig();
|
|
190
|
+
if (!config.skillLearning) return;
|
|
191
|
+
const { trajectoryId, similarCount, similar } = await captureTrajectory({
|
|
192
|
+
...opts,
|
|
193
|
+
skillThreshold: config.skillThreshold
|
|
194
|
+
});
|
|
195
|
+
if (!trajectoryId) return;
|
|
196
|
+
if (similarCount >= config.skillThreshold) {
|
|
197
|
+
const unprocessed = similar.filter((t) => !t.skillId);
|
|
198
|
+
if (unprocessed.length >= config.skillThreshold) {
|
|
199
|
+
extractSkill(unprocessed, config.skillModel).catch((err) => {
|
|
200
|
+
process.stderr.write(
|
|
201
|
+
`[skill-learning] Extraction failed: ${err instanceof Error ? err.message : String(err)}
|
|
202
|
+
`
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
} catch (err) {
|
|
208
|
+
process.stderr.write(
|
|
209
|
+
`[skill-learning] captureAndLearn failed: ${err instanceof Error ? err.message : String(err)}
|
|
210
|
+
`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async function sweepTrajectories(threshold, model) {
|
|
215
|
+
const config = await loadConfig();
|
|
216
|
+
if (!config.skillLearning) return { clustersProcessed: 0, skillsExtracted: 0 };
|
|
217
|
+
const t = threshold ?? config.skillThreshold;
|
|
218
|
+
const client = getClient();
|
|
219
|
+
const result = await client.execute({
|
|
220
|
+
sql: `SELECT signature_hash, COUNT(*) as cnt
|
|
221
|
+
FROM trajectories
|
|
222
|
+
WHERE skill_id IS NULL
|
|
223
|
+
GROUP BY signature_hash
|
|
224
|
+
HAVING cnt >= ?
|
|
225
|
+
ORDER BY cnt DESC
|
|
226
|
+
LIMIT 10`,
|
|
227
|
+
args: [t]
|
|
228
|
+
});
|
|
229
|
+
let clustersProcessed = 0;
|
|
230
|
+
let skillsExtracted = 0;
|
|
231
|
+
for (const row of result.rows) {
|
|
232
|
+
const hash = String(row.signature_hash);
|
|
233
|
+
const trajResult = await client.execute({
|
|
234
|
+
sql: `SELECT id, task_id, agent_id, project_name, task_title, signature, signature_hash, tool_count, created_at
|
|
235
|
+
FROM trajectories
|
|
236
|
+
WHERE signature_hash = ? AND skill_id IS NULL
|
|
237
|
+
ORDER BY created_at DESC
|
|
238
|
+
LIMIT 10`,
|
|
239
|
+
args: [hash]
|
|
240
|
+
});
|
|
241
|
+
const trajectories = trajResult.rows.map((r) => ({
|
|
242
|
+
id: String(r.id),
|
|
243
|
+
taskId: String(r.task_id),
|
|
244
|
+
agentId: String(r.agent_id),
|
|
245
|
+
projectName: String(r.project_name),
|
|
246
|
+
taskTitle: String(r.task_title),
|
|
247
|
+
signature: JSON.parse(String(r.signature)),
|
|
248
|
+
signatureHash: String(r.signature_hash),
|
|
249
|
+
toolCount: Number(r.tool_count),
|
|
250
|
+
skillId: null,
|
|
251
|
+
createdAt: String(r.created_at)
|
|
252
|
+
}));
|
|
253
|
+
if (trajectories.length >= t) {
|
|
254
|
+
clustersProcessed++;
|
|
255
|
+
const skillId = await extractSkill(trajectories, model ?? config.skillModel);
|
|
256
|
+
if (skillId) skillsExtracted++;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return { clustersProcessed, skillsExtracted };
|
|
260
|
+
}
|
|
261
|
+
function editDistance(a, b) {
|
|
262
|
+
const m = a.length;
|
|
263
|
+
const n = b.length;
|
|
264
|
+
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
|
|
265
|
+
for (let i = 0; i <= m; i++) dp[i][0] = i;
|
|
266
|
+
for (let j = 0; j <= n; j++) dp[0][j] = j;
|
|
267
|
+
for (let i = 1; i <= m; i++) {
|
|
268
|
+
for (let j = 1; j <= n; j++) {
|
|
269
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
270
|
+
dp[i][j] = Math.min(
|
|
271
|
+
dp[i - 1][j] + 1,
|
|
272
|
+
dp[i][j - 1] + 1,
|
|
273
|
+
dp[i - 1][j - 1] + cost
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return dp[m][n];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export {
|
|
281
|
+
extractTrajectory,
|
|
282
|
+
hashSignature,
|
|
283
|
+
storeTrajectory,
|
|
284
|
+
findSimilarTrajectories,
|
|
285
|
+
captureTrajectory,
|
|
286
|
+
extractSkill,
|
|
287
|
+
captureAndLearn,
|
|
288
|
+
sweepTrajectories,
|
|
289
|
+
editDistance
|
|
290
|
+
};
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractMemoryGraph
|
|
3
|
+
} from "./chunk-E2FPTCL5.js";
|
|
4
|
+
import {
|
|
5
|
+
flushBatch,
|
|
6
|
+
writeMemory
|
|
7
|
+
} from "./chunk-R54I2N2T.js";
|
|
8
|
+
import {
|
|
9
|
+
extractKeywords
|
|
10
|
+
} from "./chunk-CHCA3ZM2.js";
|
|
11
|
+
import {
|
|
12
|
+
getActiveAgent
|
|
13
|
+
} from "./chunk-44MXQ47E.js";
|
|
14
|
+
|
|
15
|
+
// src/mcp/tools/import-conversations.ts
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import crypto from "crypto";
|
|
18
|
+
import { readdir, readFile, stat } from "fs/promises";
|
|
19
|
+
import path, { resolve, normalize } from "path";
|
|
20
|
+
import os from "os";
|
|
21
|
+
|
|
22
|
+
// src/lib/conversation-parser.ts
|
|
23
|
+
function detectFormat(content, filePath) {
|
|
24
|
+
const lower = filePath.toLowerCase();
|
|
25
|
+
if (lower.endsWith(".md") || lower.endsWith(".markdown")) return "markdown";
|
|
26
|
+
const trimmed = content.trimStart();
|
|
27
|
+
if (!trimmed.startsWith("[") && !trimmed.startsWith("{")) return "markdown";
|
|
28
|
+
try {
|
|
29
|
+
const parsed = JSON.parse(trimmed);
|
|
30
|
+
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
31
|
+
const first = parsed[0];
|
|
32
|
+
if (first && typeof first === "object") {
|
|
33
|
+
if ("mapping" in first && typeof first.mapping === "object") return "chatgpt";
|
|
34
|
+
if ("chat_messages" in first) return "claude";
|
|
35
|
+
if ("uuid" in first && "name" in first) return "claude";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return "generic";
|
|
39
|
+
} catch {
|
|
40
|
+
return "markdown";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function extractChatGPTContent(node) {
|
|
44
|
+
const parts = node.message?.content?.parts;
|
|
45
|
+
if (!Array.isArray(parts)) return "";
|
|
46
|
+
return parts.filter((p) => typeof p === "string").join("\n").trim();
|
|
47
|
+
}
|
|
48
|
+
function parseChatGPT(content, sourceFile = "conversations.json") {
|
|
49
|
+
const data = JSON.parse(content);
|
|
50
|
+
if (!Array.isArray(data)) return [];
|
|
51
|
+
return data.map((conv) => {
|
|
52
|
+
const title = conv.title ?? "Untitled";
|
|
53
|
+
const messages = [];
|
|
54
|
+
if (conv.mapping) {
|
|
55
|
+
const nodes = Object.values(conv.mapping).filter((n) => n.message?.content?.parts && extractChatGPTContent(n).length > 0).sort((a, b) => (a.message?.create_time ?? 0) - (b.message?.create_time ?? 0));
|
|
56
|
+
for (const node of nodes) {
|
|
57
|
+
const role = node.message?.author?.role ?? "unknown";
|
|
58
|
+
const text = extractChatGPTContent(node);
|
|
59
|
+
if (!text) continue;
|
|
60
|
+
const createTime = node.message?.create_time;
|
|
61
|
+
messages.push({
|
|
62
|
+
role,
|
|
63
|
+
content: text,
|
|
64
|
+
timestamp: createTime ? new Date(createTime * 1e3).toISOString() : void 0
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { title, messages, sourceFile };
|
|
69
|
+
}).filter((c) => c.messages.length > 0);
|
|
70
|
+
}
|
|
71
|
+
function parseClaude(content, sourceFile = "conversations.json") {
|
|
72
|
+
const data = JSON.parse(content);
|
|
73
|
+
if (!Array.isArray(data)) return [];
|
|
74
|
+
return data.map((conv) => {
|
|
75
|
+
const title = conv.name ?? conv.uuid ?? "Untitled";
|
|
76
|
+
const messages = [];
|
|
77
|
+
if (conv.chat_messages) {
|
|
78
|
+
for (const msg of conv.chat_messages) {
|
|
79
|
+
let text = msg.text ?? "";
|
|
80
|
+
if (!text && Array.isArray(msg.content)) {
|
|
81
|
+
text = msg.content.filter((c) => c.type === "text" && c.text).map((c) => c.text).join("\n");
|
|
82
|
+
}
|
|
83
|
+
if (!text.trim()) continue;
|
|
84
|
+
messages.push({
|
|
85
|
+
role: msg.sender ?? "unknown",
|
|
86
|
+
content: text.trim(),
|
|
87
|
+
timestamp: msg.created_at
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return { title, messages, sourceFile };
|
|
92
|
+
}).filter((c) => c.messages.length > 0);
|
|
93
|
+
}
|
|
94
|
+
function parseGeneric(content, sourceFile = "conversations.json") {
|
|
95
|
+
const trimmed = content.trimStart();
|
|
96
|
+
try {
|
|
97
|
+
const data = JSON.parse(trimmed);
|
|
98
|
+
if (Array.isArray(data)) {
|
|
99
|
+
if (data.length > 0 && typeof data[0] === "object" && ("role" in data[0] || "content" in data[0])) {
|
|
100
|
+
const messages = data.map((msg) => ({
|
|
101
|
+
role: msg.role ?? "unknown",
|
|
102
|
+
content: (msg.content ?? msg.text ?? "").trim(),
|
|
103
|
+
timestamp: msg.timestamp ?? msg.created_at
|
|
104
|
+
})).filter((m) => m.content.length > 0);
|
|
105
|
+
if (messages.length > 0) {
|
|
106
|
+
return [{ title: sourceFile, messages, sourceFile }];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
return parseMarkdown(content, sourceFile);
|
|
113
|
+
}
|
|
114
|
+
function parseMarkdown(content, sourceFile = "conversation.md") {
|
|
115
|
+
const lines = content.split("\n");
|
|
116
|
+
const conversations = [];
|
|
117
|
+
let currentTitle = sourceFile;
|
|
118
|
+
let currentMessages = [];
|
|
119
|
+
let currentRole = "user";
|
|
120
|
+
let currentContent = [];
|
|
121
|
+
function flushMessage() {
|
|
122
|
+
const text = currentContent.join("\n").trim();
|
|
123
|
+
if (text) {
|
|
124
|
+
currentMessages.push({ role: currentRole, content: text });
|
|
125
|
+
}
|
|
126
|
+
currentContent = [];
|
|
127
|
+
}
|
|
128
|
+
function flushConversation() {
|
|
129
|
+
flushMessage();
|
|
130
|
+
if (currentMessages.length > 0) {
|
|
131
|
+
conversations.push({ title: currentTitle, messages: [...currentMessages], sourceFile });
|
|
132
|
+
}
|
|
133
|
+
currentMessages = [];
|
|
134
|
+
}
|
|
135
|
+
for (const line of lines) {
|
|
136
|
+
const h1Match = line.match(/^# (.+)$/);
|
|
137
|
+
if (h1Match) {
|
|
138
|
+
flushConversation();
|
|
139
|
+
currentTitle = h1Match[1];
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const roleMatch = line.match(/^(?:##\s+|\*\*)(user|assistant|human|system|ai|claude|gpt)(?:\*\*)?:?\s*/i);
|
|
143
|
+
if (roleMatch) {
|
|
144
|
+
flushMessage();
|
|
145
|
+
const raw = roleMatch[1].toLowerCase();
|
|
146
|
+
currentRole = raw === "human" || raw === "user" ? "user" : raw === "assistant" || raw === "ai" || raw === "claude" || raw === "gpt" ? "assistant" : raw;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
currentContent.push(line);
|
|
150
|
+
}
|
|
151
|
+
flushConversation();
|
|
152
|
+
return conversations;
|
|
153
|
+
}
|
|
154
|
+
function conversationsToMemories(convos, format = "generic") {
|
|
155
|
+
const memories = [];
|
|
156
|
+
for (const convo of convos) {
|
|
157
|
+
for (const msg of convo.messages) {
|
|
158
|
+
if (msg.content.length < 10) continue;
|
|
159
|
+
memories.push({
|
|
160
|
+
text: msg.content,
|
|
161
|
+
kind: "conversation",
|
|
162
|
+
metadata: {
|
|
163
|
+
source_type: "import",
|
|
164
|
+
source_format: format,
|
|
165
|
+
source_path: convo.sourceFile,
|
|
166
|
+
conversation_title: convo.title,
|
|
167
|
+
role: msg.role,
|
|
168
|
+
original_timestamp: msg.timestamp
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return memories;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/mcp/tools/import-conversations.ts
|
|
177
|
+
var DEFAULT_CAP = 5e4;
|
|
178
|
+
var YIELD_INTERVAL = 50;
|
|
179
|
+
async function readInputFiles(inputPath) {
|
|
180
|
+
const files = [];
|
|
181
|
+
const info = await stat(inputPath);
|
|
182
|
+
if (info.isDirectory()) {
|
|
183
|
+
const entries = await readdir(inputPath);
|
|
184
|
+
for (const entry of entries) {
|
|
185
|
+
const fullPath = path.join(inputPath, entry);
|
|
186
|
+
const entryStat = await stat(fullPath);
|
|
187
|
+
if (entryStat.isFile() && (entry.endsWith(".json") || entry.endsWith(".md") || entry.endsWith(".txt"))) {
|
|
188
|
+
files.push({ path: fullPath, content: await readFile(fullPath, "utf-8") });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
files.push({ path: inputPath, content: await readFile(inputPath, "utf-8") });
|
|
193
|
+
}
|
|
194
|
+
return files;
|
|
195
|
+
}
|
|
196
|
+
function parseFile(content, filePath, requestedFormat) {
|
|
197
|
+
const format = requestedFormat && requestedFormat !== "auto" ? requestedFormat : detectFormat(content, filePath);
|
|
198
|
+
let conversations;
|
|
199
|
+
switch (format) {
|
|
200
|
+
case "chatgpt":
|
|
201
|
+
conversations = parseChatGPT(content, filePath);
|
|
202
|
+
break;
|
|
203
|
+
case "claude":
|
|
204
|
+
conversations = parseClaude(content, filePath);
|
|
205
|
+
break;
|
|
206
|
+
case "markdown":
|
|
207
|
+
conversations = parseMarkdown(content, filePath);
|
|
208
|
+
break;
|
|
209
|
+
case "generic":
|
|
210
|
+
default:
|
|
211
|
+
conversations = parseGeneric(content, filePath);
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
return { format, conversations };
|
|
215
|
+
}
|
|
216
|
+
function registerImportConversations(server) {
|
|
217
|
+
server.registerTool(
|
|
218
|
+
"import_conversations",
|
|
219
|
+
{
|
|
220
|
+
title: "Import Conversations",
|
|
221
|
+
description: "Import ChatGPT, Claude, or generic conversation exports into memory. Parses, extracts keywords + graph entities, stores with proper metadata. Deduplicates via content_hash. Supports JSON, markdown, and directory input.",
|
|
222
|
+
inputSchema: {
|
|
223
|
+
path: z.string().describe("File or directory path to import"),
|
|
224
|
+
format: z.enum(["chatgpt", "claude", "generic", "markdown", "auto"]).default("auto").describe("Source format (default: auto-detect)"),
|
|
225
|
+
project_name: z.string().optional().describe("Project name for imported memories"),
|
|
226
|
+
dry_run: z.boolean().default(false).describe("Parse and report counts without storing"),
|
|
227
|
+
force: z.boolean().default(false).describe("Override 50K memory cap"),
|
|
228
|
+
agent_id: z.string().optional().describe("Agent to attribute memories to (default: current agent)")
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
async ({ path: inputPath, format: requestedFormat, project_name, dry_run, force, agent_id: overrideAgentId }) => {
|
|
232
|
+
const { agentId: currentAgentId, agentRole } = getActiveAgent();
|
|
233
|
+
const agentId = overrideAgentId ?? currentAgentId;
|
|
234
|
+
const projectName = project_name ?? "imported";
|
|
235
|
+
const resolvedPath = resolve(normalize(inputPath));
|
|
236
|
+
const home = process.env.HOME || os.homedir();
|
|
237
|
+
const blocked = ["/etc", "/root", "/var", "/usr", "/sys", "/proc"];
|
|
238
|
+
const sensitiveHome = [".ssh", ".gnupg", ".aws", ".config/gcloud"];
|
|
239
|
+
for (const dir of blocked) {
|
|
240
|
+
if (resolvedPath.startsWith(dir + "/") || resolvedPath === dir) {
|
|
241
|
+
return {
|
|
242
|
+
content: [{ type: "text", text: `Access denied: cannot read files from ${dir}` }],
|
|
243
|
+
isError: true
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
for (const rel of sensitiveHome) {
|
|
248
|
+
const full = resolve(home, rel);
|
|
249
|
+
if (resolvedPath.startsWith(full + "/") || resolvedPath === full) {
|
|
250
|
+
return {
|
|
251
|
+
content: [{ type: "text", text: `Access denied: cannot read files from ~/${rel}` }],
|
|
252
|
+
isError: true
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
let files;
|
|
257
|
+
try {
|
|
258
|
+
files = await readInputFiles(inputPath);
|
|
259
|
+
} catch (err) {
|
|
260
|
+
return {
|
|
261
|
+
content: [{ type: "text", text: `Error reading ${inputPath}: ${err instanceof Error ? err.message : String(err)}` }],
|
|
262
|
+
isError: true
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
if (files.length === 0) {
|
|
266
|
+
return {
|
|
267
|
+
content: [{ type: "text", text: `No importable files found at ${inputPath}` }],
|
|
268
|
+
isError: true
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
const allMemories = [];
|
|
272
|
+
let conversationCount = 0;
|
|
273
|
+
const formatCounts = {};
|
|
274
|
+
for (const file of files) {
|
|
275
|
+
const { format, conversations } = parseFile(file.content, file.path, requestedFormat);
|
|
276
|
+
formatCounts[format] = (formatCounts[format] ?? 0) + conversations.length;
|
|
277
|
+
conversationCount += conversations.length;
|
|
278
|
+
const memories = conversationsToMemories(conversations, format);
|
|
279
|
+
allMemories.push(...memories);
|
|
280
|
+
}
|
|
281
|
+
if (allMemories.length > DEFAULT_CAP && !force) {
|
|
282
|
+
return {
|
|
283
|
+
content: [{
|
|
284
|
+
type: "text",
|
|
285
|
+
text: `Import would create ${allMemories.length} memories (cap: ${DEFAULT_CAP}). Use force=true to override. Files: ${files.length}, Conversations: ${conversationCount}`
|
|
286
|
+
}],
|
|
287
|
+
isError: true
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
if (dry_run) {
|
|
291
|
+
const formatSummary2 = Object.entries(formatCounts).map(([f, c]) => `${f}: ${c}`).join(", ");
|
|
292
|
+
return {
|
|
293
|
+
content: [{
|
|
294
|
+
type: "text",
|
|
295
|
+
text: `[DRY RUN] Would import:
|
|
296
|
+
Files: ${files.length}
|
|
297
|
+
Conversations: ${conversationCount} (${formatSummary2})
|
|
298
|
+
Memories: ${allMemories.length}
|
|
299
|
+
Agent: ${agentId}
|
|
300
|
+
Project: ${projectName}`
|
|
301
|
+
}]
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
let stored = 0;
|
|
305
|
+
let skipped = 0;
|
|
306
|
+
for (let i = 0; i < allMemories.length; i++) {
|
|
307
|
+
const memory = allMemories[i];
|
|
308
|
+
try {
|
|
309
|
+
const keywords = extractKeywords(memory.text);
|
|
310
|
+
const keywordsStr = keywords.length > 0 ? keywords.join(" ") : null;
|
|
311
|
+
let entities = null;
|
|
312
|
+
try {
|
|
313
|
+
const graph = extractMemoryGraph(memory.text, agentId, projectName, "import_conversations");
|
|
314
|
+
if (graph.entities.length > 0) {
|
|
315
|
+
entities = JSON.stringify(graph.entities.map((e) => e.name));
|
|
316
|
+
}
|
|
317
|
+
} catch {
|
|
318
|
+
}
|
|
319
|
+
const id = crypto.randomUUID();
|
|
320
|
+
await writeMemory({
|
|
321
|
+
id,
|
|
322
|
+
agent_id: agentId,
|
|
323
|
+
agent_role: agentRole,
|
|
324
|
+
session_id: process.env.SESSION_ID ?? "import",
|
|
325
|
+
timestamp: memory.metadata.original_timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
326
|
+
tool_name: "import_conversations",
|
|
327
|
+
project_name: projectName,
|
|
328
|
+
has_error: false,
|
|
329
|
+
raw_text: memory.text,
|
|
330
|
+
vector: null,
|
|
331
|
+
// Backfill later
|
|
332
|
+
source_path: memory.metadata.source_path,
|
|
333
|
+
source_type: "import",
|
|
334
|
+
memory_type: "conversation",
|
|
335
|
+
tier: 3,
|
|
336
|
+
// Passive ingestion
|
|
337
|
+
importance: memory.metadata.role === "assistant" ? 5 : 4,
|
|
338
|
+
intent: "observation",
|
|
339
|
+
domain: projectName,
|
|
340
|
+
referenced_entities: entities,
|
|
341
|
+
keywords: keywordsStr,
|
|
342
|
+
valid_from: memory.metadata.original_timestamp ?? void 0
|
|
343
|
+
});
|
|
344
|
+
stored++;
|
|
345
|
+
} catch {
|
|
346
|
+
skipped++;
|
|
347
|
+
}
|
|
348
|
+
if ((i + 1) % YIELD_INTERVAL === 0) {
|
|
349
|
+
await flushBatch();
|
|
350
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
351
|
+
if ((i + 1) % (YIELD_INTERVAL * 10) === 0) {
|
|
352
|
+
process.stderr.write(`[import] ${i + 1}/${allMemories.length} memories processed, ${stored} stored
|
|
353
|
+
`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
await flushBatch();
|
|
358
|
+
const formatSummary = Object.entries(formatCounts).map(([f, c]) => `${f}: ${c}`).join(", ");
|
|
359
|
+
return {
|
|
360
|
+
content: [{
|
|
361
|
+
type: "text",
|
|
362
|
+
text: `Import complete.
|
|
363
|
+
Files: ${files.length}
|
|
364
|
+
Conversations: ${conversationCount} (${formatSummary})
|
|
365
|
+
Memories stored: ${stored}
|
|
366
|
+
Duplicates skipped: ${skipped}
|
|
367
|
+
Agent: ${agentId}
|
|
368
|
+
Project: ${projectName}`
|
|
369
|
+
}]
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export {
|
|
376
|
+
registerImportConversations
|
|
377
|
+
};
|