memorix 1.2.2 → 1.2.4
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 +27 -0
- package/README.md +3 -3
- package/README.zh-CN.md +3 -3
- package/TEAM.md +86 -86
- package/dist/cli/index.js +5199 -4726
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +428 -49
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.js +97 -18
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +27 -0
- package/dist/sdk.js +428 -49
- package/dist/sdk.js.map +1 -1
- package/docs/1.2.4-PERSISTENT-MEMORY-DELIVERY.md +86 -0
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +13 -1
- package/docs/API_REFERENCE.md +13 -3
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/dev-log/progress.txt +60 -9
- package/package.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/src/audit/index.ts +156 -156
- package/src/cli/capability-map.ts +1 -1
- package/src/cli/command-guide.ts +4 -1
- package/src/cli/commands/agent-integrations.ts +5 -1
- package/src/cli/commands/audit-list.ts +89 -89
- package/src/cli/commands/background.ts +659 -659
- package/src/cli/commands/codegraph.ts +1 -1
- package/src/cli/commands/context.ts +9 -1
- package/src/cli/commands/formation.ts +48 -48
- package/src/cli/commands/git-hook-install.ts +111 -111
- package/src/cli/commands/handoff.ts +54 -54
- package/src/cli/commands/hooks-status.ts +63 -63
- package/src/cli/commands/ingest-commit.ts +153 -153
- package/src/cli/commands/ingest-image.ts +66 -66
- package/src/cli/commands/ingest-log.ts +180 -180
- package/src/cli/commands/ingest.ts +44 -44
- package/src/cli/commands/integrate-shared.ts +15 -15
- package/src/cli/commands/lock.ts +82 -82
- package/src/cli/commands/message.ts +104 -104
- package/src/cli/commands/poll.ts +58 -58
- package/src/cli/commands/purge-all-memory.ts +85 -85
- package/src/cli/commands/purge-project-memory.ts +83 -83
- package/src/cli/commands/reasoning.ts +118 -118
- package/src/cli/commands/resume.ts +31 -0
- package/src/cli/commands/serve-shared.ts +118 -118
- package/src/cli/commands/session.ts +15 -7
- package/src/cli/commands/skills.ts +114 -114
- package/src/cli/commands/task.ts +167 -167
- package/src/cli/commands/transfer.ts +47 -47
- package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
- package/src/cli/index.ts +3 -1
- package/src/cli/tui/ChatView.tsx +234 -234
- package/src/cli/tui/CommandBar.tsx +312 -312
- package/src/cli/tui/ContextRail.tsx +118 -118
- package/src/cli/tui/HeaderBar.tsx +72 -72
- package/src/cli/tui/LogoBanner.tsx +51 -51
- package/src/cli/tui/Sidebar.tsx +179 -179
- package/src/cli/tui/index.ts +41 -41
- package/src/cli/tui/markdown-render.tsx +371 -371
- package/src/cli/tui/session-service.ts +3 -2
- package/src/cli/tui/use-mouse.ts +157 -157
- package/src/cli/tui/useNavigation.ts +56 -56
- package/src/cli/update-checker.ts +211 -211
- package/src/cli/version.ts +7 -7
- package/src/cli/workbench.ts +1 -1
- package/src/codegraph/auto-context.ts +54 -1
- package/src/codegraph/task-lens.ts +29 -0
- package/src/compact/token-budget.ts +89 -74
- package/src/config/toml-loader.ts +9 -5
- package/src/dashboard/project-classification.ts +64 -64
- package/src/embedding/fastembed-provider.ts +142 -142
- package/src/embedding/transformers-provider.ts +111 -111
- package/src/git/extractor.ts +209 -209
- package/src/git/hooks-path.ts +85 -85
- package/src/hooks/handler.ts +127 -66
- package/src/hooks/installers/index.ts +5 -4
- package/src/hooks/official-skills.ts +6 -4
- package/src/hooks/pattern-detector.ts +173 -173
- package/src/hooks/rules/memorix-agent-rules.md +9 -7
- package/src/hooks/significance-filter.ts +250 -250
- package/src/knowledge/context-assembly.ts +4 -1
- package/src/knowledge/workset.ts +89 -1
- package/src/llm/memory-manager.ts +328 -328
- package/src/llm/provider.ts +885 -885
- package/src/llm/quality.ts +248 -248
- package/src/memory/attribution-guard.ts +249 -249
- package/src/memory/disclosure-policy.ts +135 -135
- package/src/memory/entity-extractor.ts +197 -197
- package/src/memory/formation/evaluate.ts +217 -217
- package/src/memory/formation/extract.ts +361 -361
- package/src/memory/formation/index.ts +417 -417
- package/src/memory/formation/resolve.ts +344 -344
- package/src/memory/formation/types.ts +315 -315
- package/src/memory/freshness.ts +122 -122
- package/src/memory/graph.ts +197 -197
- package/src/memory/refs.ts +94 -94
- package/src/memory/secret-filter.ts +79 -79
- package/src/memory/session.ts +158 -9
- package/src/multimodal/image-loader.ts +143 -143
- package/src/orchestrate/adapters/claude-stream.ts +192 -192
- package/src/orchestrate/adapters/claude.ts +111 -111
- package/src/orchestrate/adapters/codex-stream.ts +134 -134
- package/src/orchestrate/adapters/codex.ts +41 -41
- package/src/orchestrate/adapters/gemini-stream.ts +166 -166
- package/src/orchestrate/adapters/gemini.ts +42 -42
- package/src/orchestrate/adapters/index.ts +73 -73
- package/src/orchestrate/adapters/opencode-stream.ts +143 -143
- package/src/orchestrate/adapters/opencode.ts +47 -47
- package/src/orchestrate/adapters/spawn-helper.ts +286 -286
- package/src/orchestrate/adapters/types.ts +77 -77
- package/src/orchestrate/capability-router.ts +284 -284
- package/src/orchestrate/context-compact.ts +188 -188
- package/src/orchestrate/cost-tracker.ts +219 -219
- package/src/orchestrate/error-recovery.ts +191 -191
- package/src/orchestrate/evidence.ts +140 -140
- package/src/orchestrate/ledger.ts +110 -110
- package/src/orchestrate/memorix-bridge.ts +343 -343
- package/src/orchestrate/output-budget.ts +80 -80
- package/src/orchestrate/permission.ts +152 -152
- package/src/orchestrate/pipeline-trace.ts +131 -131
- package/src/orchestrate/prompt-builder.ts +155 -155
- package/src/orchestrate/ring-buffer.ts +37 -37
- package/src/orchestrate/task-graph.ts +389 -389
- package/src/orchestrate/worktree.ts +232 -232
- package/src/project/aliases.ts +374 -374
- package/src/project/detector.ts +268 -268
- package/src/rules/adapters/claude-code.ts +99 -99
- package/src/rules/adapters/codex.ts +97 -97
- package/src/rules/adapters/copilot.ts +124 -124
- package/src/rules/adapters/cursor.ts +114 -114
- package/src/rules/adapters/kiro.ts +126 -126
- package/src/rules/adapters/trae.ts +56 -56
- package/src/rules/adapters/windsurf.ts +83 -83
- package/src/rules/syncer.ts +235 -235
- package/src/sdk.ts +299 -299
- package/src/search/intent-detector.ts +289 -289
- package/src/search/query-expansion.ts +52 -52
- package/src/server/formation-timeout.ts +27 -27
- package/src/server.ts +144 -10
- package/src/skills/mini-skills.ts +386 -386
- package/src/store/bun-sqlite-compat.ts +118 -15
- package/src/store/chat-store.ts +119 -119
- package/src/store/graph-store.ts +249 -249
- package/src/store/mini-skill-store.ts +349 -349
- package/src/store/persistence-json.ts +212 -212
- package/src/store/persistence.ts +291 -291
- package/src/store/project-affinity.ts +195 -195
- package/src/store/sqlite-db.ts +3 -3
- package/src/team/event-bus.ts +76 -76
- package/src/team/file-locks.ts +173 -173
- package/src/team/handoff.ts +161 -161
- package/src/team/messages.ts +203 -203
- package/src/team/poll.ts +132 -132
- package/src/team/tasks.ts +211 -211
- package/src/workspace/mcp-adapters/codex.ts +191 -191
- package/src/workspace/mcp-adapters/copilot.ts +105 -105
- package/src/workspace/mcp-adapters/cursor.ts +53 -53
- package/src/workspace/mcp-adapters/kiro.ts +64 -64
- package/src/workspace/mcp-adapters/opencode.ts +123 -123
- package/src/workspace/mcp-adapters/trae.ts +134 -134
- package/src/workspace/mcp-adapters/windsurf.ts +91 -91
- package/src/workspace/sanitizer.ts +60 -60
- package/src/workspace/workflow-sync.ts +131 -131
package/dist/sdk.js
CHANGED
|
@@ -86,36 +86,111 @@ var init_visibility = __esm({
|
|
|
86
86
|
|
|
87
87
|
// src/store/bun-sqlite-compat.ts
|
|
88
88
|
import { createRequire } from "module";
|
|
89
|
+
function loadNodeSqlite() {
|
|
90
|
+
const nodeSqlite = requireFromHere("node:sqlite");
|
|
91
|
+
return nodeSqlite.DatabaseSync;
|
|
92
|
+
}
|
|
93
|
+
function isNativeBindingFailure(error) {
|
|
94
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
95
|
+
return /could not locate the bindings file|better_sqlite3\.node|module did not self-register/i.test(message);
|
|
96
|
+
}
|
|
97
|
+
function addCompatibility(db2) {
|
|
98
|
+
if (!db2.pragma) {
|
|
99
|
+
db2.pragma = function(pragma, options) {
|
|
100
|
+
const statement = db2.prepare(`PRAGMA ${pragma}`);
|
|
101
|
+
if (options?.simple) {
|
|
102
|
+
const result = statement.get();
|
|
103
|
+
return result ? Object.values(result)[0] : void 0;
|
|
104
|
+
}
|
|
105
|
+
return statement.all();
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (!db2.transaction) {
|
|
109
|
+
let depth = 0;
|
|
110
|
+
let sequence = 0;
|
|
111
|
+
db2.transaction = function(fn) {
|
|
112
|
+
return (...args) => {
|
|
113
|
+
const outermost = depth === 0;
|
|
114
|
+
const savepoint = `memorix_tx_${++sequence}`;
|
|
115
|
+
if (outermost) {
|
|
116
|
+
db2.exec("BEGIN IMMEDIATE");
|
|
117
|
+
} else {
|
|
118
|
+
db2.exec(`SAVEPOINT ${savepoint}`);
|
|
119
|
+
}
|
|
120
|
+
depth += 1;
|
|
121
|
+
try {
|
|
122
|
+
const result = fn(...args);
|
|
123
|
+
if (result && typeof result.then === "function") {
|
|
124
|
+
throw new Error("[memorix] SQLite transactions must be synchronous");
|
|
125
|
+
}
|
|
126
|
+
depth -= 1;
|
|
127
|
+
if (outermost) {
|
|
128
|
+
db2.exec("COMMIT");
|
|
129
|
+
} else {
|
|
130
|
+
db2.exec(`RELEASE SAVEPOINT ${savepoint}`);
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
depth -= 1;
|
|
135
|
+
try {
|
|
136
|
+
if (outermost) {
|
|
137
|
+
db2.exec("ROLLBACK");
|
|
138
|
+
} else {
|
|
139
|
+
db2.exec(`ROLLBACK TO SAVEPOINT ${savepoint}`);
|
|
140
|
+
db2.exec(`RELEASE SAVEPOINT ${savepoint}`);
|
|
141
|
+
}
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return db2;
|
|
150
|
+
}
|
|
151
|
+
function instantiateDatabase(Sqlite, filePath, options) {
|
|
152
|
+
return driver === "node:sqlite" && options === void 0 ? new Sqlite(filePath) : new Sqlite(filePath, options);
|
|
153
|
+
}
|
|
89
154
|
function loadSqlite() {
|
|
90
155
|
if (Database) return Database;
|
|
156
|
+
if (process.env.MEMORIX_SQLITE_DRIVER === "node") {
|
|
157
|
+
Database = loadNodeSqlite();
|
|
158
|
+
driver = "node:sqlite";
|
|
159
|
+
return Database;
|
|
160
|
+
}
|
|
91
161
|
try {
|
|
92
162
|
Database = requireFromHere("better-sqlite3");
|
|
163
|
+
driver = "better-sqlite3";
|
|
164
|
+
return Database;
|
|
165
|
+
} catch {
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
Database = loadNodeSqlite();
|
|
169
|
+
driver = "node:sqlite";
|
|
93
170
|
return Database;
|
|
94
171
|
} catch {
|
|
95
172
|
}
|
|
96
173
|
try {
|
|
97
174
|
const bunSqlite = requireFromHere("bun:sqlite");
|
|
98
175
|
Database = bunSqlite.Database;
|
|
176
|
+
driver = "bun:sqlite";
|
|
99
177
|
return Database;
|
|
100
178
|
} catch {
|
|
101
|
-
throw new Error("[memorix]
|
|
179
|
+
throw new Error("[memorix] SQLite is unavailable (better-sqlite3, node:sqlite, and bun:sqlite failed)");
|
|
102
180
|
}
|
|
103
181
|
}
|
|
104
182
|
function createDatabase(path25, options) {
|
|
105
183
|
const Sqlite = loadSqlite();
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return db2.prepare(`PRAGMA ${pragma}`).all();
|
|
114
|
-
};
|
|
184
|
+
try {
|
|
185
|
+
return addCompatibility(instantiateDatabase(Sqlite, path25, options));
|
|
186
|
+
} catch (error) {
|
|
187
|
+
if (driver !== "better-sqlite3" || !isNativeBindingFailure(error)) throw error;
|
|
188
|
+
Database = loadNodeSqlite();
|
|
189
|
+
driver = "node:sqlite";
|
|
190
|
+
return addCompatibility(instantiateDatabase(Database, path25, options));
|
|
115
191
|
}
|
|
116
|
-
return db2;
|
|
117
192
|
}
|
|
118
|
-
var Database, requireFromHere;
|
|
193
|
+
var Database, driver, requireFromHere;
|
|
119
194
|
var init_bun_sqlite_compat = __esm({
|
|
120
195
|
"src/store/bun-sqlite-compat.ts"() {
|
|
121
196
|
"use strict";
|
|
@@ -134,7 +209,7 @@ function loadBetterSqlite3() {
|
|
|
134
209
|
BetterSqlite3 = loadSqlite();
|
|
135
210
|
return BetterSqlite3;
|
|
136
211
|
} catch {
|
|
137
|
-
throw new Error("[memorix] SQLite is not available (
|
|
212
|
+
throw new Error("[memorix] SQLite is not available (better-sqlite3, node:sqlite, and bun:sqlite all failed)");
|
|
138
213
|
}
|
|
139
214
|
}
|
|
140
215
|
function hasColumn(db2, table, column) {
|
|
@@ -1942,7 +2017,16 @@ function truncateToTokenBudget(text, budget) {
|
|
|
1942
2017
|
result = result.slice(0, Math.floor(result.length * 0.9));
|
|
1943
2018
|
}
|
|
1944
2019
|
if (result.length < text.length) {
|
|
1945
|
-
|
|
2020
|
+
const nextCharacter = text.charAt(result.length);
|
|
2021
|
+
if (nextCharacter && !/[\s.,;:!?)}\]]/.test(nextCharacter)) {
|
|
2022
|
+
const boundary = result.search(/\s+\S*$/);
|
|
2023
|
+
result = boundary > 0 ? result.slice(0, boundary).trimEnd() : "";
|
|
2024
|
+
}
|
|
2025
|
+
while (result && fitsInBudget(result + "...", budget) === false) {
|
|
2026
|
+
const boundary = result.lastIndexOf(" ");
|
|
2027
|
+
result = boundary > 0 ? result.slice(0, boundary).trimEnd() : "";
|
|
2028
|
+
}
|
|
2029
|
+
result = result ? result + "..." : "...";
|
|
1946
2030
|
}
|
|
1947
2031
|
}
|
|
1948
2032
|
return result;
|
|
@@ -2595,6 +2679,9 @@ function parseTomlValue(raw, filePath, line) {
|
|
|
2595
2679
|
if (raw.startsWith('"') && raw.endsWith('"')) {
|
|
2596
2680
|
return raw.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
2597
2681
|
}
|
|
2682
|
+
if (raw.startsWith("'") && raw.endsWith("'")) {
|
|
2683
|
+
return raw.slice(1, -1);
|
|
2684
|
+
}
|
|
2598
2685
|
if (raw === "true") return true;
|
|
2599
2686
|
if (raw === "false") return false;
|
|
2600
2687
|
if (/^-?\d+$/.test(raw)) return Number.parseInt(raw, 10);
|
|
@@ -2607,7 +2694,7 @@ function parseTomlValue(raw, filePath, line) {
|
|
|
2607
2694
|
throw new Error(`Unsupported TOML value in ${filePath}:${line}`);
|
|
2608
2695
|
}
|
|
2609
2696
|
function stripComment(line) {
|
|
2610
|
-
let
|
|
2697
|
+
let quote = null;
|
|
2611
2698
|
let escaped = false;
|
|
2612
2699
|
for (let i = 0; i < line.length; i++) {
|
|
2613
2700
|
const char = line[i];
|
|
@@ -2615,15 +2702,16 @@ function stripComment(line) {
|
|
|
2615
2702
|
escaped = false;
|
|
2616
2703
|
continue;
|
|
2617
2704
|
}
|
|
2618
|
-
if (char === "\\" &&
|
|
2705
|
+
if (char === "\\" && quote === '"') {
|
|
2619
2706
|
escaped = true;
|
|
2620
2707
|
continue;
|
|
2621
2708
|
}
|
|
2622
|
-
if (char === '"') {
|
|
2623
|
-
|
|
2709
|
+
if (char === '"' || char === "'") {
|
|
2710
|
+
if (quote === char) quote = null;
|
|
2711
|
+
else if (quote === null) quote = char;
|
|
2624
2712
|
continue;
|
|
2625
2713
|
}
|
|
2626
|
-
if (char === "#" &&
|
|
2714
|
+
if (char === "#" && quote === null) {
|
|
2627
2715
|
return line.slice(0, i);
|
|
2628
2716
|
}
|
|
2629
2717
|
}
|
|
@@ -13695,6 +13783,7 @@ var init_workflow_store = __esm({
|
|
|
13695
13783
|
var task_lens_exports = {};
|
|
13696
13784
|
__export(task_lens_exports, {
|
|
13697
13785
|
containsTaskKeyword: () => containsTaskKeyword,
|
|
13786
|
+
isContinuationTask: () => isContinuationTask,
|
|
13698
13787
|
lensPathCandidates: () => lensPathCandidates,
|
|
13699
13788
|
lensVerificationHints: () => lensVerificationHints,
|
|
13700
13789
|
rankLensPaths: () => rankLensPaths,
|
|
@@ -13760,6 +13849,12 @@ function resolveTaskLens(task) {
|
|
|
13760
13849
|
}
|
|
13761
13850
|
return best.score > 0 ? LENSES[best.id] : LENSES.general;
|
|
13762
13851
|
}
|
|
13852
|
+
function isContinuationTask(task) {
|
|
13853
|
+
const normalized = (task ?? "").trim().toLowerCase();
|
|
13854
|
+
return normalized.length > 0 && CONTINUATION_KEYWORDS.some(
|
|
13855
|
+
(keyword) => containsTaskKeyword(normalized, keyword)
|
|
13856
|
+
);
|
|
13857
|
+
}
|
|
13763
13858
|
function pathKindScore(path25, lens) {
|
|
13764
13859
|
const normalized = normalizePath2(path25).toLowerCase();
|
|
13765
13860
|
const name = normalized.split("/").pop() ?? normalized;
|
|
@@ -13903,7 +13998,7 @@ function shouldShowLensSource(source, lens, task) {
|
|
|
13903
13998
|
if (lens.id === "onboarding" || lens.id === "release" || lens.id === "docs") return score >= 40;
|
|
13904
13999
|
return score > 0;
|
|
13905
14000
|
}
|
|
13906
|
-
var STOP_WORDS, LENSES, KEYWORDS, LENS_PRIORITY;
|
|
14001
|
+
var STOP_WORDS, LENSES, KEYWORDS, CONTINUATION_KEYWORDS, LENS_PRIORITY;
|
|
13907
14002
|
var init_task_lens = __esm({
|
|
13908
14003
|
"src/codegraph/task-lens.ts"() {
|
|
13909
14004
|
"use strict";
|
|
@@ -14006,6 +14101,7 @@ var init_task_lens = __esm({
|
|
|
14006
14101
|
"fail",
|
|
14007
14102
|
"failing",
|
|
14008
14103
|
"fix",
|
|
14104
|
+
"fixing",
|
|
14009
14105
|
"issue",
|
|
14010
14106
|
"regression",
|
|
14011
14107
|
"repro",
|
|
@@ -14023,6 +14119,18 @@ var init_task_lens = __esm({
|
|
|
14023
14119
|
docs: ["doc", "docs", "readme", "\u6587\u6863", "\u8BF4\u660E"],
|
|
14024
14120
|
test: ["coverage", "fixture", "smoke", "spec", "test", "tests", "testing", "vitest", "\u6D4B\u8BD5"]
|
|
14025
14121
|
};
|
|
14122
|
+
CONTINUATION_KEYWORDS = [
|
|
14123
|
+
"continue",
|
|
14124
|
+
"resume",
|
|
14125
|
+
"pick up",
|
|
14126
|
+
"carry on",
|
|
14127
|
+
"previous session",
|
|
14128
|
+
"\u7EE7\u7EED",
|
|
14129
|
+
"\u63A5\u624B",
|
|
14130
|
+
"\u6062\u590D",
|
|
14131
|
+
"\u5EF6\u7EED",
|
|
14132
|
+
"\u4E0A\u6B21\u4F1A\u8BDD"
|
|
14133
|
+
];
|
|
14026
14134
|
LENS_PRIORITY = [
|
|
14027
14135
|
"bugfix",
|
|
14028
14136
|
"release",
|
|
@@ -15233,6 +15341,7 @@ __export(session_exports, {
|
|
|
15233
15341
|
endSession: () => endSession,
|
|
15234
15342
|
getActiveSession: () => getActiveSession,
|
|
15235
15343
|
getSessionContext: () => getSessionContext,
|
|
15344
|
+
getSessionResumeBrief: () => getSessionResumeBrief,
|
|
15236
15345
|
listSessions: () => listSessions,
|
|
15237
15346
|
scoreObservationForSessionContext: () => scoreObservationForSessionContext,
|
|
15238
15347
|
startSession: () => startSession
|
|
@@ -15300,6 +15409,62 @@ function isSystemSelfObservation(obs) {
|
|
|
15300
15409
|
const text = stringifyObservation(obs, false);
|
|
15301
15410
|
return SYSTEM_SELF_PATTERNS.some((pattern) => pattern.test(text));
|
|
15302
15411
|
}
|
|
15412
|
+
function readerForAlias(reader, aliases, observation) {
|
|
15413
|
+
if (!reader) return void 0;
|
|
15414
|
+
return reader.projectId && aliases.has(observation.projectId) ? { ...reader, projectId: observation.projectId } : reader;
|
|
15415
|
+
}
|
|
15416
|
+
function readableAliasObservations(observations2, aliases, reader) {
|
|
15417
|
+
return reader ? observations2.filter((observation) => canReadObservation(
|
|
15418
|
+
observation,
|
|
15419
|
+
readerForAlias(reader, aliases, observation)
|
|
15420
|
+
)) : observations2;
|
|
15421
|
+
}
|
|
15422
|
+
function isUsefulSessionSummary(summary) {
|
|
15423
|
+
return Boolean(summary) && !NOISE_PATTERNS2.some((pattern) => pattern.test(summary)) && !SYSTEM_SELF_PATTERNS.some((pattern) => pattern.test(summary));
|
|
15424
|
+
}
|
|
15425
|
+
function continuationTaskTokens(task) {
|
|
15426
|
+
return [...new Set(
|
|
15427
|
+
(task?.toLowerCase().match(/[a-z0-9_./-]+|[\u4e00-\u9fff]+/g) ?? []).map((token) => token.trim()).filter((token) => token.length > 1 && !["continue", "resume", "\u7EE7\u7EED", "\u63A5\u624B", "\u6062\u590D"].includes(token))
|
|
15428
|
+
)].slice(0, 8);
|
|
15429
|
+
}
|
|
15430
|
+
function continuationScore(observation, projectTokens, task) {
|
|
15431
|
+
let score = scoreObservationForSessionContext(observation, projectTokens);
|
|
15432
|
+
if (observation.type === "session-request" || observation.type === "what-changed") score += 1;
|
|
15433
|
+
const text = stringifyObservation(observation);
|
|
15434
|
+
const matches = continuationTaskTokens(task).filter((token) => text.includes(token)).length;
|
|
15435
|
+
score += Math.min(matches, 2) * 2;
|
|
15436
|
+
return score;
|
|
15437
|
+
}
|
|
15438
|
+
async function getSessionResumeBrief(projectId, task, reader) {
|
|
15439
|
+
const aliasSet = await resolveProjectIds(projectId);
|
|
15440
|
+
const [sessions, allObs] = await Promise.all([
|
|
15441
|
+
loadAliasSessions(aliasSet),
|
|
15442
|
+
loadAliasActiveObservations(aliasSet)
|
|
15443
|
+
]);
|
|
15444
|
+
const readableObs = readableAliasObservations(allObs, aliasSet, reader);
|
|
15445
|
+
const previous = sessions.filter((session) => session.status === "completed").sort((a, b) => new Date(b.endedAt || b.startedAt).getTime() - new Date(a.endedAt || a.startedAt).getTime()).find((session) => session.summary && session.summary !== "(session ended implicitly by new session start)" && isUsefulSessionSummary(session.summary));
|
|
15446
|
+
const projectTokens = tokenizeProjectId(projectId);
|
|
15447
|
+
const memories = readableObs.filter((observation) => RESUME_TYPES.has(observation.type)).filter((observation) => classifyLayer(observation) === "L2").filter((observation) => !isNoiseObservation(observation) && !isSystemSelfObservation(observation)).map((observation) => ({
|
|
15448
|
+
observation,
|
|
15449
|
+
score: continuationScore(observation, projectTokens, task)
|
|
15450
|
+
})).sort((a, b) => b.score - a.score || new Date(b.observation.createdAt).getTime() - new Date(a.observation.createdAt).getTime() || a.observation.id - b.observation.id).slice(0, 3).map(({ observation }) => ({
|
|
15451
|
+
id: observation.id,
|
|
15452
|
+
title: sanitizeCredentials(observation.title),
|
|
15453
|
+
type: observation.type,
|
|
15454
|
+
...observation.facts?.[0] ? { detail: sanitizeCredentials(observation.facts[0]) } : observation.narrative ? { detail: sanitizeCredentials(observation.narrative) } : {}
|
|
15455
|
+
}));
|
|
15456
|
+
return {
|
|
15457
|
+
...previous?.summary ? {
|
|
15458
|
+
previousSession: {
|
|
15459
|
+
id: previous.id,
|
|
15460
|
+
...previous.agent ? { agent: previous.agent } : {},
|
|
15461
|
+
...previous.endedAt ? { endedAt: previous.endedAt } : {},
|
|
15462
|
+
summary: sanitizeCredentials(previous.summary)
|
|
15463
|
+
}
|
|
15464
|
+
} : {},
|
|
15465
|
+
memories
|
|
15466
|
+
};
|
|
15467
|
+
}
|
|
15303
15468
|
function scoreObservationForSessionContext(obs, projectTokens, now3 = Date.now()) {
|
|
15304
15469
|
let score = TYPE_WEIGHTS2[obs.type] ?? 1;
|
|
15305
15470
|
const text = stringifyObservation(obs);
|
|
@@ -15346,7 +15511,7 @@ async function startSession(projectDir2, projectId, opts) {
|
|
|
15346
15511
|
status: "active",
|
|
15347
15512
|
agent: opts?.agent
|
|
15348
15513
|
};
|
|
15349
|
-
const previousContext = await getSessionContext(projectDir2, projectId);
|
|
15514
|
+
const previousContext = await getSessionContext(projectDir2, projectId, 3, opts?.reader);
|
|
15350
15515
|
const sessionStore = getSessionStore();
|
|
15351
15516
|
const aliasSet = await resolveProjectIds(projectId);
|
|
15352
15517
|
await sessionStore.atomicRolloverInsert(session, [...aliasSet], now3);
|
|
@@ -15364,23 +15529,24 @@ async function endSession(projectDir2, sessionId, summary) {
|
|
|
15364
15529
|
await sessionStore.update(session);
|
|
15365
15530
|
return session;
|
|
15366
15531
|
}
|
|
15367
|
-
async function getSessionContext(projectDir2, projectId, limit = 3) {
|
|
15532
|
+
async function getSessionContext(projectDir2, projectId, limit = 3, reader) {
|
|
15368
15533
|
const aliasSet = await resolveProjectIds(projectId);
|
|
15369
15534
|
const [sessions, allObs] = await Promise.all([
|
|
15370
15535
|
loadAliasSessions(aliasSet),
|
|
15371
15536
|
loadAliasActiveObservations(aliasSet)
|
|
15372
15537
|
]);
|
|
15538
|
+
const readableObs = readableAliasObservations(allObs, aliasSet, reader);
|
|
15373
15539
|
const isNoisySummary = (summary) => {
|
|
15374
15540
|
if (!summary) return false;
|
|
15375
15541
|
return NOISE_PATTERNS2.some((p) => p.test(summary)) || SYSTEM_SELF_PATTERNS.some((p) => p.test(summary));
|
|
15376
15542
|
};
|
|
15377
15543
|
const projectSessions = sessions.filter((session) => session.status === "completed").filter((session) => !isNoisySummary(session.summary)).sort((a, b) => new Date(b.endedAt || b.startedAt).getTime() - new Date(a.endedAt || a.startedAt).getTime()).slice(0, limit);
|
|
15378
|
-
if (projectSessions.length === 0 &&
|
|
15544
|
+
if (projectSessions.length === 0 && readableObs.length === 0) {
|
|
15379
15545
|
return "";
|
|
15380
15546
|
}
|
|
15381
15547
|
const lines = [];
|
|
15382
15548
|
const projectTokens = tokenizeProjectId(projectId);
|
|
15383
|
-
const projectObs =
|
|
15549
|
+
const projectObs = readableObs.filter((obs) => !isNoiseObservation(obs) && !isSystemSelfObservation(obs));
|
|
15384
15550
|
const l2Scored = projectObs.filter((obs) => PRIORITY_TYPES.has(obs.type) && classifyLayer(obs) === "L2").map((obs) => ({ obs, score: scoreObservationForSessionContext(obs, projectTokens) })).sort((a, b) => {
|
|
15385
15551
|
if (b.score !== a.score) return b.score - a.score;
|
|
15386
15552
|
return new Date(b.obs.createdAt).getTime() - new Date(a.obs.createdAt).getTime();
|
|
@@ -15406,7 +15572,7 @@ async function getSessionContext(projectDir2, projectId, limit = 3) {
|
|
|
15406
15572
|
const hasL1Content = l1HookObs.length > 0 || l3GitCount > 0;
|
|
15407
15573
|
if (hasL1Content) {
|
|
15408
15574
|
let graphNeighbors = [];
|
|
15409
|
-
if (activeEntities.length > 0) {
|
|
15575
|
+
if (!reader && activeEntities.length > 0) {
|
|
15410
15576
|
try {
|
|
15411
15577
|
const graphMgr = new KnowledgeGraphManager(projectDir2);
|
|
15412
15578
|
await graphMgr.init();
|
|
@@ -15520,7 +15686,7 @@ async function getActiveSession(projectDir2, projectId) {
|
|
|
15520
15686
|
const sessions = await loadAliasActiveSessions(aliasSet);
|
|
15521
15687
|
return sessions.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime())[0] ?? null;
|
|
15522
15688
|
}
|
|
15523
|
-
var PRIORITY_TYPES, TYPE_EMOJI, TYPE_WEIGHTS2, NOISE_PATTERNS2, COMMAND_TRACE_PATTERNS, SYSTEM_SELF_PATTERNS;
|
|
15689
|
+
var PRIORITY_TYPES, RESUME_TYPES, TYPE_EMOJI, TYPE_WEIGHTS2, NOISE_PATTERNS2, COMMAND_TRACE_PATTERNS, SYSTEM_SELF_PATTERNS;
|
|
15524
15690
|
var init_session = __esm({
|
|
15525
15691
|
"src/memory/session.ts"() {
|
|
15526
15692
|
"use strict";
|
|
@@ -15532,7 +15698,19 @@ var init_session = __esm({
|
|
|
15532
15698
|
init_session_store();
|
|
15533
15699
|
init_graph();
|
|
15534
15700
|
init_secret_filter();
|
|
15701
|
+
init_visibility();
|
|
15535
15702
|
PRIORITY_TYPES = /* @__PURE__ */ new Set(["gotcha", "decision", "problem-solution", "trade-off", "discovery"]);
|
|
15703
|
+
RESUME_TYPES = /* @__PURE__ */ new Set([
|
|
15704
|
+
"gotcha",
|
|
15705
|
+
"decision",
|
|
15706
|
+
"problem-solution",
|
|
15707
|
+
"trade-off",
|
|
15708
|
+
"discovery",
|
|
15709
|
+
"how-it-works",
|
|
15710
|
+
"what-changed",
|
|
15711
|
+
"reasoning",
|
|
15712
|
+
"session-request"
|
|
15713
|
+
]);
|
|
15536
15714
|
TYPE_EMOJI = {
|
|
15537
15715
|
"gotcha": "[DISCOVERY]",
|
|
15538
15716
|
"decision": "[WHY]",
|
|
@@ -15693,6 +15871,7 @@ function freshnessForMemory(status) {
|
|
|
15693
15871
|
return "unknown";
|
|
15694
15872
|
}
|
|
15695
15873
|
function receiptOmissionKind(raw) {
|
|
15874
|
+
if (raw.includes("continuation")) return "continuation";
|
|
15696
15875
|
if (raw.includes("task")) return "task";
|
|
15697
15876
|
if (raw.includes("fact")) return "current-fact";
|
|
15698
15877
|
if (raw.includes("state")) return "code-state";
|
|
@@ -15742,6 +15921,48 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15742
15921
|
trust: "source-backed"
|
|
15743
15922
|
});
|
|
15744
15923
|
appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
|
|
15924
|
+
const hasContinuation = Boolean(
|
|
15925
|
+
input.continuation?.previousSession || (input.continuation?.memories.length ?? 0) > 0
|
|
15926
|
+
);
|
|
15927
|
+
if (hasContinuation && input.continuation) {
|
|
15928
|
+
appendLine(lines, "", maxTokens, omitted, "continuation-heading");
|
|
15929
|
+
appendLine(lines, "Resume from prior work", maxTokens, omitted, "continuation-heading");
|
|
15930
|
+
if (input.continuation.previousSession) {
|
|
15931
|
+
const session = input.continuation.previousSession;
|
|
15932
|
+
const source = [session.agent, session.endedAt ? session.endedAt.slice(0, 10) : void 0].filter(Boolean).join(", ");
|
|
15933
|
+
appendLine(
|
|
15934
|
+
lines,
|
|
15935
|
+
"- Previous session" + (source ? ` (${source})` : "") + ": " + short(session.summary, 44),
|
|
15936
|
+
maxTokens,
|
|
15937
|
+
omitted,
|
|
15938
|
+
"continuation-session",
|
|
15939
|
+
selected,
|
|
15940
|
+
{
|
|
15941
|
+
kind: "continuation",
|
|
15942
|
+
id: "session:" + session.id,
|
|
15943
|
+
reason: "latest meaningful project session summary",
|
|
15944
|
+
trust: "historical"
|
|
15945
|
+
}
|
|
15946
|
+
);
|
|
15947
|
+
}
|
|
15948
|
+
for (const memory of input.continuation.memories.slice(0, 3)) {
|
|
15949
|
+
const detail = memory.detail ? ": " + short(memory.detail, CONTINUATION_DETAIL_TOKEN_BUDGET) : "";
|
|
15950
|
+
appendLine(
|
|
15951
|
+
lines,
|
|
15952
|
+
"- #" + memory.id + " " + memory.type + ": " + short(memory.title, 18) + detail,
|
|
15953
|
+
maxTokens,
|
|
15954
|
+
omitted,
|
|
15955
|
+
"continuation-memory",
|
|
15956
|
+
selected,
|
|
15957
|
+
{
|
|
15958
|
+
kind: "continuation",
|
|
15959
|
+
id: "memory:" + memory.id,
|
|
15960
|
+
reason: "durable prior-work memory",
|
|
15961
|
+
trust: "historical"
|
|
15962
|
+
}
|
|
15963
|
+
);
|
|
15964
|
+
}
|
|
15965
|
+
}
|
|
15745
15966
|
if (input.cautions.length > 0 || input.cautionMemory.length > 0) {
|
|
15746
15967
|
appendLine(lines, "", maxTokens, omitted, "caution-heading");
|
|
15747
15968
|
appendLine(lines, "Cautions", maxTokens, omitted, "caution-heading");
|
|
@@ -16010,11 +16231,25 @@ async function buildTaskWorkset(input) {
|
|
|
16010
16231
|
...input.verificationHints
|
|
16011
16232
|
]).slice(0, 4);
|
|
16012
16233
|
const normalizedCautions = unique(cautions.map((caution) => caution.kind)).map((kind) => cautions.find((caution) => caution.kind === kind)).slice(0, 6);
|
|
16234
|
+
const continuation = input.continuation && (input.continuation.previousSession || input.continuation.memories.length > 0) ? {
|
|
16235
|
+
...input.continuation.previousSession ? {
|
|
16236
|
+
previousSession: {
|
|
16237
|
+
...input.continuation.previousSession,
|
|
16238
|
+
summary: short(input.continuation.previousSession.summary, 52)
|
|
16239
|
+
}
|
|
16240
|
+
} : {},
|
|
16241
|
+
memories: input.continuation.memories.slice(0, 3).map((memory) => ({
|
|
16242
|
+
...memory,
|
|
16243
|
+
title: short(memory.title, 20),
|
|
16244
|
+
...memory.detail ? { detail: short(memory.detail, CONTINUATION_DETAIL_TOKEN_BUDGET) } : {}
|
|
16245
|
+
}))
|
|
16246
|
+
} : void 0;
|
|
16013
16247
|
const base = {
|
|
16014
16248
|
version: "1.2",
|
|
16015
16249
|
task,
|
|
16016
16250
|
lens: input.lens,
|
|
16017
16251
|
currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
|
|
16252
|
+
...continuation ? { continuation } : {},
|
|
16018
16253
|
...input.codeState ? { codeState: short(input.codeState, 28) } : {},
|
|
16019
16254
|
startHere: unique(input.startHere).slice(0, 5),
|
|
16020
16255
|
...input.semanticCode ? { semanticCode: input.semanticCode } : {},
|
|
@@ -16039,7 +16274,7 @@ async function buildTaskWorkset(input) {
|
|
|
16039
16274
|
budget: { maxTokens }
|
|
16040
16275
|
});
|
|
16041
16276
|
const receipt = {
|
|
16042
|
-
version: "1.2.
|
|
16277
|
+
version: "1.2.4",
|
|
16043
16278
|
target: input.deliveryTarget ?? "project-context",
|
|
16044
16279
|
elapsedMs: Math.max(0, Date.now() - startedAt),
|
|
16045
16280
|
budget: {
|
|
@@ -16061,6 +16296,7 @@ async function buildTaskWorkset(input) {
|
|
|
16061
16296
|
prompt: rendered.prompt
|
|
16062
16297
|
};
|
|
16063
16298
|
}
|
|
16299
|
+
var CONTINUATION_DETAIL_TOKEN_BUDGET;
|
|
16064
16300
|
var init_workset = __esm({
|
|
16065
16301
|
"src/knowledge/workset.ts"() {
|
|
16066
16302
|
"use strict";
|
|
@@ -16073,6 +16309,7 @@ var init_workset = __esm({
|
|
|
16073
16309
|
init_workspace();
|
|
16074
16310
|
init_workflow_store();
|
|
16075
16311
|
init_workflows();
|
|
16312
|
+
CONTINUATION_DETAIL_TOKEN_BUDGET = 20;
|
|
16076
16313
|
}
|
|
16077
16314
|
});
|
|
16078
16315
|
|
|
@@ -16865,6 +17102,7 @@ async function buildAutoProjectContext(input) {
|
|
|
16865
17102
|
const now3 = input.now ?? /* @__PURE__ */ new Date();
|
|
16866
17103
|
const task = input.task?.trim();
|
|
16867
17104
|
const lens = resolveTaskLens(task);
|
|
17105
|
+
const continuationRequested = input.continuation === "always" || input.continuation !== "never" && isContinuationTask(task);
|
|
16868
17106
|
const codegraphConfig = getResolvedConfig({ projectRoot: input.project.rootPath }).codegraph;
|
|
16869
17107
|
const exclude = input.exclude ?? codegraphConfig.excludePatterns;
|
|
16870
17108
|
const maxFileBytes = input.maxFileBytes ?? codegraphConfig.maxFileBytes;
|
|
@@ -17001,6 +17239,11 @@ async function buildAutoProjectContext(input) {
|
|
|
17001
17239
|
if (externalCaution) {
|
|
17002
17240
|
runtimeCautions.push({ kind: "external-codegraph-fallback", message: externalCaution });
|
|
17003
17241
|
}
|
|
17242
|
+
let continuation;
|
|
17243
|
+
if (continuationRequested) {
|
|
17244
|
+
await initSessionStore(input.dataDir);
|
|
17245
|
+
continuation = await getSessionResumeBrief(input.project.id, task, input.reader);
|
|
17246
|
+
}
|
|
17004
17247
|
const workset = await buildTaskWorkset({
|
|
17005
17248
|
projectId: input.project.id,
|
|
17006
17249
|
dataDir: input.dataDir,
|
|
@@ -17010,6 +17253,7 @@ async function buildAutoProjectContext(input) {
|
|
|
17010
17253
|
...externalOutline ? { semanticCode: externalOutline } : {},
|
|
17011
17254
|
providerQuality: providerQuality2,
|
|
17012
17255
|
currentFacts: worksetFactLines(currentFacts),
|
|
17256
|
+
...continuation ? { continuation } : {},
|
|
17013
17257
|
codeState: codeStateLine(overview),
|
|
17014
17258
|
reliableMemory: sourceSets.reliableSources.slice(0, lens.sourceLimit).map((source) => ({
|
|
17015
17259
|
id: source.observationId,
|
|
@@ -17054,12 +17298,19 @@ async function buildAutoProjectContext(input) {
|
|
|
17054
17298
|
explain,
|
|
17055
17299
|
refresh,
|
|
17056
17300
|
providerQuality: providerQuality2,
|
|
17301
|
+
...continuationRequested && workset.continuation ? { continuation: workset.continuation } : {},
|
|
17057
17302
|
workset
|
|
17058
17303
|
};
|
|
17059
17304
|
}
|
|
17060
17305
|
function formatLanguages(overview) {
|
|
17061
17306
|
return overview.code.languages.length > 0 ? overview.code.languages.map((item) => `${item.language} ${item.files}`).join(", ") : "none indexed yet";
|
|
17062
17307
|
}
|
|
17308
|
+
function compactContinuationText(text, budget) {
|
|
17309
|
+
return truncateToTokenBudget(
|
|
17310
|
+
sanitizeCredentials(text).replace(/\s+/g, " ").trim(),
|
|
17311
|
+
budget
|
|
17312
|
+
);
|
|
17313
|
+
}
|
|
17063
17314
|
function codeStateLine(overview) {
|
|
17064
17315
|
const snapshot = overview.code.latestSnapshot;
|
|
17065
17316
|
if (!snapshot) return "- Code state: no completed snapshot yet";
|
|
@@ -17199,6 +17450,23 @@ function formatAutoProjectContextSummary(context) {
|
|
|
17199
17450
|
"Reliable memory",
|
|
17200
17451
|
reliableSources.length > 0 ? `- ${reliableSources.length} current code-bound memory link(s)` : "- none yet"
|
|
17201
17452
|
);
|
|
17453
|
+
const continuation = context.workset.continuation;
|
|
17454
|
+
if (continuation?.previousSession || continuation?.memories.length) {
|
|
17455
|
+
lines.push("", "Resume from prior work");
|
|
17456
|
+
if (continuation.previousSession) {
|
|
17457
|
+
const session = continuation.previousSession;
|
|
17458
|
+
const source = [session.agent, session.endedAt ? session.endedAt.slice(0, 10) : void 0].filter(Boolean).join(", ");
|
|
17459
|
+
lines.push(
|
|
17460
|
+
"- Previous session" + (source ? ` (${source})` : "") + ": " + compactContinuationText(session.summary, 44)
|
|
17461
|
+
);
|
|
17462
|
+
}
|
|
17463
|
+
for (const memory of continuation.memories.slice(0, 3)) {
|
|
17464
|
+
const detail = memory.detail ? ": " + compactContinuationText(memory.detail, 20) : "";
|
|
17465
|
+
lines.push(
|
|
17466
|
+
"- #" + memory.id + " " + memory.type + ": " + compactContinuationText(memory.title, 18) + detail
|
|
17467
|
+
);
|
|
17468
|
+
}
|
|
17469
|
+
}
|
|
17202
17470
|
return lines.join("\n");
|
|
17203
17471
|
}
|
|
17204
17472
|
function formatAutoProjectContextPrompt(context) {
|
|
@@ -17269,8 +17537,10 @@ var init_auto_context = __esm({
|
|
|
17269
17537
|
"src/codegraph/auto-context.ts"() {
|
|
17270
17538
|
"use strict";
|
|
17271
17539
|
init_esm_shims();
|
|
17540
|
+
init_token_budget();
|
|
17272
17541
|
init_resolved_config();
|
|
17273
17542
|
init_workset();
|
|
17543
|
+
init_secret_filter();
|
|
17274
17544
|
init_binder();
|
|
17275
17545
|
init_current_facts();
|
|
17276
17546
|
init_lite_provider();
|
|
@@ -17278,6 +17548,8 @@ var init_auto_context = __esm({
|
|
|
17278
17548
|
init_project_context();
|
|
17279
17549
|
init_store();
|
|
17280
17550
|
init_admission();
|
|
17551
|
+
init_session();
|
|
17552
|
+
init_session_store();
|
|
17281
17553
|
init_task_lens();
|
|
17282
17554
|
DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
17283
17555
|
}
|
|
@@ -21472,7 +21744,8 @@ var init_official_skills = __esm({
|
|
|
21472
21744
|
"",
|
|
21473
21745
|
"| Situation | Prefer | CLI fallback |",
|
|
21474
21746
|
"|---|---|---|",
|
|
21475
|
-
'| Starting
|
|
21747
|
+
'| Starting a coding task and needing the best task-lensed project map | `memorix_project_context` with the user\'s task | `memorix context "<task>" --json` |',
|
|
21748
|
+
'| Continuing prior project work | `memorix_project_context` with the user\'s task | `memorix resume "<task>" --json` |',
|
|
21476
21749
|
'| Need structured refs/freshness for code-bound memories | `memorix_context_pack` | `memorix codegraph context-pack --task "<topic>"` |',
|
|
21477
21750
|
'| Explicit memory graph question | `memorix_graph_context` | `memorix memory graph-context --query "<topic>"` |',
|
|
21478
21751
|
'| Specific past decision, bug, file, or change | `memorix_search` | `memorix memory search --query "<topic>"` |',
|
|
@@ -21485,9 +21758,10 @@ var init_official_skills = __esm({
|
|
|
21485
21758
|
"",
|
|
21486
21759
|
"- Default first step for non-trivial coding work: call `memorix_project_context` before progress files, dev-log reads, ad-hoc file reads, or git archaeology. Pass the user's actual task so Memorix can choose a bugfix, feature, release, onboarding, refactor, docs, test, or general brief. Start by reading its suggested files.",
|
|
21487
21760
|
"- In Claude Code headless/print-mode, an initial MCP `pending` status is not a failure by itself. Prefer MCP first: use the client's tool discovery/dynamic loading to find `memorix_project_context` when it is not in the first visible tool list.",
|
|
21488
|
-
'-
|
|
21489
|
-
"-
|
|
21490
|
-
"-
|
|
21761
|
+
'- Continuation fallback is mandatory: when the user asks to continue, resume, take over, or explain prior work and MCP cannot be called in this turn, run `memorix resume "<task>" --json` exactly once before inspecting files, Git history, progress notes, or guessing. The absence of `.memorix` or visible memory files never proves project memory is empty. For a new task, use `memorix context "<task>" --json` instead. If that one command fails, report it and proceed normally; do not probe help, enumerate commands, or chain broad searches.',
|
|
21762
|
+
"- After a successful `memorix_project_context`, the brief is the default retrieval boundary. Do not call more Memorix retrieval tools after a complete brief. Use Context Pack, search, or detail only when the brief lacks a specific reference, freshness field, or fact needed for the task, or when the user explicitly asks for deeper history. In MCP, name that missing fact in `purpose` when intentionally expanding beyond the brief. Do not retrieve the same decision twice just to confirm an already-complete brief.",
|
|
21763
|
+
"- If the user asks for read-only work or says not to modify files, do not call `memorix_store` just to record an assessment. Store only when the user explicitly asks to preserve it.",
|
|
21764
|
+
"- Use `memorix_search` for a specific decision, bug, file, or prior change that the project context did not answer. Fetch detail only for a result you still need after the brief.",
|
|
21491
21765
|
"- Treat current code-bound memory as a map. Treat stale, suspect, or unbound memory as a lead that must be verified against the current code.",
|
|
21492
21766
|
"- Skip memory lookup for greetings, tiny one-off edits, or questions fully answered by the current file.",
|
|
21493
21767
|
"- If a fresh project has no memories, proceed normally and do not repeat the same empty search in the same turn.",
|
|
@@ -22708,17 +22982,18 @@ alwaysApply: true
|
|
|
22708
22982
|
"",
|
|
22709
22983
|
"## Start with Memory Autopilot",
|
|
22710
22984
|
"",
|
|
22711
|
-
`Default first step for non-trivial coding work: call \`memorix_project_context\` with the user's actual task before progress files, dev-log reads, ad-hoc file reads, or git archaeology. Memorix will choose a task-lensed brief (bugfix, feature, release, onboarding, refactor, docs, test, or general). Treat its "Start here" files as the first ${contextNoun} files to inspect.`,
|
|
22985
|
+
`Default first step for non-trivial coding work: call \`memorix_project_context\` with the user's actual task before progress files, dev-log reads, ad-hoc file reads, or git archaeology. Memorix will choose a task-lensed brief (bugfix, feature, release, onboarding, refactor, docs, test, or general). When the task is continuing prior work, the same brief also includes a bounded prior-work projection. Treat its "Start here" files as the first ${contextNoun} files to inspect.`,
|
|
22712
22986
|
"",
|
|
22713
|
-
'If the MCP tool is not visible yet but the client supports tool discovery or dynamic loading, search/select `memorix_project_context` first.
|
|
22987
|
+
'If the MCP tool is not visible yet but the client supports tool discovery or dynamic loading, search/select `memorix_project_context` first. Continuation fallback is mandatory: when the user asks to continue, resume, take over, or explain prior work and MCP cannot be called in this turn, run exactly one CLI brief with the user\'s real task before inspecting files, Git history, progress notes, or guessing: `memorix resume "<task>" --json`. For a new task, use `memorix context "<task>" --json` instead. The absence of `.memorix` or visible memory files never proves project memory is empty. If that one command fails, report it and proceed normally. Do not probe help, enumerate commands, chain broad searches, wait indefinitely on MCP startup, or hand-write tool-call syntax.',
|
|
22714
22988
|
"",
|
|
22715
|
-
"Use `memorix_context_pack` when
|
|
22989
|
+
"After a successful `memorix_project_context` result, the brief is the default retrieval boundary. Do not call more Memorix retrieval tools after a complete brief. Use `memorix_context_pack`, `memorix_search`, or `memorix_detail` only when the brief lacks a specific reference, freshness field, or fact needed for the task, or when the user explicitly asks for deeper history. In MCP, name that missing fact in `purpose` when intentionally expanding beyond the brief. Do not retrieve the same decision twice just to confirm an already-complete brief.",
|
|
22990
|
+
"If the user asks for read-only work or says not to modify files, do not call `memorix_store` just to record an assessment. Store only when the user explicitly asks to preserve it.",
|
|
22716
22991
|
"",
|
|
22717
22992
|
"## When to search memory",
|
|
22718
22993
|
"",
|
|
22719
22994
|
"Use `memorix_graph_context` for explicit memory graph questions or broad graph overview after the autopilot brief is not enough.",
|
|
22720
22995
|
"",
|
|
22721
|
-
`Use \`memorix_search\` when prior ${contextNoun} context would help \u2014 for example:`,
|
|
22996
|
+
`Use \`memorix_search\` when prior ${contextNoun} context would help and the Autopilot brief did not already answer the question \u2014 for example:`,
|
|
22722
22997
|
"- The user asks about a past decision, bug, or change",
|
|
22723
22998
|
"- You need to understand why something was designed a certain way",
|
|
22724
22999
|
"- You're continuing work that started in a previous session",
|
|
@@ -26645,6 +26920,8 @@ var BOOTSTRAP_SAFE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
26645
26920
|
"memorix_graph_context",
|
|
26646
26921
|
"memorix_context_pack"
|
|
26647
26922
|
]);
|
|
26923
|
+
var AUTOPILOT_RETRIEVAL_BOUNDARY_TTL_MS = 2 * 60 * 1e3;
|
|
26924
|
+
var READ_ONLY_TASK_PATTERN = /\b(?:do not|don't|never)\s+(?:modify|edit|change|write)\b|\bread[- ]only\b|(?:不要|不准|勿|禁止).{0,6}(?:修改|编辑|写入)|只读/i;
|
|
26648
26925
|
function shouldAwaitProjectRuntime(toolName) {
|
|
26649
26926
|
return !BOOTSTRAP_SAFE_TOOL_NAMES.has(toolName);
|
|
26650
26927
|
}
|
|
@@ -26915,7 +27192,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
26915
27192
|
};
|
|
26916
27193
|
const server = existingServer ?? new McpServer({
|
|
26917
27194
|
name: "memorix",
|
|
26918
|
-
version: true ? "1.2.
|
|
27195
|
+
version: true ? "1.2.4" : "1.0.1"
|
|
26919
27196
|
});
|
|
26920
27197
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
26921
27198
|
server.registerTool = ((name, ...args) => {
|
|
@@ -26950,11 +27227,52 @@ The path should point to a directory containing a .git folder.`
|
|
|
26950
27227
|
isTeamMember
|
|
26951
27228
|
};
|
|
26952
27229
|
};
|
|
27230
|
+
let autopilotRetrievalBoundary = null;
|
|
27231
|
+
const getActiveAutopilotRetrievalBoundary = () => {
|
|
27232
|
+
const boundary = autopilotRetrievalBoundary;
|
|
27233
|
+
if (!boundary) return null;
|
|
27234
|
+
if (boundary.projectId === project.id && Date.now() - boundary.issuedAt <= AUTOPILOT_RETRIEVAL_BOUNDARY_TTL_MS) {
|
|
27235
|
+
return boundary;
|
|
27236
|
+
}
|
|
27237
|
+
autopilotRetrievalBoundary = null;
|
|
27238
|
+
return null;
|
|
27239
|
+
};
|
|
27240
|
+
const requireExplicitAutopilotExpansion = (toolName, purpose) => {
|
|
27241
|
+
if (!getActiveAutopilotRetrievalBoundary() || purpose?.trim()) return null;
|
|
27242
|
+
return {
|
|
27243
|
+
content: [{
|
|
27244
|
+
type: "text",
|
|
27245
|
+
text: "Memorix Autopilot retrieval boundary: the latest `memorix_project_context` already supplied the default bounded brief for this coding turn, so no additional memory was retrieved. Verify the current project first. To intentionally expand beyond that brief, call `" + toolName + "` again with `purpose` naming the specific missing fact or the user's explicit request."
|
|
27246
|
+
}]
|
|
27247
|
+
};
|
|
27248
|
+
};
|
|
27249
|
+
const blockCoveredAutopilotEvidence = (toolName, observationIds, force) => {
|
|
27250
|
+
const boundary = getActiveAutopilotRetrievalBoundary();
|
|
27251
|
+
if (!boundary || force || observationIds.length === 0 || !observationIds.every((id) => boundary.coveredObservationIds.has(id))) {
|
|
27252
|
+
return null;
|
|
27253
|
+
}
|
|
27254
|
+
return {
|
|
27255
|
+
content: [{
|
|
27256
|
+
type: "text",
|
|
27257
|
+
text: "Memorix Autopilot retrieval boundary: every requested memory is already represented in the latest project brief, so no duplicate detail was retrieved. Inspect the current project or seek a new source. Retry with `force: true` only when the user explicitly asks to read the underlying record in full."
|
|
27258
|
+
}]
|
|
27259
|
+
};
|
|
27260
|
+
};
|
|
27261
|
+
const blockReadOnlyAutopilotWrite = (overrideReadOnly) => {
|
|
27262
|
+
const boundary = getActiveAutopilotRetrievalBoundary();
|
|
27263
|
+
if (!boundary?.readOnly || overrideReadOnly) return null;
|
|
27264
|
+
return {
|
|
27265
|
+
content: [{
|
|
27266
|
+
type: "text",
|
|
27267
|
+
text: "Memorix write boundary: the latest task is read-only or asks not to modify files, so no memory was stored. Persist a record only when the user explicitly asks to save it, then retry with `overrideReadOnly: true`."
|
|
27268
|
+
}]
|
|
27269
|
+
};
|
|
27270
|
+
};
|
|
26953
27271
|
server.registerTool(
|
|
26954
27272
|
"memorix_store",
|
|
26955
27273
|
{
|
|
26956
27274
|
title: "Store Memory",
|
|
26957
|
-
description: "Store a new observation/memory. Automatically indexed for search. Use type to classify: gotcha ([GOTCHA] critical pitfall), decision ([DECISION] architecture choice), problem-solution ([FIX] bug fix), how-it-works ([INFO] explanation), what-changed ([CHANGE] change), discovery ([DISCOVERY] insight), why-it-exists ([WHY] rationale), trade-off ([TRADEOFF] compromise), session-request ([SESSION] original goal). Project visibility is the default. Personal or team visibility requires an explicitly joined coordination identity.",
|
|
27275
|
+
description: "Store a new observation/memory. Automatically indexed for search. Use type to classify: gotcha ([GOTCHA] critical pitfall), decision ([DECISION] architecture choice), problem-solution ([FIX] bug fix), how-it-works ([INFO] explanation), what-changed ([CHANGE] change), discovery ([DISCOVERY] insight), why-it-exists ([WHY] rationale), trade-off ([TRADEOFF] compromise), session-request ([SESSION] original goal). Project visibility is the default. Personal or team visibility requires an explicitly joined coordination identity. For a read-only task, do not store unless the user explicitly asks to save a record.",
|
|
26958
27276
|
inputSchema: {
|
|
26959
27277
|
entityName: z2.string().describe('The entity this observation belongs to (e.g., "auth-module", "port-config")'),
|
|
26960
27278
|
type: z2.enum(OBSERVATION_TYPES).describe("Observation type for classification"),
|
|
@@ -26975,12 +27293,17 @@ The path should point to a directory containing a .git folder.`
|
|
|
26975
27293
|
relatedEntities: z2.array(z2.string()).optional().describe("Other entity names this memory cross-references"),
|
|
26976
27294
|
visibility: z2.enum(["personal", "project", "team"]).optional().describe(
|
|
26977
27295
|
"Retrieval scope. Project is the normal shared default; personal/team require memorix_session_start with joinTeam=true."
|
|
27296
|
+
),
|
|
27297
|
+
overrideReadOnly: z2.boolean().optional().describe(
|
|
27298
|
+
"Use only when the user explicitly asks to save memory during a read-only or no-modification task."
|
|
26978
27299
|
)
|
|
26979
27300
|
}
|
|
26980
27301
|
},
|
|
26981
|
-
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility }) => {
|
|
27302
|
+
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility, overrideReadOnly }) => {
|
|
26982
27303
|
const unresolved = requireResolvedProject("store memory in the current project");
|
|
26983
27304
|
if (unresolved) return unresolved;
|
|
27305
|
+
const readOnlyBoundary = blockReadOnlyAutopilotWrite(overrideReadOnly);
|
|
27306
|
+
if (readOnlyBoundary) return readOnlyBoundary;
|
|
26984
27307
|
const requestedVisibility = visibility ?? "project";
|
|
26985
27308
|
const reader = getObservationReader();
|
|
26986
27309
|
if (requestedVisibility !== "project" && !currentAgentId) {
|
|
@@ -27479,7 +27802,7 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
27479
27802
|
"memorix_search",
|
|
27480
27803
|
{
|
|
27481
27804
|
title: "Search Memory",
|
|
27482
|
-
description: "Search project memory. Returns a compact index (~50-100 tokens/result). Use memorix_detail to fetch full content for specific IDs. Use memorix_timeline to see chronological context. Searches across all observations stored from any IDE session \u2014 enabling cross-session and cross-agent context retrieval.",
|
|
27805
|
+
description: "Search project memory. Returns a compact index (~50-100 tokens/result). Do not use as a follow-up to a complete memorix_project_context brief unless a specific fact is still missing or the user asks for deeper history; provide purpose when intentionally expanding. Use memorix_detail to fetch full content for specific IDs. Use memorix_timeline to see chronological context. Searches across all observations stored from any IDE session \u2014 enabling cross-session and cross-agent context retrieval.",
|
|
27483
27806
|
inputSchema: {
|
|
27484
27807
|
query: z2.string().describe("Search query (natural language or keywords)"),
|
|
27485
27808
|
limit: z2.number().optional().describe("Max results (default: 20)"),
|
|
@@ -27495,14 +27818,24 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
27495
27818
|
),
|
|
27496
27819
|
source: z2.enum(["agent", "git", "manual"]).optional().describe(
|
|
27497
27820
|
'Filter by memory source. "git" returns only commit-derived ground truth memories. Omit for all sources.'
|
|
27821
|
+
),
|
|
27822
|
+
purpose: z2.string().optional().describe(
|
|
27823
|
+
"Why this must expand beyond the latest Autopilot brief. Name the missing fact or the user's explicit request."
|
|
27824
|
+
),
|
|
27825
|
+
force: z2.boolean().optional().describe(
|
|
27826
|
+
"Use only when the user explicitly asks to read a record already represented in the latest Autopilot brief."
|
|
27498
27827
|
)
|
|
27499
27828
|
}
|
|
27500
27829
|
},
|
|
27501
|
-
async ({ query, limit, type, maxTokens, scope, since, until, status, source }) => {
|
|
27830
|
+
async ({ query, limit, type, maxTokens, scope, since, until, status, source, purpose, force }) => {
|
|
27502
27831
|
if (scope !== "global") {
|
|
27503
27832
|
const unresolved = requireResolvedProject("search the current project");
|
|
27504
27833
|
if (unresolved) return unresolved;
|
|
27505
27834
|
}
|
|
27835
|
+
if (scope !== "global") {
|
|
27836
|
+
const boundary = requireExplicitAutopilotExpansion("memorix_search", purpose);
|
|
27837
|
+
if (boundary) return boundary;
|
|
27838
|
+
}
|
|
27506
27839
|
return withFreshIndex(async () => {
|
|
27507
27840
|
const safeLimit = limit != null ? coerceNumber(limit, 20) : void 0;
|
|
27508
27841
|
const safeMaxTokens = maxTokens != null ? coerceNumber(maxTokens, 0) : void 0;
|
|
@@ -27541,6 +27874,11 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
27541
27874
|
}
|
|
27542
27875
|
throw error;
|
|
27543
27876
|
}
|
|
27877
|
+
const activeBoundary = getActiveAutopilotRetrievalBoundary();
|
|
27878
|
+
if (scope !== "global" && activeBoundary && !force && result.entries.length > 0 && result.entries.every((entry) => activeBoundary.coveredObservationIds.has(entry.id))) {
|
|
27879
|
+
const duplicate = blockCoveredAutopilotEvidence("memorix_search", result.entries.map((entry) => entry.id), force);
|
|
27880
|
+
if (duplicate) return duplicate;
|
|
27881
|
+
}
|
|
27544
27882
|
let text = result.formatted;
|
|
27545
27883
|
try {
|
|
27546
27884
|
const { getLastSearchMode: getLastSearchMode2 } = await Promise.resolve().then(() => (init_orama_store(), orama_store_exports));
|
|
@@ -27642,6 +27980,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27642
27980
|
observations: observations2,
|
|
27643
27981
|
task,
|
|
27644
27982
|
refresh: refresh ?? "auto",
|
|
27983
|
+
reader: getObservationReader(),
|
|
27645
27984
|
enqueueRefresh: () => {
|
|
27646
27985
|
enqueueCodegraphRefresh2({
|
|
27647
27986
|
dataDir: projectDir2,
|
|
@@ -27653,6 +27992,16 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27653
27992
|
}
|
|
27654
27993
|
});
|
|
27655
27994
|
const text = format === "json" ? JSON.stringify({ ...context, brief: buildAutoProjectBrief2(context) }, null, 2) : format === "summary" ? formatAutoProjectContextSummary2(context) : formatAutoProjectContextPrompt2(context);
|
|
27995
|
+
autopilotRetrievalBoundary = {
|
|
27996
|
+
projectId: project.id,
|
|
27997
|
+
issuedAt: Date.now(),
|
|
27998
|
+
coveredObservationIds: /* @__PURE__ */ new Set([
|
|
27999
|
+
...context.workset.continuation?.memories.map((memory) => memory.id) ?? [],
|
|
28000
|
+
...context.workset.reliableMemory.map((memory) => memory.id),
|
|
28001
|
+
...context.workset.cautionMemory.map((memory) => memory.id)
|
|
28002
|
+
]),
|
|
28003
|
+
readOnly: READ_ONLY_TASK_PATTERN.test(task ?? "")
|
|
28004
|
+
};
|
|
27656
28005
|
return {
|
|
27657
28006
|
content: [{ type: "text", text }]
|
|
27658
28007
|
};
|
|
@@ -27692,18 +28041,23 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27692
28041
|
"memorix_context_pack",
|
|
27693
28042
|
{
|
|
27694
28043
|
title: "Context Pack",
|
|
27695
|
-
description: "Build a prompt-ready working context pack for a coding task. Combines relevant memories, CodeGraph Memory facts, freshness warnings, suggested reads, and verification hints.",
|
|
28044
|
+
description: "Build a prompt-ready working context pack for a coding task. Combines relevant memories, CodeGraph Memory facts, freshness warnings, suggested reads, and verification hints. After a complete memorix_project_context brief, provide purpose only when deliberately expanding beyond it.",
|
|
27696
28045
|
inputSchema: {
|
|
27697
28046
|
task: z2.string().describe("Current coding task or question"),
|
|
27698
28047
|
limit: z2.preprocess(
|
|
27699
28048
|
(value) => typeof value === "string" && value.trim() !== "" ? Number(value) : value,
|
|
27700
28049
|
z2.number().int().positive().max(100)
|
|
27701
|
-
).optional().describe("Max active memories to inspect before code-ref filtering (default: 20)")
|
|
28050
|
+
).optional().describe("Max active memories to inspect before code-ref filtering (default: 20)"),
|
|
28051
|
+
purpose: z2.string().optional().describe(
|
|
28052
|
+
"Why this must expand beyond the latest Autopilot brief. Name the missing fact or the user's explicit request."
|
|
28053
|
+
)
|
|
27702
28054
|
}
|
|
27703
28055
|
},
|
|
27704
|
-
async ({ task, limit }) => {
|
|
28056
|
+
async ({ task, limit, purpose }) => {
|
|
27705
28057
|
const unresolved = requireResolvedProject("build a context pack for the current project");
|
|
27706
28058
|
if (unresolved) return unresolved;
|
|
28059
|
+
const boundary = requireExplicitAutopilotExpansion("memorix_context_pack", purpose);
|
|
28060
|
+
if (boundary) return boundary;
|
|
27707
28061
|
const [
|
|
27708
28062
|
{ CodeGraphStore: CodeGraphStore2 },
|
|
27709
28063
|
{ assembleContextPackForTask: assembleContextPackForTask2, attachTaskWorkset: attachTaskWorkset2, buildContextPackPrompt: buildContextPackPrompt2 },
|
|
@@ -28448,7 +28802,7 @@ ${actions.join("\n")}`
|
|
|
28448
28802
|
"memorix_detail",
|
|
28449
28803
|
{
|
|
28450
28804
|
title: "Memory Details",
|
|
28451
|
-
description: 'Fetch full observation or mini-skill details \u2014 includes source kind (explicit memory / hook trace / git evidence), value category, and cross-references (~500-1000 tokens each). Always use memorix_search first to find relevant IDs, then fetch only what you need. Accepts typed refs from search results (e.g. "obs:42", "skill:3") via the typedRefs field, or legacy numeric ids / object refs for backward compatibility.',
|
|
28805
|
+
description: 'Fetch full observation or mini-skill details \u2014 includes source kind (explicit memory / hook trace / git evidence), value category, and cross-references (~500-1000 tokens each). Do not re-fetch content already covered by a complete memorix_project_context brief unless a specific fact is still missing or the user asks for deeper history; provide purpose when intentionally expanding. Always use memorix_search first to find relevant IDs, then fetch only what you need. Accepts typed refs from search results (e.g. "obs:42", "skill:3") via the typedRefs field, or legacy numeric ids / object refs for backward compatibility.',
|
|
28452
28806
|
inputSchema: {
|
|
28453
28807
|
ids: z2.array(z2.number()).optional().describe("Observation IDs to fetch (legacy, from memorix_search results)"),
|
|
28454
28808
|
refs: z2.array(
|
|
@@ -28457,16 +28811,36 @@ ${actions.join("\n")}`
|
|
|
28457
28811
|
projectId: z2.string().optional().describe("Project ID for global-search disambiguation")
|
|
28458
28812
|
})
|
|
28459
28813
|
).optional().describe("Explicit observation refs. Prefer this for global search results."),
|
|
28460
|
-
typedRefs: z2.array(z2.string()).optional().describe('Typed memory refs from search results, e.g. "obs:42", "skill:3", "obs:42@org/proj"')
|
|
28814
|
+
typedRefs: z2.array(z2.string()).optional().describe('Typed memory refs from search results, e.g. "obs:42", "skill:3", "obs:42@org/proj"'),
|
|
28815
|
+
purpose: z2.string().optional().describe(
|
|
28816
|
+
"Why this must expand beyond the latest Autopilot brief. Name the missing fact or the user's explicit request."
|
|
28817
|
+
),
|
|
28818
|
+
force: z2.boolean().optional().describe(
|
|
28819
|
+
"Use only when the user explicitly asks to read a record already represented in the latest Autopilot brief."
|
|
28820
|
+
)
|
|
28461
28821
|
}
|
|
28462
28822
|
},
|
|
28463
|
-
async ({ ids, refs, typedRefs }) => {
|
|
28823
|
+
async ({ ids, refs, typedRefs, purpose, force }) => {
|
|
28464
28824
|
const safeIds = coerceNumberArray(ids);
|
|
28465
28825
|
const safeRefs = coerceObservationRefs(refs);
|
|
28466
28826
|
const safeTypedRefs = coerceStringArray(typedRefs);
|
|
28827
|
+
const hasCrossProjectRef = safeRefs.some((ref) => ref.projectId && ref.projectId !== project.id) || safeTypedRefs.some((ref) => ref.includes("@") && !ref.endsWith(`@${project.id}`));
|
|
28828
|
+
if (!hasCrossProjectRef) {
|
|
28829
|
+
const boundary = requireExplicitAutopilotExpansion("memorix_detail", purpose);
|
|
28830
|
+
if (boundary) return boundary;
|
|
28831
|
+
const requestedObservationIds = [
|
|
28832
|
+
...safeIds,
|
|
28833
|
+
...safeRefs.map((ref) => ref.id),
|
|
28834
|
+
...safeTypedRefs.flatMap((ref) => {
|
|
28835
|
+
const match = /^obs:(\d+)(?:@.+)?$/i.exec(ref.trim());
|
|
28836
|
+
return match ? [Number(match[1])] : [];
|
|
28837
|
+
})
|
|
28838
|
+
];
|
|
28839
|
+
const duplicate = blockCoveredAutopilotEvidence("memorix_detail", requestedObservationIds, force);
|
|
28840
|
+
if (duplicate) return duplicate;
|
|
28841
|
+
}
|
|
28467
28842
|
let result;
|
|
28468
28843
|
try {
|
|
28469
|
-
const hasCrossProjectRef = safeRefs.some((ref) => ref.projectId && ref.projectId !== project.id) || safeTypedRefs.some((ref) => ref.includes("@") && !ref.endsWith(`@${project.id}`));
|
|
28470
28844
|
const reader = getObservationReader(hasCrossProjectRef ? "global" : "project");
|
|
28471
28845
|
if (safeTypedRefs.length > 0) {
|
|
28472
28846
|
result = await compactDetail(safeTypedRefs, { reader });
|
|
@@ -29416,7 +29790,6 @@ Ensure the path points to a directory containing a .git folder (or a subdirector
|
|
|
29416
29790
|
const unresolved = requireResolvedProject("start a project session");
|
|
29417
29791
|
if (unresolved) return unresolved;
|
|
29418
29792
|
const { startSession: startSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
29419
|
-
const result = await startSession2(projectDir2, project.id, { sessionId, agent });
|
|
29420
29793
|
const llmStatus = isLLMEnabled() ? `LLM enhanced mode: ${getLLMConfig()?.provider}/${getLLMConfig()?.model} (fact extraction + auto-dedup active)` : "LLM mode: off (set MEMORIX_LLM_API_KEY to enable enhanced memory quality)";
|
|
29421
29794
|
const shouldJoinTeam = !!joinTeam;
|
|
29422
29795
|
let registeredAgent = null;
|
|
@@ -29473,6 +29846,11 @@ Ensure the path points to a directory containing a .git folder (or a subdirector
|
|
|
29473
29846
|
}
|
|
29474
29847
|
} catch {
|
|
29475
29848
|
}
|
|
29849
|
+
const result = await startSession2(projectDir2, project.id, {
|
|
29850
|
+
sessionId,
|
|
29851
|
+
agent,
|
|
29852
|
+
reader: getObservationReader()
|
|
29853
|
+
});
|
|
29476
29854
|
const lines = [
|
|
29477
29855
|
`[OK] Session started: ${result.session.id}`,
|
|
29478
29856
|
`Project: ${project.name} (${project.id})`,
|
|
@@ -29595,7 +29973,7 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
29595
29973
|
async ({ limit }) => {
|
|
29596
29974
|
const safeLimit = limit != null ? coerceNumber(limit, 3) : 3;
|
|
29597
29975
|
const { getSessionContext: getSessionContext2, listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
29598
|
-
const context = await getSessionContext2(projectDir2, project.id, safeLimit);
|
|
29976
|
+
const context = await getSessionContext2(projectDir2, project.id, safeLimit, getObservationReader());
|
|
29599
29977
|
const sessions = await listSessions2(projectDir2, project.id);
|
|
29600
29978
|
const activeSessions = sessions.filter((s) => s.status === "active");
|
|
29601
29979
|
const completedSessions = sessions.filter((s) => s.status === "completed");
|
|
@@ -30488,6 +30866,7 @@ fi
|
|
|
30488
30866
|
if (newCanonicalId === project.id && projectResolved) return false;
|
|
30489
30867
|
console.error(`[memorix] Switching project: ${project.id} \u2192 ${newCanonicalId}`);
|
|
30490
30868
|
currentAgentId = void 0;
|
|
30869
|
+
autopilotRetrievalBoundary = null;
|
|
30491
30870
|
const canonicalProjectDir = newCanonicalId !== newDetected.id ? await getProjectDataDir(newCanonicalId) : newProjectDir;
|
|
30492
30871
|
projectResolved = true;
|
|
30493
30872
|
projectResolutionError = null;
|