linger-skill 0.2.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/CHANGELOG.md +31 -0
- package/LICENSE +21 -0
- package/PRIVACY.md +31 -0
- package/README.md +300 -0
- package/README.zh-CN.md +300 -0
- package/SECURITY.md +25 -0
- package/dist/adapter-evidence.d.ts +31 -0
- package/dist/adapter-evidence.js +166 -0
- package/dist/adapter-evidence.js.map +1 -0
- package/dist/adapters.d.ts +20 -0
- package/dist/adapters.js +105 -0
- package/dist/adapters.js.map +1 -0
- package/dist/capture.d.ts +16 -0
- package/dist/capture.js +108 -0
- package/dist/capture.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +217 -0
- package/dist/cli.js.map +1 -0
- package/dist/control.d.ts +9 -0
- package/dist/control.js +38 -0
- package/dist/control.js.map +1 -0
- package/dist/decision-extraction.d.ts +4 -0
- package/dist/decision-extraction.js +63 -0
- package/dist/decision-extraction.js.map +1 -0
- package/dist/decisions.d.ts +60 -0
- package/dist/decisions.js +166 -0
- package/dist/decisions.js.map +1 -0
- package/dist/delete.d.ts +21 -0
- package/dist/delete.js +96 -0
- package/dist/delete.js.map +1 -0
- package/dist/doctor.d.ts +6 -0
- package/dist/doctor.js +208 -0
- package/dist/doctor.js.map +1 -0
- package/dist/enrichment.d.ts +82 -0
- package/dist/enrichment.js +359 -0
- package/dist/enrichment.js.map +1 -0
- package/dist/eval-adversarial.d.ts +2 -0
- package/dist/eval-adversarial.js +20 -0
- package/dist/eval-adversarial.js.map +1 -0
- package/dist/eval-cli.d.ts +2 -0
- package/dist/eval-cli.js +83 -0
- package/dist/eval-cli.js.map +1 -0
- package/dist/eval-generator.d.ts +41 -0
- package/dist/eval-generator.js +36 -0
- package/dist/eval-generator.js.map +1 -0
- package/dist/eval-heldout.d.ts +2 -0
- package/dist/eval-heldout.js +56 -0
- package/dist/eval-heldout.js.map +1 -0
- package/dist/eval-io.d.ts +6 -0
- package/dist/eval-io.js +15 -0
- package/dist/eval-io.js.map +1 -0
- package/dist/eval-runner.d.ts +8 -0
- package/dist/eval-runner.js +56 -0
- package/dist/eval-runner.js.map +1 -0
- package/dist/eval-scorer.d.ts +27 -0
- package/dist/eval-scorer.js +28 -0
- package/dist/eval-scorer.js.map +1 -0
- package/dist/hook-cli.d.ts +2 -0
- package/dist/hook-cli.js +28 -0
- package/dist/hook-cli.js.map +1 -0
- package/dist/hook-command.d.ts +9 -0
- package/dist/hook-command.js +31 -0
- package/dist/hook-command.js.map +1 -0
- package/dist/hook-handler.d.ts +24 -0
- package/dist/hook-handler.js +93 -0
- package/dist/hook-handler.js.map +1 -0
- package/dist/hook-installer.d.ts +4 -0
- package/dist/hook-installer.js +120 -0
- package/dist/hook-installer.js.map +1 -0
- package/dist/hook-ownership.d.ts +8 -0
- package/dist/hook-ownership.js +51 -0
- package/dist/hook-ownership.js.map +1 -0
- package/dist/install-consent.d.ts +4 -0
- package/dist/install-consent.js +20 -0
- package/dist/install-consent.js.map +1 -0
- package/dist/installer.d.ts +33 -0
- package/dist/installer.js +140 -0
- package/dist/installer.js.map +1 -0
- package/dist/io.d.ts +7 -0
- package/dist/io.js +86 -0
- package/dist/io.js.map +1 -0
- package/dist/memory-events.d.ts +31 -0
- package/dist/memory-events.js +109 -0
- package/dist/memory-events.js.map +1 -0
- package/dist/memory-intent.d.ts +1 -0
- package/dist/memory-intent.js +4 -0
- package/dist/memory-intent.js.map +1 -0
- package/dist/paths.d.ts +15 -0
- package/dist/paths.js +24 -0
- package/dist/paths.js.map +1 -0
- package/dist/pending.d.ts +21 -0
- package/dist/pending.js +76 -0
- package/dist/pending.js.map +1 -0
- package/dist/processed-markdown.d.ts +3 -0
- package/dist/processed-markdown.js +29 -0
- package/dist/processed-markdown.js.map +1 -0
- package/dist/processing-policy.d.ts +19 -0
- package/dist/processing-policy.js +79 -0
- package/dist/processing-policy.js.map +1 -0
- package/dist/processing.d.ts +4 -0
- package/dist/processing.js +143 -0
- package/dist/processing.js.map +1 -0
- package/dist/purge.d.ts +7 -0
- package/dist/purge.js +16 -0
- package/dist/purge.js.map +1 -0
- package/dist/recall-sampling.d.ts +53 -0
- package/dist/recall-sampling.js +201 -0
- package/dist/recall-sampling.js.map +1 -0
- package/dist/recall.d.ts +19 -0
- package/dist/recall.js +70 -0
- package/dist/recall.js.map +1 -0
- package/dist/record-paths.d.ts +18 -0
- package/dist/record-paths.js +38 -0
- package/dist/record-paths.js.map +1 -0
- package/dist/repair.d.ts +4 -0
- package/dist/repair.js +83 -0
- package/dist/repair.js.map +1 -0
- package/dist/runtime-identity.d.ts +1 -0
- package/dist/runtime-identity.js +31 -0
- package/dist/runtime-identity.js.map +1 -0
- package/dist/runtime-installer.d.ts +7 -0
- package/dist/runtime-installer.js +43 -0
- package/dist/runtime-installer.js.map +1 -0
- package/dist/schema-validation.d.ts +26 -0
- package/dist/schema-validation.js +222 -0
- package/dist/schema-validation.js.map +1 -0
- package/dist/search.d.ts +16 -0
- package/dist/search.js +192 -0
- package/dist/search.js.map +1 -0
- package/dist/sensitivity.d.ts +6 -0
- package/dist/sensitivity.js +27 -0
- package/dist/sensitivity.js.map +1 -0
- package/dist/tag-registry.d.ts +21 -0
- package/dist/tag-registry.js +70 -0
- package/dist/tag-registry.js.map +1 -0
- package/dist/term-graph.d.ts +18 -0
- package/dist/term-graph.js +63 -0
- package/dist/term-graph.js.map +1 -0
- package/dist/types.d.ts +68 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/vault-candidates.d.ts +1 -0
- package/dist/vault-candidates.js +57 -0
- package/dist/vault-candidates.js.map +1 -0
- package/dist/vault.d.ts +28 -0
- package/dist/vault.js +158 -0
- package/dist/vault.js.map +1 -0
- package/dist/windows-command.d.ts +6 -0
- package/dist/windows-command.js +9 -0
- package/dist/windows-command.js.map +1 -0
- package/package.json +53 -0
- package/scripts/release-readiness.mjs +58 -0
- package/skills/linger/SKILL.md +56 -0
- package/skills/linger/agents/openai.yaml +4 -0
- package/skills/linger/references/protocol.md +84 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { mkdir, readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { atomicJson } from "./io.js";
|
|
4
|
+
import { parseManagedHookCommand, posixHookCommand } from "./hook-command.js";
|
|
5
|
+
import { windowsCommand } from "./windows-command.js";
|
|
6
|
+
import { runtimeFingerprint } from "./runtime-identity.js";
|
|
7
|
+
export async function installHooks(home, packageRoot, adapters) {
|
|
8
|
+
const node = process.execPath;
|
|
9
|
+
const hook = path.join(packageRoot, "dist", "hook-cli.js");
|
|
10
|
+
const runtimeIdentity = await runtimeFingerprint(packageRoot, node);
|
|
11
|
+
const written = [];
|
|
12
|
+
if (adapters.includes("claude-code")) {
|
|
13
|
+
const file = path.join(home, ".claude", "settings.json");
|
|
14
|
+
const settings = await jsonObjectOr(file);
|
|
15
|
+
const hooks = asHooks(settings.hooks);
|
|
16
|
+
removeLingerHookCommands(hooks, home, "claude-code");
|
|
17
|
+
addLingerHooks(hooks, "claude-code", node, hook, runtimeIdentity, ["SessionStart", "UserPromptSubmit", "Stop", "StopFailure"]);
|
|
18
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
19
|
+
await atomicJson(file, { ...settings, hooks });
|
|
20
|
+
written.push(file);
|
|
21
|
+
}
|
|
22
|
+
if (adapters.includes("codex")) {
|
|
23
|
+
const file = path.join(home, ".codex", "hooks.json");
|
|
24
|
+
const document = await jsonObjectOr(file);
|
|
25
|
+
const hooks = asHooks(document.hooks);
|
|
26
|
+
removeLingerHookCommands(hooks, home, "codex");
|
|
27
|
+
addLingerHooks(hooks, "codex", node, hook, runtimeIdentity, ["SessionStart", "UserPromptSubmit", "Stop"]);
|
|
28
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
29
|
+
await atomicJson(file, { ...document, hooks });
|
|
30
|
+
written.push(file);
|
|
31
|
+
}
|
|
32
|
+
return written;
|
|
33
|
+
}
|
|
34
|
+
export async function uninstallHooks(home, adapters, claudeAutoMemory) {
|
|
35
|
+
const changed = [];
|
|
36
|
+
for (const [adapter, file] of [
|
|
37
|
+
["claude-code", path.join(home, ".claude", "settings.json")],
|
|
38
|
+
["codex", path.join(home, ".codex", "hooks.json")]
|
|
39
|
+
]) {
|
|
40
|
+
if (!adapters.includes(adapter) && !(adapter === "claude-code" && claudeAutoMemory))
|
|
41
|
+
continue;
|
|
42
|
+
const document = await jsonObjectOr(file);
|
|
43
|
+
const hooks = asHooks(document.hooks);
|
|
44
|
+
const removed = removeLingerHookCommands(hooks, home, adapter);
|
|
45
|
+
let restored = false;
|
|
46
|
+
if (adapter === "claude-code" && claudeAutoMemory && document.autoMemoryEnabled === false) {
|
|
47
|
+
if (claudeAutoMemory.had_value)
|
|
48
|
+
document.autoMemoryEnabled = claudeAutoMemory.previous_value;
|
|
49
|
+
else
|
|
50
|
+
delete document.autoMemoryEnabled;
|
|
51
|
+
restored = true;
|
|
52
|
+
}
|
|
53
|
+
if (removed || restored) {
|
|
54
|
+
await atomicJson(file, { ...document, hooks });
|
|
55
|
+
changed.push(file);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return changed;
|
|
59
|
+
}
|
|
60
|
+
function addLingerHooks(hooks, adapter, node, hook, runtimeIdentity, events) {
|
|
61
|
+
const command = posixHookCommand({ adapter, node, hook, runtime_identity: runtimeIdentity });
|
|
62
|
+
const commandWindows = windowsCommand({ adapter, node, hook, runtimeIdentity });
|
|
63
|
+
for (const event of events) {
|
|
64
|
+
const groups = hooks[event] ?? [];
|
|
65
|
+
const already = groups.some(group => group.hooks.some(hook => hook.command === command));
|
|
66
|
+
if (!already)
|
|
67
|
+
groups.push({ ...(event === "SessionStart" ? { matcher: "startup|resume|compact" } : {}), hooks: [{ type: "command", command, commandWindows, timeout: 10, statusMessage: "Linger capture" }] });
|
|
68
|
+
hooks[event] = groups;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function removeLingerHookCommands(hooks, home, adapter) {
|
|
72
|
+
let removed = false;
|
|
73
|
+
for (const event of Object.keys(hooks)) {
|
|
74
|
+
const groups = hooks[event] ?? [];
|
|
75
|
+
for (const group of groups) {
|
|
76
|
+
const before = group.hooks.length;
|
|
77
|
+
group.hooks = group.hooks.filter(hook => !isLingerHookCommand(hook.command, home, adapter));
|
|
78
|
+
removed ||= before !== group.hooks.length;
|
|
79
|
+
}
|
|
80
|
+
hooks[event] = groups.filter(group => group.hooks.length);
|
|
81
|
+
if (!hooks[event].length)
|
|
82
|
+
delete hooks[event];
|
|
83
|
+
}
|
|
84
|
+
return removed;
|
|
85
|
+
}
|
|
86
|
+
function asHooks(value) {
|
|
87
|
+
if (value === undefined)
|
|
88
|
+
return {};
|
|
89
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
90
|
+
throw new Error("Invalid host hooks configuration");
|
|
91
|
+
for (const groups of Object.values(value)) {
|
|
92
|
+
if (!Array.isArray(groups))
|
|
93
|
+
throw new Error("Invalid host hook groups");
|
|
94
|
+
for (const group of groups) {
|
|
95
|
+
if (!group || typeof group !== "object" || Array.isArray(group) || !Array.isArray(group.hooks))
|
|
96
|
+
throw new Error("Invalid host hook group");
|
|
97
|
+
for (const hook of group.hooks)
|
|
98
|
+
if (!hook || typeof hook !== "object" || Array.isArray(hook) || typeof hook.command !== "string")
|
|
99
|
+
throw new Error("Invalid host hook command");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
async function jsonObjectOr(file) {
|
|
105
|
+
try {
|
|
106
|
+
const value = JSON.parse(await readFile(file, "utf8"));
|
|
107
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
108
|
+
throw new Error("Invalid host configuration document");
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (error.code === "ENOENT")
|
|
113
|
+
return {};
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function isLingerHookCommand(command, home, adapter) {
|
|
118
|
+
return Boolean(parseManagedHookCommand(command, home, adapter));
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=hook-installer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-installer.js","sourceRoot":"","sources":["../src/hook-installer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RawEvent, Role } from "./types.js";
|
|
2
|
+
export declare function findRecentEquivalentHookEvent(root: string, input: {
|
|
3
|
+
projectId: string;
|
|
4
|
+
role: Role;
|
|
5
|
+
content: string;
|
|
6
|
+
now?: number;
|
|
7
|
+
}): Promise<RawEvent | undefined>;
|
|
8
|
+
export declare function assertDecisionEvidenceNotHookOwned(root: string, projectId: string, evidenceRefs: string[], now?: number): Promise<void>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { readJson } from "./io.js";
|
|
4
|
+
import { isExplicitMemoryRequest } from "./memory-intent.js";
|
|
5
|
+
import { assertSafeId, vaultPaths } from "./paths.js";
|
|
6
|
+
import { assertRawRecordPath } from "./record-paths.js";
|
|
7
|
+
import { assertRawEvent } from "./schema-validation.js";
|
|
8
|
+
import { listJsonFiles } from "./vault.js";
|
|
9
|
+
const HOOK_AGENTS = new Set(["claude-code", "codex"]);
|
|
10
|
+
const OWNERSHIP_WINDOW_MS = 5 * 60 * 1000;
|
|
11
|
+
export async function findRecentEquivalentHookEvent(root, input) {
|
|
12
|
+
const normalized = normalize(input.content);
|
|
13
|
+
if (normalized.length < 8)
|
|
14
|
+
return undefined;
|
|
15
|
+
const events = await recentHookEvents(root, input.projectId, input.now ?? Date.now());
|
|
16
|
+
return events.find(event => event.role === input.role && isExplicitMemoryRequest(event.content) && equivalent(normalized, normalize(event.content)));
|
|
17
|
+
}
|
|
18
|
+
export async function assertDecisionEvidenceNotHookOwned(root, projectId, evidenceRefs, now = Date.now()) {
|
|
19
|
+
const refs = new Set(evidenceRefs);
|
|
20
|
+
const owned = (await recentHookEvents(root, projectId, now)).find(event => refs.has(event.event_id));
|
|
21
|
+
if (owned)
|
|
22
|
+
throw new Error(`Recent lifecycle hook evidence ${owned.event_id} is already owned by automatic processing; refusing duplicate decision-add`);
|
|
23
|
+
}
|
|
24
|
+
async function recentHookEvents(root, projectId, now) {
|
|
25
|
+
const project = assertSafeId(projectId, "project id");
|
|
26
|
+
const p = vaultPaths(root);
|
|
27
|
+
const events = (await Promise.all((await listJsonFiles(path.join(p.raw, project), p.root)).map(async (file) => {
|
|
28
|
+
try {
|
|
29
|
+
const value = await readJson(file);
|
|
30
|
+
assertRawEvent(value);
|
|
31
|
+
assertRawRecordPath(p, file, value);
|
|
32
|
+
if (createHash("sha256").update(value.content).digest("hex") !== value.content_hash)
|
|
33
|
+
return undefined;
|
|
34
|
+
const age = now - Date.parse(value.timestamp);
|
|
35
|
+
if (!HOOK_AGENTS.has(value.source_agent) || !Number.isFinite(age) || age < 0 || age > OWNERSHIP_WINDOW_MS)
|
|
36
|
+
return undefined;
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
}))).filter((event) => Boolean(event));
|
|
43
|
+
return events.sort((left, right) => right.timestamp.localeCompare(left.timestamp) || right.seq_id - left.seq_id);
|
|
44
|
+
}
|
|
45
|
+
function normalize(value) {
|
|
46
|
+
return value.normalize("NFKC").toLowerCase().replace(/\s+/g, " ").trim();
|
|
47
|
+
}
|
|
48
|
+
function equivalent(shorter, longer) {
|
|
49
|
+
return shorter === longer || shorter.includes(longer) || longer.includes(shorter);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=hook-ownership.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-ownership.js","sourceRoot":"","sources":["../src/hook-ownership.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Readable, Writable } from "node:stream";
|
|
2
|
+
import type { AdapterName } from "./adapters.js";
|
|
3
|
+
export declare function confirmPrivacyConsent(input: Readable, output: Writable): Promise<boolean>;
|
|
4
|
+
export declare function parseAdapterSelection(value?: string): AdapterName[];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PRIVACY_NOTICE } from "./installer.js";
|
|
2
|
+
export async function confirmPrivacyConsent(input, output) {
|
|
3
|
+
output.write(`${PRIVACY_NOTICE}\nType YES to install Linger, or anything else to cancel: `);
|
|
4
|
+
let text = "";
|
|
5
|
+
for await (const chunk of input) {
|
|
6
|
+
text += chunk.toString();
|
|
7
|
+
if (/\r?\n/.test(text))
|
|
8
|
+
break;
|
|
9
|
+
}
|
|
10
|
+
return text.split(/\r?\n/, 1)[0]?.trim() === "YES";
|
|
11
|
+
}
|
|
12
|
+
export function parseAdapterSelection(value) {
|
|
13
|
+
if (value === undefined)
|
|
14
|
+
return ["claude-code", "codex"];
|
|
15
|
+
const adapters = [...new Set(value.split(",").map(item => item.trim()).filter(Boolean))];
|
|
16
|
+
if (!adapters.length || adapters.some(item => item !== "claude-code" && item !== "codex"))
|
|
17
|
+
throw new Error("--adapters must be claude-code, codex, or both comma-separated");
|
|
18
|
+
return adapters;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=install-consent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-consent.js","sourceRoot":"","sources":["../src/install-consent.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,KAAe,EAAE,MAAgB;IAC3E,MAAM,CAAC,KAAK,CAAC,GAAG,cAAc,4DAA4D,CAAC,CAAC;IAC5F,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;QAChC,IAAI,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACzB,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,MAAM;IAChC,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,KAAK,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAc;IAClD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACzF,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,aAAa,IAAI,IAAI,KAAK,OAAO,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAC;IAC7K,OAAO,QAAyB,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { capabilityReport, type AdapterName } from "./adapters.js";
|
|
2
|
+
export declare const PRIVACY_NOTICE = "Linger stores visible conversations in local files. Recalled evidence and bounded normal-sensitivity enrichment batches may be sent to the model provider configured in the current Codex or Claude Code host. Linger does not configure a separate model provider or API key, and it does not disable or replace host-owned memory. Local-first does not mean data never leaves this device. No telemetry is installed, and uninstall preserves the vault.";
|
|
3
|
+
export interface InstallManifest {
|
|
4
|
+
schema_version: 1;
|
|
5
|
+
package_version: string;
|
|
6
|
+
installed_at: string;
|
|
7
|
+
package_root: string;
|
|
8
|
+
runtime_root: string;
|
|
9
|
+
adapters: AdapterName[];
|
|
10
|
+
files: string[];
|
|
11
|
+
backups: string[];
|
|
12
|
+
hook_files: string[];
|
|
13
|
+
/** Legacy v0.2.1 migration state. New installs never create this field. */
|
|
14
|
+
claude_auto_memory?: ClaudeAutoMemoryState;
|
|
15
|
+
}
|
|
16
|
+
export interface ClaudeAutoMemoryState {
|
|
17
|
+
had_value: boolean;
|
|
18
|
+
previous_value?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface InstallOptions {
|
|
21
|
+
home: string;
|
|
22
|
+
packageRoot: string;
|
|
23
|
+
adapters?: AdapterName[];
|
|
24
|
+
}
|
|
25
|
+
export declare function install(options: InstallOptions): Promise<{
|
|
26
|
+
manifest: InstallManifest;
|
|
27
|
+
capabilities: Awaited<ReturnType<typeof capabilityReport>>;
|
|
28
|
+
privacy_notice: string;
|
|
29
|
+
}>;
|
|
30
|
+
export declare function uninstall(home: string): Promise<{
|
|
31
|
+
removed: string[];
|
|
32
|
+
vault_preserved: true;
|
|
33
|
+
}>;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { cp, mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { capabilityReport } from "./adapters.js";
|
|
4
|
+
import { atomicJson } from "./io.js";
|
|
5
|
+
import { installHooks, uninstallHooks } from "./hook-installer.js";
|
|
6
|
+
import { installRuntime, uninstallRuntime } from "./runtime-installer.js";
|
|
7
|
+
import { assertSafeId } from "./paths.js";
|
|
8
|
+
export const PRIVACY_NOTICE = "Linger stores visible conversations in local files. Recalled evidence and bounded normal-sensitivity enrichment batches may be sent to the model provider configured in the current Codex or Claude Code host. Linger does not configure a separate model provider or API key, and it does not disable or replace host-owned memory. Local-first does not mean data never leaves this device. No telemetry is installed, and uninstall preserves the vault.";
|
|
9
|
+
export async function install(options) {
|
|
10
|
+
const adapters = options.adapters ?? ["claude-code", "codex"];
|
|
11
|
+
assertAdapters(adapters);
|
|
12
|
+
const sourceSkill = path.join(options.packageRoot, "skills", "linger");
|
|
13
|
+
await stat(path.join(sourceSkill, "SKILL.md"));
|
|
14
|
+
const stateRoot = path.join(options.home, ".linger");
|
|
15
|
+
const manifestFile = path.join(stateRoot, "install-manifest.json");
|
|
16
|
+
await mkdir(stateRoot, { recursive: true, mode: 0o700 });
|
|
17
|
+
const previousManifest = await existingManifest(options.home, manifestFile);
|
|
18
|
+
const staleAdapters = previousManifest?.adapters.filter(adapter => !adapters.includes(adapter)) ?? [];
|
|
19
|
+
if (staleAdapters.length || previousManifest?.claude_auto_memory) {
|
|
20
|
+
await uninstallHooks(options.home, staleAdapters, previousManifest?.claude_auto_memory);
|
|
21
|
+
}
|
|
22
|
+
const currentDestinations = new Set(adapters.map(adapter => path.resolve(skillDestination(options.home, adapter))));
|
|
23
|
+
for (const target of previousManifest?.files ?? []) {
|
|
24
|
+
if (!currentDestinations.has(path.resolve(target)) && await isManaged(target))
|
|
25
|
+
await rm(target, { recursive: true, force: true });
|
|
26
|
+
}
|
|
27
|
+
const backups = [];
|
|
28
|
+
const files = [];
|
|
29
|
+
for (const adapter of adapters) {
|
|
30
|
+
const destination = skillDestination(options.home, adapter);
|
|
31
|
+
if (await exists(destination)) {
|
|
32
|
+
if (await isManaged(destination))
|
|
33
|
+
await rm(destination, { recursive: true, force: true });
|
|
34
|
+
else {
|
|
35
|
+
const backup = `${destination}.backup-${Date.now()}`;
|
|
36
|
+
await rename(destination, backup);
|
|
37
|
+
backups.push(backup);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
await mkdir(destination, { recursive: true, mode: 0o700 });
|
|
41
|
+
await cp(sourceSkill, destination, { recursive: true, force: false });
|
|
42
|
+
await writeFile(path.join(destination, ".linger-managed"), "managed by linger-skill\n", { mode: 0o600 });
|
|
43
|
+
files.push(destination);
|
|
44
|
+
}
|
|
45
|
+
const runtime = await installRuntime(options.home, options.packageRoot);
|
|
46
|
+
const hookFiles = await installHooks(options.home, runtime.root, adapters);
|
|
47
|
+
const manifest = { schema_version: 1, package_version: runtime.version, installed_at: new Date().toISOString(), package_root: options.packageRoot, runtime_root: runtime.root, adapters, files, backups, hook_files: hookFiles };
|
|
48
|
+
await atomicJson(manifestFile, manifest);
|
|
49
|
+
return { manifest, capabilities: await capabilityReport(options.home), privacy_notice: PRIVACY_NOTICE };
|
|
50
|
+
}
|
|
51
|
+
export async function uninstall(home) {
|
|
52
|
+
const manifestFile = path.join(home, ".linger", "install-manifest.json");
|
|
53
|
+
let manifest;
|
|
54
|
+
try {
|
|
55
|
+
manifest = JSON.parse(await readFile(manifestFile, "utf8"));
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (error.code === "ENOENT")
|
|
59
|
+
return { removed: [], vault_preserved: true };
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
assertInstallManifest(home, manifest);
|
|
63
|
+
const removed = [];
|
|
64
|
+
for (const target of manifest.files) {
|
|
65
|
+
if (!isInside(home, target))
|
|
66
|
+
throw new Error(`Refusing to remove path outside home: ${target}`);
|
|
67
|
+
if (await isManaged(target)) {
|
|
68
|
+
await rm(target, { recursive: true, force: true });
|
|
69
|
+
removed.push(target);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
await uninstallHooks(home, manifest.adapters, manifest.claude_auto_memory);
|
|
73
|
+
await uninstallRuntime(home, manifest.runtime_root);
|
|
74
|
+
await rm(manifestFile, { force: true });
|
|
75
|
+
return { removed, vault_preserved: true };
|
|
76
|
+
}
|
|
77
|
+
function skillDestination(home, adapter) {
|
|
78
|
+
return path.join(home, adapter === "claude-code" ? ".claude" : ".codex", "skills", "linger");
|
|
79
|
+
}
|
|
80
|
+
function assertAdapters(adapters) {
|
|
81
|
+
if (!Array.isArray(adapters) || !adapters.length || !adapters.every(adapter => adapter === "claude-code" || adapter === "codex") || new Set(adapters).size !== adapters.length)
|
|
82
|
+
throw new Error("Invalid install adapters");
|
|
83
|
+
}
|
|
84
|
+
function assertInstallManifest(home, value) {
|
|
85
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
86
|
+
throw new Error("Invalid install manifest");
|
|
87
|
+
const item = value;
|
|
88
|
+
if (item.schema_version !== 1)
|
|
89
|
+
throw new Error("Invalid install manifest schema");
|
|
90
|
+
const packageVersion = typeof item.package_version === "string" ? assertSafeId(item.package_version, "package version") : (() => { throw new Error("Invalid install manifest package version"); })();
|
|
91
|
+
if (typeof item.installed_at !== "string" || !Number.isFinite(Date.parse(item.installed_at)))
|
|
92
|
+
throw new Error("Invalid install manifest timestamp");
|
|
93
|
+
if (typeof item.package_root !== "string" || typeof item.runtime_root !== "string")
|
|
94
|
+
throw new Error("Invalid install manifest paths");
|
|
95
|
+
assertAdapters(item.adapters);
|
|
96
|
+
for (const field of ["files", "backups", "hook_files"])
|
|
97
|
+
if (!Array.isArray(item[field]) || !item[field].every(entry => typeof entry === "string"))
|
|
98
|
+
throw new Error(`Invalid install manifest ${field}`);
|
|
99
|
+
if (item.claude_auto_memory !== undefined)
|
|
100
|
+
assertClaudeAutoMemoryState(item.claude_auto_memory);
|
|
101
|
+
const allowedTargets = new Set(item.adapters.map(adapter => path.resolve(skillDestination(home, adapter))));
|
|
102
|
+
if (!item.files.every(file => allowedTargets.has(path.resolve(file))))
|
|
103
|
+
throw new Error("Invalid install manifest target");
|
|
104
|
+
const allowedRuntime = path.resolve(home, ".linger", "runtime", packageVersion);
|
|
105
|
+
if (path.resolve(item.runtime_root) !== allowedRuntime)
|
|
106
|
+
throw new Error("Invalid install manifest runtime");
|
|
107
|
+
}
|
|
108
|
+
function isInside(parent, child) { const relative = path.relative(path.resolve(parent), path.resolve(child)); return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative); }
|
|
109
|
+
async function isManaged(dir) { return await exists(path.join(dir, ".linger-managed")); }
|
|
110
|
+
async function exists(file) { try {
|
|
111
|
+
await stat(file);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return false;
|
|
116
|
+
} }
|
|
117
|
+
async function existingManifest(home, file) {
|
|
118
|
+
try {
|
|
119
|
+
const value = JSON.parse(await readFile(file, "utf8"));
|
|
120
|
+
assertInstallManifest(home, value);
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (error.code === "ENOENT")
|
|
125
|
+
return undefined;
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function assertClaudeAutoMemoryState(value) {
|
|
130
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
131
|
+
throw new Error("Invalid install manifest Claude auto-memory state");
|
|
132
|
+
const item = value;
|
|
133
|
+
if (typeof item.had_value !== "boolean")
|
|
134
|
+
throw new Error("Invalid install manifest Claude auto-memory state");
|
|
135
|
+
if (item.had_value ? typeof item.previous_value !== "boolean" : item.previous_value !== undefined)
|
|
136
|
+
throw new Error("Invalid install manifest Claude auto-memory state");
|
|
137
|
+
if (Object.keys(item).some(key => key !== "had_value" && key !== "previous_value"))
|
|
138
|
+
throw new Error("Invalid install manifest Claude auto-memory state");
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=installer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installer.js","sourceRoot":"","sources":["../src/installer.ts"],"names":[],"mappings":"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"}
|
package/dist/io.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function ensureDir(dir: string): Promise<void>;
|
|
2
|
+
export declare function atomicWrite(file: string, data: string): Promise<void>;
|
|
3
|
+
export declare function atomicJson(file: string, value: unknown): Promise<void>;
|
|
4
|
+
export declare function readJson<T>(file: string): Promise<T>;
|
|
5
|
+
export declare function assertReadableInside(root: string, target: string): Promise<void>;
|
|
6
|
+
export declare function assertWritableInside(root: string, target: string): Promise<void>;
|
|
7
|
+
export declare function withFileLock<T>(lockFile: string, action: () => Promise<T>): Promise<T>;
|
package/dist/io.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { lstat, mkdir, open, readFile, realpath, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
export async function ensureDir(dir) {
|
|
5
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
6
|
+
}
|
|
7
|
+
export async function atomicWrite(file, data) {
|
|
8
|
+
await ensureDir(path.dirname(file));
|
|
9
|
+
const tmp = path.join(path.dirname(file), `.${path.basename(file)}.${randomUUID()}.tmp`);
|
|
10
|
+
await writeFile(tmp, data, { encoding: "utf8", mode: 0o600, flag: "wx" });
|
|
11
|
+
await rename(tmp, file);
|
|
12
|
+
}
|
|
13
|
+
export async function atomicJson(file, value) {
|
|
14
|
+
await atomicWrite(file, `${JSON.stringify(value, null, 2)}\n`);
|
|
15
|
+
}
|
|
16
|
+
export async function readJson(file) {
|
|
17
|
+
return JSON.parse(await readFile(file, "utf8"));
|
|
18
|
+
}
|
|
19
|
+
export async function assertReadableInside(root, target) {
|
|
20
|
+
const rootResolved = path.resolve(root);
|
|
21
|
+
const targetResolved = path.resolve(target);
|
|
22
|
+
const lexical = path.relative(rootResolved, targetResolved);
|
|
23
|
+
if (!lexical || lexical.startsWith("..") || path.isAbsolute(lexical))
|
|
24
|
+
throw new Error("Read path escapes Vault");
|
|
25
|
+
const [rootReal, targetReal] = await Promise.all([realpath(rootResolved), realpath(targetResolved)]);
|
|
26
|
+
const physical = path.relative(rootReal, targetReal);
|
|
27
|
+
if (!physical || physical.startsWith("..") || path.isAbsolute(physical))
|
|
28
|
+
throw new Error("Read path escapes Vault through symlink");
|
|
29
|
+
if ((await lstat(targetResolved)).isSymbolicLink())
|
|
30
|
+
throw new Error("Read target is a symlink");
|
|
31
|
+
}
|
|
32
|
+
export async function assertWritableInside(root, target) {
|
|
33
|
+
const rootResolved = path.resolve(root);
|
|
34
|
+
const targetResolved = path.resolve(target);
|
|
35
|
+
const lexical = path.relative(rootResolved, targetResolved);
|
|
36
|
+
if (!lexical || lexical.startsWith("..") || path.isAbsolute(lexical))
|
|
37
|
+
throw new Error("Write path escapes Vault");
|
|
38
|
+
const rootReal = await realpath(rootResolved);
|
|
39
|
+
let ancestor = path.dirname(targetResolved);
|
|
40
|
+
while (true) {
|
|
41
|
+
try {
|
|
42
|
+
ancestor = await realpath(ancestor);
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (error.code !== "ENOENT")
|
|
47
|
+
throw error;
|
|
48
|
+
const parent = path.dirname(ancestor);
|
|
49
|
+
if (parent === ancestor)
|
|
50
|
+
throw error;
|
|
51
|
+
ancestor = parent;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const physical = path.relative(rootReal, ancestor);
|
|
55
|
+
if (physical.startsWith("..") || path.isAbsolute(physical))
|
|
56
|
+
throw new Error("Write path escapes Vault through symlink");
|
|
57
|
+
try {
|
|
58
|
+
if ((await lstat(targetResolved)).isSymbolicLink())
|
|
59
|
+
throw new Error("Write target is a symlink");
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (error.code !== "ENOENT")
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export async function withFileLock(lockFile, action) {
|
|
67
|
+
await ensureDir(path.dirname(lockFile));
|
|
68
|
+
let handle;
|
|
69
|
+
try {
|
|
70
|
+
handle = await open(lockFile, "wx", 0o600);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error.code === "EEXIST")
|
|
74
|
+
throw new Error(`Vault is busy: ${lockFile}`);
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
return await action();
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
await handle.close();
|
|
82
|
+
const { unlink } = await import("node:fs/promises");
|
|
83
|
+
await unlink(lockFile).catch(() => undefined);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=io.js.map
|
package/dist/io.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.js","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7F,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW;IACzC,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,IAAY;IAC1D,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,UAAU,EAAE,MAAM,CAAC,CAAC;IACzF,MAAM,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1E,MAAM,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY,EAAE,KAAc;IAC3D,MAAM,WAAW,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAI,IAAY;IAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAM,CAAC;AACvD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,IAAY,EAAE,MAAc;IACrE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;IAC5D,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IACjH,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IACrG,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACrD,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IACpI,IAAI,CAAC,MAAM,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;AAClG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,IAAY,EAAE,MAAc;IACrE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;IAC5D,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAClH,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC9C,IAAI,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAC5C,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC;YAAC,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAAC,MAAM;QAAC,CAAC;QACnD,OAAO,KAAK,EAAE,CAAC;YACb,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAC;YACpE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACtC,IAAI,MAAM,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAC;YACrC,QAAQ,GAAG,MAAM,CAAC;QACpB,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACnD,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IACxH,IAAI,CAAC;QAAC,IAAI,CAAC,MAAM,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAAC,CAAC;IACzG,OAAO,KAAK,EAAE,CAAC;QAAC,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,KAAK,CAAC;IAAC,CAAC;AACxF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAI,QAAgB,EAAE,MAAwB;IAC9E,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,QAAQ,EAAE,CAAC,CAAC;QACtG,MAAM,KAAK,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,EAAE,CAAC;IACxB,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QACpD,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAChD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ProcessedMemory } from "./types.js";
|
|
2
|
+
export type MemoryControlKind = "forget" | "correct" | "delete";
|
|
3
|
+
export interface MemoryControlEvent {
|
|
4
|
+
schema_version: 1;
|
|
5
|
+
event_id: string;
|
|
6
|
+
kind: MemoryControlKind;
|
|
7
|
+
project_id: string;
|
|
8
|
+
target_memory_id: string;
|
|
9
|
+
replacement_memory_id?: string;
|
|
10
|
+
reason?: string;
|
|
11
|
+
evidence_refs: string[];
|
|
12
|
+
timestamp: string;
|
|
13
|
+
}
|
|
14
|
+
export interface EffectiveMemoryState {
|
|
15
|
+
status: "active" | "revoked" | "superseded" | "deleted";
|
|
16
|
+
replacement_memory_id?: string;
|
|
17
|
+
event_ids: string[];
|
|
18
|
+
}
|
|
19
|
+
export declare function appendMemoryControl(root: string, input: Omit<MemoryControlEvent, "schema_version" | "event_id" | "timestamp"> & {
|
|
20
|
+
timestamp?: string;
|
|
21
|
+
}): Promise<MemoryControlEvent>;
|
|
22
|
+
export declare function effectiveMemoryStates(root: string, projectId: string): Promise<Map<string, EffectiveMemoryState>>;
|
|
23
|
+
export declare function correctMemory(root: string, projectId: string, targetMemoryId: string, correction: {
|
|
24
|
+
summary: string;
|
|
25
|
+
evidenceRefs: string[];
|
|
26
|
+
reason?: string;
|
|
27
|
+
source?: "user_explicit" | "agent_inferred";
|
|
28
|
+
}): Promise<{
|
|
29
|
+
memory: ProcessedMemory;
|
|
30
|
+
event: MemoryControlEvent;
|
|
31
|
+
}>;
|