@iamem/amem 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +534 -0
- package/dist/activity.d.ts +27 -0
- package/dist/activity.js +202 -0
- package/dist/api/routes.d.ts +31 -0
- package/dist/api/routes.js +1345 -0
- package/dist/attest.d.ts +52 -0
- package/dist/attest.js +192 -0
- package/dist/backup-schedule.d.ts +25 -0
- package/dist/backup-schedule.js +216 -0
- package/dist/capture.d.ts +34 -0
- package/dist/capture.js +257 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1007 -0
- package/dist/context.d.ts +46 -0
- package/dist/context.js +334 -0
- package/dist/crypto.d.ts +39 -0
- package/dist/crypto.js +166 -0
- package/dist/db.d.ts +192 -0
- package/dist/db.js +666 -0
- package/dist/draft-quality.d.ts +19 -0
- package/dist/draft-quality.js +85 -0
- package/dist/embed.d.ts +76 -0
- package/dist/embed.js +331 -0
- package/dist/estimate.d.ts +32 -0
- package/dist/estimate.js +69 -0
- package/dist/freshness.d.ts +15 -0
- package/dist/freshness.js +93 -0
- package/dist/hook.d.ts +16 -0
- package/dist/hook.js +177 -0
- package/dist/hygiene-schedule.d.ts +28 -0
- package/dist/hygiene-schedule.js +221 -0
- package/dist/hygiene.d.ts +61 -0
- package/dist/hygiene.js +196 -0
- package/dist/install/claude.d.ts +6 -0
- package/dist/install/claude.js +69 -0
- package/dist/install/cursor.d.ts +7 -0
- package/dist/install/cursor.js +80 -0
- package/dist/install/hosts.d.ts +21 -0
- package/dist/install/hosts.js +186 -0
- package/dist/install/skills.d.ts +8 -0
- package/dist/install/skills.js +67 -0
- package/dist/it-pack.d.ts +20 -0
- package/dist/it-pack.js +84 -0
- package/dist/kinds.d.ts +18 -0
- package/dist/kinds.js +106 -0
- package/dist/license.d.ts +48 -0
- package/dist/license.js +172 -0
- package/dist/mcp.d.ts +40 -0
- package/dist/mcp.js +435 -0
- package/dist/paths.d.ts +11 -0
- package/dist/paths.js +55 -0
- package/dist/personal.d.ts +7 -0
- package/dist/personal.js +44 -0
- package/dist/platforms.d.ts +11 -0
- package/dist/platforms.js +32 -0
- package/dist/policy.d.ts +46 -0
- package/dist/policy.js +254 -0
- package/dist/prefs.d.ts +6 -0
- package/dist/prefs.js +11 -0
- package/dist/proposal.d.ts +90 -0
- package/dist/proposal.js +376 -0
- package/dist/publish.d.ts +28 -0
- package/dist/publish.js +57 -0
- package/dist/remember-contract.d.ts +23 -0
- package/dist/remember-contract.js +117 -0
- package/dist/repo-identity.d.ts +15 -0
- package/dist/repo-identity.js +82 -0
- package/dist/rules-sync.d.ts +7 -0
- package/dist/rules-sync.js +47 -0
- package/dist/savings-export.d.ts +49 -0
- package/dist/savings-export.js +141 -0
- package/dist/scan.d.ts +16 -0
- package/dist/scan.js +109 -0
- package/dist/search.d.ts +25 -0
- package/dist/search.js +150 -0
- package/dist/service.d.ts +20 -0
- package/dist/service.js +254 -0
- package/dist/shop.d.ts +9 -0
- package/dist/shop.js +15 -0
- package/dist/ui/server.d.ts +21 -0
- package/dist/ui/server.js +268 -0
- package/dist/vault.d.ts +25 -0
- package/dist/vault.js +42 -0
- package/dist/workspace-setup.d.ts +8 -0
- package/dist/workspace-setup.js +55 -0
- package/docs/agent-install-prompt.md +41 -0
- package/docs/backlog.md +59 -0
- package/docs/enterprise-endpoint.md +98 -0
- package/docs/license.md +54 -0
- package/docs/npm-release.md +38 -0
- package/docs/remember-contract.md +42 -0
- package/package.json +64 -0
- package/scripts/mdm-offboard.sh +14 -0
- package/skills/amem-bootstrap/SKILL.md +83 -0
- package/skills/amem-update-working-memory/SKILL.md +54 -0
- package/templates/cursor-rule.mdc +23 -0
- package/templates/mdm/co.amem.managed.plist +33 -0
- package/templates/policy.deny-default.toml +20 -0
- package/templates/policy.example.toml +22 -0
- package/ui-static/app.js +3966 -0
- package/ui-static/index.html +178 -0
- package/ui-static/orbit.js +389 -0
- package/ui-static/styles.css +2411 -0
package/dist/capture.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { insertProposalDraft, listProposalDrafts, listProposalDraftsAll, listUsageEvents, setProposalDraftStatus, } from "./db.js";
|
|
5
|
+
import { compactClaimText, compactFromNotes, inferClaimKind, isDurableCapture } from "./kinds.js";
|
|
6
|
+
import { loadPolicy } from "./policy.js";
|
|
7
|
+
import { applyProposal } from "./proposal.js";
|
|
8
|
+
import { scoreProposal } from "./draft-quality.js";
|
|
9
|
+
import { tokenJaccard } from "./search.js";
|
|
10
|
+
import { isAutoApplyAll } from "./prefs.js";
|
|
11
|
+
const TRIVIAL = /^(ok|okay|yes|yep|no|nah|thanks|thank you|continue|go ahead|sure|please)\.?$/i;
|
|
12
|
+
const SECRET = /password|api[_-]?key|secret|token\s*[:=]|begin (rsa |openssh )?private/i;
|
|
13
|
+
const PATH_RE = /\b(?:[\w.-]+\/)*[\w.-]+\.(?:ts|tsx|js|jsx|mjs|cjs|py|go|rs|md|json|yml|yaml|sql)\b/g;
|
|
14
|
+
export function isUsefulCaptureText(text) {
|
|
15
|
+
const t = text.trim();
|
|
16
|
+
if (t.length < 16)
|
|
17
|
+
return false;
|
|
18
|
+
if (TRIVIAL.test(t))
|
|
19
|
+
return false;
|
|
20
|
+
if (SECRET.test(t))
|
|
21
|
+
return false;
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
export function extractCaptureAnchors(text, repoRoot) {
|
|
25
|
+
const found = [...text.matchAll(PATH_RE)].map((m) => m[0]);
|
|
26
|
+
const unique = [...new Set(found)].slice(0, 6);
|
|
27
|
+
return unique.filter((p) => existsSync(resolve(repoRoot, p)));
|
|
28
|
+
}
|
|
29
|
+
function draftExistsWithSource(repoId, source) {
|
|
30
|
+
return listProposalDrafts(repoId, { limit: 40 }).some((d) => d.source === source);
|
|
31
|
+
}
|
|
32
|
+
function buildClaimDraft(input) {
|
|
33
|
+
if (!isUsefulCaptureText(input.prompt) && !isUsefulCaptureText(input.answer ?? "")) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const anchors = extractCaptureAnchors(`${input.prompt}\n${input.answer ?? ""}`, input.repoRoot);
|
|
37
|
+
const kind = input.forceKind ?? inferClaimKind(input.prompt, input.answer ?? "");
|
|
38
|
+
const usableAnchors = anchors.length > 0 ? anchors : kind === "constraint" || kind === "gotcha" ? ["README.md"] : [];
|
|
39
|
+
if (!isDurableCapture(input.prompt, input.answer, usableAnchors.length)) {
|
|
40
|
+
if (usableAnchors.length === 0)
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const text = compactClaimText(input.prompt, input.answer);
|
|
44
|
+
const id = `${input.idPrefix}_${createHash("sha256").update(text).digest("hex").slice(0, 12)}`;
|
|
45
|
+
return {
|
|
46
|
+
id,
|
|
47
|
+
kind,
|
|
48
|
+
anchors: usableAnchors,
|
|
49
|
+
proposal: {
|
|
50
|
+
claims: [
|
|
51
|
+
{
|
|
52
|
+
id,
|
|
53
|
+
kind,
|
|
54
|
+
text,
|
|
55
|
+
code_anchors: usableAnchors,
|
|
56
|
+
source_ref: input.sourceRef,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const AUTO_APPLY_SCORE = 60;
|
|
63
|
+
const DURABLE_AUTO_KINDS = new Set(["constraint", "gotcha", "structure", "howto", "owner"]);
|
|
64
|
+
/** High-quality durable facts apply without waiting for amem_remember. */
|
|
65
|
+
export function shouldAutoApplyProposal(proposal) {
|
|
66
|
+
const quality = scoreProposal(proposal);
|
|
67
|
+
if (quality.reject || quality.score < AUTO_APPLY_SCORE)
|
|
68
|
+
return false;
|
|
69
|
+
const claim = proposal.claims?.[0];
|
|
70
|
+
if (!claim)
|
|
71
|
+
return false;
|
|
72
|
+
if (!DURABLE_AUTO_KINDS.has((claim.kind || "").toLowerCase()))
|
|
73
|
+
return false;
|
|
74
|
+
const anchors = (claim.code_anchors ?? []).filter((a) => a && a !== "README.md");
|
|
75
|
+
return anchors.length > 0;
|
|
76
|
+
}
|
|
77
|
+
function maybeAutoApplyDraft(repoId, draft, proposal) {
|
|
78
|
+
const kinds = loadPolicy().policy.auto_apply_kinds ?? [];
|
|
79
|
+
const claimKind = proposal.claims?.[0]?.kind;
|
|
80
|
+
const policyHit = Boolean(claimKind) &&
|
|
81
|
+
kinds.length > 0 &&
|
|
82
|
+
kinds.map((k) => k.toLowerCase()).includes(claimKind.toLowerCase());
|
|
83
|
+
if (!isAutoApplyAll() && !policyHit && !shouldAutoApplyProposal(proposal))
|
|
84
|
+
return draft;
|
|
85
|
+
try {
|
|
86
|
+
applyProposal(repoId, proposal, loadPolicy().policy);
|
|
87
|
+
return setProposalDraftStatus(draft.id, "applied") ?? draft;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return draft;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Apply pending drafts (skips quality.reject). Used when auto-approve is turned on. */
|
|
94
|
+
export function applyPendingDrafts(repoId) {
|
|
95
|
+
const pending = repoId
|
|
96
|
+
? listProposalDrafts(repoId, { status: "pending", limit: 200 })
|
|
97
|
+
: listProposalDraftsAll({ status: "pending", limit: 200 });
|
|
98
|
+
const applied = [];
|
|
99
|
+
let skipped = 0;
|
|
100
|
+
const policy = loadPolicy().policy;
|
|
101
|
+
for (const draft of pending) {
|
|
102
|
+
let proposal;
|
|
103
|
+
try {
|
|
104
|
+
proposal = JSON.parse(draft.proposal_json);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
skipped += 1;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (scoreProposal(proposal).reject) {
|
|
111
|
+
skipped += 1;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
applyProposal(draft.repo_id, proposal, policy);
|
|
116
|
+
setProposalDraftStatus(draft.id, "applied");
|
|
117
|
+
applied.push(draft.id);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
skipped += 1;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return { applied, skipped };
|
|
124
|
+
}
|
|
125
|
+
function storeDraft(input) {
|
|
126
|
+
if (draftExistsWithSource(input.repo.id, input.source))
|
|
127
|
+
return null;
|
|
128
|
+
const draft = insertProposalDraft({
|
|
129
|
+
repoId: input.repo.id,
|
|
130
|
+
platform: input.platform,
|
|
131
|
+
sessionId: input.sessionId,
|
|
132
|
+
title: input.title || input.built.id,
|
|
133
|
+
proposal: input.built.proposal,
|
|
134
|
+
source: input.source,
|
|
135
|
+
});
|
|
136
|
+
return maybeAutoApplyDraft(input.repo.id, draft, input.built.proposal);
|
|
137
|
+
}
|
|
138
|
+
export function captureSessionDraft(input) {
|
|
139
|
+
let prompt = input.prompt;
|
|
140
|
+
let answer = input.answer;
|
|
141
|
+
if (input.notes && input.notes.length >= 2) {
|
|
142
|
+
const compacted = compactFromNotes(input.notes);
|
|
143
|
+
if (compacted) {
|
|
144
|
+
prompt = compacted.prompt;
|
|
145
|
+
answer = compacted.answer || answer;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const built = buildClaimDraft({
|
|
149
|
+
prompt,
|
|
150
|
+
answer,
|
|
151
|
+
repoRoot: input.repo.root_path,
|
|
152
|
+
idPrefix: "claim.session",
|
|
153
|
+
sourceRef: "session-end-draft",
|
|
154
|
+
});
|
|
155
|
+
if (!built)
|
|
156
|
+
return null;
|
|
157
|
+
if (scoreProposal(built.proposal).reject)
|
|
158
|
+
return null;
|
|
159
|
+
const first = storeDraft({
|
|
160
|
+
repo: input.repo,
|
|
161
|
+
platform: input.platform,
|
|
162
|
+
sessionId: input.sessionId,
|
|
163
|
+
title: prompt.trim().replace(/\s+/g, " ").slice(0, 96),
|
|
164
|
+
source: `session-end:${built.id}`,
|
|
165
|
+
built,
|
|
166
|
+
});
|
|
167
|
+
for (const extra of extraSessionFacts(input, built.id)) {
|
|
168
|
+
storeDraft({
|
|
169
|
+
repo: input.repo,
|
|
170
|
+
platform: input.platform,
|
|
171
|
+
sessionId: input.sessionId,
|
|
172
|
+
title: extra.proposal.claims?.[0]?.text?.slice(0, 96) || extra.id,
|
|
173
|
+
source: `session-end:${extra.id}`,
|
|
174
|
+
built: extra,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return first;
|
|
178
|
+
}
|
|
179
|
+
function extraSessionFacts(input, skipId) {
|
|
180
|
+
const answer = input.answer ?? "";
|
|
181
|
+
const sentences = answer
|
|
182
|
+
.split(/(?<=[.!?])\s+/)
|
|
183
|
+
.map((s) => s.replace(/\s+/g, " ").trim())
|
|
184
|
+
.filter((s) => s.length >= 48 && [...s.matchAll(PATH_RE)].length > 0);
|
|
185
|
+
const out = [];
|
|
186
|
+
for (const sentence of sentences.slice(0, 3)) {
|
|
187
|
+
const built = buildClaimDraft({
|
|
188
|
+
prompt: sentence,
|
|
189
|
+
answer: sentence,
|
|
190
|
+
repoRoot: input.repo.root_path,
|
|
191
|
+
idPrefix: "claim.auto",
|
|
192
|
+
sourceRef: "session-auto-capture",
|
|
193
|
+
});
|
|
194
|
+
if (!built || built.id === skipId)
|
|
195
|
+
continue;
|
|
196
|
+
if (scoreProposal(built.proposal).reject)
|
|
197
|
+
continue;
|
|
198
|
+
if (tokenJaccard(sentence, input.answer || input.prompt) > 0.72)
|
|
199
|
+
continue;
|
|
200
|
+
out.push(built);
|
|
201
|
+
if (out.length >= 2)
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
export function findRecentContextMisses(repoId, opts = {}) {
|
|
207
|
+
const limit = opts.limit ?? 8;
|
|
208
|
+
const events = listUsageEvents({ repoId, days: 2 }).slice(0, 40);
|
|
209
|
+
return events
|
|
210
|
+
.filter((e) => {
|
|
211
|
+
if ((e.claims_count ?? 0) > 0)
|
|
212
|
+
return false;
|
|
213
|
+
if (e.query === "(session start)")
|
|
214
|
+
return false;
|
|
215
|
+
if (opts.sessionId && e.session_id && e.session_id !== opts.sessionId)
|
|
216
|
+
return false;
|
|
217
|
+
return true;
|
|
218
|
+
})
|
|
219
|
+
.slice(0, limit);
|
|
220
|
+
}
|
|
221
|
+
export function captureMissLearnDraft(input) {
|
|
222
|
+
if (!input.answer || input.answer.trim().length < 40)
|
|
223
|
+
return null;
|
|
224
|
+
if (SECRET.test(input.answer))
|
|
225
|
+
return null;
|
|
226
|
+
const source = `miss-learn:${input.miss.id}`;
|
|
227
|
+
if (draftExistsWithSource(input.repo.id, source))
|
|
228
|
+
return null;
|
|
229
|
+
const prompt = input.miss.query?.trim() || "Prior unanswered question";
|
|
230
|
+
const built = buildClaimDraft({
|
|
231
|
+
prompt,
|
|
232
|
+
answer: input.answer,
|
|
233
|
+
repoRoot: input.repo.root_path,
|
|
234
|
+
idPrefix: "claim.learned",
|
|
235
|
+
sourceRef: source,
|
|
236
|
+
forceKind: inferClaimKind(prompt, input.answer) === "session" ? "gotcha" : undefined,
|
|
237
|
+
});
|
|
238
|
+
if (!built)
|
|
239
|
+
return null;
|
|
240
|
+
const realAnchors = built.anchors.filter((a) => a !== "README.md");
|
|
241
|
+
if (realAnchors.length === 0)
|
|
242
|
+
return null;
|
|
243
|
+
built.proposal.claims[0].code_anchors = realAnchors;
|
|
244
|
+
if (scoreProposal(built.proposal).reject)
|
|
245
|
+
return null;
|
|
246
|
+
return storeDraft({
|
|
247
|
+
repo: input.repo,
|
|
248
|
+
platform: input.platform,
|
|
249
|
+
sessionId: input.sessionId,
|
|
250
|
+
title: `Learned: ${prompt.replace(/\s+/g, " ").slice(0, 80)}`,
|
|
251
|
+
source,
|
|
252
|
+
built,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
export function pendingDraftCount(repoId) {
|
|
256
|
+
return listProposalDrafts(repoId, { status: "pending" }).length;
|
|
257
|
+
}
|
package/dist/cli.d.ts
ADDED