@dyyz1993/pi-coding-agent 0.74.45 → 0.74.47
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/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +13 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/extensions/auto-memory/__tests__/extract-result.test.ts +42 -0
- package/dist/extensions/auto-memory/__tests__/prefetch-history.test.ts +136 -0
- package/dist/extensions/auto-memory/__tests__/prompts.test.ts +29 -0
- package/dist/extensions/auto-memory/__tests__/skip-rules.test.ts +366 -0
- package/dist/extensions/auto-memory/contract.d.ts +16 -0
- package/dist/extensions/auto-memory/contract.d.ts.map +1 -1
- package/dist/extensions/auto-memory/contract.js.map +1 -1
- package/dist/extensions/auto-memory/contract.ts +16 -0
- package/dist/extensions/auto-memory/index.ts +134 -13
- package/dist/extensions/auto-memory/prompts.ts +10 -0
- package/dist/extensions/auto-memory/skip-rules.ts +2 -0
- package/dist/extensions/bash-ext/index.ts +855 -845
- package/dist/extensions/claude-hooks-compat/index.ts +12 -7
- package/dist/extensions/coordinator/handler.test.ts +388 -123
- package/dist/extensions/coordinator/handler.ts +78 -12
- package/dist/extensions/coordinator/index.ts +267 -198
- package/dist/extensions/coordinator/types.d.ts +16 -0
- package/dist/extensions/coordinator/types.d.ts.map +1 -1
- package/dist/extensions/coordinator/types.js.map +1 -1
- package/dist/extensions/coordinator/types.ts +57 -49
- package/dist/extensions/lsp/lsp/index.ts +15 -9
- package/dist/extensions/lsp/lsp/lsp-clangd-e2e.test.ts +229 -0
- package/dist/extensions/message-bridge/index.ts +14 -11
- package/dist/extensions/session-supervisor/index.ts +14 -8
- package/dist/extensions/subagent-v2/index.ts +58 -42
- package/dist/extensions/todo-ext/index.ts +7 -3
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +9 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
|
|
3
|
+
describe("ExtractResult type shape", () => {
|
|
4
|
+
it("MemoryFileEntry has filename, name, description", () => {
|
|
5
|
+
const entry = { filename: "test.md", name: "Test Policy", description: "A test description" };
|
|
6
|
+
expect(entry.filename).toBe("test.md");
|
|
7
|
+
expect(entry.name).toBe("Test Policy");
|
|
8
|
+
expect(entry.description).toBe("A test description");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("ExtractResult has created and updated arrays of MemoryFileEntry", () => {
|
|
12
|
+
const result = {
|
|
13
|
+
created: [
|
|
14
|
+
{ filename: "a.md", name: "Alpha", description: "First" },
|
|
15
|
+
{ filename: "b.md", name: "Beta", description: "Second" },
|
|
16
|
+
],
|
|
17
|
+
updated: [
|
|
18
|
+
{ filename: "c.md", name: "Gamma", description: "Third" },
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
expect(result.created).toHaveLength(2);
|
|
22
|
+
expect(result.updated).toHaveLength(1);
|
|
23
|
+
expect(result.created[0].name).toBe("Alpha");
|
|
24
|
+
expect(result.updated[0].name).toBe("Gamma");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("empty result is valid", () => {
|
|
28
|
+
const result = { created: [], updated: [] };
|
|
29
|
+
expect(result.created).toHaveLength(0);
|
|
30
|
+
expect(result.updated).toHaveLength(0);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("update entries can have description derived from append text", () => {
|
|
34
|
+
const appendText = "Added new section about testing strategies";
|
|
35
|
+
const entry = {
|
|
36
|
+
filename: "testing.md",
|
|
37
|
+
name: "testing.md",
|
|
38
|
+
description: appendText.slice(0, 80),
|
|
39
|
+
};
|
|
40
|
+
expect(entry.description).toBe("Added new section about testing strategies");
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { buildPrefetchUserMessage } from "../index.js";
|
|
3
|
+
import type { SkipRule, HistoryEntry } from "../skip-rules.js";
|
|
4
|
+
|
|
5
|
+
function extractHistoryJSON(output: string): unknown[] {
|
|
6
|
+
const marker = "## 最近 Prefetch 历史\n";
|
|
7
|
+
const idx = output.indexOf(marker);
|
|
8
|
+
if (idx === -1) throw new Error("history section not found in output");
|
|
9
|
+
const json = output.slice(idx + marker.length);
|
|
10
|
+
return JSON.parse(json);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const baseRule: SkipRule = { pattern: "test", mode: "exact", action: "skip", builtin: true };
|
|
14
|
+
const customRule: SkipRule = { pattern: "custom_", mode: "prefix", action: "skip", builtin: false };
|
|
15
|
+
|
|
16
|
+
function makeHistory(overrides: Partial<HistoryEntry> = {}): HistoryEntry {
|
|
17
|
+
return {
|
|
18
|
+
query: "test query",
|
|
19
|
+
selected: ["file1.md"],
|
|
20
|
+
skipped: false,
|
|
21
|
+
skip_hits: [],
|
|
22
|
+
guard_hits: [],
|
|
23
|
+
timestamp: Date.now(),
|
|
24
|
+
...overrides,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe("buildPrefetchUserMessage", () => {
|
|
29
|
+
it("serializes history entry with userMarkedIrrelevant: true", () => {
|
|
30
|
+
const history = [makeHistory({ userMarkedIrrelevant: true })];
|
|
31
|
+
const output = buildPrefetchUserMessage("q", "manifest", [], history);
|
|
32
|
+
const parsed = extractHistoryJSON(output) as Array<Record<string, unknown>>;
|
|
33
|
+
expect(parsed[0].userMarkedIrrelevant).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("serializes history entry with userMarkedIrrelevant: false", () => {
|
|
37
|
+
const history = [makeHistory({ userMarkedIrrelevant: false })];
|
|
38
|
+
const output = buildPrefetchUserMessage("q", "manifest", [], history);
|
|
39
|
+
const parsed = extractHistoryJSON(output) as Array<Record<string, unknown>>;
|
|
40
|
+
expect(parsed[0].userMarkedIrrelevant).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("serializes history entry with irrelevantFiles", () => {
|
|
44
|
+
const history = [makeHistory({ irrelevantFiles: ["a.ts", "b.ts"] })];
|
|
45
|
+
const output = buildPrefetchUserMessage("q", "manifest", [], history);
|
|
46
|
+
const parsed = extractHistoryJSON(output) as Array<Record<string, unknown>>;
|
|
47
|
+
expect(parsed[0].irrelevantFiles).toEqual(["a.ts", "b.ts"]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("produces valid output with empty history", () => {
|
|
51
|
+
const output = buildPrefetchUserMessage("q", "manifest", [], []);
|
|
52
|
+
const parsed = extractHistoryJSON(output);
|
|
53
|
+
expect(parsed).toEqual([]);
|
|
54
|
+
expect(output).toContain("## 当前查询\nq");
|
|
55
|
+
expect(output).toContain("## 可用文件\nmanifest");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("includes custom rules when present", () => {
|
|
59
|
+
const output = buildPrefetchUserMessage("q", "manifest", [baseRule, customRule], []);
|
|
60
|
+
expect(output).toContain('"pattern": "custom_"');
|
|
61
|
+
expect(output).toContain('"mode": "prefix"');
|
|
62
|
+
expect(output).toContain('"action": "skip"');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('shows "(no custom rules)" when no custom rules', () => {
|
|
66
|
+
const output = buildPrefetchUserMessage("q", "manifest", [baseRule], []);
|
|
67
|
+
expect(output).toContain("(no custom rules)");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("serializes multiple history entries", () => {
|
|
71
|
+
const history = [
|
|
72
|
+
makeHistory({ query: "q1", selected: ["a.md"] }),
|
|
73
|
+
makeHistory({ query: "q2", selected: ["b.md"] }),
|
|
74
|
+
makeHistory({ query: "q3", selected: ["c.md"] }),
|
|
75
|
+
];
|
|
76
|
+
const output = buildPrefetchUserMessage("q", "manifest", [], history);
|
|
77
|
+
const parsed = extractHistoryJSON(output) as Array<Record<string, unknown>>;
|
|
78
|
+
expect(parsed).toHaveLength(3);
|
|
79
|
+
expect(parsed[0].query).toBe("q1");
|
|
80
|
+
expect(parsed[1].query).toBe("q2");
|
|
81
|
+
expect(parsed[2].query).toBe("q3");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("handles large history (>5 entries) without crashing", () => {
|
|
85
|
+
const history = Array.from({ length: 10 }, (_, i) =>
|
|
86
|
+
makeHistory({ query: `q${i}`, selected: [`f${i}.md`], timestamp: 1000 + i }),
|
|
87
|
+
);
|
|
88
|
+
const output = buildPrefetchUserMessage("q", "manifest", [], history);
|
|
89
|
+
const parsed = extractHistoryJSON(output) as Array<Record<string, unknown>>;
|
|
90
|
+
expect(parsed).toHaveLength(10);
|
|
91
|
+
expect(parsed[0].query).toBe("q0");
|
|
92
|
+
expect(parsed[9].query).toBe("q9");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("includes query in output", () => {
|
|
96
|
+
const output = buildPrefetchUserMessage("find all todos", "manifest", [], []);
|
|
97
|
+
expect(output).toContain("## 当前查询\nfind all todos");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("includes manifest in output", () => {
|
|
101
|
+
const output = buildPrefetchUserMessage("q", "file list here", [], []);
|
|
102
|
+
expect(output).toContain("## 可用文件\nfile list here");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("serializes entry with both userMarkedIrrelevant and irrelevantFiles", () => {
|
|
106
|
+
const history = [
|
|
107
|
+
makeHistory({
|
|
108
|
+
userMarkedIrrelevant: true,
|
|
109
|
+
irrelevantFiles: ["x.ts", "y.ts"],
|
|
110
|
+
query: "bad query",
|
|
111
|
+
}),
|
|
112
|
+
];
|
|
113
|
+
const output = buildPrefetchUserMessage("q", "manifest", [], history);
|
|
114
|
+
const parsed = extractHistoryJSON(output) as Array<Record<string, unknown>>;
|
|
115
|
+
expect(parsed[0].userMarkedIrrelevant).toBe(true);
|
|
116
|
+
expect(parsed[0].irrelevantFiles).toEqual(["x.ts", "y.ts"]);
|
|
117
|
+
expect(parsed[0].query).toBe("bad query");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("handles entry without userMarkedIrrelevant field (backward compat)", () => {
|
|
121
|
+
const history: HistoryEntry[] = [
|
|
122
|
+
{
|
|
123
|
+
query: "old query",
|
|
124
|
+
selected: ["old.md"],
|
|
125
|
+
skipped: false,
|
|
126
|
+
skip_hits: [],
|
|
127
|
+
guard_hits: [],
|
|
128
|
+
timestamp: 5000,
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
const output = buildPrefetchUserMessage("q", "manifest", [], history);
|
|
132
|
+
const parsed = extractHistoryJSON(output) as Array<Record<string, unknown>>;
|
|
133
|
+
expect(parsed[0].userMarkedIrrelevant).toBe(false);
|
|
134
|
+
expect(parsed[0].irrelevantFiles).toEqual([]);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { SELECT_MEMORIES_PROMPT } from "../prompts.js";
|
|
3
|
+
|
|
4
|
+
describe("SELECT_MEMORIES_PROMPT", () => {
|
|
5
|
+
it('contains "userMarkedIrrelevant" section', () => {
|
|
6
|
+
expect(SELECT_MEMORIES_PROMPT).toContain("userMarkedIrrelevant");
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('contains "用户反馈净化" section', () => {
|
|
10
|
+
expect(SELECT_MEMORIES_PROMPT).toContain("用户反馈净化");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("mentions minimum 2 marks needed for rules", () => {
|
|
14
|
+
expect(SELECT_MEMORIES_PROMPT).toMatch(/至少.*2.*不相关标记/);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('still contains "文件选择" section', () => {
|
|
18
|
+
expect(SELECT_MEMORIES_PROMPT).toContain("文件选择");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('still contains "关键词净化" section', () => {
|
|
22
|
+
expect(SELECT_MEMORIES_PROMPT).toContain("关键词净化");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("has correct task numbering", () => {
|
|
26
|
+
expect(SELECT_MEMORIES_PROMPT).toContain("## 任务 1:");
|
|
27
|
+
expect(SELECT_MEMORIES_PROMPT).toContain("## 任务 2:");
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { mkdtemp, rm } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
import {
|
|
6
|
+
addHistoryEntry,
|
|
7
|
+
applyPurification,
|
|
8
|
+
evaluateRules,
|
|
9
|
+
getDefaultStore,
|
|
10
|
+
loadSkipWordStore,
|
|
11
|
+
matchRule,
|
|
12
|
+
saveSkipWordStore,
|
|
13
|
+
type HistoryEntry,
|
|
14
|
+
type SkipRule,
|
|
15
|
+
type SkipWordStore,
|
|
16
|
+
} from "../skip-rules.js";
|
|
17
|
+
|
|
18
|
+
function makeEntry(overrides: Partial<HistoryEntry> = {}): HistoryEntry {
|
|
19
|
+
return {
|
|
20
|
+
query: "test query",
|
|
21
|
+
selected: [],
|
|
22
|
+
skipped: false,
|
|
23
|
+
skip_hits: [],
|
|
24
|
+
guard_hits: [],
|
|
25
|
+
timestamp: Date.now(),
|
|
26
|
+
...overrides,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let tempDir: string;
|
|
31
|
+
|
|
32
|
+
beforeEach(async () => {
|
|
33
|
+
tempDir = await mkdtemp(join(tmpdir(), "skip-rules-test-"));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(async () => {
|
|
37
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("addHistoryEntry with userMarkedIrrelevant", () => {
|
|
41
|
+
it("adds entry with userMarkedIrrelevant:true and irrelevantFiles", () => {
|
|
42
|
+
const store = getDefaultStore();
|
|
43
|
+
const entry = makeEntry({
|
|
44
|
+
query: "继续",
|
|
45
|
+
userMarkedIrrelevant: true,
|
|
46
|
+
irrelevantFiles: ["src/foo.ts", "src/bar.ts"],
|
|
47
|
+
});
|
|
48
|
+
const result = addHistoryEntry(store, entry);
|
|
49
|
+
expect(result.history).toHaveLength(1);
|
|
50
|
+
expect(result.history[0].userMarkedIrrelevant).toBe(true);
|
|
51
|
+
expect(result.history[0].irrelevantFiles).toEqual(["src/foo.ts", "src/bar.ts"]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("preserves existing entries when adding new one", () => {
|
|
55
|
+
const store = getDefaultStore();
|
|
56
|
+
const e1 = makeEntry({ query: "first" });
|
|
57
|
+
const s1 = addHistoryEntry(store, e1);
|
|
58
|
+
const e2 = makeEntry({
|
|
59
|
+
query: "second",
|
|
60
|
+
userMarkedIrrelevant: true,
|
|
61
|
+
irrelevantFiles: ["a.ts"],
|
|
62
|
+
});
|
|
63
|
+
const s2 = addHistoryEntry(s1, e2);
|
|
64
|
+
expect(s2.history).toHaveLength(2);
|
|
65
|
+
expect(s2.history[0].query).toBe("first");
|
|
66
|
+
expect(s2.history[1].query).toBe("second");
|
|
67
|
+
expect(s2.history[1].userMarkedIrrelevant).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("trims to MAX_HISTORY (20) even with irrelevant entries", () => {
|
|
71
|
+
const store = getDefaultStore();
|
|
72
|
+
let current = store;
|
|
73
|
+
for (let i = 0; i < 25; i++) {
|
|
74
|
+
current = addHistoryEntry(current, makeEntry({
|
|
75
|
+
query: `q${i}`,
|
|
76
|
+
userMarkedIrrelevant: i % 2 === 0,
|
|
77
|
+
irrelevantFiles: i % 2 === 0 ? [`file${i}.ts`] : undefined,
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
expect(current.history).toHaveLength(20);
|
|
81
|
+
expect(current.history[0].query).toBe("q5");
|
|
82
|
+
expect(current.history[19].query).toBe("q24");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("handles mixed history: some with userMarkedIrrelevant, some without", () => {
|
|
86
|
+
const store = getDefaultStore();
|
|
87
|
+
let current = store;
|
|
88
|
+
current = addHistoryEntry(current, makeEntry({ query: "normal" }));
|
|
89
|
+
current = addHistoryEntry(current, makeEntry({
|
|
90
|
+
query: "irrelevant",
|
|
91
|
+
userMarkedIrrelevant: true,
|
|
92
|
+
irrelevantFiles: ["x.ts"],
|
|
93
|
+
}));
|
|
94
|
+
current = addHistoryEntry(current, makeEntry({ query: "another normal" }));
|
|
95
|
+
|
|
96
|
+
expect(current.history).toHaveLength(3);
|
|
97
|
+
expect(current.history[0].userMarkedIrrelevant).toBeUndefined();
|
|
98
|
+
expect(current.history[0].irrelevantFiles).toBeUndefined();
|
|
99
|
+
expect(current.history[1].userMarkedIrrelevant).toBe(true);
|
|
100
|
+
expect(current.history[1].irrelevantFiles).toEqual(["x.ts"]);
|
|
101
|
+
expect(current.history[2].userMarkedIrrelevant).toBeUndefined();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe("matchRule", () => {
|
|
106
|
+
it("exact match: 'ok' matches 'ok'", () => {
|
|
107
|
+
const rule: SkipRule = { pattern: "ok", mode: "exact", action: "skip", builtin: true };
|
|
108
|
+
expect(matchRule("ok", rule)).toBe(true);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("exact match: 'ok' does not match 'okay'", () => {
|
|
112
|
+
const rule: SkipRule = { pattern: "ok", mode: "exact", action: "skip", builtin: true };
|
|
113
|
+
expect(matchRule("okay", rule)).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("prefix match: '帮' matches '帮我看看'", () => {
|
|
117
|
+
const rule: SkipRule = { pattern: "帮", mode: "prefix", action: "guard", builtin: true };
|
|
118
|
+
expect(matchRule("帮我看看", rule)).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("prefix match: '帮' does not match '请帮我'", () => {
|
|
122
|
+
const rule: SkipRule = { pattern: "帮", mode: "prefix", action: "guard", builtin: true };
|
|
123
|
+
expect(matchRule("请帮我", rule)).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("contains match: '?' matches '怎么弄?'", () => {
|
|
127
|
+
const rule: SkipRule = { pattern: "?", mode: "contains", action: "guard", builtin: true };
|
|
128
|
+
expect(matchRule("怎么弄?", rule)).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("contains match: '\\n' matches 'line1\\nline2'", () => {
|
|
132
|
+
const rule: SkipRule = { pattern: "\n", mode: "contains", action: "guard", builtin: true };
|
|
133
|
+
expect(matchRule("line1\nline2", rule)).toBe(true);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("regex match: pattern '^test.+$' matches 'test123'", () => {
|
|
137
|
+
const rule: SkipRule = { pattern: "^test.+$", mode: "regex", action: "skip" };
|
|
138
|
+
expect(matchRule("test123", rule)).toBe(true);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("regex match: invalid regex returns false without crashing", () => {
|
|
142
|
+
const rule: SkipRule = { pattern: "([unclosed", mode: "regex", action: "skip" };
|
|
143
|
+
expect(matchRule("anything", rule)).toBe(false);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("empty query returns false", () => {
|
|
147
|
+
const rule: SkipRule = { pattern: "ok", mode: "exact", action: "skip" };
|
|
148
|
+
expect(matchRule("", rule)).toBe(false);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("empty pattern returns false", () => {
|
|
152
|
+
const rule: SkipRule = { pattern: "", mode: "exact", action: "skip" };
|
|
153
|
+
expect(matchRule("ok", rule)).toBe(false);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe("evaluateRules", () => {
|
|
158
|
+
it("skip rule only → shouldSkip=true", () => {
|
|
159
|
+
const rules: SkipRule[] = [
|
|
160
|
+
{ pattern: "ok", mode: "exact", action: "skip", builtin: true },
|
|
161
|
+
];
|
|
162
|
+
const result = evaluateRules("ok", rules);
|
|
163
|
+
expect(result.shouldSkip).toBe(true);
|
|
164
|
+
expect(result.skipHits).toEqual(["ok"]);
|
|
165
|
+
expect(result.guardHits).toEqual([]);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("guard rule only → shouldSkip=false", () => {
|
|
169
|
+
const rules: SkipRule[] = [
|
|
170
|
+
{ pattern: "?", mode: "contains", action: "guard", builtin: true },
|
|
171
|
+
];
|
|
172
|
+
const result = evaluateRules("how?", rules);
|
|
173
|
+
expect(result.shouldSkip).toBe(false);
|
|
174
|
+
expect(result.guardHits).toEqual(["?"]);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("both skip and guard match → shouldSkip=false (guard wins)", () => {
|
|
178
|
+
const rules: SkipRule[] = [
|
|
179
|
+
{ pattern: "ok", mode: "prefix", action: "skip", builtin: true },
|
|
180
|
+
{ pattern: "?", mode: "contains", action: "guard", builtin: true },
|
|
181
|
+
];
|
|
182
|
+
const result = evaluateRules("ok?", rules);
|
|
183
|
+
expect(result.shouldSkip).toBe(false);
|
|
184
|
+
expect(result.skipHits).toEqual(["ok"]);
|
|
185
|
+
expect(result.guardHits).toEqual(["?"]);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("no rules match → shouldSkip=false", () => {
|
|
189
|
+
const rules: SkipRule[] = [
|
|
190
|
+
{ pattern: "ok", mode: "exact", action: "skip", builtin: true },
|
|
191
|
+
];
|
|
192
|
+
const result = evaluateRules("hello world", rules);
|
|
193
|
+
expect(result.shouldSkip).toBe(false);
|
|
194
|
+
expect(result.skipHits).toEqual([]);
|
|
195
|
+
expect(result.guardHits).toEqual([]);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("multiple skip rules match → all listed in skipHits", () => {
|
|
199
|
+
const rules: SkipRule[] = [
|
|
200
|
+
{ pattern: "好的", mode: "exact", action: "skip", builtin: true },
|
|
201
|
+
{ pattern: "好的", mode: "prefix", action: "skip", builtin: true },
|
|
202
|
+
{ pattern: "好", mode: "prefix", action: "skip", builtin: true },
|
|
203
|
+
];
|
|
204
|
+
const result = evaluateRules("好的", rules);
|
|
205
|
+
expect(result.shouldSkip).toBe(true);
|
|
206
|
+
expect(result.skipHits).toEqual(["好的", "好的", "好"]);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe("applyPurification", () => {
|
|
211
|
+
it("add_rules adds custom skip rule", () => {
|
|
212
|
+
const store = getDefaultStore();
|
|
213
|
+
const result = applyPurification(store, {
|
|
214
|
+
add_rules: [{ pattern: "custom_skip", mode: "exact", action: "skip" }],
|
|
215
|
+
});
|
|
216
|
+
const added = result.rules.find(
|
|
217
|
+
(r) => r.pattern === "custom_skip" && r.action === "skip" && !r.builtin,
|
|
218
|
+
);
|
|
219
|
+
expect(added).toBeDefined();
|
|
220
|
+
expect(added!.mode).toBe("exact");
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("add_rules does not duplicate existing rule", () => {
|
|
224
|
+
const store = getDefaultStore();
|
|
225
|
+
const s1 = applyPurification(store, {
|
|
226
|
+
add_rules: [{ pattern: "dup", mode: "exact", action: "skip" }],
|
|
227
|
+
});
|
|
228
|
+
const countBefore = s1.rules.filter((r) => r.pattern === "dup").length;
|
|
229
|
+
const s2 = applyPurification(s1, {
|
|
230
|
+
add_rules: [{ pattern: "dup", mode: "exact", action: "skip" }],
|
|
231
|
+
});
|
|
232
|
+
const countAfter = s2.rules.filter((r) => r.pattern === "dup").length;
|
|
233
|
+
expect(countBefore).toBe(countAfter);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("remove_rules removes non-builtin rule only", () => {
|
|
237
|
+
const store = getDefaultStore();
|
|
238
|
+
const s1 = applyPurification(store, {
|
|
239
|
+
add_rules: [{ pattern: "removeme", mode: "exact", action: "skip" }],
|
|
240
|
+
});
|
|
241
|
+
expect(s1.rules.some((r) => r.pattern === "removeme")).toBe(true);
|
|
242
|
+
const s2 = applyPurification(s1, {
|
|
243
|
+
remove_rules: [{ pattern: "removeme", mode: "exact" }],
|
|
244
|
+
});
|
|
245
|
+
expect(s2.rules.some((r) => r.pattern === "removeme")).toBe(false);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("remove_rules does NOT remove builtin rule", () => {
|
|
249
|
+
const store = getDefaultStore();
|
|
250
|
+
const builtinCountBefore = store.rules.filter((r) => r.pattern === "ok" && r.builtin).length;
|
|
251
|
+
const result = applyPurification(store, {
|
|
252
|
+
remove_rules: [{ pattern: "ok", mode: "exact" }],
|
|
253
|
+
});
|
|
254
|
+
const builtinCountAfter = result.rules.filter((r) => r.pattern === "ok" && r.builtin).length;
|
|
255
|
+
expect(builtinCountAfter).toBe(builtinCountBefore);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it("bad_skips with suggestion 'remove' removes non-builtin skip", () => {
|
|
259
|
+
const store = getDefaultStore();
|
|
260
|
+
const s1 = applyPurification(store, {
|
|
261
|
+
add_rules: [{ pattern: "bad_skip", mode: "exact", action: "skip" }],
|
|
262
|
+
});
|
|
263
|
+
const s2 = applyPurification(s1, {
|
|
264
|
+
bad_skips: [{
|
|
265
|
+
query: "bad_skip",
|
|
266
|
+
matched_rules: ["bad_skip"],
|
|
267
|
+
reason: "too aggressive",
|
|
268
|
+
suggestion: "remove",
|
|
269
|
+
}],
|
|
270
|
+
});
|
|
271
|
+
expect(s2.rules.some((r) => r.pattern === "bad_skip" && !r.builtin)).toBe(false);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("bad_skips with suggestion 'add_guard' adds guard for builtin skip", () => {
|
|
275
|
+
const store = getDefaultStore();
|
|
276
|
+
const result = applyPurification(store, {
|
|
277
|
+
bad_skips: [{
|
|
278
|
+
query: "ok",
|
|
279
|
+
matched_rules: ["ok"],
|
|
280
|
+
reason: "should not skip questions",
|
|
281
|
+
suggestion: "add_guard",
|
|
282
|
+
}],
|
|
283
|
+
});
|
|
284
|
+
const guard = result.rules.find(
|
|
285
|
+
(r) => r.pattern === "ok" && r.action === "guard" && !r.builtin,
|
|
286
|
+
);
|
|
287
|
+
expect(guard).toBeDefined();
|
|
288
|
+
expect(guard!.mode).toBe("exact");
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("enforces MAX_SKIP_RULES (50) limit", () => {
|
|
292
|
+
const store = getDefaultStore();
|
|
293
|
+
const add_rules = Array.from({ length: 55 }, (_, i) => ({
|
|
294
|
+
pattern: `custom_skip_${i}`,
|
|
295
|
+
mode: "exact" as const,
|
|
296
|
+
action: "skip" as const,
|
|
297
|
+
}));
|
|
298
|
+
const result = applyPurification(store, { add_rules });
|
|
299
|
+
const nonBuiltinSkips = result.rules.filter((r) => r.action === "skip" && !r.builtin);
|
|
300
|
+
expect(nonBuiltinSkips.length).toBeLessThanOrEqual(50);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("enforces MAX_GUARD_RULES (30) limit", () => {
|
|
304
|
+
const store = getDefaultStore();
|
|
305
|
+
const add_rules = Array.from({ length: 35 }, (_, i) => ({
|
|
306
|
+
pattern: `custom_guard_${i}`,
|
|
307
|
+
mode: "exact" as const,
|
|
308
|
+
action: "guard" as const,
|
|
309
|
+
}));
|
|
310
|
+
const result = applyPurification(store, { add_rules });
|
|
311
|
+
const nonBuiltinGuards = result.rules.filter((r) => r.action === "guard" && !r.builtin);
|
|
312
|
+
expect(nonBuiltinGuards.length).toBeLessThanOrEqual(30);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("updates lastPurifyTimestamp", () => {
|
|
316
|
+
const store = getDefaultStore();
|
|
317
|
+
expect(store.lastPurifyTimestamp).toBe(0);
|
|
318
|
+
const before = Date.now();
|
|
319
|
+
const result = applyPurification(store, { add_rules: [] });
|
|
320
|
+
const after = Date.now();
|
|
321
|
+
expect(result.lastPurifyTimestamp).toBeGreaterThanOrEqual(before);
|
|
322
|
+
expect(result.lastPurifyTimestamp).toBeLessThanOrEqual(after);
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
describe("full flow: save and load with irrelevant files", () => {
|
|
327
|
+
it("creates history with 3 irrelevant marks and verifies all present", () => {
|
|
328
|
+
const store = getDefaultStore();
|
|
329
|
+
let current = store;
|
|
330
|
+
const files = [["a.ts"], ["b.ts", "c.ts"], ["d.ts"]];
|
|
331
|
+
for (let i = 0; i < 3; i++) {
|
|
332
|
+
current = addHistoryEntry(current, makeEntry({
|
|
333
|
+
query: `q${i}`,
|
|
334
|
+
userMarkedIrrelevant: true,
|
|
335
|
+
irrelevantFiles: files[i],
|
|
336
|
+
}));
|
|
337
|
+
}
|
|
338
|
+
for (let i = 0; i < 3; i++) {
|
|
339
|
+
expect(current.history[i].userMarkedIrrelevant).toBe(true);
|
|
340
|
+
expect(current.history[i].irrelevantFiles).toEqual(files[i]);
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it("saves store to temp file, loads back, and verifies data integrity", async () => {
|
|
345
|
+
let store = getDefaultStore();
|
|
346
|
+
store = addHistoryEntry(store, makeEntry({
|
|
347
|
+
query: "save_test",
|
|
348
|
+
userMarkedIrrelevant: true,
|
|
349
|
+
irrelevantFiles: ["saved1.ts", "saved2.ts"],
|
|
350
|
+
}));
|
|
351
|
+
store = applyPurification(store, {
|
|
352
|
+
add_rules: [{ pattern: "custom_rule", mode: "exact", action: "skip" }],
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
await saveSkipWordStore(tempDir, store);
|
|
356
|
+
const loaded = loadSkipWordStore(tempDir);
|
|
357
|
+
|
|
358
|
+
expect(loaded.version).toBe(store.version);
|
|
359
|
+
expect(loaded.history).toHaveLength(1);
|
|
360
|
+
expect(loaded.history[0].query).toBe("save_test");
|
|
361
|
+
expect(loaded.history[0].userMarkedIrrelevant).toBe(true);
|
|
362
|
+
expect(loaded.history[0].irrelevantFiles).toEqual(["saved1.ts", "saved2.ts"]);
|
|
363
|
+
expect(loaded.rules.some((r) => r.pattern === "custom_rule")).toBe(true);
|
|
364
|
+
expect(loaded.lastPurifyTimestamp).toBe(store.lastPurifyTimestamp);
|
|
365
|
+
});
|
|
366
|
+
});
|
|
@@ -18,6 +18,15 @@ export interface MemoryUserRememberParams {
|
|
|
18
18
|
sourceMessageIds?: string[];
|
|
19
19
|
content?: string;
|
|
20
20
|
}
|
|
21
|
+
export interface MemoryMarkIrrelevantParams {
|
|
22
|
+
query: string;
|
|
23
|
+
selectedFiles: string[];
|
|
24
|
+
}
|
|
25
|
+
export interface MemoryIrrelevantMarkedEvent {
|
|
26
|
+
type: "memory_irrelevant_marked";
|
|
27
|
+
query: string;
|
|
28
|
+
selectedFiles: string[];
|
|
29
|
+
}
|
|
21
30
|
export interface BookmarkCreatingEvent {
|
|
22
31
|
type: "bookmark_creating";
|
|
23
32
|
}
|
|
@@ -41,11 +50,18 @@ export interface MemoryChannelContract extends ChannelContract {
|
|
|
41
50
|
ok: boolean;
|
|
42
51
|
};
|
|
43
52
|
};
|
|
53
|
+
"memory.markIrrelevant": {
|
|
54
|
+
params: MemoryMarkIrrelevantParams;
|
|
55
|
+
return: {
|
|
56
|
+
ok: boolean;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
44
59
|
};
|
|
45
60
|
events: {
|
|
46
61
|
bookmark_creating: BookmarkCreatingEvent;
|
|
47
62
|
memory_updated: MemoryUpdatedEvent;
|
|
48
63
|
memory_update_failed: MemoryUpdateFailedEvent;
|
|
64
|
+
memory_irrelevant_marked: MemoryIrrelevantMarkedEvent;
|
|
49
65
|
};
|
|
50
66
|
}
|
|
51
67
|
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEjE,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAE5C,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,mBAAmB,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,cAAc,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACvC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC7D,OAAO,EAAE;QACR,aAAa,EAAE;YACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAC9B,MAAM,EAAE,gBAAgB,CAAC;SACzB,CAAC;QACF,qBAAqB,EAAE;YACtB,MAAM,EAAE,wBAAwB,CAAC;YACjC,MAAM,EAAE;gBAAE,EAAE,EAAE,OAAO,CAAA;aAAE,CAAC;SACxB,CAAC;KACF,CAAC;IACF,MAAM,EAAE;QACP,iBAAiB,EAAE,qBAAqB,CAAC;QACzC,cAAc,EAAE,kBAAkB,CAAC;QACnC,oBAAoB,EAAE,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEjE,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAE5C,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,0BAA0B;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,2BAA2B;IAC3C,IAAI,EAAE,0BAA0B,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,mBAAmB,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,cAAc,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACvC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC7D,OAAO,EAAE;QACR,aAAa,EAAE;YACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAC9B,MAAM,EAAE,gBAAgB,CAAC;SACzB,CAAC;QACF,qBAAqB,EAAE;YACtB,MAAM,EAAE,wBAAwB,CAAC;YACjC,MAAM,EAAE;gBAAE,EAAE,EAAE,OAAO,CAAA;aAAE,CAAC;SACxB,CAAC;QACF,uBAAuB,EAAE;YACxB,MAAM,EAAE,0BAA0B,CAAC;YACnC,MAAM,EAAE;gBAAE,EAAE,EAAE,OAAO,CAAA;aAAE,CAAC;SACxB,CAAC;KACF,CAAC;IACF,MAAM,EAAE;QACP,iBAAiB,EAAE,qBAAqB,CAAC;QACzC,cAAc,EAAE,kBAAkB,CAAC;QACnC,oBAAoB,EAAE,uBAAuB,CAAC;QAC9C,wBAAwB,EAAE,2BAA2B,CAAC;KACtD,CAAC;CACF","sourcesContent":["import type { ChannelContract } from \"@dyyz1993/pi-coding-agent\";\n\nexport const MEMORY_CHANNEL_NAME = \"memory\";\n\nexport interface MemoryFileInfo {\n\tfilename: string;\n\tfilePath: string;\n\tdescription: string | null;\n\ttype: string | null;\n\tmtimeMs: number;\n}\n\nexport interface MemoryListResult {\n\ttype: \"list_result\";\n\tfiles: MemoryFileInfo[];\n\tentrypointContent: string | null;\n\tmemoryDir: string;\n}\n\nexport interface MemoryUserRememberParams {\n\tsourceSessionId?: string;\n\tsourceMessageIds?: string[];\n\tcontent?: string;\n}\n\nexport interface MemoryMarkIrrelevantParams {\n\tquery: string;\n\tselectedFiles: string[];\n}\n\nexport interface MemoryIrrelevantMarkedEvent {\n\ttype: \"memory_irrelevant_marked\";\n\tquery: string;\n\tselectedFiles: string[];\n}\n\nexport interface BookmarkCreatingEvent {\n\ttype: \"bookmark_creating\";\n}\n\nexport interface MemoryUpdatedEvent {\n\ttype: \"memory_updated\";\n\tfiles: MemoryFileInfo[];\n}\n\nexport interface MemoryUpdateFailedEvent {\n\ttype: \"memory_update_failed\";\n\treason: string;\n}\n\nexport interface MemoryChannelContract extends ChannelContract {\n\tmethods: {\n\t\t\"memory.list\": {\n\t\t\tparams: Record<string, never>;\n\t\t\treturn: MemoryListResult;\n\t\t};\n\t\t\"memory.userRemember\": {\n\t\t\tparams: MemoryUserRememberParams;\n\t\t\treturn: { ok: boolean };\n\t\t};\n\t\t\"memory.markIrrelevant\": {\n\t\t\tparams: MemoryMarkIrrelevantParams;\n\t\t\treturn: { ok: boolean };\n\t\t};\n\t};\n\tevents: {\n\t\tbookmark_creating: BookmarkCreatingEvent;\n\t\tmemory_updated: MemoryUpdatedEvent;\n\t\tmemory_update_failed: MemoryUpdateFailedEvent;\n\t\tmemory_irrelevant_marked: MemoryIrrelevantMarkedEvent;\n\t};\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["contract.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC","sourcesContent":["import type { ChannelContract } from \"@dyyz1993/pi-coding-agent\";\n\nexport const MEMORY_CHANNEL_NAME = \"memory\";\n\nexport interface MemoryFileInfo {\n\tfilename: string;\n\tfilePath: string;\n\tdescription: string | null;\n\ttype: string | null;\n\tmtimeMs: number;\n}\n\nexport interface MemoryListResult {\n\ttype: \"list_result\";\n\tfiles: MemoryFileInfo[];\n\tentrypointContent: string | null;\n\tmemoryDir: string;\n}\n\nexport interface MemoryUserRememberParams {\n\tsourceSessionId?: string;\n\tsourceMessageIds?: string[];\n\tcontent?: string;\n}\n\nexport interface BookmarkCreatingEvent {\n\ttype: \"bookmark_creating\";\n}\n\nexport interface MemoryUpdatedEvent {\n\ttype: \"memory_updated\";\n\tfiles: MemoryFileInfo[];\n}\n\nexport interface MemoryUpdateFailedEvent {\n\ttype: \"memory_update_failed\";\n\treason: string;\n}\n\nexport interface MemoryChannelContract extends ChannelContract {\n\tmethods: {\n\t\t\"memory.list\": {\n\t\t\tparams: Record<string, never>;\n\t\t\treturn: MemoryListResult;\n\t\t};\n\t\t\"memory.userRemember\": {\n\t\t\tparams: MemoryUserRememberParams;\n\t\t\treturn: { ok: boolean };\n\t\t};\n\t};\n\tevents: {\n\t\tbookmark_creating: BookmarkCreatingEvent;\n\t\tmemory_updated: MemoryUpdatedEvent;\n\t\tmemory_update_failed: MemoryUpdateFailedEvent;\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["contract.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC","sourcesContent":["import type { ChannelContract } from \"@dyyz1993/pi-coding-agent\";\n\nexport const MEMORY_CHANNEL_NAME = \"memory\";\n\nexport interface MemoryFileInfo {\n\tfilename: string;\n\tfilePath: string;\n\tdescription: string | null;\n\ttype: string | null;\n\tmtimeMs: number;\n}\n\nexport interface MemoryListResult {\n\ttype: \"list_result\";\n\tfiles: MemoryFileInfo[];\n\tentrypointContent: string | null;\n\tmemoryDir: string;\n}\n\nexport interface MemoryUserRememberParams {\n\tsourceSessionId?: string;\n\tsourceMessageIds?: string[];\n\tcontent?: string;\n}\n\nexport interface MemoryMarkIrrelevantParams {\n\tquery: string;\n\tselectedFiles: string[];\n}\n\nexport interface MemoryIrrelevantMarkedEvent {\n\ttype: \"memory_irrelevant_marked\";\n\tquery: string;\n\tselectedFiles: string[];\n}\n\nexport interface BookmarkCreatingEvent {\n\ttype: \"bookmark_creating\";\n}\n\nexport interface MemoryUpdatedEvent {\n\ttype: \"memory_updated\";\n\tfiles: MemoryFileInfo[];\n}\n\nexport interface MemoryUpdateFailedEvent {\n\ttype: \"memory_update_failed\";\n\treason: string;\n}\n\nexport interface MemoryChannelContract extends ChannelContract {\n\tmethods: {\n\t\t\"memory.list\": {\n\t\t\tparams: Record<string, never>;\n\t\t\treturn: MemoryListResult;\n\t\t};\n\t\t\"memory.userRemember\": {\n\t\t\tparams: MemoryUserRememberParams;\n\t\t\treturn: { ok: boolean };\n\t\t};\n\t\t\"memory.markIrrelevant\": {\n\t\t\tparams: MemoryMarkIrrelevantParams;\n\t\t\treturn: { ok: boolean };\n\t\t};\n\t};\n\tevents: {\n\t\tbookmark_creating: BookmarkCreatingEvent;\n\t\tmemory_updated: MemoryUpdatedEvent;\n\t\tmemory_update_failed: MemoryUpdateFailedEvent;\n\t\tmemory_irrelevant_marked: MemoryIrrelevantMarkedEvent;\n\t};\n}\n"]}
|
|
@@ -23,6 +23,17 @@ export interface MemoryUserRememberParams {
|
|
|
23
23
|
content?: string;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export interface MemoryMarkIrrelevantParams {
|
|
27
|
+
query: string;
|
|
28
|
+
selectedFiles: string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface MemoryIrrelevantMarkedEvent {
|
|
32
|
+
type: "memory_irrelevant_marked";
|
|
33
|
+
query: string;
|
|
34
|
+
selectedFiles: string[];
|
|
35
|
+
}
|
|
36
|
+
|
|
26
37
|
export interface BookmarkCreatingEvent {
|
|
27
38
|
type: "bookmark_creating";
|
|
28
39
|
}
|
|
@@ -47,10 +58,15 @@ export interface MemoryChannelContract extends ChannelContract {
|
|
|
47
58
|
params: MemoryUserRememberParams;
|
|
48
59
|
return: { ok: boolean };
|
|
49
60
|
};
|
|
61
|
+
"memory.markIrrelevant": {
|
|
62
|
+
params: MemoryMarkIrrelevantParams;
|
|
63
|
+
return: { ok: boolean };
|
|
64
|
+
};
|
|
50
65
|
};
|
|
51
66
|
events: {
|
|
52
67
|
bookmark_creating: BookmarkCreatingEvent;
|
|
53
68
|
memory_updated: MemoryUpdatedEvent;
|
|
54
69
|
memory_update_failed: MemoryUpdateFailedEvent;
|
|
70
|
+
memory_irrelevant_marked: MemoryIrrelevantMarkedEvent;
|
|
55
71
|
};
|
|
56
72
|
}
|