memorix 1.2.1 → 1.2.3
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 +23 -0
- package/README.md +14 -2
- package/README.zh-CN.md +14 -2
- package/dist/cli/index.js +15424 -13780
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +1337 -536
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.d.ts +1 -1
- package/dist/maintenance-runner.js +8458 -8087
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +23 -0
- package/dist/sdk.d.ts +7 -2
- package/dist/sdk.js +1365 -542
- package/dist/sdk.js.map +1 -1
- package/dist/types.d.ts +49 -1
- package/dist/types.js.map +1 -1
- package/docs/1.2.2-MEMORY-CONTROL-PLANE.md +434 -0
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +4 -0
- package/docs/API_REFERENCE.md +24 -4
- package/docs/README.md +1 -1
- package/docs/dev-log/progress.txt +101 -11
- package/package.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/src/cli/command-guide.ts +192 -0
- package/src/cli/commands/audit.ts +9 -4
- package/src/cli/commands/cleanup.ts +5 -1
- package/src/cli/commands/codegraph.ts +15 -5
- package/src/cli/commands/context.ts +3 -2
- package/src/cli/commands/doctor.ts +4 -2
- package/src/cli/commands/explain.ts +9 -3
- package/src/cli/commands/handoff.ts +21 -7
- package/src/cli/commands/identity.ts +116 -0
- package/src/cli/commands/ingest-image.ts +5 -3
- package/src/cli/commands/lock.ts +11 -10
- package/src/cli/commands/memory.ts +58 -21
- package/src/cli/commands/message.ts +19 -14
- package/src/cli/commands/operator-shared.ts +98 -3
- package/src/cli/commands/poll.ts +16 -6
- package/src/cli/commands/reasoning.ts +17 -3
- package/src/cli/commands/retention.ts +9 -4
- package/src/cli/commands/serve-http.ts +8 -2
- package/src/cli/commands/session.ts +44 -10
- package/src/cli/commands/skills.ts +10 -5
- package/src/cli/commands/status.ts +4 -3
- package/src/cli/commands/task.ts +26 -17
- package/src/cli/commands/team.ts +14 -10
- package/src/cli/commands/transfer.ts +63 -10
- package/src/cli/identity.ts +89 -0
- package/src/cli/index.ts +96 -19
- package/src/cli/invocation.ts +115 -0
- package/src/cli/tui/chat-service.ts +41 -18
- package/src/cli/tui/data.ts +23 -44
- package/src/cli/tui/operator-context.ts +60 -0
- package/src/cli/tui/session-service.ts +3 -2
- package/src/cli/tui/views/MemoryView.tsx +10 -8
- package/src/codegraph/auto-context.ts +31 -2
- package/src/codegraph/context-pack.ts +1 -0
- package/src/codegraph/project-context.ts +2 -0
- package/src/compact/engine.ts +26 -10
- package/src/compact/index-format.ts +25 -2
- package/src/dashboard/server.ts +46 -9
- package/src/hooks/admission.ts +117 -0
- package/src/hooks/handler.ts +98 -91
- package/src/knowledge/context-assembly.ts +97 -0
- package/src/knowledge/workset.ts +179 -10
- package/src/memory/admission.ts +57 -0
- package/src/memory/consolidation.ts +13 -2
- package/src/memory/disclosure-policy.ts +6 -1
- package/src/memory/export-import.ts +11 -3
- package/src/memory/graph-context.ts +8 -2
- package/src/memory/observations.ts +162 -4
- package/src/memory/quality-audit.ts +2 -0
- package/src/memory/retention.ts +22 -2
- package/src/memory/session.ts +29 -11
- package/src/memory/visibility.ts +80 -0
- package/src/orchestrate/memorix-bridge.ts +38 -0
- package/src/runtime/control-plane-maintenance.ts +1 -0
- package/src/runtime/isolated-maintenance.ts +1 -0
- package/src/runtime/lifecycle.ts +18 -0
- package/src/runtime/maintenance-jobs.ts +1 -0
- package/src/runtime/maintenance-runner.ts +2 -0
- package/src/runtime/project-maintenance.ts +89 -0
- package/src/sdk.ts +35 -5
- package/src/server.ts +267 -83
- package/src/store/orama-store.ts +61 -6
- package/src/store/sqlite-db.ts +23 -1
- package/src/store/sqlite-store.ts +12 -2
- package/src/team/handoff.ts +7 -0
- package/src/types.ts +51 -0
- package/src/wiki/generator.ts +2 -0
package/dist/index.js
CHANGED
|
@@ -209,7 +209,11 @@ CREATE TABLE IF NOT EXISTS observations (
|
|
|
209
209
|
relatedCommits TEXT,
|
|
210
210
|
relatedEntities TEXT,
|
|
211
211
|
sourceDetail TEXT,
|
|
212
|
-
valueCategory TEXT
|
|
212
|
+
valueCategory TEXT,
|
|
213
|
+
admissionState TEXT,
|
|
214
|
+
admissionReason TEXT,
|
|
215
|
+
visibility TEXT,
|
|
216
|
+
sharedWithAgentIds TEXT
|
|
213
217
|
);
|
|
214
218
|
`;
|
|
215
219
|
CREATE_MINI_SKILLS_TABLE = `
|
|
@@ -632,6 +636,8 @@ CREATE INDEX IF NOT EXISTS idx_observations_projectId ON observations(projectId)
|
|
|
632
636
|
CREATE INDEX IF NOT EXISTS idx_observations_topicKey ON observations(projectId, topicKey);
|
|
633
637
|
CREATE INDEX IF NOT EXISTS idx_observations_status ON observations(status);
|
|
634
638
|
CREATE INDEX IF NOT EXISTS idx_observations_project_status_id ON observations(projectId, status, id);
|
|
639
|
+
CREATE INDEX IF NOT EXISTS idx_observations_project_admission ON observations(projectId, status, admissionState, id);
|
|
640
|
+
CREATE INDEX IF NOT EXISTS idx_observations_project_visibility ON observations(projectId, status, visibility, id);
|
|
635
641
|
CREATE INDEX IF NOT EXISTS idx_mini_skills_projectId ON mini_skills(projectId);
|
|
636
642
|
CREATE INDEX IF NOT EXISTS idx_sessions_projectId ON sessions(projectId);
|
|
637
643
|
CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(projectId, status);
|
|
@@ -676,6 +682,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
|
676
682
|
WHERE status IN ('pending', 'running', 'retry');
|
|
677
683
|
`;
|
|
678
684
|
SCHEMA_MIGRATIONS = [
|
|
685
|
+
{
|
|
686
|
+
id: "1.2.2-observation-admission",
|
|
687
|
+
apply: (db2) => {
|
|
688
|
+
addColumnIfMissing(db2, "observations", "admissionState", "admissionState TEXT");
|
|
689
|
+
addColumnIfMissing(db2, "observations", "admissionReason", "admissionReason TEXT");
|
|
690
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_observations_project_admission ON observations(projectId, status, admissionState, id)");
|
|
691
|
+
}
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
id: "1.2.2-observation-visibility",
|
|
695
|
+
apply: (db2) => {
|
|
696
|
+
addColumnIfMissing(db2, "observations", "visibility", "visibility TEXT");
|
|
697
|
+
addColumnIfMissing(db2, "observations", "sharedWithAgentIds", "sharedWithAgentIds TEXT");
|
|
698
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_observations_project_visibility ON observations(projectId, status, visibility, id)");
|
|
699
|
+
}
|
|
700
|
+
},
|
|
679
701
|
{
|
|
680
702
|
id: "1.2-code-state-snapshots",
|
|
681
703
|
apply: (db2) => {
|
|
@@ -2118,6 +2140,58 @@ var init_mini_skills = __esm({
|
|
|
2118
2140
|
}
|
|
2119
2141
|
});
|
|
2120
2142
|
|
|
2143
|
+
// src/memory/visibility.ts
|
|
2144
|
+
function resolveObservationVisibility(record) {
|
|
2145
|
+
switch (record.visibility) {
|
|
2146
|
+
case "personal":
|
|
2147
|
+
case "team":
|
|
2148
|
+
case "project":
|
|
2149
|
+
return record.visibility;
|
|
2150
|
+
default:
|
|
2151
|
+
return "project";
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
function sharedAgentIds(record) {
|
|
2155
|
+
if (Array.isArray(record.sharedWithAgentIds)) return record.sharedWithAgentIds;
|
|
2156
|
+
if (typeof record.sharedWithAgentIds !== "string" || !record.sharedWithAgentIds) return [];
|
|
2157
|
+
try {
|
|
2158
|
+
const parsed = JSON.parse(record.sharedWithAgentIds);
|
|
2159
|
+
return Array.isArray(parsed) ? parsed.filter((id) => typeof id === "string") : [];
|
|
2160
|
+
} catch {
|
|
2161
|
+
return [];
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
function canReadObservation(record, reader) {
|
|
2165
|
+
if (!reader) return true;
|
|
2166
|
+
const sameProject = reader.projectId === record.projectId;
|
|
2167
|
+
const visibility = resolveObservationVisibility(record);
|
|
2168
|
+
if (visibility === "project") return !reader.projectId || sameProject;
|
|
2169
|
+
if (!sameProject || !reader.agentId) return false;
|
|
2170
|
+
if (visibility === "team") return reader.isTeamMember === true;
|
|
2171
|
+
return record.createdByAgentId === reader.agentId || sharedAgentIds(record).includes(reader.agentId);
|
|
2172
|
+
}
|
|
2173
|
+
function canManageObservation(record, reader) {
|
|
2174
|
+
if (!reader) return true;
|
|
2175
|
+
if (!reader.projectId || reader.projectId !== record.projectId) return false;
|
|
2176
|
+
switch (resolveObservationVisibility(record)) {
|
|
2177
|
+
case "project":
|
|
2178
|
+
return true;
|
|
2179
|
+
case "team":
|
|
2180
|
+
return reader.isTeamMember === true;
|
|
2181
|
+
case "personal":
|
|
2182
|
+
return Boolean(reader.agentId && record.createdByAgentId === reader.agentId);
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
function filterReadableObservations(observations2, reader) {
|
|
2186
|
+
return reader ? observations2.filter((observation) => canReadObservation(observation, reader)) : [...observations2];
|
|
2187
|
+
}
|
|
2188
|
+
var init_visibility = __esm({
|
|
2189
|
+
"src/memory/visibility.ts"() {
|
|
2190
|
+
"use strict";
|
|
2191
|
+
init_esm_shims();
|
|
2192
|
+
}
|
|
2193
|
+
});
|
|
2194
|
+
|
|
2121
2195
|
// src/config/config-paths.ts
|
|
2122
2196
|
import { join } from "path";
|
|
2123
2197
|
function getGlobalConfigTomlPath(homeDir) {
|
|
@@ -5345,7 +5419,8 @@ __export(orama_store_exports, {
|
|
|
5345
5419
|
makeOramaObservationId: () => makeOramaObservationId,
|
|
5346
5420
|
removeObservation: () => removeObservation,
|
|
5347
5421
|
resetDb: () => resetDb,
|
|
5348
|
-
searchObservations: () => searchObservations
|
|
5422
|
+
searchObservations: () => searchObservations,
|
|
5423
|
+
updateObservationMetadata: () => updateObservationMetadata
|
|
5349
5424
|
});
|
|
5350
5425
|
import { create, insert as insert2, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
5351
5426
|
function getLastSearchMode(projectId) {
|
|
@@ -5423,6 +5498,11 @@ async function initializeDb(options, generation) {
|
|
|
5423
5498
|
source: "string",
|
|
5424
5499
|
sourceDetail: "string",
|
|
5425
5500
|
valueCategory: "string",
|
|
5501
|
+
admissionState: "string",
|
|
5502
|
+
admissionReason: "string",
|
|
5503
|
+
visibility: "string",
|
|
5504
|
+
createdByAgentId: "string",
|
|
5505
|
+
sharedWithAgentIds: "string",
|
|
5426
5506
|
documentType: "string",
|
|
5427
5507
|
knowledgeLayer: "string"
|
|
5428
5508
|
};
|
|
@@ -5561,6 +5641,13 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
5561
5641
|
lastAccessedAt: obs.lastAccessedAt || "",
|
|
5562
5642
|
status: obs.status ?? "active",
|
|
5563
5643
|
source: obs.source || "agent",
|
|
5644
|
+
sourceDetail: obs.sourceDetail ?? "",
|
|
5645
|
+
valueCategory: obs.valueCategory ?? "",
|
|
5646
|
+
admissionState: obs.admissionState ?? "",
|
|
5647
|
+
admissionReason: obs.admissionReason ?? "",
|
|
5648
|
+
visibility: obs.visibility ?? "project",
|
|
5649
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
5650
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
5564
5651
|
documentType: "observation",
|
|
5565
5652
|
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
5566
5653
|
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
@@ -5594,6 +5681,16 @@ async function insertObservation(doc) {
|
|
|
5594
5681
|
await insert2(database, doc);
|
|
5595
5682
|
rememberObservationDoc(doc);
|
|
5596
5683
|
}
|
|
5684
|
+
async function updateObservationMetadata(projectId, observationId2, patch) {
|
|
5685
|
+
const database = await getDb();
|
|
5686
|
+
const id = makeOramaObservationId(projectId, observationId2);
|
|
5687
|
+
const existing = getByID(database, id);
|
|
5688
|
+
if (!existing) return false;
|
|
5689
|
+
const next = { ...existing, ...patch };
|
|
5690
|
+
await update(database, id, next);
|
|
5691
|
+
rememberObservationDoc(next);
|
|
5692
|
+
return true;
|
|
5693
|
+
}
|
|
5597
5694
|
async function removeObservation(oramaId) {
|
|
5598
5695
|
const database = await getDb();
|
|
5599
5696
|
await remove2(database, oramaId);
|
|
@@ -5747,7 +5844,7 @@ async function searchObservations(options) {
|
|
|
5747
5844
|
if (!projectIds) return true;
|
|
5748
5845
|
const doc = hit.document;
|
|
5749
5846
|
return projectIds.includes(doc.projectId);
|
|
5750
|
-
}).filter((hit) => {
|
|
5847
|
+
}).filter((hit) => canReadObservation(hit.document, options.reader)).filter((hit) => {
|
|
5751
5848
|
if (statusFilter === "all") return true;
|
|
5752
5849
|
const doc = hit.document;
|
|
5753
5850
|
return (doc.status || "active") === statusFilter;
|
|
@@ -5778,6 +5875,8 @@ async function searchObservations(options) {
|
|
|
5778
5875
|
source: doc.source || "agent",
|
|
5779
5876
|
sourceDetail: doc.sourceDetail || void 0,
|
|
5780
5877
|
valueCategory: doc.valueCategory || void 0,
|
|
5878
|
+
admissionState: doc.admissionState || void 0,
|
|
5879
|
+
visibility: doc.visibility || void 0,
|
|
5781
5880
|
entityName: doc.entityName || void 0,
|
|
5782
5881
|
documentType: doc.documentType || "observation",
|
|
5783
5882
|
knowledgeLayer: doc.knowledgeLayer || "project-truth",
|
|
@@ -5821,6 +5920,12 @@ async function searchObservations(options) {
|
|
|
5821
5920
|
score: isCommandStyleEntry(entry.title) ? entry.score * 0.3 : entry.score
|
|
5822
5921
|
}));
|
|
5823
5922
|
}
|
|
5923
|
+
if (hasQuery) {
|
|
5924
|
+
const qualifiedEntries = intermediate.filter(
|
|
5925
|
+
(entry) => entry.admissionState !== "candidate" && entry.admissionState !== "ephemeral"
|
|
5926
|
+
);
|
|
5927
|
+
if (qualifiedEntries.length > 0) intermediate = qualifiedEntries;
|
|
5928
|
+
}
|
|
5824
5929
|
if (intentResult?.preferChronological) {
|
|
5825
5930
|
intermediate.sort((a, b) => new Date(b.rawTime).getTime() - new Date(a.rawTime).getTime());
|
|
5826
5931
|
} else {
|
|
@@ -5964,7 +6069,8 @@ async function searchObservations(options) {
|
|
|
5964
6069
|
}
|
|
5965
6070
|
let entries = intermediate.map(({ rawTime: _, _isCommandLog: _c, ...rest }) => rest);
|
|
5966
6071
|
for (const hit of results.hits) {
|
|
5967
|
-
|
|
6072
|
+
const doc = hit.document;
|
|
6073
|
+
if (canReadObservation(doc, options.reader)) rememberObservationDoc(doc);
|
|
5968
6074
|
}
|
|
5969
6075
|
if (hasQuery && originalQuery) {
|
|
5970
6076
|
const queryLower = originalQuery.toLowerCase();
|
|
@@ -6008,7 +6114,8 @@ async function searchObservations(options) {
|
|
|
6008
6114
|
entries = applyTokenBudget(entries, options.maxTokens);
|
|
6009
6115
|
}
|
|
6010
6116
|
if (options.trackAccess !== false) {
|
|
6011
|
-
const
|
|
6117
|
+
const returnedKeys = new Set(entries.map((entry) => makeEntryKey(entry.projectId, entry.id)));
|
|
6118
|
+
const hitDocs = results.hits.map((hit) => ({ id: hit.id, doc: hit.document })).filter(({ doc }) => returnedKeys.has(makeEntryKey(doc.projectId, doc.observationId)));
|
|
6012
6119
|
recordAccessBatch(hitDocs).catch(() => {
|
|
6013
6120
|
});
|
|
6014
6121
|
}
|
|
@@ -6040,11 +6147,11 @@ async function getObservationsByIds(ids, projectId) {
|
|
|
6040
6147
|
}
|
|
6041
6148
|
return results;
|
|
6042
6149
|
}
|
|
6043
|
-
async function getTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3) {
|
|
6150
|
+
async function getTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3, reader) {
|
|
6044
6151
|
const { withFreshIndex: withFreshIndex2 } = await Promise.resolve().then(() => (init_freshness(), freshness_exports));
|
|
6045
6152
|
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
6046
6153
|
const rawObs = await withFreshIndex2(() => getAllObservations2());
|
|
6047
|
-
const allObs = projectId ? rawObs.filter((o) => o.projectId === projectId) : rawObs;
|
|
6154
|
+
const allObs = (projectId ? rawObs.filter((o) => o.projectId === projectId) : rawObs).filter((observation) => canReadObservation(observation, reader));
|
|
6048
6155
|
const sorted = allObs.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
6049
6156
|
const anchorIndex = sorted.findIndex((o) => o.id === anchorId);
|
|
6050
6157
|
if (anchorIndex === -1) {
|
|
@@ -6061,7 +6168,9 @@ async function getTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3)
|
|
|
6061
6168
|
tokens: obs.tokens,
|
|
6062
6169
|
source: obs.source || void 0,
|
|
6063
6170
|
sourceDetail: obs.sourceDetail || void 0,
|
|
6064
|
-
valueCategory: obs.valueCategory || void 0
|
|
6171
|
+
valueCategory: obs.valueCategory || void 0,
|
|
6172
|
+
admissionState: obs.admissionState || void 0,
|
|
6173
|
+
visibility: obs.visibility || void 0
|
|
6065
6174
|
};
|
|
6066
6175
|
};
|
|
6067
6176
|
const before = sorted.slice(Math.max(0, anchorIndex - depthBefore), anchorIndex).map(toIndexEntry);
|
|
@@ -6129,6 +6238,7 @@ var init_orama_store = __esm({
|
|
|
6129
6238
|
init_esm_shims();
|
|
6130
6239
|
init_types();
|
|
6131
6240
|
init_mini_skills();
|
|
6241
|
+
init_visibility();
|
|
6132
6242
|
init_provider();
|
|
6133
6243
|
init_project_affinity();
|
|
6134
6244
|
init_intent_detector();
|
|
@@ -6185,6 +6295,10 @@ function obsToRow(obs) {
|
|
|
6185
6295
|
relatedEntities: obs.relatedEntities ? JSON.stringify(obs.relatedEntities) : null,
|
|
6186
6296
|
sourceDetail: obs.sourceDetail ?? null,
|
|
6187
6297
|
valueCategory: obs.valueCategory ?? null,
|
|
6298
|
+
admissionState: obs.admissionState ?? null,
|
|
6299
|
+
admissionReason: obs.admissionReason ?? null,
|
|
6300
|
+
visibility: obs.visibility ?? null,
|
|
6301
|
+
sharedWithAgentIds: obs.sharedWithAgentIds ? JSON.stringify(obs.sharedWithAgentIds) : null,
|
|
6188
6302
|
createdByAgentId: obs.createdByAgentId ?? null,
|
|
6189
6303
|
writeGeneration: obs.writeGeneration ?? 0
|
|
6190
6304
|
};
|
|
@@ -6215,6 +6329,10 @@ function rowToObs(row) {
|
|
|
6215
6329
|
...row.relatedEntities ? { relatedEntities: safeJsonParse3(row.relatedEntities, []) } : {},
|
|
6216
6330
|
...row.sourceDetail ? { sourceDetail: row.sourceDetail } : {},
|
|
6217
6331
|
...row.valueCategory ? { valueCategory: row.valueCategory } : {},
|
|
6332
|
+
...row.admissionState ? { admissionState: row.admissionState } : {},
|
|
6333
|
+
...row.admissionReason ? { admissionReason: row.admissionReason } : {},
|
|
6334
|
+
...row.visibility ? { visibility: row.visibility } : {},
|
|
6335
|
+
...row.sharedWithAgentIds ? { sharedWithAgentIds: safeJsonParse3(row.sharedWithAgentIds, []) } : {},
|
|
6218
6336
|
...row.createdByAgentId ? { createdByAgentId: row.createdByAgentId } : {},
|
|
6219
6337
|
...row.writeGeneration ? { writeGeneration: row.writeGeneration } : {}
|
|
6220
6338
|
};
|
|
@@ -6266,12 +6384,14 @@ var init_sqlite_store = __esm({
|
|
|
6266
6384
|
(id, entityName, type, title, narrative, facts, filesModified, concepts, tokens,
|
|
6267
6385
|
createdAt, updatedAt, projectId, hasCausalLanguage, topicKey, revisionCount,
|
|
6268
6386
|
sessionId, status, progress, source, commitHash, relatedCommits, relatedEntities,
|
|
6269
|
-
sourceDetail, valueCategory,
|
|
6387
|
+
sourceDetail, valueCategory, admissionState, admissionReason, visibility, sharedWithAgentIds,
|
|
6388
|
+
createdByAgentId, writeGeneration)
|
|
6270
6389
|
VALUES
|
|
6271
6390
|
(@id, @entityName, @type, @title, @narrative, @facts, @filesModified, @concepts, @tokens,
|
|
6272
6391
|
@createdAt, @updatedAt, @projectId, @hasCausalLanguage, @topicKey, @revisionCount,
|
|
6273
6392
|
@sessionId, @status, @progress, @source, @commitHash, @relatedCommits, @relatedEntities,
|
|
6274
|
-
@sourceDetail, @valueCategory, @
|
|
6393
|
+
@sourceDetail, @valueCategory, @admissionState, @admissionReason, @visibility, @sharedWithAgentIds,
|
|
6394
|
+
@createdByAgentId, @writeGeneration)
|
|
6275
6395
|
`);
|
|
6276
6396
|
this.stmtUpdate = this.stmtInsert;
|
|
6277
6397
|
this.stmtSetStatus = this.db.prepare(`UPDATE observations SET status = ? WHERE id = ?`);
|
|
@@ -6823,6 +6943,7 @@ var init_maintenance_jobs = __esm({
|
|
|
6823
6943
|
"retention-archive",
|
|
6824
6944
|
"consolidation",
|
|
6825
6945
|
"codegraph-refresh",
|
|
6946
|
+
"observation-qualify",
|
|
6826
6947
|
"claim-derive",
|
|
6827
6948
|
"claim-requalification",
|
|
6828
6949
|
"knowledge-compile",
|
|
@@ -7181,7 +7302,8 @@ __export(lifecycle_exports, {
|
|
|
7181
7302
|
enqueueClaimDerivation: () => enqueueClaimDerivation,
|
|
7182
7303
|
enqueueClaimRequalification: () => enqueueClaimRequalification,
|
|
7183
7304
|
enqueueCodegraphRefresh: () => enqueueCodegraphRefresh,
|
|
7184
|
-
enqueueKnowledgeFollowups: () => enqueueKnowledgeFollowups
|
|
7305
|
+
enqueueKnowledgeFollowups: () => enqueueKnowledgeFollowups,
|
|
7306
|
+
enqueueObservationQualification: () => enqueueObservationQualification
|
|
7185
7307
|
});
|
|
7186
7308
|
function queueFor(input) {
|
|
7187
7309
|
return input.queue ?? new MaintenanceJobStore(input.dataDir);
|
|
@@ -7216,6 +7338,14 @@ function enqueueClaimDerivation(input) {
|
|
|
7216
7338
|
payload: { observationId: input.observationId }
|
|
7217
7339
|
});
|
|
7218
7340
|
}
|
|
7341
|
+
function enqueueObservationQualification(input) {
|
|
7342
|
+
queueFor(input).enqueue({
|
|
7343
|
+
projectId: input.projectId,
|
|
7344
|
+
kind: "observation-qualify",
|
|
7345
|
+
dedupeKey: "observation-qualify",
|
|
7346
|
+
payload: { source: input.source, limit: 100 }
|
|
7347
|
+
});
|
|
7348
|
+
}
|
|
7219
7349
|
function enqueueKnowledgeFollowups(input) {
|
|
7220
7350
|
const queue = queueFor(input);
|
|
7221
7351
|
const payload = {
|
|
@@ -8080,6 +8210,7 @@ __export(observations_exports, {
|
|
|
8080
8210
|
resolveObservations: () => resolveObservations,
|
|
8081
8211
|
storeObservation: () => storeObservation,
|
|
8082
8212
|
suggestTopicKey: () => suggestTopicKey,
|
|
8213
|
+
updateObservationAdmission: () => updateObservationAdmission,
|
|
8083
8214
|
withFreshObservations: () => withFreshObservations
|
|
8084
8215
|
});
|
|
8085
8216
|
function logEmbeddingFailureOnce(key, message) {
|
|
@@ -8146,6 +8277,18 @@ function queueClaimDerivation(observation) {
|
|
|
8146
8277
|
} catch {
|
|
8147
8278
|
}
|
|
8148
8279
|
}
|
|
8280
|
+
function queueObservationQualification(observation) {
|
|
8281
|
+
const dataDir = projectDir;
|
|
8282
|
+
if (!dataDir || observation.admissionState !== "candidate") return;
|
|
8283
|
+
try {
|
|
8284
|
+
enqueueObservationQualification({
|
|
8285
|
+
dataDir,
|
|
8286
|
+
projectId: observation.projectId,
|
|
8287
|
+
source: "automatic-capture:" + observation.id
|
|
8288
|
+
});
|
|
8289
|
+
} catch {
|
|
8290
|
+
}
|
|
8291
|
+
}
|
|
8149
8292
|
function isVectorCompatibleWithCurrentIndex(embedding) {
|
|
8150
8293
|
if (!embedding) return false;
|
|
8151
8294
|
const vectorDimensions = getVectorDimensions();
|
|
@@ -8201,6 +8344,9 @@ async function storeObservation(input) {
|
|
|
8201
8344
|
(o) => o.topicKey === input.topicKey && o.projectId === input.projectId
|
|
8202
8345
|
);
|
|
8203
8346
|
if (existing) {
|
|
8347
|
+
if (input.visibilityReader && !canManageObservation(existing, input.visibilityReader)) {
|
|
8348
|
+
throw new Error("Cannot update a memory outside this session's write scope.");
|
|
8349
|
+
}
|
|
8204
8350
|
return { observation: await upsertObservation(existing, input, now3), upserted: true };
|
|
8205
8351
|
}
|
|
8206
8352
|
}
|
|
@@ -8237,6 +8383,9 @@ async function storeObservation(input) {
|
|
|
8237
8383
|
if (input.topicKey) {
|
|
8238
8384
|
const diskExisting = await tx.findByTopicKey(input.projectId, input.topicKey);
|
|
8239
8385
|
if (diskExisting) {
|
|
8386
|
+
if (input.visibilityReader && !canManageObservation(diskExisting, input.visibilityReader)) {
|
|
8387
|
+
throw new Error("Cannot update a memory outside this session's write scope.");
|
|
8388
|
+
}
|
|
8240
8389
|
upsertedInsideLock = true;
|
|
8241
8390
|
observation = diskExisting;
|
|
8242
8391
|
return;
|
|
@@ -8267,6 +8416,10 @@ async function storeObservation(input) {
|
|
|
8267
8416
|
relatedEntities: input.relatedEntities,
|
|
8268
8417
|
sourceDetail: input.sourceDetail,
|
|
8269
8418
|
valueCategory: input.valueCategory,
|
|
8419
|
+
admissionState: input.admissionState,
|
|
8420
|
+
admissionReason: input.admissionReason ? sanitizeCredentials(input.admissionReason) : void 0,
|
|
8421
|
+
visibility: input.visibility ?? "project",
|
|
8422
|
+
sharedWithAgentIds: input.sharedWithAgentIds,
|
|
8270
8423
|
createdByAgentId: input.createdByAgentId,
|
|
8271
8424
|
// Predict the generation that atomic() will commit after this callback.
|
|
8272
8425
|
// bumpGeneration() runs after fn(tx) returns, incrementing by 1.
|
|
@@ -8313,6 +8466,10 @@ async function storeObservation(input) {
|
|
|
8313
8466
|
relatedEntities: input.relatedEntities,
|
|
8314
8467
|
sourceDetail: input.sourceDetail,
|
|
8315
8468
|
valueCategory: input.valueCategory,
|
|
8469
|
+
admissionState: input.admissionState,
|
|
8470
|
+
admissionReason: input.admissionReason ? sanitizeCredentials(input.admissionReason) : void 0,
|
|
8471
|
+
visibility: input.visibility ?? "project",
|
|
8472
|
+
sharedWithAgentIds: input.sharedWithAgentIds,
|
|
8316
8473
|
createdByAgentId: input.createdByAgentId,
|
|
8317
8474
|
writeGeneration: 0
|
|
8318
8475
|
};
|
|
@@ -8336,7 +8493,12 @@ async function storeObservation(input) {
|
|
|
8336
8493
|
status: "active",
|
|
8337
8494
|
source: input.source ?? "agent",
|
|
8338
8495
|
sourceDetail: input.sourceDetail ?? "",
|
|
8339
|
-
valueCategory: input.valueCategory ?? ""
|
|
8496
|
+
valueCategory: input.valueCategory ?? "",
|
|
8497
|
+
admissionState: input.admissionState ?? "",
|
|
8498
|
+
admissionReason: input.admissionReason ? sanitizeCredentials(input.admissionReason) : "",
|
|
8499
|
+
visibility: input.visibility ?? "project",
|
|
8500
|
+
createdByAgentId: input.createdByAgentId ?? "",
|
|
8501
|
+
sharedWithAgentIds: JSON.stringify(input.sharedWithAgentIds ?? [])
|
|
8340
8502
|
};
|
|
8341
8503
|
await insertObservation(doc);
|
|
8342
8504
|
};
|
|
@@ -8345,7 +8507,10 @@ async function storeObservation(input) {
|
|
|
8345
8507
|
return { observation: await upsertObservation(observation, input, now3), upserted: true };
|
|
8346
8508
|
}
|
|
8347
8509
|
await bindObservationCodeRefsBestEffort(observation);
|
|
8348
|
-
|
|
8510
|
+
queueObservationQualification(observation);
|
|
8511
|
+
if (resolveObservationVisibility(observation) === "project") {
|
|
8512
|
+
queueClaimDerivation(observation);
|
|
8513
|
+
}
|
|
8349
8514
|
const obsId = observation.id;
|
|
8350
8515
|
vectorMissingIds.add(obsId);
|
|
8351
8516
|
const searchableText = [input.title, input.narrative, ...input.facts ?? []].join(" ");
|
|
@@ -8418,6 +8583,10 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8418
8583
|
if (input.progress) existing.progress = input.progress;
|
|
8419
8584
|
if (input.sourceDetail !== void 0) existing.sourceDetail = input.sourceDetail;
|
|
8420
8585
|
if (input.valueCategory !== void 0) existing.valueCategory = input.valueCategory;
|
|
8586
|
+
if (input.admissionState !== void 0) existing.admissionState = input.admissionState;
|
|
8587
|
+
if (input.admissionReason !== void 0) existing.admissionReason = sanitizeCredentials(input.admissionReason);
|
|
8588
|
+
if (input.visibility !== void 0) existing.visibility = input.visibility;
|
|
8589
|
+
if (input.sharedWithAgentIds !== void 0) existing.sharedWithAgentIds = input.sharedWithAgentIds;
|
|
8421
8590
|
const doc = {
|
|
8422
8591
|
id: makeOramaObservationId(existing.projectId, existing.id),
|
|
8423
8592
|
observationId: existing.id,
|
|
@@ -8436,7 +8605,12 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8436
8605
|
status: "active",
|
|
8437
8606
|
source: existing.source ?? "agent",
|
|
8438
8607
|
sourceDetail: existing.sourceDetail ?? "",
|
|
8439
|
-
valueCategory: existing.valueCategory ?? ""
|
|
8608
|
+
valueCategory: existing.valueCategory ?? "",
|
|
8609
|
+
admissionState: existing.admissionState ?? "",
|
|
8610
|
+
admissionReason: existing.admissionReason ?? "",
|
|
8611
|
+
visibility: existing.visibility ?? "project",
|
|
8612
|
+
createdByAgentId: existing.createdByAgentId ?? "",
|
|
8613
|
+
sharedWithAgentIds: JSON.stringify(existing.sharedWithAgentIds ?? [])
|
|
8440
8614
|
};
|
|
8441
8615
|
const oramaId = makeOramaObservationId(existing.projectId, existing.id);
|
|
8442
8616
|
try {
|
|
@@ -8459,7 +8633,10 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8459
8633
|
await store.update(existing);
|
|
8460
8634
|
}
|
|
8461
8635
|
await bindObservationCodeRefsBestEffort(existing);
|
|
8462
|
-
|
|
8636
|
+
queueObservationQualification(existing);
|
|
8637
|
+
if (resolveObservationVisibility(existing) === "project") {
|
|
8638
|
+
queueClaimDerivation(existing);
|
|
8639
|
+
}
|
|
8463
8640
|
const searchableText = [input.title, input.narrative, ...input.facts ?? []].join(" ");
|
|
8464
8641
|
const obsId = existing.id;
|
|
8465
8642
|
vectorMissingIds.add(obsId);
|
|
@@ -8498,6 +8675,56 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8498
8675
|
function getObservation(id, projectId) {
|
|
8499
8676
|
return observations.find((o) => o.id === id && (projectId ? o.projectId === projectId : true));
|
|
8500
8677
|
}
|
|
8678
|
+
async function updateObservationAdmission(input) {
|
|
8679
|
+
await ensureFreshObservations();
|
|
8680
|
+
const cached = observations.find(
|
|
8681
|
+
(observation) => observation.id === input.observationId && (!input.projectId || observation.projectId === input.projectId)
|
|
8682
|
+
);
|
|
8683
|
+
if (!cached) return void 0;
|
|
8684
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
8685
|
+
const admissionReason = sanitizeCredentials(input.admissionReason).slice(0, 240);
|
|
8686
|
+
const apply = (current) => {
|
|
8687
|
+
if (input.projectId && current.projectId !== input.projectId) return void 0;
|
|
8688
|
+
if (input.expectedState && current.admissionState !== input.expectedState) return void 0;
|
|
8689
|
+
return {
|
|
8690
|
+
...current,
|
|
8691
|
+
admissionState: input.admissionState,
|
|
8692
|
+
admissionReason,
|
|
8693
|
+
...input.visibility !== void 0 ? { visibility: input.visibility } : {},
|
|
8694
|
+
updatedAt: now3
|
|
8695
|
+
};
|
|
8696
|
+
};
|
|
8697
|
+
let updated;
|
|
8698
|
+
if (projectDir) {
|
|
8699
|
+
const store = getObservationStore();
|
|
8700
|
+
await store.atomic(async (tx) => {
|
|
8701
|
+
const current = await tx.getById(input.observationId);
|
|
8702
|
+
if (!current) return;
|
|
8703
|
+
const next = apply(current);
|
|
8704
|
+
if (!next) return;
|
|
8705
|
+
await tx.update(next);
|
|
8706
|
+
updated = next;
|
|
8707
|
+
});
|
|
8708
|
+
} else {
|
|
8709
|
+
updated = apply(cached);
|
|
8710
|
+
}
|
|
8711
|
+
if (!updated) return void 0;
|
|
8712
|
+
observations = observations.map(
|
|
8713
|
+
(observation) => observation.id === updated.id && observation.projectId === updated.projectId ? updated : observation
|
|
8714
|
+
);
|
|
8715
|
+
try {
|
|
8716
|
+
await updateObservationMetadata(updated.projectId, updated.id, {
|
|
8717
|
+
admissionState: updated.admissionState,
|
|
8718
|
+
admissionReason: updated.admissionReason,
|
|
8719
|
+
...updated.visibility !== void 0 ? { visibility: updated.visibility } : {}
|
|
8720
|
+
});
|
|
8721
|
+
} catch {
|
|
8722
|
+
}
|
|
8723
|
+
if (updated.admissionState === "qualified" && resolveObservationVisibility(updated) === "project") {
|
|
8724
|
+
queueClaimDerivation(updated);
|
|
8725
|
+
}
|
|
8726
|
+
return updated;
|
|
8727
|
+
}
|
|
8501
8728
|
async function resolveObservations(ids, status = "resolved") {
|
|
8502
8729
|
const resolved = [];
|
|
8503
8730
|
const notFound = [];
|
|
@@ -8537,7 +8764,12 @@ async function resolveObservations(ids, status = "resolved") {
|
|
|
8537
8764
|
status,
|
|
8538
8765
|
source: obs.source ?? "agent",
|
|
8539
8766
|
sourceDetail: obs.sourceDetail ?? "",
|
|
8540
|
-
valueCategory: obs.valueCategory ?? ""
|
|
8767
|
+
valueCategory: obs.valueCategory ?? "",
|
|
8768
|
+
admissionState: obs.admissionState ?? "",
|
|
8769
|
+
admissionReason: obs.admissionReason ?? "",
|
|
8770
|
+
visibility: obs.visibility ?? "project",
|
|
8771
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
8772
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
8541
8773
|
};
|
|
8542
8774
|
await insertObservation(doc);
|
|
8543
8775
|
const obsId = obs.id;
|
|
@@ -8667,6 +8899,11 @@ async function reindexObservations() {
|
|
|
8667
8899
|
source: obs.source ?? "agent",
|
|
8668
8900
|
sourceDetail: obs.sourceDetail ?? "",
|
|
8669
8901
|
valueCategory: obs.valueCategory ?? "",
|
|
8902
|
+
admissionState: obs.admissionState ?? "",
|
|
8903
|
+
admissionReason: obs.admissionReason ?? "",
|
|
8904
|
+
visibility: obs.visibility ?? "project",
|
|
8905
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
8906
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
8670
8907
|
...compatibleEmbedding ? { embedding: compatibleEmbedding } : {}
|
|
8671
8908
|
};
|
|
8672
8909
|
await insertObservation(doc);
|
|
@@ -8739,6 +8976,7 @@ async function probeSearchIndex(projectId) {
|
|
|
8739
8976
|
await searchObservations({
|
|
8740
8977
|
query: "semantic memory retrieval status",
|
|
8741
8978
|
projectId,
|
|
8979
|
+
reader: { projectId },
|
|
8742
8980
|
limit: 1,
|
|
8743
8981
|
status: "all",
|
|
8744
8982
|
trackAccess: false
|
|
@@ -8804,6 +9042,11 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
8804
9042
|
source: obs.source ?? "agent",
|
|
8805
9043
|
sourceDetail: obs.sourceDetail ?? "",
|
|
8806
9044
|
valueCategory: obs.valueCategory ?? "",
|
|
9045
|
+
admissionState: obs.admissionState ?? "",
|
|
9046
|
+
admissionReason: obs.admissionReason ?? "",
|
|
9047
|
+
visibility: obs.visibility ?? "project",
|
|
9048
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
9049
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
8807
9050
|
embedding
|
|
8808
9051
|
};
|
|
8809
9052
|
await insertObservation(doc);
|
|
@@ -8845,6 +9088,7 @@ var init_observations = __esm({
|
|
|
8845
9088
|
init_provider();
|
|
8846
9089
|
init_secret_filter();
|
|
8847
9090
|
init_lifecycle();
|
|
9091
|
+
init_visibility();
|
|
8848
9092
|
observations = [];
|
|
8849
9093
|
nextId = 1;
|
|
8850
9094
|
projectDir = null;
|
|
@@ -9124,8 +9368,9 @@ function resolveSourceDetail(sourceDetail, source) {
|
|
|
9124
9368
|
return void 0;
|
|
9125
9369
|
}
|
|
9126
9370
|
function classifyLayer(fields) {
|
|
9127
|
-
const { valueCategory } = fields;
|
|
9371
|
+
const { valueCategory, admissionState } = fields;
|
|
9128
9372
|
const sd = resolveSourceDetail(fields.sourceDetail, fields.source);
|
|
9373
|
+
if (admissionState === "candidate" || admissionState === "ephemeral") return "L1";
|
|
9129
9374
|
if (valueCategory === "core") return "L2";
|
|
9130
9375
|
if (sd === "hook") return "L1";
|
|
9131
9376
|
if (sd === "git-ingest") return "L3";
|
|
@@ -9183,6 +9428,7 @@ function getEffectiveRetentionDays(doc) {
|
|
|
9183
9428
|
}
|
|
9184
9429
|
function isImmune(doc) {
|
|
9185
9430
|
if (doc.type === "probe") return false;
|
|
9431
|
+
if (doc.admissionState === "candidate" || doc.admissionState === "ephemeral") return false;
|
|
9186
9432
|
if (doc.valueCategory === "core") return true;
|
|
9187
9433
|
const importance = getImportanceLevel(doc);
|
|
9188
9434
|
if (importance === "critical") return true;
|
|
@@ -9192,6 +9438,7 @@ function isImmune(doc) {
|
|
|
9192
9438
|
}
|
|
9193
9439
|
function getImmunityReason(doc) {
|
|
9194
9440
|
if (doc.type === "probe") return null;
|
|
9441
|
+
if (doc.admissionState === "candidate" || doc.admissionState === "ephemeral") return null;
|
|
9195
9442
|
if (doc.valueCategory === "core") return "core valueCategory (formation-classified)";
|
|
9196
9443
|
const importance = getImportanceLevel(doc);
|
|
9197
9444
|
if (importance === "critical") return "critical importance";
|
|
@@ -9324,7 +9571,9 @@ function toRetentionDocument(obs, accessMap) {
|
|
|
9324
9571
|
status: obs.status ?? "active",
|
|
9325
9572
|
source: obs.source ?? "agent",
|
|
9326
9573
|
sourceDetail: obs.sourceDetail ?? "",
|
|
9327
|
-
valueCategory: obs.valueCategory ?? ""
|
|
9574
|
+
valueCategory: obs.valueCategory ?? "",
|
|
9575
|
+
admissionState: obs.admissionState ?? "",
|
|
9576
|
+
admissionReason: obs.admissionReason ?? ""
|
|
9328
9577
|
};
|
|
9329
9578
|
}
|
|
9330
9579
|
async function archiveExpiredBatch(_projectDir, options) {
|
|
@@ -9338,7 +9587,7 @@ async function archiveExpiredBatch(_projectDir, options) {
|
|
|
9338
9587
|
});
|
|
9339
9588
|
const hasMore = page.length > limit;
|
|
9340
9589
|
const scanned = hasMore ? page.slice(0, limit) : page;
|
|
9341
|
-
const candidateIds = scanned.filter((observation) => getRetentionZone(
|
|
9590
|
+
const candidateIds = scanned.filter((observation) => !options.reader || canManageObservation(observation, options.reader)).filter((observation) => getRetentionZone(
|
|
9342
9591
|
toRetentionDocument(observation, options.accessMap),
|
|
9343
9592
|
options.referenceTime
|
|
9344
9593
|
) === "archive-candidate").map((observation) => observation.id);
|
|
@@ -9351,7 +9600,7 @@ async function archiveExpiredBatch(_projectDir, options) {
|
|
|
9351
9600
|
const nextCursor = hasMore && scanned.length > 0 ? scanned[scanned.length - 1].id : void 0;
|
|
9352
9601
|
return nextCursor === void 0 ? { archived, scanned: scanned.length } : { archived, scanned: scanned.length, nextCursor };
|
|
9353
9602
|
}
|
|
9354
|
-
async function archiveExpired(projectDir2, referenceTime, accessMap, projectId) {
|
|
9603
|
+
async function archiveExpired(projectDir2, referenceTime, accessMap, projectId, reader) {
|
|
9355
9604
|
const store = getObservationStore();
|
|
9356
9605
|
if (projectId) {
|
|
9357
9606
|
let afterId;
|
|
@@ -9361,12 +9610,14 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId)
|
|
|
9361
9610
|
projectId,
|
|
9362
9611
|
afterId,
|
|
9363
9612
|
referenceTime,
|
|
9364
|
-
accessMap
|
|
9613
|
+
accessMap,
|
|
9614
|
+
reader
|
|
9365
9615
|
});
|
|
9366
9616
|
archived += batch.archived;
|
|
9367
9617
|
afterId = batch.nextCursor;
|
|
9368
9618
|
} while (afterId !== void 0);
|
|
9369
|
-
const
|
|
9619
|
+
const remainingObservations = await store.loadByProject(projectId, { status: "active" });
|
|
9620
|
+
const remaining = reader ? remainingObservations.filter((observation) => canManageObservation(observation, reader)).length : remainingObservations.length;
|
|
9370
9621
|
return { archived, remaining };
|
|
9371
9622
|
}
|
|
9372
9623
|
return await store.atomic(async (tx) => {
|
|
@@ -9396,6 +9647,7 @@ var init_retention = __esm({
|
|
|
9396
9647
|
"use strict";
|
|
9397
9648
|
init_esm_shims();
|
|
9398
9649
|
init_obs_store();
|
|
9650
|
+
init_visibility();
|
|
9399
9651
|
RETENTION_DAYS = {
|
|
9400
9652
|
critical: 365,
|
|
9401
9653
|
high: 180,
|
|
@@ -9431,6 +9683,49 @@ var init_retention = __esm({
|
|
|
9431
9683
|
}
|
|
9432
9684
|
});
|
|
9433
9685
|
|
|
9686
|
+
// src/memory/admission.ts
|
|
9687
|
+
var admission_exports = {};
|
|
9688
|
+
__export(admission_exports, {
|
|
9689
|
+
isCandidateObservation: () => isCandidateObservation,
|
|
9690
|
+
isEligibleForAutomaticDelivery: () => isEligibleForAutomaticDelivery,
|
|
9691
|
+
isEligibleForKnowledgePromotion: () => isEligibleForKnowledgePromotion,
|
|
9692
|
+
qualifyCandidateFromCurrentCode: () => qualifyCandidateFromCurrentCode
|
|
9693
|
+
});
|
|
9694
|
+
function isEligibleForAutomaticDelivery(observation) {
|
|
9695
|
+
return observation.admissionState !== "ephemeral" && observation.admissionState !== "candidate";
|
|
9696
|
+
}
|
|
9697
|
+
function isCandidateObservation(observation) {
|
|
9698
|
+
return observation.admissionState === "candidate";
|
|
9699
|
+
}
|
|
9700
|
+
function isEligibleForKnowledgePromotion(observation) {
|
|
9701
|
+
return isEligibleForAutomaticDelivery(observation) && observation.valueCategory !== "ephemeral" && resolveObservationVisibility(observation) === "project";
|
|
9702
|
+
}
|
|
9703
|
+
function qualifyCandidateFromCurrentCode(input) {
|
|
9704
|
+
if (!isCandidateObservation(input.observation)) return void 0;
|
|
9705
|
+
if (input.observation.valueCategory === "ephemeral") return void 0;
|
|
9706
|
+
if (input.currentCodeReferenceCount <= 0) return void 0;
|
|
9707
|
+
const typeLabel = DURABLE_AUTOMATIC_TYPES.has(input.observation.type) ? "high-value automatic record" : "automatic record";
|
|
9708
|
+
return {
|
|
9709
|
+
admissionState: "qualified",
|
|
9710
|
+
admissionReason: `${typeLabel} qualified against ${input.currentCodeReferenceCount} current Code Memory reference(s)`
|
|
9711
|
+
};
|
|
9712
|
+
}
|
|
9713
|
+
var DURABLE_AUTOMATIC_TYPES;
|
|
9714
|
+
var init_admission = __esm({
|
|
9715
|
+
"src/memory/admission.ts"() {
|
|
9716
|
+
"use strict";
|
|
9717
|
+
init_esm_shims();
|
|
9718
|
+
init_visibility();
|
|
9719
|
+
DURABLE_AUTOMATIC_TYPES = /* @__PURE__ */ new Set([
|
|
9720
|
+
"decision",
|
|
9721
|
+
"gotcha",
|
|
9722
|
+
"problem-solution",
|
|
9723
|
+
"trade-off",
|
|
9724
|
+
"why-it-exists"
|
|
9725
|
+
]);
|
|
9726
|
+
}
|
|
9727
|
+
});
|
|
9728
|
+
|
|
9434
9729
|
// src/workspace/workflow-sync.ts
|
|
9435
9730
|
import matter9 from "gray-matter";
|
|
9436
9731
|
var WorkflowSyncer;
|
|
@@ -10831,6 +11126,7 @@ var init_isolated_maintenance = __esm({
|
|
|
10831
11126
|
"retention-archive",
|
|
10832
11127
|
"consolidation",
|
|
10833
11128
|
"codegraph-refresh",
|
|
11129
|
+
"observation-qualify",
|
|
10834
11130
|
"claim-derive",
|
|
10835
11131
|
"claim-requalification",
|
|
10836
11132
|
"knowledge-compile",
|
|
@@ -11266,9 +11562,10 @@ async function loadConsolidationPage(projectId, options) {
|
|
|
11266
11562
|
return hasMore && observations2.length > 0 ? { observations: observations2, nextCursor: observations2[observations2.length - 1].id } : { observations: observations2 };
|
|
11267
11563
|
}
|
|
11268
11564
|
function findClusters(observations2, threshold) {
|
|
11269
|
-
|
|
11565
|
+
const eligible = observations2.filter(isEligibleForAutomaticDelivery).filter((observation) => resolveObservationVisibility(observation) === "project");
|
|
11566
|
+
if (eligible.length < MIN_CLUSTER_SIZE) return [];
|
|
11270
11567
|
const groups = /* @__PURE__ */ new Map();
|
|
11271
|
-
for (const obs of
|
|
11568
|
+
for (const obs of eligible) {
|
|
11272
11569
|
const key = `${obs.entityName}::${obs.type}`;
|
|
11273
11570
|
const group = groups.get(key) ?? [];
|
|
11274
11571
|
group.push(obs);
|
|
@@ -11405,6 +11702,8 @@ var init_consolidation = __esm({
|
|
|
11405
11702
|
"use strict";
|
|
11406
11703
|
init_esm_shims();
|
|
11407
11704
|
init_obs_store();
|
|
11705
|
+
init_admission();
|
|
11706
|
+
init_visibility();
|
|
11408
11707
|
DEFAULT_SIMILARITY_THRESHOLD = 0.45;
|
|
11409
11708
|
HIGH_VALUE_SIMILARITY_THRESHOLD = 0.85;
|
|
11410
11709
|
HIGH_VALUE_TYPES = /* @__PURE__ */ new Set(["gotcha", "decision", "trade-off", "reasoning", "problem-solution"]);
|
|
@@ -14423,6 +14722,15 @@ function claimDerivationCursor(payload) {
|
|
|
14423
14722
|
const value = payload.cursor;
|
|
14424
14723
|
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
14425
14724
|
}
|
|
14725
|
+
function observationQualificationBatchSize(payload) {
|
|
14726
|
+
const value = payload.limit;
|
|
14727
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE;
|
|
14728
|
+
return Math.min(500, Math.max(1, Math.floor(value)));
|
|
14729
|
+
}
|
|
14730
|
+
function observationQualificationCursor(payload) {
|
|
14731
|
+
const value = payload.cursor;
|
|
14732
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
14733
|
+
}
|
|
14426
14734
|
function observationId(payload) {
|
|
14427
14735
|
const value = payload.observationId;
|
|
14428
14736
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : void 0;
|
|
@@ -14601,6 +14909,71 @@ function createProjectMaintenanceHandler(projectId, projectDir2, projectRoot, op
|
|
|
14601
14909
|
...snapshot?.id ? { snapshotId: snapshot.id } : {},
|
|
14602
14910
|
...options.maintenanceQueue ? { queue: options.maintenanceQueue } : {}
|
|
14603
14911
|
});
|
|
14912
|
+
enqueueObservationQualification({
|
|
14913
|
+
projectId,
|
|
14914
|
+
dataDir: projectDir2,
|
|
14915
|
+
source: "codegraph-refresh",
|
|
14916
|
+
...options.maintenanceQueue ? { queue: options.maintenanceQueue } : {}
|
|
14917
|
+
});
|
|
14918
|
+
return { action: "complete" };
|
|
14919
|
+
}
|
|
14920
|
+
if (job.kind === "observation-qualify") {
|
|
14921
|
+
const [
|
|
14922
|
+
{ CodeGraphStore: CodeGraphStore2 },
|
|
14923
|
+
{ bindObservationToCode: bindObservationToCode2 },
|
|
14924
|
+
{ getObservationStore: getObservationStore2 },
|
|
14925
|
+
{ qualifyCandidateFromCurrentCode: qualifyCandidateFromCurrentCode2 },
|
|
14926
|
+
{ updateObservationAdmission: updateObservationAdmission2 }
|
|
14927
|
+
] = await Promise.all([
|
|
14928
|
+
Promise.resolve().then(() => (init_store(), store_exports)),
|
|
14929
|
+
Promise.resolve().then(() => (init_binder(), binder_exports)),
|
|
14930
|
+
Promise.resolve().then(() => (init_obs_store(), obs_store_exports)),
|
|
14931
|
+
Promise.resolve().then(() => (init_admission(), admission_exports)),
|
|
14932
|
+
Promise.resolve().then(() => (init_observations(), observations_exports))
|
|
14933
|
+
]);
|
|
14934
|
+
const codeStore = new CodeGraphStore2();
|
|
14935
|
+
await codeStore.init(projectDir2);
|
|
14936
|
+
const limit = observationQualificationBatchSize(job.payload);
|
|
14937
|
+
const page = await getObservationStore2().loadByProject(projectId, {
|
|
14938
|
+
status: "active",
|
|
14939
|
+
afterId: observationQualificationCursor(job.payload),
|
|
14940
|
+
limit: limit + 1
|
|
14941
|
+
});
|
|
14942
|
+
const hasMore = page.length > limit;
|
|
14943
|
+
const observations2 = hasMore ? page.slice(0, limit) : page;
|
|
14944
|
+
const indexedAtMs = Date.parse(codeStore.status(projectId).indexedAt ?? "");
|
|
14945
|
+
for (const observation of observations2) {
|
|
14946
|
+
if (observation.admissionState !== "candidate") continue;
|
|
14947
|
+
if (!Number.isFinite(indexedAtMs) || indexedAtMs < Date.parse(observation.createdAt)) continue;
|
|
14948
|
+
await bindObservationToCode2(codeStore, observation);
|
|
14949
|
+
const currentCodeReferenceCount = codeStore.listObservationRefs(projectId, observation.id).filter((reference) => reference.status === "current").length;
|
|
14950
|
+
const qualification = qualifyCandidateFromCurrentCode2({
|
|
14951
|
+
observation,
|
|
14952
|
+
currentCodeReferenceCount
|
|
14953
|
+
});
|
|
14954
|
+
if (!qualification) continue;
|
|
14955
|
+
await updateObservationAdmission2({
|
|
14956
|
+
observationId: observation.id,
|
|
14957
|
+
projectId,
|
|
14958
|
+
expectedState: "candidate",
|
|
14959
|
+
// Automatic capture earns project visibility only after a current
|
|
14960
|
+
// Code Memory link exists. Explicit/private records retain scope.
|
|
14961
|
+
...observation.sourceDetail === "hook" ? { visibility: "project" } : {},
|
|
14962
|
+
...qualification
|
|
14963
|
+
});
|
|
14964
|
+
}
|
|
14965
|
+
if (hasMore && observations2.length > 0) {
|
|
14966
|
+
return {
|
|
14967
|
+
action: "reschedule",
|
|
14968
|
+
delayMs: 0,
|
|
14969
|
+
resetAttempts: true,
|
|
14970
|
+
payload: {
|
|
14971
|
+
...job.payload,
|
|
14972
|
+
cursor: observations2[observations2.length - 1].id,
|
|
14973
|
+
limit
|
|
14974
|
+
}
|
|
14975
|
+
};
|
|
14976
|
+
}
|
|
14604
14977
|
return { action: "complete" };
|
|
14605
14978
|
}
|
|
14606
14979
|
if (job.kind === "claim-derive") {
|
|
@@ -14766,7 +15139,7 @@ function createProjectMaintenanceDispatcher(projectId, projectDir2, projectRoot,
|
|
|
14766
15139
|
return isolatedRunner({ job, projectRoot, dataDir: projectDir2 });
|
|
14767
15140
|
};
|
|
14768
15141
|
}
|
|
14769
|
-
var DEFAULT_VECTOR_BATCH_SIZE, DEFAULT_RETENTION_BATCH_SIZE, DEFAULT_CONSOLIDATION_BATCH_SIZE, DEFAULT_CODEGRAPH_MAX_FILES, DEFAULT_CLAIM_DERIVATION_BATCH_SIZE, VECTOR_RETRY_DELAY_MS, DEDUP_PER_PAIR_TIMEOUT_MS;
|
|
15142
|
+
var DEFAULT_VECTOR_BATCH_SIZE, DEFAULT_RETENTION_BATCH_SIZE, DEFAULT_CONSOLIDATION_BATCH_SIZE, DEFAULT_CODEGRAPH_MAX_FILES, DEFAULT_CLAIM_DERIVATION_BATCH_SIZE, DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE, VECTOR_RETRY_DELAY_MS, DEDUP_PER_PAIR_TIMEOUT_MS;
|
|
14770
15143
|
var init_project_maintenance = __esm({
|
|
14771
15144
|
"src/runtime/project-maintenance.ts"() {
|
|
14772
15145
|
"use strict";
|
|
@@ -14778,6 +15151,7 @@ var init_project_maintenance = __esm({
|
|
|
14778
15151
|
DEFAULT_CONSOLIDATION_BATCH_SIZE = 200;
|
|
14779
15152
|
DEFAULT_CODEGRAPH_MAX_FILES = 5e3;
|
|
14780
15153
|
DEFAULT_CLAIM_DERIVATION_BATCH_SIZE = 100;
|
|
15154
|
+
DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE = 100;
|
|
14781
15155
|
VECTOR_RETRY_DELAY_MS = 5e3;
|
|
14782
15156
|
DEDUP_PER_PAIR_TIMEOUT_MS = 5e3;
|
|
14783
15157
|
}
|
|
@@ -14968,7 +15342,7 @@ async function startSession(projectDir2, projectId, opts) {
|
|
|
14968
15342
|
status: "active",
|
|
14969
15343
|
agent: opts?.agent
|
|
14970
15344
|
};
|
|
14971
|
-
const previousContext = await getSessionContext(projectDir2, projectId);
|
|
15345
|
+
const previousContext = await getSessionContext(projectDir2, projectId, 3, opts?.reader);
|
|
14972
15346
|
const sessionStore = getSessionStore();
|
|
14973
15347
|
const aliasSet = await resolveProjectIds(projectId);
|
|
14974
15348
|
await sessionStore.atomicRolloverInsert(session, [...aliasSet], now3);
|
|
@@ -14986,23 +15360,27 @@ async function endSession(projectDir2, sessionId, summary) {
|
|
|
14986
15360
|
await sessionStore.update(session);
|
|
14987
15361
|
return session;
|
|
14988
15362
|
}
|
|
14989
|
-
async function getSessionContext(projectDir2, projectId, limit = 3) {
|
|
15363
|
+
async function getSessionContext(projectDir2, projectId, limit = 3, reader) {
|
|
14990
15364
|
const aliasSet = await resolveProjectIds(projectId);
|
|
14991
15365
|
const [sessions, allObs] = await Promise.all([
|
|
14992
15366
|
loadAliasSessions(aliasSet),
|
|
14993
15367
|
loadAliasActiveObservations(aliasSet)
|
|
14994
15368
|
]);
|
|
15369
|
+
const readableObs = reader ? allObs.filter((observation) => {
|
|
15370
|
+
const observationReader = reader.projectId && aliasSet.has(observation.projectId) ? { ...reader, projectId: observation.projectId } : reader;
|
|
15371
|
+
return canReadObservation(observation, observationReader);
|
|
15372
|
+
}) : allObs;
|
|
14995
15373
|
const isNoisySummary = (summary) => {
|
|
14996
15374
|
if (!summary) return false;
|
|
14997
15375
|
return NOISE_PATTERNS2.some((p) => p.test(summary)) || SYSTEM_SELF_PATTERNS.some((p) => p.test(summary));
|
|
14998
15376
|
};
|
|
14999
15377
|
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);
|
|
15000
|
-
if (projectSessions.length === 0 &&
|
|
15378
|
+
if (projectSessions.length === 0 && readableObs.length === 0) {
|
|
15001
15379
|
return "";
|
|
15002
15380
|
}
|
|
15003
15381
|
const lines = [];
|
|
15004
15382
|
const projectTokens = tokenizeProjectId(projectId);
|
|
15005
|
-
const projectObs =
|
|
15383
|
+
const projectObs = readableObs.filter((obs) => !isNoiseObservation(obs) && !isSystemSelfObservation(obs));
|
|
15006
15384
|
const l2Scored = projectObs.filter((obs) => PRIORITY_TYPES.has(obs.type) && classifyLayer(obs) === "L2").map((obs) => ({ obs, score: scoreObservationForSessionContext(obs, projectTokens) })).sort((a, b) => {
|
|
15007
15385
|
if (b.score !== a.score) return b.score - a.score;
|
|
15008
15386
|
return new Date(b.obs.createdAt).getTime() - new Date(a.obs.createdAt).getTime();
|
|
@@ -15019,16 +15397,16 @@ async function getSessionContext(projectDir2, projectId, limit = 3) {
|
|
|
15019
15397
|
return true;
|
|
15020
15398
|
});
|
|
15021
15399
|
})() : l2Scored).slice(0, 5).map(({ obs }) => obs);
|
|
15022
|
-
const l1HookObs = projectObs.filter((obs) => classifyLayer(obs) === "L1").sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, 3);
|
|
15400
|
+
const l1HookObs = projectObs.filter((obs) => isEligibleForAutomaticDelivery(obs) && classifyLayer(obs) === "L1").sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, 3);
|
|
15023
15401
|
const l3GitCount = projectObs.filter((obs) => classifyLayer(obs) === "L3").length;
|
|
15024
|
-
const totalHookCount = projectObs.filter((obs) => classifyLayer(obs) === "L1").length;
|
|
15402
|
+
const totalHookCount = projectObs.filter((obs) => isEligibleForAutomaticDelivery(obs) && classifyLayer(obs) === "L1").length;
|
|
15025
15403
|
const activeEntities = [
|
|
15026
15404
|
...new Set(l2Obs.map((o) => o.entityName).filter((n) => !!n && n.trim().length > 0))
|
|
15027
15405
|
].slice(0, 5);
|
|
15028
15406
|
const hasL1Content = l1HookObs.length > 0 || l3GitCount > 0;
|
|
15029
15407
|
if (hasL1Content) {
|
|
15030
15408
|
let graphNeighbors = [];
|
|
15031
|
-
if (activeEntities.length > 0) {
|
|
15409
|
+
if (!reader && activeEntities.length > 0) {
|
|
15032
15410
|
try {
|
|
15033
15411
|
const graphMgr = new KnowledgeGraphManager(projectDir2);
|
|
15034
15412
|
await graphMgr.init();
|
|
@@ -15147,12 +15525,14 @@ var init_session = __esm({
|
|
|
15147
15525
|
"src/memory/session.ts"() {
|
|
15148
15526
|
"use strict";
|
|
15149
15527
|
init_esm_shims();
|
|
15528
|
+
init_admission();
|
|
15150
15529
|
init_disclosure_policy();
|
|
15151
15530
|
init_aliases();
|
|
15152
15531
|
init_obs_store();
|
|
15153
15532
|
init_session_store();
|
|
15154
15533
|
init_graph();
|
|
15155
15534
|
init_secret_filter();
|
|
15535
|
+
init_visibility();
|
|
15156
15536
|
PRIORITY_TYPES = /* @__PURE__ */ new Set(["gotcha", "decision", "problem-solution", "trade-off", "discovery"]);
|
|
15157
15537
|
TYPE_EMOJI = {
|
|
15158
15538
|
"gotcha": "[DISCOVERY]",
|
|
@@ -15299,27 +15679,80 @@ function workflowOutput(selection) {
|
|
|
15299
15679
|
cautions: selection.cautions.map((caution) => short(caution, 22))
|
|
15300
15680
|
};
|
|
15301
15681
|
}
|
|
15302
|
-
function appendLine(lines, candidate, maxTokens, omitted, omittedKind) {
|
|
15682
|
+
function appendLine(lines, candidate, maxTokens, omitted, omittedKind, selected, receiptSelection) {
|
|
15303
15683
|
const next = lines.length ? lines.join("\n") + "\n" + candidate : candidate;
|
|
15304
15684
|
if (countTextTokens(next) <= maxTokens) {
|
|
15305
15685
|
lines.push(candidate);
|
|
15686
|
+
if (receiptSelection) selected?.push(receiptSelection);
|
|
15306
15687
|
return true;
|
|
15307
15688
|
}
|
|
15308
15689
|
omitted.push(omittedKind);
|
|
15309
15690
|
return false;
|
|
15310
15691
|
}
|
|
15692
|
+
function freshnessForMemory(status) {
|
|
15693
|
+
if (status === "current" || status === "suspect" || status === "stale") return status;
|
|
15694
|
+
return "unknown";
|
|
15695
|
+
}
|
|
15696
|
+
function receiptOmissionKind(raw) {
|
|
15697
|
+
if (raw.includes("task")) return "task";
|
|
15698
|
+
if (raw.includes("fact")) return "current-fact";
|
|
15699
|
+
if (raw.includes("state")) return "code-state";
|
|
15700
|
+
if (raw.includes("semantic")) return "semantic-code";
|
|
15701
|
+
if (raw.includes("start")) return "start-here";
|
|
15702
|
+
if (raw.includes("memory")) return "memory";
|
|
15703
|
+
if (raw.includes("claim")) return "claim";
|
|
15704
|
+
if (raw.includes("knowledge-page")) return "knowledge-page";
|
|
15705
|
+
if (raw.includes("workflow")) return "workflow";
|
|
15706
|
+
if (raw.includes("verification")) return "verification";
|
|
15707
|
+
if (raw.includes("caution")) return "caution";
|
|
15708
|
+
return void 0;
|
|
15709
|
+
}
|
|
15710
|
+
function receiptOmissions(omitted, hiddenCautionMemoryCount) {
|
|
15711
|
+
const counts = /* @__PURE__ */ new Map();
|
|
15712
|
+
for (const raw of omitted) {
|
|
15713
|
+
const kind = receiptOmissionKind(raw);
|
|
15714
|
+
if (!kind || raw.endsWith("-heading")) continue;
|
|
15715
|
+
counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
15716
|
+
}
|
|
15717
|
+
const receipt = [...counts.entries()].map(([kind, count2]) => ({
|
|
15718
|
+
kind,
|
|
15719
|
+
reason: "token-budget",
|
|
15720
|
+
count: count2
|
|
15721
|
+
}));
|
|
15722
|
+
if (hiddenCautionMemoryCount > 0) {
|
|
15723
|
+
receipt.push({
|
|
15724
|
+
kind: "caution",
|
|
15725
|
+
reason: "hidden-by-task-lens",
|
|
15726
|
+
count: hiddenCautionMemoryCount
|
|
15727
|
+
});
|
|
15728
|
+
}
|
|
15729
|
+
return receipt;
|
|
15730
|
+
}
|
|
15731
|
+
function scheduledActions(cautions) {
|
|
15732
|
+
return cautions.filter((caution) => caution.kind === "codegraph-refresh-queued").map((caution) => short(caution.message, 28));
|
|
15733
|
+
}
|
|
15311
15734
|
function renderTaskWorksetPrompt(input) {
|
|
15312
15735
|
const maxTokens = input.budget?.maxTokens ?? 180;
|
|
15313
15736
|
const omitted = [];
|
|
15737
|
+
const selected = [];
|
|
15314
15738
|
const lines = ["Memorix Autopilot Brief"];
|
|
15315
15739
|
const task = short(input.task || "Continue the current task.", 34);
|
|
15316
|
-
appendLine(lines, "Task: " + task, maxTokens, omitted, "task-detail"
|
|
15740
|
+
appendLine(lines, "Task: " + task, maxTokens, omitted, "task-detail", selected, {
|
|
15741
|
+
kind: "task",
|
|
15742
|
+
reason: "current task supplied by the caller",
|
|
15743
|
+
trust: "source-backed"
|
|
15744
|
+
});
|
|
15317
15745
|
appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
|
|
15318
15746
|
if (input.cautions.length > 0 || input.cautionMemory.length > 0) {
|
|
15319
15747
|
appendLine(lines, "", maxTokens, omitted, "caution-heading");
|
|
15320
15748
|
appendLine(lines, "Cautions", maxTokens, omitted, "caution-heading");
|
|
15321
15749
|
for (const caution of input.cautions.slice(0, 6)) {
|
|
15322
|
-
appendLine(lines, "- " + short(caution.message, 22), maxTokens, omitted, "caution"
|
|
15750
|
+
appendLine(lines, "- " + short(caution.message, 22), maxTokens, omitted, "caution", selected, {
|
|
15751
|
+
kind: "caution",
|
|
15752
|
+
id: "caution:" + caution.kind,
|
|
15753
|
+
reason: "current project caution",
|
|
15754
|
+
trust: "source-backed"
|
|
15755
|
+
});
|
|
15323
15756
|
}
|
|
15324
15757
|
for (const memory of input.cautionMemory.slice(0, 3)) {
|
|
15325
15758
|
const location = memory.path ? memory.path + (memory.symbol ? "#" + memory.symbol : "") : "no current code location";
|
|
@@ -15329,7 +15762,15 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15329
15762
|
"- #" + memory.id + " " + memory.status + ": " + short(memory.title, 18) + " (" + location + reason + ")",
|
|
15330
15763
|
maxTokens,
|
|
15331
15764
|
omitted,
|
|
15332
|
-
"caution-memory"
|
|
15765
|
+
"caution-memory",
|
|
15766
|
+
selected,
|
|
15767
|
+
{
|
|
15768
|
+
kind: "memory",
|
|
15769
|
+
id: "memory:" + memory.id,
|
|
15770
|
+
reason: "task-relevant memory requiring source verification",
|
|
15771
|
+
freshness: freshnessForMemory(memory.status),
|
|
15772
|
+
trust: "historical"
|
|
15773
|
+
}
|
|
15333
15774
|
);
|
|
15334
15775
|
}
|
|
15335
15776
|
if (input.hiddenCautionMemoryCount > 0) {
|
|
@@ -15340,13 +15781,22 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15340
15781
|
appendLine(lines, "", maxTokens, omitted, "facts-heading");
|
|
15341
15782
|
appendLine(lines, "Current project facts", maxTokens, omitted, "facts-heading");
|
|
15342
15783
|
for (const fact of input.currentFacts.slice(0, 4)) {
|
|
15343
|
-
appendLine(lines, "- " + short(fact, 40), maxTokens, omitted, "current-fact"
|
|
15784
|
+
appendLine(lines, "- " + short(fact, 40), maxTokens, omitted, "current-fact", selected, {
|
|
15785
|
+
kind: "current-fact",
|
|
15786
|
+
reason: "current project state",
|
|
15787
|
+
trust: "source-backed"
|
|
15788
|
+
});
|
|
15344
15789
|
}
|
|
15345
15790
|
}
|
|
15346
15791
|
if (input.codeState) {
|
|
15347
15792
|
appendLine(lines, "", maxTokens, omitted, "state-heading");
|
|
15348
15793
|
appendLine(lines, "Project state", maxTokens, omitted, "state-heading");
|
|
15349
|
-
appendLine(lines, input.codeState, maxTokens, omitted, "code-state"
|
|
15794
|
+
appendLine(lines, input.codeState, maxTokens, omitted, "code-state", selected, {
|
|
15795
|
+
kind: "code-state",
|
|
15796
|
+
...input.provenance.snapshotId ? { id: "snapshot:" + input.provenance.snapshotId } : {},
|
|
15797
|
+
reason: "latest available Code State snapshot",
|
|
15798
|
+
trust: "source-backed"
|
|
15799
|
+
});
|
|
15350
15800
|
}
|
|
15351
15801
|
if (input.semanticCode && (input.semanticCode.entryPoints.length > 0 || input.semanticCode.relations.length > 0)) {
|
|
15352
15802
|
appendLine(lines, "", maxTokens, omitted, "semantic-code-heading");
|
|
@@ -15358,7 +15808,14 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15358
15808
|
"- " + location + ": " + short(relation.from.name, 12) + " " + short(relation.kind, 8) + " " + short(relation.to.name, 12),
|
|
15359
15809
|
maxTokens,
|
|
15360
15810
|
omitted,
|
|
15361
|
-
"semantic-relation"
|
|
15811
|
+
"semantic-relation",
|
|
15812
|
+
selected,
|
|
15813
|
+
{
|
|
15814
|
+
kind: "semantic-code",
|
|
15815
|
+
id: "code:" + location,
|
|
15816
|
+
reason: "validated optional semantic code relation",
|
|
15817
|
+
trust: "derived"
|
|
15818
|
+
}
|
|
15362
15819
|
);
|
|
15363
15820
|
}
|
|
15364
15821
|
if (input.semanticCode.relations.length === 0) {
|
|
@@ -15369,7 +15826,14 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15369
15826
|
"- " + location + ": " + short(entry.name, 16) + " (" + short(entry.kind, 8) + ")",
|
|
15370
15827
|
maxTokens,
|
|
15371
15828
|
omitted,
|
|
15372
|
-
"semantic-entry"
|
|
15829
|
+
"semantic-entry",
|
|
15830
|
+
selected,
|
|
15831
|
+
{
|
|
15832
|
+
kind: "semantic-code",
|
|
15833
|
+
id: "code:" + location,
|
|
15834
|
+
reason: "validated optional semantic code entry point",
|
|
15835
|
+
trust: "derived"
|
|
15836
|
+
}
|
|
15373
15837
|
);
|
|
15374
15838
|
}
|
|
15375
15839
|
}
|
|
@@ -15378,7 +15842,12 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15378
15842
|
appendLine(lines, "", maxTokens, omitted, "start-heading");
|
|
15379
15843
|
appendLine(lines, "Start here", maxTokens, omitted, "start-heading");
|
|
15380
15844
|
for (const source of input.startHere.slice(0, 5)) {
|
|
15381
|
-
appendLine(lines, "- " + source, maxTokens, omitted, "start-here"
|
|
15845
|
+
appendLine(lines, "- " + source, maxTokens, omitted, "start-here", selected, {
|
|
15846
|
+
kind: "start-here",
|
|
15847
|
+
id: "path:" + source,
|
|
15848
|
+
reason: "task-lensed starting point",
|
|
15849
|
+
trust: "derived"
|
|
15850
|
+
});
|
|
15382
15851
|
}
|
|
15383
15852
|
}
|
|
15384
15853
|
if (input.reliableMemory.length > 0) {
|
|
@@ -15391,7 +15860,15 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15391
15860
|
"- #" + memory.id + " " + memory.type + ": " + short(memory.title, 18) + " (" + location + ")",
|
|
15392
15861
|
maxTokens,
|
|
15393
15862
|
omitted,
|
|
15394
|
-
"reliable-memory"
|
|
15863
|
+
"reliable-memory",
|
|
15864
|
+
selected,
|
|
15865
|
+
{
|
|
15866
|
+
kind: "memory",
|
|
15867
|
+
id: "memory:" + memory.id,
|
|
15868
|
+
reason: "current code-bound memory",
|
|
15869
|
+
freshness: freshnessForMemory(memory.status),
|
|
15870
|
+
trust: "historical"
|
|
15871
|
+
}
|
|
15395
15872
|
);
|
|
15396
15873
|
}
|
|
15397
15874
|
}
|
|
@@ -15399,10 +15876,25 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15399
15876
|
appendLine(lines, "", maxTokens, omitted, "knowledge-heading");
|
|
15400
15877
|
appendLine(lines, "Project knowledge", maxTokens, omitted, "knowledge-heading");
|
|
15401
15878
|
for (const claim of input.claims.slice(0, 3)) {
|
|
15402
|
-
appendLine(lines, "- " + claim.assertion + " [" + claim.id + "]", maxTokens, omitted, "claim"
|
|
15879
|
+
appendLine(lines, "- " + claim.assertion + " [" + claim.id + "]", maxTokens, omitted, "claim", selected, {
|
|
15880
|
+
kind: "claim",
|
|
15881
|
+
id: "claim:" + claim.id,
|
|
15882
|
+
reason: "source-qualified task match",
|
|
15883
|
+
trust: "source-backed"
|
|
15884
|
+
});
|
|
15403
15885
|
}
|
|
15404
15886
|
for (const page of input.pages.slice(0, 2)) {
|
|
15405
|
-
|
|
15887
|
+
const supportsDeliveredClaim = page.claimIds.some((claimId) => selected.some((item) => item.kind === "claim" && item.id === "claim:" + claimId));
|
|
15888
|
+
if (!supportsDeliveredClaim) {
|
|
15889
|
+
omitted.push("knowledge-page-dependency");
|
|
15890
|
+
continue;
|
|
15891
|
+
}
|
|
15892
|
+
appendLine(lines, "- page: " + page.relativePath, maxTokens, omitted, "knowledge-page", selected, {
|
|
15893
|
+
kind: "knowledge-page",
|
|
15894
|
+
id: "page:" + page.id,
|
|
15895
|
+
reason: "approved page linked to a selected claim",
|
|
15896
|
+
trust: "source-backed"
|
|
15897
|
+
});
|
|
15406
15898
|
}
|
|
15407
15899
|
}
|
|
15408
15900
|
if (input.workflows.length > 0) {
|
|
@@ -15414,7 +15906,14 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15414
15906
|
"- " + workflow.title + ": " + workflow.firstPhase.title + " - " + workflow.firstPhase.instructions,
|
|
15415
15907
|
maxTokens,
|
|
15416
15908
|
omitted,
|
|
15417
|
-
"workflow"
|
|
15909
|
+
"workflow",
|
|
15910
|
+
selected,
|
|
15911
|
+
{
|
|
15912
|
+
kind: "workflow",
|
|
15913
|
+
id: "workflow:" + workflow.id,
|
|
15914
|
+
reason: "task-matching project workflow",
|
|
15915
|
+
trust: "source-backed"
|
|
15916
|
+
}
|
|
15418
15917
|
);
|
|
15419
15918
|
}
|
|
15420
15919
|
}
|
|
@@ -15422,16 +15921,23 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15422
15921
|
appendLine(lines, "", maxTokens, omitted, "verification-heading");
|
|
15423
15922
|
appendLine(lines, "Verify", maxTokens, omitted, "verification-heading");
|
|
15424
15923
|
for (const check of input.verification.slice(0, 4)) {
|
|
15425
|
-
appendLine(lines, "- " + short(check, 20), maxTokens, omitted, "verification"
|
|
15924
|
+
appendLine(lines, "- " + short(check, 20), maxTokens, omitted, "verification", selected, {
|
|
15925
|
+
kind: "verification",
|
|
15926
|
+
reason: "task-lensed verification guidance",
|
|
15927
|
+
trust: "derived"
|
|
15928
|
+
});
|
|
15426
15929
|
}
|
|
15427
15930
|
}
|
|
15428
15931
|
return {
|
|
15429
15932
|
prompt: lines.join("\n"),
|
|
15430
15933
|
tokenCount: countTextTokens(lines.join("\n")),
|
|
15431
|
-
omitted: unique(omitted)
|
|
15934
|
+
omitted: unique(omitted),
|
|
15935
|
+
omittedItems: omitted,
|
|
15936
|
+
selected
|
|
15432
15937
|
};
|
|
15433
15938
|
}
|
|
15434
15939
|
async function buildTaskWorkset(input) {
|
|
15940
|
+
const startedAt = Date.now();
|
|
15435
15941
|
const task = input.task?.trim() ?? "";
|
|
15436
15942
|
const maxTokens = Math.max(96, Math.min(Math.floor(input.maxTokens ?? 180), 320));
|
|
15437
15943
|
const cautions = [...input.runtimeCautions ?? [], ...snapshotCautions(input)];
|
|
@@ -15533,6 +16039,18 @@ async function buildTaskWorkset(input) {
|
|
|
15533
16039
|
...base,
|
|
15534
16040
|
budget: { maxTokens }
|
|
15535
16041
|
});
|
|
16042
|
+
const receipt = {
|
|
16043
|
+
version: "1.2.2",
|
|
16044
|
+
target: input.deliveryTarget ?? "project-context",
|
|
16045
|
+
elapsedMs: Math.max(0, Date.now() - startedAt),
|
|
16046
|
+
budget: {
|
|
16047
|
+
maxTokens,
|
|
16048
|
+
tokenCount: rendered.tokenCount
|
|
16049
|
+
},
|
|
16050
|
+
selected: rendered.selected,
|
|
16051
|
+
omitted: receiptOmissions(rendered.omittedItems, base.hiddenCautionMemoryCount),
|
|
16052
|
+
scheduledActions: scheduledActions(normalizedCautions)
|
|
16053
|
+
};
|
|
15536
16054
|
return {
|
|
15537
16055
|
...base,
|
|
15538
16056
|
budget: {
|
|
@@ -15540,6 +16058,7 @@ async function buildTaskWorkset(input) {
|
|
|
15540
16058
|
tokenCount: rendered.tokenCount,
|
|
15541
16059
|
omitted: rendered.omitted
|
|
15542
16060
|
},
|
|
16061
|
+
receipt,
|
|
15543
16062
|
prompt: rendered.prompt
|
|
15544
16063
|
};
|
|
15545
16064
|
}
|
|
@@ -16317,6 +16836,9 @@ import path19 from "path";
|
|
|
16317
16836
|
function activeProjectObservations(observations2, projectId) {
|
|
16318
16837
|
return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
|
|
16319
16838
|
}
|
|
16839
|
+
function deliveryEligibleProjectObservations(observations2, projectId) {
|
|
16840
|
+
return activeProjectObservations(observations2, projectId).filter((observation) => isEligibleForAutomaticDelivery(observation));
|
|
16841
|
+
}
|
|
16320
16842
|
function decideRefresh(input) {
|
|
16321
16843
|
if (input.mode === "never") {
|
|
16322
16844
|
return { performed: false, reason: "disabled", message: "Automatic project scan disabled." };
|
|
@@ -16391,13 +16913,27 @@ async function buildAutoProjectContext(input) {
|
|
|
16391
16913
|
activeProjectObservations(input.observations, input.project.id)
|
|
16392
16914
|
);
|
|
16393
16915
|
try {
|
|
16394
|
-
const {
|
|
16916
|
+
const { MaintenanceTargetStore: MaintenanceTargetStore2 } = await Promise.resolve().then(() => (init_maintenance_targets(), maintenance_targets_exports));
|
|
16917
|
+
new MaintenanceTargetStore2(input.dataDir).register({
|
|
16918
|
+
projectId: input.project.id,
|
|
16919
|
+
projectRoot: input.project.rootPath,
|
|
16920
|
+
dataDir: input.dataDir
|
|
16921
|
+
});
|
|
16922
|
+
const {
|
|
16923
|
+
enqueueClaimRequalification: enqueueClaimRequalification2,
|
|
16924
|
+
enqueueObservationQualification: enqueueObservationQualification2
|
|
16925
|
+
} = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
|
|
16395
16926
|
enqueueClaimRequalification2({
|
|
16396
16927
|
dataDir: input.dataDir,
|
|
16397
16928
|
projectId: input.project.id,
|
|
16398
16929
|
source: "foreground-refresh",
|
|
16399
16930
|
snapshotId: store.latestSnapshot(input.project.id)?.id
|
|
16400
16931
|
});
|
|
16932
|
+
enqueueObservationQualification2({
|
|
16933
|
+
dataDir: input.dataDir,
|
|
16934
|
+
projectId: input.project.id,
|
|
16935
|
+
source: "foreground-refresh"
|
|
16936
|
+
});
|
|
16401
16937
|
} catch {
|
|
16402
16938
|
}
|
|
16403
16939
|
refresh = { ...refresh, backfill };
|
|
@@ -16414,7 +16950,9 @@ async function buildAutoProjectContext(input) {
|
|
|
16414
16950
|
const explain = buildProjectContextExplain({
|
|
16415
16951
|
project: input.project,
|
|
16416
16952
|
store,
|
|
16417
|
-
|
|
16953
|
+
// Binding sees all active evidence, but automatic delivery sees only
|
|
16954
|
+
// evidence that has passed the control-plane admission gate.
|
|
16955
|
+
observations: deliveryEligibleProjectObservations(input.observations, input.project.id),
|
|
16418
16956
|
exclude
|
|
16419
16957
|
});
|
|
16420
16958
|
const overview = explain.overview;
|
|
@@ -16505,7 +17043,8 @@ async function buildAutoProjectContext(input) {
|
|
|
16505
17043
|
suspect: overview.freshness.suspect,
|
|
16506
17044
|
stale: overview.freshness.stale
|
|
16507
17045
|
},
|
|
16508
|
-
runtimeCautions
|
|
17046
|
+
runtimeCautions,
|
|
17047
|
+
...input.deliveryTarget ? { deliveryTarget: input.deliveryTarget } : {}
|
|
16509
17048
|
});
|
|
16510
17049
|
return {
|
|
16511
17050
|
project: input.project,
|
|
@@ -16739,6 +17278,7 @@ var init_auto_context = __esm({
|
|
|
16739
17278
|
init_external_provider();
|
|
16740
17279
|
init_project_context();
|
|
16741
17280
|
init_store();
|
|
17281
|
+
init_admission();
|
|
16742
17282
|
init_task_lens();
|
|
16743
17283
|
DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
16744
17284
|
}
|
|
@@ -16931,7 +17471,8 @@ async function attachTaskWorkset(input) {
|
|
|
16931
17471
|
suspect: input.pack.warnings.filter((warning) => warning.status === "suspect").length,
|
|
16932
17472
|
stale: input.pack.warnings.filter((warning) => warning.status === "stale").length
|
|
16933
17473
|
},
|
|
16934
|
-
runtimeCautions: input.runtimeCautions
|
|
17474
|
+
runtimeCautions: input.runtimeCautions,
|
|
17475
|
+
deliveryTarget: "context-pack"
|
|
16935
17476
|
});
|
|
16936
17477
|
return { ...input.pack, workset };
|
|
16937
17478
|
}
|
|
@@ -18343,11 +18884,14 @@ __export(export_import_exports, {
|
|
|
18343
18884
|
exportAsMarkdown: () => exportAsMarkdown,
|
|
18344
18885
|
importFromJson: () => importFromJson
|
|
18345
18886
|
});
|
|
18346
|
-
async function exportAsJson(projectDir2, projectId) {
|
|
18887
|
+
async function exportAsJson(projectDir2, projectId, reader) {
|
|
18347
18888
|
const store = getObservationStore();
|
|
18348
18889
|
const allObs = await store.loadAll();
|
|
18349
18890
|
const allSessions = await getSessionStore().loadAll();
|
|
18350
|
-
const projectObs =
|
|
18891
|
+
const projectObs = filterReadableObservations(
|
|
18892
|
+
allObs.filter((o) => o.projectId === projectId),
|
|
18893
|
+
reader
|
|
18894
|
+
);
|
|
18351
18895
|
const projectSessions = allSessions.filter((s) => s.projectId === projectId);
|
|
18352
18896
|
const typeBreakdown = {};
|
|
18353
18897
|
for (const obs of projectObs) {
|
|
@@ -18366,8 +18910,8 @@ async function exportAsJson(projectDir2, projectId) {
|
|
|
18366
18910
|
}
|
|
18367
18911
|
};
|
|
18368
18912
|
}
|
|
18369
|
-
async function exportAsMarkdown(projectDir2, projectId) {
|
|
18370
|
-
const data = await exportAsJson(projectDir2, projectId);
|
|
18913
|
+
async function exportAsMarkdown(projectDir2, projectId, reader) {
|
|
18914
|
+
const data = await exportAsJson(projectDir2, projectId, reader);
|
|
18371
18915
|
const lines = [];
|
|
18372
18916
|
lines.push(`# Memorix Export: ${projectId}`);
|
|
18373
18917
|
lines.push(`Exported: ${data.exportedAt}`);
|
|
@@ -18469,6 +19013,7 @@ var init_export_import = __esm({
|
|
|
18469
19013
|
"src/memory/export-import.ts"() {
|
|
18470
19014
|
"use strict";
|
|
18471
19015
|
init_esm_shims();
|
|
19016
|
+
init_visibility();
|
|
18472
19017
|
init_obs_store();
|
|
18473
19018
|
init_session_store();
|
|
18474
19019
|
OBSERVATION_ICONS2 = {
|
|
@@ -18645,6 +19190,7 @@ function isEligible(o, projectId) {
|
|
|
18645
19190
|
if (isCommandLog2(o)) return false;
|
|
18646
19191
|
if (isInactive(o)) return false;
|
|
18647
19192
|
if (isOtherProject(o, projectId)) return false;
|
|
19193
|
+
if (!isEligibleForKnowledgePromotion(o)) return false;
|
|
18648
19194
|
if (o.valueCategory === "ephemeral") return false;
|
|
18649
19195
|
if (!contextualHasSubstance(o)) return false;
|
|
18650
19196
|
return true;
|
|
@@ -18793,6 +19339,7 @@ var init_generator = __esm({
|
|
|
18793
19339
|
"src/wiki/generator.ts"() {
|
|
18794
19340
|
"use strict";
|
|
18795
19341
|
init_esm_shims();
|
|
19342
|
+
init_admission();
|
|
18796
19343
|
COMMAND_LOG_TITLE4 = /^(Ran:|Command:|Executed:)\s/i;
|
|
18797
19344
|
SECTION_DEFS = [
|
|
18798
19345
|
{
|
|
@@ -19079,6 +19626,9 @@ function sendError(res, message, status = 500) {
|
|
|
19079
19626
|
function filterByProject(items, projectId) {
|
|
19080
19627
|
return items.filter((item) => item.projectId === projectId);
|
|
19081
19628
|
}
|
|
19629
|
+
function filterDashboardObservations(observations2, projectId) {
|
|
19630
|
+
return filterReadableObservations(observations2, { projectId });
|
|
19631
|
+
}
|
|
19082
19632
|
function isActiveStatus(status) {
|
|
19083
19633
|
return (status ?? "active") === "active";
|
|
19084
19634
|
}
|
|
@@ -19126,7 +19676,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19126
19676
|
switch (apiPath) {
|
|
19127
19677
|
case "/projects": {
|
|
19128
19678
|
try {
|
|
19129
|
-
const allObs =
|
|
19679
|
+
const allObs = filterReadableObservations(
|
|
19680
|
+
await getObservationStore().loadAll(),
|
|
19681
|
+
{}
|
|
19682
|
+
);
|
|
19130
19683
|
const projectSet = /* @__PURE__ */ new Map();
|
|
19131
19684
|
for (const obs of allObs) {
|
|
19132
19685
|
if (!isActiveStatus(obs.status)) continue;
|
|
@@ -19175,13 +19728,19 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19175
19728
|
await initGraphStore(effectiveDataDir);
|
|
19176
19729
|
const gStore = getGraphStore();
|
|
19177
19730
|
const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
19178
|
-
const graphObs =
|
|
19731
|
+
const graphObs = filterDashboardObservations(
|
|
19732
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19733
|
+
effectiveProjectId
|
|
19734
|
+
);
|
|
19179
19735
|
const scoped = scopeKnowledgeGraphToProject(graph, graphObs);
|
|
19180
19736
|
sendJson(res, { entities: scoped.entities, relations: scoped.relations });
|
|
19181
19737
|
break;
|
|
19182
19738
|
}
|
|
19183
19739
|
case "/observations": {
|
|
19184
|
-
const observations2 =
|
|
19740
|
+
const observations2 = filterDashboardObservations(
|
|
19741
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19742
|
+
effectiveProjectId
|
|
19743
|
+
);
|
|
19185
19744
|
sendJson(res, observations2);
|
|
19186
19745
|
break;
|
|
19187
19746
|
}
|
|
@@ -19194,7 +19753,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19194
19753
|
case "/stats": {
|
|
19195
19754
|
await initGraphStore(effectiveDataDir);
|
|
19196
19755
|
const graph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
|
|
19197
|
-
const observations2 =
|
|
19756
|
+
const observations2 = filterDashboardObservations(
|
|
19757
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19758
|
+
effectiveProjectId
|
|
19759
|
+
);
|
|
19198
19760
|
const nextId2 = await getObservationStore().loadIdCounter();
|
|
19199
19761
|
const projectGraphCounts = computeProjectGraphCounts(graph.entities, graph.relations, observations2);
|
|
19200
19762
|
const typeCounts = {};
|
|
@@ -19312,7 +19874,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19312
19874
|
break;
|
|
19313
19875
|
}
|
|
19314
19876
|
case "/retention": {
|
|
19315
|
-
const observations2 =
|
|
19877
|
+
const observations2 = filterDashboardObservations(
|
|
19878
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19879
|
+
effectiveProjectId
|
|
19880
|
+
);
|
|
19316
19881
|
const now3 = Date.now();
|
|
19317
19882
|
const scored = observations2.filter((obs) => obs.type !== "probe").map((obs) => {
|
|
19318
19883
|
const age = now3 - new Date(obs.createdAt || now3).getTime();
|
|
@@ -19350,7 +19915,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19350
19915
|
const { generateKnowledgeBase: generateKnowledgeBase2 } = await Promise.resolve().then(() => (init_generator(), generator_exports));
|
|
19351
19916
|
const { initMiniSkillStore: initMiniSkillStore2, getMiniSkillStore: getMiniSkillStore2 } = await Promise.resolve().then(() => (init_mini_skill_store(), mini_skill_store_exports));
|
|
19352
19917
|
await initMiniSkillStore2(effectiveDataDir);
|
|
19353
|
-
const allObs =
|
|
19918
|
+
const allObs = filterDashboardObservations(
|
|
19919
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19920
|
+
effectiveProjectId
|
|
19921
|
+
);
|
|
19354
19922
|
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
19355
19923
|
const overview = generateKnowledgeBase2({
|
|
19356
19924
|
projectId: effectiveProjectId,
|
|
@@ -19366,7 +19934,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19366
19934
|
const { initGraphStore: initGraphStore2, getGraphStore: getGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
19367
19935
|
await initMiniSkillStore2(effectiveDataDir);
|
|
19368
19936
|
await initGraphStore2(effectiveDataDir);
|
|
19369
|
-
const allObs =
|
|
19937
|
+
const allObs = filterDashboardObservations(
|
|
19938
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19939
|
+
effectiveProjectId
|
|
19940
|
+
);
|
|
19370
19941
|
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
19371
19942
|
const fullGraph = { entities: getGraphStore2().loadEntities(), relations: getGraphStore2().loadRelations() };
|
|
19372
19943
|
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
@@ -19489,7 +20060,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19489
20060
|
break;
|
|
19490
20061
|
}
|
|
19491
20062
|
case "/identity": {
|
|
19492
|
-
const allObs = await getObservationStore().loadAll();
|
|
20063
|
+
const allObs = filterReadableObservations(await getObservationStore().loadAll(), {});
|
|
19493
20064
|
const allProjectIds = [...new Set(allObs.map((o) => o.projectId).filter(Boolean))];
|
|
19494
20065
|
let classifyProjectId2 = () => "real";
|
|
19495
20066
|
let isDirtyProjectId2 = () => false;
|
|
@@ -19574,6 +20145,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19574
20145
|
sendError(res, "Observation not found", 404);
|
|
19575
20146
|
} else if (matchObs.projectId !== effectiveProjectId) {
|
|
19576
20147
|
sendError(res, `Observation #${obsId} belongs to project "${matchObs.projectId}", not "${effectiveProjectId}"`, 403);
|
|
20148
|
+
} else if (!canManageObservation(matchObs, { projectId: effectiveProjectId })) {
|
|
20149
|
+
sendError(res, `Observation #${obsId} is not manageable from the unbound dashboard.`, 403);
|
|
19577
20150
|
} else {
|
|
19578
20151
|
await obsStore.remove(obsId);
|
|
19579
20152
|
try {
|
|
@@ -19595,7 +20168,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19595
20168
|
if (apiPath === "/export") {
|
|
19596
20169
|
await initGraphStore(effectiveDataDir);
|
|
19597
20170
|
const fullGraph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
|
|
19598
|
-
const observations2 =
|
|
20171
|
+
const observations2 = filterDashboardObservations(
|
|
20172
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
20173
|
+
effectiveProjectId
|
|
20174
|
+
);
|
|
19599
20175
|
const nextId2 = await getObservationStore().loadIdCounter();
|
|
19600
20176
|
const scoped = scopeKnowledgeGraphToProject(fullGraph, observations2);
|
|
19601
20177
|
const exportData = {
|
|
@@ -19919,6 +20495,7 @@ var init_server = __esm({
|
|
|
19919
20495
|
init_yaml_loader();
|
|
19920
20496
|
init_yaml_loader();
|
|
19921
20497
|
init_graph_scope();
|
|
20498
|
+
init_visibility();
|
|
19922
20499
|
MIME_TYPES = {
|
|
19923
20500
|
".html": "text/html; charset=utf-8",
|
|
19924
20501
|
".css": "text/css; charset=utf-8",
|
|
@@ -20696,6 +21273,11 @@ async function createHandoffArtifact(input, storeObservation2, teamStore) {
|
|
|
20696
21273
|
sourceDetail: "explicit",
|
|
20697
21274
|
valueCategory: "core",
|
|
20698
21275
|
// immune to retention archival
|
|
21276
|
+
// A direct handoff is not a project-wide memo. The sender retains access
|
|
21277
|
+
// and the explicit recipient receives a read grant. Broadcast handoffs
|
|
21278
|
+
// are deliberately visible only to active team members.
|
|
21279
|
+
visibility: input.toAgentId ? "personal" : "team",
|
|
21280
|
+
...input.toAgentId ? { sharedWithAgentIds: [input.toAgentId] } : {},
|
|
20699
21281
|
createdByAgentId: input.fromAgentId
|
|
20700
21282
|
});
|
|
20701
21283
|
try {
|
|
@@ -22695,6 +23277,7 @@ async function createAutoRelations(obs, extracted, graphManager) {
|
|
|
22695
23277
|
// src/server.ts
|
|
22696
23278
|
init_entity_extractor();
|
|
22697
23279
|
init_graph_scope();
|
|
23280
|
+
init_visibility();
|
|
22698
23281
|
|
|
22699
23282
|
// src/compact/engine.ts
|
|
22700
23283
|
init_esm_shims();
|
|
@@ -22820,7 +23403,15 @@ function formatTimeline(timeline) {
|
|
|
22820
23403
|
function formatObservationDetail(doc) {
|
|
22821
23404
|
const icon = getTypeIcon(doc.type);
|
|
22822
23405
|
const lines = [];
|
|
22823
|
-
const header = buildProvenanceHeader(
|
|
23406
|
+
const header = buildProvenanceHeader(
|
|
23407
|
+
doc.sourceDetail,
|
|
23408
|
+
doc.valueCategory,
|
|
23409
|
+
doc.admissionState,
|
|
23410
|
+
doc.admissionReason,
|
|
23411
|
+
doc.source,
|
|
23412
|
+
doc.commitHash,
|
|
23413
|
+
doc.relatedCommits
|
|
23414
|
+
);
|
|
22824
23415
|
if (header) {
|
|
22825
23416
|
lines.push(header);
|
|
22826
23417
|
lines.push("");
|
|
@@ -22855,7 +23446,7 @@ function formatObservationDetail(doc) {
|
|
|
22855
23446
|
}
|
|
22856
23447
|
return lines.join("\n");
|
|
22857
23448
|
}
|
|
22858
|
-
function buildProvenanceHeader(sourceDetail, valueCategory, source, commitHash, relatedCommits) {
|
|
23449
|
+
function buildProvenanceHeader(sourceDetail, valueCategory, admissionState, admissionReason, source, commitHash, relatedCommits) {
|
|
22859
23450
|
const sd = resolveSourceDetail(sourceDetail, source);
|
|
22860
23451
|
if (!sd) return "";
|
|
22861
23452
|
const label = sourceKindLabel(sd);
|
|
@@ -22866,11 +23457,21 @@ function buildProvenanceHeader(sourceDetail, valueCategory, source, commitHash,
|
|
|
22866
23457
|
if (verificationLine) {
|
|
22867
23458
|
lines.push(verificationLine);
|
|
22868
23459
|
}
|
|
22869
|
-
if (valueCategory === "core") {
|
|
23460
|
+
if (valueCategory === "core" && admissionState !== "candidate" && admissionState !== "ephemeral") {
|
|
22870
23461
|
lines.push("[CORE] Core \u2014 immune to decay");
|
|
23462
|
+
} else if (valueCategory === "core") {
|
|
23463
|
+
lines.push("[CORE] Core candidate \u2014 not durable until qualification completes");
|
|
22871
23464
|
} else if (valueCategory === "ephemeral") {
|
|
22872
23465
|
lines.push("[WARN] Ephemeral \u2014 short-lived signal");
|
|
22873
23466
|
}
|
|
23467
|
+
if (admissionState === "candidate") {
|
|
23468
|
+
lines.push("[PENDING] Candidate \u2014 excluded from automatic context until current Code Memory qualifies it");
|
|
23469
|
+
} else if (admissionState === "ephemeral") {
|
|
23470
|
+
lines.push("[TRACE] Ephemeral capture \u2014 excluded from automatic context");
|
|
23471
|
+
} else if (admissionState === "qualified") {
|
|
23472
|
+
lines.push("[QUALIFIED] Automatic evidence passed current Code Memory qualification");
|
|
23473
|
+
}
|
|
23474
|
+
if (admissionReason) lines.push(`Admission: ${redactCredentials(admissionReason)}`);
|
|
22874
23475
|
return lines.join("\n");
|
|
22875
23476
|
}
|
|
22876
23477
|
function sourceKindLabel(sd) {
|
|
@@ -22951,6 +23552,7 @@ init_obs_store();
|
|
|
22951
23552
|
init_mini_skill_store();
|
|
22952
23553
|
init_mini_skills();
|
|
22953
23554
|
init_secret_filter();
|
|
23555
|
+
init_visibility();
|
|
22954
23556
|
function normalizeMemoryBrowseQuery(query) {
|
|
22955
23557
|
const trimmed = query.trim();
|
|
22956
23558
|
if (!trimmed) return "";
|
|
@@ -22993,11 +23595,13 @@ async function compactSearch(options) {
|
|
|
22993
23595
|
);
|
|
22994
23596
|
let formatted = formatIndexTable(entries, searchOptions.query, !options.projectId);
|
|
22995
23597
|
if (entries.length === 0 && options.projectId) {
|
|
22996
|
-
const allObservations = getAllObservations();
|
|
23598
|
+
const allObservations = filterReadableObservations(getAllObservations(), options.reader);
|
|
22997
23599
|
const projectAliases = new Set(await resolveAliases(options.projectId).catch(() => [options.projectId]));
|
|
22998
23600
|
const projectHasStoredMemory = allObservations.some((obs) => obs.projectId && projectAliases.has(obs.projectId));
|
|
22999
23601
|
if (!projectHasStoredMemory) {
|
|
23000
|
-
formatted = `
|
|
23602
|
+
formatted = options.reader ? `No Memorix memories are available to this session yet.
|
|
23603
|
+
|
|
23604
|
+
The tool call worked, but this session has no project-visible memory to retrieve.` : `This project does not have any Memorix memories yet.
|
|
23001
23605
|
|
|
23002
23606
|
It looks like a fresh project: the tool call worked, but there is nothing stored to retrieve yet.
|
|
23003
23607
|
|
|
@@ -23011,8 +23615,8 @@ This project does have stored Memorix memories, but none matched the current que
|
|
|
23011
23615
|
const totalTokens = countTextTokens(formatted);
|
|
23012
23616
|
return { entries, formatted, totalTokens };
|
|
23013
23617
|
}
|
|
23014
|
-
async function compactTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3) {
|
|
23015
|
-
const result = await getTimeline(anchorId, projectId, depthBefore, depthAfter);
|
|
23618
|
+
async function compactTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3, reader) {
|
|
23619
|
+
const result = await getTimeline(anchorId, projectId, depthBefore, depthAfter, reader);
|
|
23016
23620
|
const timeline = {
|
|
23017
23621
|
anchorId,
|
|
23018
23622
|
anchorEntry: result.anchor,
|
|
@@ -23023,7 +23627,7 @@ async function compactTimeline(anchorId, projectId, depthBefore = 3, depthAfter
|
|
|
23023
23627
|
const totalTokens = countTextTokens(formatted);
|
|
23024
23628
|
return { timeline, formatted, totalTokens };
|
|
23025
23629
|
}
|
|
23026
|
-
async function compactDetail(idsOrRefs) {
|
|
23630
|
+
async function compactDetail(idsOrRefs, options = {}) {
|
|
23027
23631
|
const invalidRefs = [];
|
|
23028
23632
|
const parsedRefs = [];
|
|
23029
23633
|
for (let i = 0; i < idsOrRefs.length; i++) {
|
|
@@ -23080,7 +23684,7 @@ async function compactDetail(idsOrRefs) {
|
|
|
23080
23684
|
const missingRefs = [];
|
|
23081
23685
|
for (const ref of refs) {
|
|
23082
23686
|
const obs = getObservation(ref.id, ref.projectId);
|
|
23083
|
-
if (obs && (ref.projectId ? obs.projectId === ref.projectId : true)) {
|
|
23687
|
+
if (obs && (ref.projectId ? obs.projectId === ref.projectId : true) && canReadObservation(obs, options.reader)) {
|
|
23084
23688
|
documentMap.set(toRefKey(ref), {
|
|
23085
23689
|
id: makeOramaObservationId(obs.projectId, obs.id),
|
|
23086
23690
|
observationId: obs.id,
|
|
@@ -23099,7 +23703,12 @@ async function compactDetail(idsOrRefs) {
|
|
|
23099
23703
|
status: obs.status ?? "active",
|
|
23100
23704
|
source: obs.source ?? "agent",
|
|
23101
23705
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23102
|
-
valueCategory: obs.valueCategory ?? ""
|
|
23706
|
+
valueCategory: obs.valueCategory ?? "",
|
|
23707
|
+
admissionState: obs.admissionState ?? "",
|
|
23708
|
+
admissionReason: obs.admissionReason ?? "",
|
|
23709
|
+
visibility: obs.visibility ?? "project",
|
|
23710
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
23711
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
23103
23712
|
});
|
|
23104
23713
|
} else {
|
|
23105
23714
|
missingRefs.push(ref);
|
|
@@ -23109,17 +23718,17 @@ async function compactDetail(idsOrRefs) {
|
|
|
23109
23718
|
for (const ref of missingRefs) {
|
|
23110
23719
|
const fallbackDocs = await getObservationsByIds([ref.id], ref.projectId);
|
|
23111
23720
|
const doc = fallbackDocs[0];
|
|
23112
|
-
if (doc) {
|
|
23721
|
+
if (doc && canReadObservation(doc, options.reader)) {
|
|
23113
23722
|
documentMap.set(toRefKey(ref), doc);
|
|
23114
23723
|
continue;
|
|
23115
23724
|
}
|
|
23116
23725
|
const stored = await getPersistedObservation(ref);
|
|
23117
|
-
if (stored) {
|
|
23726
|
+
if (stored && canReadObservation(stored, options.reader)) {
|
|
23118
23727
|
documentMap.set(toRefKey(ref), observationToDocument(stored));
|
|
23119
23728
|
}
|
|
23120
23729
|
}
|
|
23121
23730
|
}
|
|
23122
|
-
const allObs = getAllObservations();
|
|
23731
|
+
const allObs = filterReadableObservations(getAllObservations(), options.reader);
|
|
23123
23732
|
const crossRefMap = /* @__PURE__ */ new Map();
|
|
23124
23733
|
for (const ref of refs) {
|
|
23125
23734
|
const obs = getObservation(ref.id, ref.projectId);
|
|
@@ -23233,7 +23842,12 @@ function observationToDocument(obs) {
|
|
|
23233
23842
|
status: obs.status ?? "active",
|
|
23234
23843
|
source: obs.source ?? "agent",
|
|
23235
23844
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23236
|
-
valueCategory: obs.valueCategory ?? ""
|
|
23845
|
+
valueCategory: obs.valueCategory ?? "",
|
|
23846
|
+
admissionState: obs.admissionState ?? "",
|
|
23847
|
+
admissionReason: obs.admissionReason ?? "",
|
|
23848
|
+
visibility: obs.visibility ?? "project",
|
|
23849
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
23850
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
23237
23851
|
};
|
|
23238
23852
|
}
|
|
23239
23853
|
function formatMiniSkillDetail(skill2, provenanceStatus) {
|
|
@@ -23352,7 +23966,9 @@ function auditMemoryQuality(observations2, options) {
|
|
|
23352
23966
|
status: obs.status ?? "active",
|
|
23353
23967
|
source: obs.source ?? "agent",
|
|
23354
23968
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23355
|
-
valueCategory: obs.valueCategory ?? ""
|
|
23969
|
+
valueCategory: obs.valueCategory ?? "",
|
|
23970
|
+
admissionState: obs.admissionState ?? "",
|
|
23971
|
+
admissionReason: obs.admissionReason ?? ""
|
|
23356
23972
|
}, options.referenceTime) !== "active").map((obs) => makeEntry(obs, "Outside active retention zone"));
|
|
23357
23973
|
const summary = {
|
|
23358
23974
|
total: scoped.length,
|
|
@@ -23394,6 +24010,7 @@ function auditMemoryQuality(observations2, options) {
|
|
|
23394
24010
|
|
|
23395
24011
|
// src/memory/graph-context.ts
|
|
23396
24012
|
init_retention();
|
|
24013
|
+
init_admission();
|
|
23397
24014
|
function formatGraphContextPrompt(packet) {
|
|
23398
24015
|
const lines = [
|
|
23399
24016
|
"## Memory Context Packet",
|
|
@@ -23551,7 +24168,9 @@ function scoreForPacket(obs, queryTokens, referenceTime, broadMemoryQuery) {
|
|
|
23551
24168
|
status: obs.status ?? "active",
|
|
23552
24169
|
source: obs.source ?? "agent",
|
|
23553
24170
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23554
|
-
valueCategory: obs.valueCategory ?? ""
|
|
24171
|
+
valueCategory: obs.valueCategory ?? "",
|
|
24172
|
+
admissionState: obs.admissionState ?? "",
|
|
24173
|
+
admissionReason: obs.admissionReason ?? ""
|
|
23555
24174
|
}, referenceTime) !== "active") {
|
|
23556
24175
|
score -= 2;
|
|
23557
24176
|
}
|
|
@@ -23645,8 +24264,11 @@ function buildGraphContextPacket(observations2, options) {
|
|
|
23645
24264
|
...audit.issues.orphans.map((entry) => entry.id),
|
|
23646
24265
|
...audit.issues.retentionCandidates.map((entry) => entry.id)
|
|
23647
24266
|
]);
|
|
23648
|
-
const
|
|
23649
|
-
|
|
24267
|
+
const deliveryEligible = observations2.filter(
|
|
24268
|
+
(obs) => obs.projectId === options.projectId && isEligibleForAutomaticDelivery(obs)
|
|
24269
|
+
);
|
|
24270
|
+
const filteredObservations = deliveryEligible.filter((obs) => !riskIds.has(obs.id));
|
|
24271
|
+
const baseObservations = filteredObservations.length > 0 ? filteredObservations : deliveryEligible;
|
|
23650
24272
|
const memories = pickMemories(baseObservations, options.projectId, options.query, options.limit ?? 5, referenceTime);
|
|
23651
24273
|
const entities = buildEntities(memories);
|
|
23652
24274
|
const entityNames = new Set(entities.map((entity) => entity.name));
|
|
@@ -26133,6 +26755,8 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
26133
26755
|
let projectResolutionError = null;
|
|
26134
26756
|
let explicitProjectBound = false;
|
|
26135
26757
|
let currentAgentId;
|
|
26758
|
+
let teamStore;
|
|
26759
|
+
let initTeamStoreForProject;
|
|
26136
26760
|
if (detectedProject) {
|
|
26137
26761
|
rawProject = detectedProject;
|
|
26138
26762
|
} else {
|
|
@@ -26380,7 +27004,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
26380
27004
|
};
|
|
26381
27005
|
const server = existingServer ?? new McpServer({
|
|
26382
27006
|
name: "memorix",
|
|
26383
|
-
version: true ? "1.2.
|
|
27007
|
+
version: true ? "1.2.3" : "1.0.1"
|
|
26384
27008
|
});
|
|
26385
27009
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
26386
27010
|
server.registerTool = ((name, ...args) => {
|
|
@@ -26400,11 +27024,26 @@ The path should point to a directory containing a .git folder.`
|
|
|
26400
27024
|
}
|
|
26401
27025
|
return originalRegisterTool(name, ...args);
|
|
26402
27026
|
});
|
|
27027
|
+
const getObservationReader = (scope = "project") => {
|
|
27028
|
+
let isTeamMember = false;
|
|
27029
|
+
if (teamFeaturesEnabled && currentAgentId) {
|
|
27030
|
+
try {
|
|
27031
|
+
const agent = teamStore.getAgent(currentAgentId);
|
|
27032
|
+
isTeamMember = agent?.project_id === project.id && agent.status === "active";
|
|
27033
|
+
} catch {
|
|
27034
|
+
}
|
|
27035
|
+
}
|
|
27036
|
+
return {
|
|
27037
|
+
...scope === "project" ? { projectId: project.id } : {},
|
|
27038
|
+
...currentAgentId ? { agentId: currentAgentId } : {},
|
|
27039
|
+
isTeamMember
|
|
27040
|
+
};
|
|
27041
|
+
};
|
|
26403
27042
|
server.registerTool(
|
|
26404
27043
|
"memorix_store",
|
|
26405
27044
|
{
|
|
26406
27045
|
title: "Store Memory",
|
|
26407
|
-
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).
|
|
27046
|
+
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.",
|
|
26408
27047
|
inputSchema: {
|
|
26409
27048
|
entityName: z2.string().describe('The entity this observation belongs to (e.g., "auth-module", "port-config")'),
|
|
26410
27049
|
type: z2.enum(OBSERVATION_TYPES).describe("Observation type for classification"),
|
|
@@ -26422,361 +27061,214 @@ The path should point to a directory containing a .git folder.`
|
|
|
26422
27061
|
completion: z2.number().optional().describe("Completion percentage 0-100")
|
|
26423
27062
|
}).optional().describe("Progress tracking for task/feature observations"),
|
|
26424
27063
|
relatedCommits: z2.array(z2.string()).optional().describe("Git commit hashes this memory relates to (links ground truth \u2194 reasoning)"),
|
|
26425
|
-
relatedEntities: z2.array(z2.string()).optional().describe("Other entity names this memory cross-references")
|
|
27064
|
+
relatedEntities: z2.array(z2.string()).optional().describe("Other entity names this memory cross-references"),
|
|
27065
|
+
visibility: z2.enum(["personal", "project", "team"]).optional().describe(
|
|
27066
|
+
"Retrieval scope. Project is the normal shared default; personal/team require memorix_session_start with joinTeam=true."
|
|
27067
|
+
)
|
|
26426
27068
|
}
|
|
26427
27069
|
},
|
|
26428
|
-
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities }) => {
|
|
27070
|
+
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility }) => {
|
|
26429
27071
|
const unresolved = requireResolvedProject("store memory in the current project");
|
|
26430
27072
|
if (unresolved) return unresolved;
|
|
26431
|
-
|
|
26432
|
-
|
|
26433
|
-
|
|
26434
|
-
|
|
26435
|
-
|
|
26436
|
-
|
|
26437
|
-
|
|
26438
|
-
|
|
26439
|
-
|
|
26440
|
-
|
|
26441
|
-
|
|
26442
|
-
|
|
26443
|
-
|
|
26444
|
-
|
|
26445
|
-
|
|
27073
|
+
const requestedVisibility = visibility ?? "project";
|
|
27074
|
+
const reader = getObservationReader();
|
|
27075
|
+
if (requestedVisibility !== "project" && !currentAgentId) {
|
|
27076
|
+
return {
|
|
27077
|
+
content: [{ type: "text", text: "Personal or team memory requires memorix_session_start with joinTeam=true so Memorix can bind an owner." }],
|
|
27078
|
+
isError: true
|
|
27079
|
+
};
|
|
27080
|
+
}
|
|
27081
|
+
if (requestedVisibility === "team" && !reader.isTeamMember) {
|
|
27082
|
+
return {
|
|
27083
|
+
content: [{ type: "text", text: "Team memory requires an active coordination membership in the current project." }],
|
|
27084
|
+
isError: true
|
|
27085
|
+
};
|
|
27086
|
+
}
|
|
27087
|
+
try {
|
|
27088
|
+
return await withFreshIndex(async () => {
|
|
27089
|
+
let entityName = rawEntityName;
|
|
27090
|
+
let type = rawType;
|
|
27091
|
+
let title = rawTitle;
|
|
27092
|
+
let safeFacts = facts ? coerceStringArray(facts) : void 0;
|
|
27093
|
+
const safeFiles = filesModified ? coerceStringArray(filesModified) : void 0;
|
|
27094
|
+
const safeConcepts = concepts ? coerceStringArray(concepts) : void 0;
|
|
27095
|
+
let formationMode = "active";
|
|
27096
|
+
if (process.env.MEMORIX_FORMATION_MODE) {
|
|
27097
|
+
formationMode = process.env.MEMORIX_FORMATION_MODE;
|
|
27098
|
+
} else {
|
|
27099
|
+
try {
|
|
27100
|
+
const { getBehaviorConfig: getBehaviorConfig2 } = await Promise.resolve().then(() => (init_behavior(), behavior_exports));
|
|
27101
|
+
formationMode = getBehaviorConfig2({ projectRoot: project.rootPath }).formationMode;
|
|
27102
|
+
} catch {
|
|
27103
|
+
}
|
|
26446
27104
|
}
|
|
26447
|
-
|
|
26448
|
-
|
|
26449
|
-
|
|
26450
|
-
|
|
26451
|
-
|
|
26452
|
-
|
|
26453
|
-
|
|
26454
|
-
|
|
26455
|
-
|
|
26456
|
-
|
|
27105
|
+
const useFormation = formationMode === "active";
|
|
27106
|
+
let formationResult = null;
|
|
27107
|
+
let formationNote = "";
|
|
27108
|
+
if (useFormation && !topicKey && !progress) {
|
|
27109
|
+
let currentFormationStage = "setup";
|
|
27110
|
+
const completedFormationStages = {};
|
|
27111
|
+
const formationStartTime = Date.now();
|
|
27112
|
+
const onFormationStageEvent = (event) => {
|
|
27113
|
+
if (event.status === "start") {
|
|
27114
|
+
currentFormationStage = event.stage;
|
|
27115
|
+
return;
|
|
27116
|
+
}
|
|
26457
27117
|
currentFormationStage = event.stage;
|
|
26458
|
-
|
|
26459
|
-
|
|
26460
|
-
|
|
26461
|
-
if (event.stageDurationMs !== void 0) {
|
|
26462
|
-
completedFormationStages[event.stage] = event.stageDurationMs;
|
|
26463
|
-
}
|
|
26464
|
-
};
|
|
26465
|
-
try {
|
|
26466
|
-
const formationConfig = {
|
|
26467
|
-
mode: "active",
|
|
26468
|
-
useLLM: isLLMEnabled(),
|
|
26469
|
-
minValueScore: 0.3,
|
|
26470
|
-
searchMemories: async (q, limit, pid) => {
|
|
26471
|
-
const result = await compactSearch({ query: q, limit, projectId: pid, status: "active" });
|
|
26472
|
-
if (result.entries.length === 0) return [];
|
|
26473
|
-
const details = await compactDetail(result.entries.map((e) => e.id));
|
|
26474
|
-
return details.documents.map((d, i) => ({
|
|
26475
|
-
id: Number(d.id.replace("obs-", "")),
|
|
26476
|
-
observationId: d.observationId,
|
|
26477
|
-
title: d.title,
|
|
26478
|
-
narrative: d.narrative,
|
|
26479
|
-
facts: d.facts,
|
|
26480
|
-
entityName: d.entityName,
|
|
26481
|
-
type: d.type,
|
|
26482
|
-
score: result.entries[i]?.score ?? 0
|
|
26483
|
-
}));
|
|
26484
|
-
},
|
|
26485
|
-
getObservation: (id) => {
|
|
26486
|
-
const o = getObservation(id);
|
|
26487
|
-
if (!o) return null;
|
|
26488
|
-
return {
|
|
26489
|
-
id: o.id,
|
|
26490
|
-
entityName: o.entityName,
|
|
26491
|
-
type: o.type,
|
|
26492
|
-
title: o.title,
|
|
26493
|
-
narrative: o.narrative,
|
|
26494
|
-
facts: o.facts,
|
|
26495
|
-
topicKey: o.topicKey
|
|
26496
|
-
};
|
|
26497
|
-
},
|
|
26498
|
-
getEntityNames: () => graphManager.getEntityNames(),
|
|
26499
|
-
onStageEvent: onFormationStageEvent
|
|
27118
|
+
if (event.stageDurationMs !== void 0) {
|
|
27119
|
+
completedFormationStages[event.stage] = event.stageDurationMs;
|
|
27120
|
+
}
|
|
26500
27121
|
};
|
|
26501
|
-
|
|
26502
|
-
|
|
26503
|
-
|
|
26504
|
-
|
|
26505
|
-
|
|
26506
|
-
|
|
26507
|
-
|
|
26508
|
-
|
|
26509
|
-
|
|
26510
|
-
|
|
26511
|
-
|
|
26512
|
-
|
|
26513
|
-
|
|
26514
|
-
|
|
26515
|
-
|
|
27122
|
+
try {
|
|
27123
|
+
const formationConfig = {
|
|
27124
|
+
mode: "active",
|
|
27125
|
+
useLLM: isLLMEnabled(),
|
|
27126
|
+
minValueScore: 0.3,
|
|
27127
|
+
searchMemories: async (q, limit, pid) => {
|
|
27128
|
+
const result = await compactSearch({ query: q, limit, projectId: pid, status: "active", reader });
|
|
27129
|
+
if (result.entries.length === 0) return [];
|
|
27130
|
+
const details = await compactDetail(result.entries.map((e) => e.id), { reader });
|
|
27131
|
+
return details.documents.map((d, i) => ({
|
|
27132
|
+
id: Number(d.id.replace("obs-", "")),
|
|
27133
|
+
observationId: d.observationId,
|
|
27134
|
+
title: d.title,
|
|
27135
|
+
narrative: d.narrative,
|
|
27136
|
+
facts: d.facts,
|
|
27137
|
+
entityName: d.entityName,
|
|
27138
|
+
type: d.type,
|
|
27139
|
+
score: result.entries[i]?.score ?? 0
|
|
27140
|
+
}));
|
|
27141
|
+
},
|
|
27142
|
+
getObservation: (id) => {
|
|
27143
|
+
const o = getObservation(id);
|
|
27144
|
+
if (!o || o.projectId !== project.id || !canReadObservation(o, reader)) return null;
|
|
27145
|
+
return {
|
|
27146
|
+
id: o.id,
|
|
27147
|
+
entityName: o.entityName,
|
|
27148
|
+
type: o.type,
|
|
27149
|
+
title: o.title,
|
|
27150
|
+
narrative: o.narrative,
|
|
27151
|
+
facts: o.facts,
|
|
27152
|
+
topicKey: o.topicKey
|
|
27153
|
+
};
|
|
27154
|
+
},
|
|
27155
|
+
getEntityNames: () => graphManager.getEntityNames(),
|
|
27156
|
+
onStageEvent: onFormationStageEvent
|
|
27157
|
+
};
|
|
27158
|
+
formationResult = await withTimeout2(
|
|
27159
|
+
runFormation({
|
|
27160
|
+
entityName,
|
|
27161
|
+
type,
|
|
27162
|
+
title,
|
|
27163
|
+
narrative,
|
|
27164
|
+
facts: safeFacts,
|
|
27165
|
+
projectId: project.id,
|
|
27166
|
+
source: "explicit"
|
|
27167
|
+
}, formationConfig),
|
|
27168
|
+
FORMATION_TIMEOUT_MS,
|
|
27169
|
+
"Formation pipeline"
|
|
27170
|
+
);
|
|
27171
|
+
const modeIcon = "[FAST]";
|
|
27172
|
+
formationNote = `
|
|
26516
27173
|
${modeIcon} Formation[active]: ${formationResult.evaluation.category} (${formationResult.evaluation.score.toFixed(2)}) | ${formationResult.resolution.action} | ${formationResult.pipeline.durationMs}ms`;
|
|
26517
|
-
|
|
26518
|
-
|
|
26519
|
-
|
|
26520
|
-
|
|
26521
|
-
|
|
26522
|
-
|
|
26523
|
-
|
|
26524
|
-
|
|
26525
|
-
|
|
26526
|
-
|
|
26527
|
-
|
|
26528
|
-
|
|
26529
|
-
|
|
26530
|
-
|
|
26531
|
-
|
|
26532
|
-
|
|
26533
|
-
|
|
27174
|
+
if (formationResult.extraction.extractedFacts.length > 0) {
|
|
27175
|
+
formationNote += ` | +${formationResult.extraction.extractedFacts.length} facts`;
|
|
27176
|
+
}
|
|
27177
|
+
if (formationResult.extraction.titleImproved) formationNote += " | title\u2191";
|
|
27178
|
+
if (formationResult.extraction.entityResolved) formationNote += ` | entity\u2192${formationResult.entityName}`;
|
|
27179
|
+
if (formationResult.extraction.typeCorrected) formationNote += ` | type\u2192${formationResult.type}`;
|
|
27180
|
+
} catch (formationErr) {
|
|
27181
|
+
const isTimeout = formationErr instanceof Error && formationErr.message.includes("timed out");
|
|
27182
|
+
const elapsedMs = Date.now() - formationStartTime;
|
|
27183
|
+
const stageSummary = formatFormationStageDurations(completedFormationStages);
|
|
27184
|
+
console.error(
|
|
27185
|
+
`[memorix] Formation ${isTimeout ? "timed out" : "failed"} in memorix_store after ${elapsedMs}ms/${FORMATION_TIMEOUT_MS}ms at stage ${currentFormationStage}${stageSummary ? ` | completed: ${stageSummary}` : ""}`
|
|
27186
|
+
);
|
|
27187
|
+
if (!isTimeout && formationErr instanceof Error) {
|
|
27188
|
+
console.error(`[memorix] Formation error: ${formationErr.message}`);
|
|
27189
|
+
}
|
|
27190
|
+
formationNote = `
|
|
26534
27191
|
[WARN] Formation ${isTimeout ? "timed out" : "failed"} \u2014 storing base observation without enrichment`;
|
|
26535
|
-
}
|
|
26536
|
-
}
|
|
26537
|
-
if (useFormation && formationResult && formationResult.resolution.action !== "new") {
|
|
26538
|
-
const { action: action2, targetId, reason } = formationResult.resolution;
|
|
26539
|
-
if (action2 === "merge" && targetId) {
|
|
26540
|
-
const targetObs = getObservation(targetId);
|
|
26541
|
-
if (targetObs) {
|
|
26542
|
-
await storeObservation({
|
|
26543
|
-
entityName: targetObs.entityName,
|
|
26544
|
-
type: targetObs.type,
|
|
26545
|
-
title: formationResult.title,
|
|
26546
|
-
narrative: formationResult.narrative,
|
|
26547
|
-
facts: formationResult.facts,
|
|
26548
|
-
filesModified: safeFiles,
|
|
26549
|
-
concepts: safeConcepts,
|
|
26550
|
-
projectId: project.id,
|
|
26551
|
-
topicKey: targetObs.topicKey,
|
|
26552
|
-
progress,
|
|
26553
|
-
sourceDetail: "explicit",
|
|
26554
|
-
createdByAgentId: currentAgentId
|
|
26555
|
-
});
|
|
26556
|
-
return {
|
|
26557
|
-
content: [{
|
|
26558
|
-
type: "text",
|
|
26559
|
-
text: `[UPDATED] Formation MERGE: merged into #${targetId} (${reason})${formationNote}`
|
|
26560
|
-
}]
|
|
26561
|
-
};
|
|
26562
|
-
}
|
|
26563
|
-
} else if (action2 === "evolve" && targetId) {
|
|
26564
|
-
const targetObs = getObservation(targetId);
|
|
26565
|
-
if (targetObs) {
|
|
26566
|
-
await storeObservation({
|
|
26567
|
-
entityName: targetObs.entityName,
|
|
26568
|
-
type: targetObs.type,
|
|
26569
|
-
title: formationResult.title,
|
|
26570
|
-
narrative: formationResult.narrative,
|
|
26571
|
-
facts: formationResult.facts,
|
|
26572
|
-
filesModified: safeFiles,
|
|
26573
|
-
concepts: safeConcepts,
|
|
26574
|
-
projectId: project.id,
|
|
26575
|
-
topicKey: targetObs.topicKey,
|
|
26576
|
-
progress,
|
|
26577
|
-
sourceDetail: "explicit",
|
|
26578
|
-
createdByAgentId: currentAgentId
|
|
26579
|
-
});
|
|
26580
|
-
return {
|
|
26581
|
-
content: [{
|
|
26582
|
-
type: "text",
|
|
26583
|
-
text: `[UPDATED] Formation EVOLVE: evolved #${targetId} (${reason})${formationNote}`
|
|
26584
|
-
}]
|
|
26585
|
-
};
|
|
26586
27192
|
}
|
|
26587
|
-
} else if (action2 === "discard") {
|
|
26588
|
-
return {
|
|
26589
|
-
content: [{
|
|
26590
|
-
type: "text",
|
|
26591
|
-
text: `[SKIP] Formation DISCARD: ${reason}${formationNote}`
|
|
26592
|
-
}]
|
|
26593
|
-
};
|
|
26594
27193
|
}
|
|
26595
|
-
|
|
26596
|
-
|
|
26597
|
-
|
|
26598
|
-
|
|
26599
|
-
|
|
26600
|
-
|
|
26601
|
-
|
|
26602
|
-
|
|
26603
|
-
|
|
26604
|
-
|
|
26605
|
-
|
|
26606
|
-
|
|
26607
|
-
|
|
26608
|
-
|
|
26609
|
-
|
|
26610
|
-
|
|
26611
|
-
|
|
26612
|
-
|
|
26613
|
-
|
|
26614
|
-
|
|
26615
|
-
|
|
26616
|
-
}));
|
|
26617
|
-
const decision = await withTimeout2(
|
|
26618
|
-
compactOnWrite(
|
|
26619
|
-
{ title, narrative, facts: safeFacts ?? [] },
|
|
26620
|
-
existingMemories
|
|
26621
|
-
),
|
|
26622
|
-
COMPACT_ON_WRITE_TIMEOUT_MS,
|
|
26623
|
-
"Compact-on-write"
|
|
26624
|
-
);
|
|
26625
|
-
if (decision.action === "UPDATE" && decision.targetId) {
|
|
26626
|
-
const targetObs = getObservation(decision.targetId);
|
|
26627
|
-
if (targetObs) {
|
|
26628
|
-
await storeObservation({
|
|
26629
|
-
entityName: targetObs.entityName,
|
|
26630
|
-
type: targetObs.type,
|
|
26631
|
-
title: decision.mergedNarrative ? title : targetObs.title,
|
|
26632
|
-
narrative: decision.mergedNarrative ?? narrative,
|
|
26633
|
-
facts: decision.mergedFacts ?? safeFacts,
|
|
26634
|
-
filesModified: safeFiles,
|
|
26635
|
-
concepts: safeConcepts,
|
|
26636
|
-
projectId: project.id,
|
|
26637
|
-
topicKey: targetObs.topicKey,
|
|
26638
|
-
progress,
|
|
26639
|
-
sourceDetail: "explicit",
|
|
26640
|
-
createdByAgentId: currentAgentId
|
|
26641
|
-
});
|
|
26642
|
-
compactAction = `[UPDATED] Compact UPDATE: merged into #${decision.targetId} (${decision.reason})`;
|
|
26643
|
-
compactMerged = true;
|
|
26644
|
-
return {
|
|
26645
|
-
content: [{
|
|
26646
|
-
type: "text",
|
|
26647
|
-
text: `${compactAction}
|
|
26648
|
-
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
26649
|
-
}]
|
|
26650
|
-
};
|
|
26651
|
-
}
|
|
26652
|
-
} else if (decision.action === "NONE") {
|
|
27194
|
+
if (useFormation && formationResult && formationResult.resolution.action !== "new") {
|
|
27195
|
+
const { action: action2, targetId, reason } = formationResult.resolution;
|
|
27196
|
+
if (action2 === "merge" && targetId) {
|
|
27197
|
+
const targetObs = getObservation(targetId);
|
|
27198
|
+
if (targetObs && targetObs.projectId === project.id && canReadObservation(targetObs, reader)) {
|
|
27199
|
+
await storeObservation({
|
|
27200
|
+
entityName: targetObs.entityName,
|
|
27201
|
+
type: targetObs.type,
|
|
27202
|
+
title: formationResult.title,
|
|
27203
|
+
narrative: formationResult.narrative,
|
|
27204
|
+
facts: formationResult.facts,
|
|
27205
|
+
filesModified: safeFiles,
|
|
27206
|
+
concepts: safeConcepts,
|
|
27207
|
+
projectId: project.id,
|
|
27208
|
+
topicKey: targetObs.topicKey,
|
|
27209
|
+
progress,
|
|
27210
|
+
sourceDetail: "explicit",
|
|
27211
|
+
createdByAgentId: currentAgentId,
|
|
27212
|
+
visibility,
|
|
27213
|
+
visibilityReader: reader
|
|
27214
|
+
});
|
|
26653
27215
|
return {
|
|
26654
27216
|
content: [{
|
|
26655
27217
|
type: "text",
|
|
26656
|
-
text: `[
|
|
26657
|
-
Existing memory #${decision.targetId} already covers this.
|
|
26658
|
-
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
27218
|
+
text: `[UPDATED] Formation MERGE: merged into #${targetId} (${reason})${formationNote}`
|
|
26659
27219
|
}]
|
|
26660
27220
|
};
|
|
26661
|
-
} else if (decision.action === "DELETE" && decision.targetId) {
|
|
26662
|
-
const { resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
26663
|
-
await resolveObservations2([decision.targetId], "resolved");
|
|
26664
|
-
compactAction = ` | Compact: resolved outdated #${decision.targetId}`;
|
|
26665
27221
|
}
|
|
26666
|
-
|
|
26667
|
-
|
|
26668
|
-
|
|
26669
|
-
|
|
26670
|
-
|
|
26671
|
-
|
|
27222
|
+
} else if (action2 === "evolve" && targetId) {
|
|
27223
|
+
const targetObs = getObservation(targetId);
|
|
27224
|
+
if (targetObs && targetObs.projectId === project.id && canReadObservation(targetObs, reader)) {
|
|
27225
|
+
await storeObservation({
|
|
27226
|
+
entityName: targetObs.entityName,
|
|
27227
|
+
type: targetObs.type,
|
|
27228
|
+
title: formationResult.title,
|
|
27229
|
+
narrative: formationResult.narrative,
|
|
27230
|
+
facts: formationResult.facts,
|
|
27231
|
+
filesModified: safeFiles,
|
|
27232
|
+
concepts: safeConcepts,
|
|
27233
|
+
projectId: project.id,
|
|
27234
|
+
topicKey: targetObs.topicKey,
|
|
27235
|
+
progress,
|
|
27236
|
+
sourceDetail: "explicit",
|
|
27237
|
+
createdByAgentId: currentAgentId,
|
|
27238
|
+
visibility,
|
|
27239
|
+
visibilityReader: reader
|
|
27240
|
+
});
|
|
27241
|
+
return {
|
|
27242
|
+
content: [{
|
|
27243
|
+
type: "text",
|
|
27244
|
+
text: `[UPDATED] Formation EVOLVE: evolved #${targetId} (${reason})${formationNote}`
|
|
27245
|
+
}]
|
|
27246
|
+
};
|
|
26672
27247
|
}
|
|
27248
|
+
} else if (action2 === "discard") {
|
|
27249
|
+
return {
|
|
27250
|
+
content: [{
|
|
27251
|
+
type: "text",
|
|
27252
|
+
text: `[SKIP] Formation DISCARD: ${reason}${formationNote}`
|
|
27253
|
+
}]
|
|
27254
|
+
};
|
|
26673
27255
|
}
|
|
26674
|
-
} catch {
|
|
26675
|
-
}
|
|
26676
|
-
}
|
|
26677
|
-
if (formationResult && formationResult.resolution.action === "new") {
|
|
26678
|
-
const llmFacts = formationResult.extraction.extractedFacts;
|
|
26679
|
-
if (llmFacts.length > 0) {
|
|
26680
|
-
const currentFacts = safeFacts ?? [];
|
|
26681
|
-
const currentLower = new Set(currentFacts.map((f) => f.toLowerCase().trim()));
|
|
26682
|
-
const newFacts = llmFacts.filter((f) => !currentLower.has(f.toLowerCase().trim()));
|
|
26683
|
-
if (newFacts.length > 0) {
|
|
26684
|
-
safeFacts = [...currentFacts, ...newFacts];
|
|
26685
|
-
}
|
|
26686
|
-
}
|
|
26687
|
-
if (formationResult.extraction.titleImproved && formationResult.title) {
|
|
26688
|
-
title = formationResult.title;
|
|
26689
|
-
}
|
|
26690
|
-
if (formationResult.extraction.typeCorrected && formationResult.type) {
|
|
26691
|
-
type = formationResult.type;
|
|
26692
|
-
}
|
|
26693
|
-
if (formationResult.extraction.entityResolved && formationResult.entityName) {
|
|
26694
|
-
entityName = formationResult.entityName;
|
|
26695
|
-
}
|
|
26696
|
-
}
|
|
26697
|
-
await graphManager.createEntities([
|
|
26698
|
-
{ name: entityName, entityType: "auto", observations: [] }
|
|
26699
|
-
]);
|
|
26700
|
-
let sessionId;
|
|
26701
|
-
try {
|
|
26702
|
-
const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
26703
|
-
const active = await getActiveSession2(projectDir2, project.id);
|
|
26704
|
-
if (active) sessionId = active.id;
|
|
26705
|
-
} catch {
|
|
26706
|
-
}
|
|
26707
|
-
let finalNarrative = narrative;
|
|
26708
|
-
let compressionNote = "";
|
|
26709
|
-
try {
|
|
26710
|
-
const { compressNarrative: compressNarrative2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
|
|
26711
|
-
const { compressed, saved, usedLLM } = await withTimeout2(
|
|
26712
|
-
compressNarrative2(narrative, safeFacts, type),
|
|
26713
|
-
COMPRESSION_TIMEOUT_MS,
|
|
26714
|
-
"Narrative compression"
|
|
26715
|
-
);
|
|
26716
|
-
if (usedLLM && saved > 0) {
|
|
26717
|
-
finalNarrative = compressed;
|
|
26718
|
-
compressionNote = ` | compressed -${saved} tokens`;
|
|
26719
|
-
}
|
|
26720
|
-
} catch {
|
|
26721
|
-
}
|
|
26722
|
-
let attributionWarning = "";
|
|
26723
|
-
try {
|
|
26724
|
-
const attrCheck = await checkProjectAttribution(entityName, project.id, getAllObservations());
|
|
26725
|
-
if (attrCheck.suspicious) {
|
|
26726
|
-
attributionWarning = `
|
|
26727
|
-
[WARN] Attribution notice: entity "${entityName}" has 0 observations in "${project.id}" but ${attrCheck.count} in "${attrCheck.knownIn}" (confidence: ${attrCheck.confidence}). Verify the correct project is bound before storing.`;
|
|
26728
27256
|
}
|
|
26729
|
-
|
|
26730
|
-
|
|
26731
|
-
|
|
26732
|
-
entityName,
|
|
26733
|
-
type,
|
|
26734
|
-
title,
|
|
26735
|
-
narrative: finalNarrative,
|
|
26736
|
-
facts: safeFacts,
|
|
26737
|
-
filesModified: safeFiles,
|
|
26738
|
-
concepts: safeConcepts,
|
|
26739
|
-
projectId: project.id,
|
|
26740
|
-
topicKey,
|
|
26741
|
-
sessionId,
|
|
26742
|
-
progress,
|
|
26743
|
-
relatedCommits,
|
|
26744
|
-
relatedEntities,
|
|
26745
|
-
sourceDetail: "explicit",
|
|
26746
|
-
valueCategory: formationResult?.evaluation.category,
|
|
26747
|
-
createdByAgentId: currentAgentId
|
|
26748
|
-
});
|
|
26749
|
-
await graphManager.addObservations([
|
|
26750
|
-
{ entityName, contents: [`[#${obs.id}] ${title}`] }
|
|
26751
|
-
]);
|
|
26752
|
-
const extracted = extractEntities([title, narrative, ...safeFacts ?? []].join(" "));
|
|
26753
|
-
const autoRelCount = await createAutoRelations(obs, extracted, graphManager);
|
|
26754
|
-
const enrichmentParts = [];
|
|
26755
|
-
const autoFiles = obs.filesModified.filter((f) => !(safeFiles ?? []).includes(f));
|
|
26756
|
-
const autoConcepts = obs.concepts.filter((c) => !(safeConcepts ?? []).includes(c));
|
|
26757
|
-
if (autoFiles.length > 0) enrichmentParts.push(`+${autoFiles.length} files extracted`);
|
|
26758
|
-
if (autoConcepts.length > 0) enrichmentParts.push(`+${autoConcepts.length} concepts enriched`);
|
|
26759
|
-
if (autoRelCount > 0) enrichmentParts.push(`+${autoRelCount} relations auto-created`);
|
|
26760
|
-
if (obs.hasCausalLanguage) enrichmentParts.push("causal language detected");
|
|
26761
|
-
if (upserted) enrichmentParts.push(`topic upserted (rev ${obs.revisionCount ?? 1})`);
|
|
26762
|
-
const enrichment = enrichmentParts.length > 0 ? `
|
|
26763
|
-
Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
26764
|
-
const action = upserted ? "[UPDATED] Updated" : "[OK] Stored";
|
|
26765
|
-
if (!useFormation && !topicKey && !progress) {
|
|
26766
|
-
const shadowFormation = async () => {
|
|
26767
|
-
let oldCompactDecision = null;
|
|
27257
|
+
let compactAction = "";
|
|
27258
|
+
let compactMerged = false;
|
|
27259
|
+
if (!useFormation && !topicKey && !progress) {
|
|
26768
27260
|
try {
|
|
26769
|
-
const compactStart = Date.now();
|
|
26770
27261
|
const searchResult = await compactSearch({
|
|
26771
27262
|
query: `${title} ${narrative.substring(0, 200)}`,
|
|
26772
27263
|
limit: 5,
|
|
26773
27264
|
projectId: project.id,
|
|
26774
|
-
status: "active"
|
|
27265
|
+
status: "active",
|
|
27266
|
+
reader
|
|
26775
27267
|
});
|
|
26776
27268
|
const similarEntries = searchResult.entries.map((e) => e);
|
|
26777
27269
|
if (similarEntries.length > 0) {
|
|
26778
27270
|
const similarIds = similarEntries.map((e) => e.id);
|
|
26779
|
-
const details = await compactDetail(similarIds);
|
|
27271
|
+
const details = await compactDetail(similarIds, { reader });
|
|
26780
27272
|
const existingMemories = details.documents.map((d, i) => ({
|
|
26781
27273
|
id: d.observationId,
|
|
26782
27274
|
title: d.title,
|
|
@@ -26784,78 +27276,266 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
|
26784
27276
|
facts: d.facts,
|
|
26785
27277
|
score: similarEntries[i]?.score ?? 0
|
|
26786
27278
|
}));
|
|
26787
|
-
const decision = await
|
|
26788
|
-
|
|
26789
|
-
|
|
27279
|
+
const decision = await withTimeout2(
|
|
27280
|
+
compactOnWrite(
|
|
27281
|
+
{ title, narrative, facts: safeFacts ?? [] },
|
|
27282
|
+
existingMemories
|
|
27283
|
+
),
|
|
27284
|
+
COMPACT_ON_WRITE_TIMEOUT_MS,
|
|
27285
|
+
"Compact-on-write"
|
|
26790
27286
|
);
|
|
26791
|
-
|
|
26792
|
-
|
|
26793
|
-
|
|
26794
|
-
|
|
26795
|
-
|
|
26796
|
-
|
|
27287
|
+
if (decision.action === "UPDATE" && decision.targetId) {
|
|
27288
|
+
const targetObs = getObservation(decision.targetId);
|
|
27289
|
+
if (targetObs && targetObs.projectId === project.id && canReadObservation(targetObs, reader)) {
|
|
27290
|
+
await storeObservation({
|
|
27291
|
+
entityName: targetObs.entityName,
|
|
27292
|
+
type: targetObs.type,
|
|
27293
|
+
title: decision.mergedNarrative ? title : targetObs.title,
|
|
27294
|
+
narrative: decision.mergedNarrative ?? narrative,
|
|
27295
|
+
facts: decision.mergedFacts ?? safeFacts,
|
|
27296
|
+
filesModified: safeFiles,
|
|
27297
|
+
concepts: safeConcepts,
|
|
27298
|
+
projectId: project.id,
|
|
27299
|
+
topicKey: targetObs.topicKey,
|
|
27300
|
+
progress,
|
|
27301
|
+
sourceDetail: "explicit",
|
|
27302
|
+
createdByAgentId: currentAgentId,
|
|
27303
|
+
visibility,
|
|
27304
|
+
visibilityReader: reader
|
|
27305
|
+
});
|
|
27306
|
+
compactAction = `[UPDATED] Compact UPDATE: merged into #${decision.targetId} (${decision.reason})`;
|
|
27307
|
+
compactMerged = true;
|
|
27308
|
+
return {
|
|
27309
|
+
content: [{
|
|
27310
|
+
type: "text",
|
|
27311
|
+
text: `${compactAction}
|
|
27312
|
+
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
27313
|
+
}]
|
|
27314
|
+
};
|
|
27315
|
+
}
|
|
27316
|
+
} else if (decision.action === "NONE") {
|
|
27317
|
+
return {
|
|
27318
|
+
content: [{
|
|
27319
|
+
type: "text",
|
|
27320
|
+
text: `[SKIP] Compact SKIP: ${decision.reason}
|
|
27321
|
+
Existing memory #${decision.targetId} already covers this.
|
|
27322
|
+
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
27323
|
+
}]
|
|
27324
|
+
};
|
|
27325
|
+
} else if (decision.action === "DELETE" && decision.targetId) {
|
|
27326
|
+
const { resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27327
|
+
await resolveObservations2([decision.targetId], "resolved");
|
|
27328
|
+
compactAction = ` | Compact: resolved outdated #${decision.targetId}`;
|
|
27329
|
+
}
|
|
27330
|
+
if (decision.enrichedFacts && decision.enrichedFacts.length > 0) {
|
|
27331
|
+
const currentFacts = safeFacts ?? [];
|
|
27332
|
+
const newFacts = decision.enrichedFacts.filter((f) => !currentFacts.includes(f));
|
|
27333
|
+
if (newFacts.length > 0) {
|
|
27334
|
+
compactAction += ` | +${newFacts.length} LLM-extracted facts`;
|
|
27335
|
+
}
|
|
27336
|
+
}
|
|
26797
27337
|
}
|
|
26798
27338
|
} catch {
|
|
26799
27339
|
}
|
|
26800
|
-
|
|
26801
|
-
|
|
26802
|
-
|
|
26803
|
-
|
|
26804
|
-
|
|
26805
|
-
|
|
26806
|
-
|
|
26807
|
-
|
|
26808
|
-
|
|
26809
|
-
|
|
26810
|
-
|
|
26811
|
-
|
|
26812
|
-
|
|
26813
|
-
|
|
26814
|
-
|
|
26815
|
-
|
|
26816
|
-
|
|
26817
|
-
|
|
26818
|
-
|
|
26819
|
-
|
|
26820
|
-
|
|
26821
|
-
|
|
26822
|
-
|
|
26823
|
-
|
|
26824
|
-
|
|
26825
|
-
|
|
26826
|
-
const
|
|
26827
|
-
|
|
26828
|
-
|
|
26829
|
-
|
|
27340
|
+
}
|
|
27341
|
+
if (formationResult && formationResult.resolution.action === "new") {
|
|
27342
|
+
const llmFacts = formationResult.extraction.extractedFacts;
|
|
27343
|
+
if (llmFacts.length > 0) {
|
|
27344
|
+
const currentFacts = safeFacts ?? [];
|
|
27345
|
+
const currentLower = new Set(currentFacts.map((f) => f.toLowerCase().trim()));
|
|
27346
|
+
const newFacts = llmFacts.filter((f) => !currentLower.has(f.toLowerCase().trim()));
|
|
27347
|
+
if (newFacts.length > 0) {
|
|
27348
|
+
safeFacts = [...currentFacts, ...newFacts];
|
|
27349
|
+
}
|
|
27350
|
+
}
|
|
27351
|
+
if (formationResult.extraction.titleImproved && formationResult.title) {
|
|
27352
|
+
title = formationResult.title;
|
|
27353
|
+
}
|
|
27354
|
+
if (formationResult.extraction.typeCorrected && formationResult.type) {
|
|
27355
|
+
type = formationResult.type;
|
|
27356
|
+
}
|
|
27357
|
+
if (formationResult.extraction.entityResolved && formationResult.entityName) {
|
|
27358
|
+
entityName = formationResult.entityName;
|
|
27359
|
+
}
|
|
27360
|
+
}
|
|
27361
|
+
await graphManager.createEntities([
|
|
27362
|
+
{ name: entityName, entityType: "auto", observations: [] }
|
|
27363
|
+
]);
|
|
27364
|
+
let sessionId;
|
|
27365
|
+
try {
|
|
27366
|
+
const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
27367
|
+
const active = await getActiveSession2(projectDir2, project.id);
|
|
27368
|
+
if (active) sessionId = active.id;
|
|
27369
|
+
} catch {
|
|
27370
|
+
}
|
|
27371
|
+
let finalNarrative = narrative;
|
|
27372
|
+
let compressionNote = "";
|
|
27373
|
+
try {
|
|
27374
|
+
const { compressNarrative: compressNarrative2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
|
|
27375
|
+
const { compressed, saved, usedLLM } = await withTimeout2(
|
|
27376
|
+
compressNarrative2(narrative, safeFacts, type),
|
|
27377
|
+
COMPRESSION_TIMEOUT_MS,
|
|
27378
|
+
"Narrative compression"
|
|
26830
27379
|
);
|
|
26831
|
-
|
|
26832
|
-
|
|
26833
|
-
|
|
26834
|
-
formationAction: formed.resolution.action,
|
|
26835
|
-
formationTargetId: formed.resolution.targetId,
|
|
26836
|
-
oldCompactAction: oldCompactDecision.action,
|
|
26837
|
-
oldCompactTargetId: oldCompactDecision.targetId,
|
|
26838
|
-
oldCompactReason: oldCompactDecision.reason,
|
|
26839
|
-
formationValueScore: formed.evaluation.score,
|
|
26840
|
-
formationValueCategory: formed.evaluation.category,
|
|
26841
|
-
formationDurationMs: formed.pipeline.durationMs,
|
|
26842
|
-
compactDurationMs: oldCompactDecision.durationMs
|
|
26843
|
-
});
|
|
27380
|
+
if (usedLLM && saved > 0) {
|
|
27381
|
+
finalNarrative = compressed;
|
|
27382
|
+
compressionNote = ` | compressed -${saved} tokens`;
|
|
26844
27383
|
}
|
|
26845
|
-
}
|
|
26846
|
-
|
|
27384
|
+
} catch {
|
|
27385
|
+
}
|
|
27386
|
+
let attributionWarning = "";
|
|
27387
|
+
try {
|
|
27388
|
+
const attrCheck = await checkProjectAttribution(
|
|
27389
|
+
entityName,
|
|
27390
|
+
project.id,
|
|
27391
|
+
filterReadableObservations(getAllObservations(), reader)
|
|
27392
|
+
);
|
|
27393
|
+
if (attrCheck.suspicious) {
|
|
27394
|
+
attributionWarning = `
|
|
27395
|
+
[WARN] Attribution notice: entity "${entityName}" has 0 observations in "${project.id}" but ${attrCheck.count} in "${attrCheck.knownIn}" (confidence: ${attrCheck.confidence}). Verify the correct project is bound before storing.`;
|
|
27396
|
+
}
|
|
27397
|
+
} catch {
|
|
27398
|
+
}
|
|
27399
|
+
const { observation: obs, upserted } = await storeObservation({
|
|
27400
|
+
entityName,
|
|
27401
|
+
type,
|
|
27402
|
+
title,
|
|
27403
|
+
narrative: finalNarrative,
|
|
27404
|
+
facts: safeFacts,
|
|
27405
|
+
filesModified: safeFiles,
|
|
27406
|
+
concepts: safeConcepts,
|
|
27407
|
+
projectId: project.id,
|
|
27408
|
+
topicKey,
|
|
27409
|
+
sessionId,
|
|
27410
|
+
progress,
|
|
27411
|
+
relatedCommits,
|
|
27412
|
+
relatedEntities,
|
|
27413
|
+
sourceDetail: "explicit",
|
|
27414
|
+
valueCategory: formationResult?.evaluation.category,
|
|
27415
|
+
createdByAgentId: currentAgentId,
|
|
27416
|
+
visibility,
|
|
27417
|
+
visibilityReader: reader
|
|
26847
27418
|
});
|
|
26848
|
-
|
|
26849
|
-
|
|
26850
|
-
|
|
26851
|
-
|
|
26852
|
-
|
|
26853
|
-
|
|
27419
|
+
await graphManager.addObservations([
|
|
27420
|
+
{ entityName, contents: [`[#${obs.id}] ${title}`] }
|
|
27421
|
+
]);
|
|
27422
|
+
const extracted = extractEntities([title, narrative, ...safeFacts ?? []].join(" "));
|
|
27423
|
+
const autoRelCount = await createAutoRelations(obs, extracted, graphManager);
|
|
27424
|
+
const enrichmentParts = [];
|
|
27425
|
+
const autoFiles = obs.filesModified.filter((f) => !(safeFiles ?? []).includes(f));
|
|
27426
|
+
const autoConcepts = obs.concepts.filter((c) => !(safeConcepts ?? []).includes(c));
|
|
27427
|
+
if (autoFiles.length > 0) enrichmentParts.push(`+${autoFiles.length} files extracted`);
|
|
27428
|
+
if (autoConcepts.length > 0) enrichmentParts.push(`+${autoConcepts.length} concepts enriched`);
|
|
27429
|
+
if (autoRelCount > 0) enrichmentParts.push(`+${autoRelCount} relations auto-created`);
|
|
27430
|
+
if (obs.hasCausalLanguage) enrichmentParts.push("causal language detected");
|
|
27431
|
+
if (upserted) enrichmentParts.push(`topic upserted (rev ${obs.revisionCount ?? 1})`);
|
|
27432
|
+
const enrichment = enrichmentParts.length > 0 ? `
|
|
27433
|
+
Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
27434
|
+
const action = upserted ? "[UPDATED] Updated" : "[OK] Stored";
|
|
27435
|
+
if (!useFormation && !topicKey && !progress) {
|
|
27436
|
+
const shadowFormation = async () => {
|
|
27437
|
+
let oldCompactDecision = null;
|
|
27438
|
+
try {
|
|
27439
|
+
const compactStart = Date.now();
|
|
27440
|
+
const searchResult = await compactSearch({
|
|
27441
|
+
query: `${title} ${narrative.substring(0, 200)}`,
|
|
27442
|
+
limit: 5,
|
|
27443
|
+
projectId: project.id,
|
|
27444
|
+
status: "active",
|
|
27445
|
+
reader
|
|
27446
|
+
});
|
|
27447
|
+
const similarEntries = searchResult.entries.map((e) => e);
|
|
27448
|
+
if (similarEntries.length > 0) {
|
|
27449
|
+
const similarIds = similarEntries.map((e) => e.id);
|
|
27450
|
+
const details = await compactDetail(similarIds, { reader });
|
|
27451
|
+
const existingMemories = details.documents.map((d, i) => ({
|
|
27452
|
+
id: d.observationId,
|
|
27453
|
+
title: d.title,
|
|
27454
|
+
narrative: d.narrative,
|
|
27455
|
+
facts: d.facts,
|
|
27456
|
+
score: similarEntries[i]?.score ?? 0
|
|
27457
|
+
}));
|
|
27458
|
+
const decision = await compactOnWrite(
|
|
27459
|
+
{ title, narrative, facts: safeFacts ?? [] },
|
|
27460
|
+
existingMemories
|
|
27461
|
+
);
|
|
27462
|
+
oldCompactDecision = {
|
|
27463
|
+
action: decision.action,
|
|
27464
|
+
targetId: decision.targetId,
|
|
27465
|
+
reason: decision.reason,
|
|
27466
|
+
durationMs: Date.now() - compactStart
|
|
27467
|
+
};
|
|
27468
|
+
}
|
|
27469
|
+
} catch {
|
|
27470
|
+
}
|
|
27471
|
+
const formationConfig = {
|
|
27472
|
+
mode: formationMode,
|
|
27473
|
+
useLLM: isLLMEnabled(),
|
|
27474
|
+
minValueScore: 0.3,
|
|
27475
|
+
searchMemories: async (q, limit, pid) => {
|
|
27476
|
+
const result = await compactSearch({ query: q, limit, projectId: pid, status: "active", reader });
|
|
27477
|
+
if (result.entries.length === 0) return [];
|
|
27478
|
+
const details = await compactDetail(result.entries.map((e) => e.id), { reader });
|
|
27479
|
+
return details.documents.map((d, i) => ({
|
|
27480
|
+
id: Number(d.id.replace("obs-", "")),
|
|
27481
|
+
observationId: d.observationId,
|
|
27482
|
+
title: d.title,
|
|
27483
|
+
narrative: d.narrative,
|
|
27484
|
+
facts: d.facts,
|
|
27485
|
+
entityName: d.entityName,
|
|
27486
|
+
type: d.type,
|
|
27487
|
+
score: result.entries[i]?.score ?? 0
|
|
27488
|
+
}));
|
|
27489
|
+
},
|
|
27490
|
+
getObservation: (id) => {
|
|
27491
|
+
const o = getObservation(id);
|
|
27492
|
+
if (!o || o.projectId !== project.id || !canReadObservation(o, reader)) return null;
|
|
27493
|
+
return { id: o.id, entityName: o.entityName, type: o.type, title: o.title, narrative: o.narrative, facts: o.facts, topicKey: o.topicKey };
|
|
27494
|
+
},
|
|
27495
|
+
getEntityNames: () => graphManager.getEntityNames()
|
|
27496
|
+
};
|
|
27497
|
+
const formed = await withTimeout2(
|
|
27498
|
+
runFormation({ entityName, type, title, narrative, facts: safeFacts, projectId: project.id, source: "explicit", topicKey }, formationConfig),
|
|
27499
|
+
FORMATION_TIMEOUT_MS,
|
|
27500
|
+
"Shadow formation"
|
|
27501
|
+
);
|
|
27502
|
+
const { recordBeforeAfterMetrics: recordBeforeAfterMetrics2 } = await Promise.resolve().then(() => (init_formation(), formation_exports));
|
|
27503
|
+
if (oldCompactDecision) {
|
|
27504
|
+
recordBeforeAfterMetrics2({
|
|
27505
|
+
formationAction: formed.resolution.action,
|
|
27506
|
+
formationTargetId: formed.resolution.targetId,
|
|
27507
|
+
oldCompactAction: oldCompactDecision.action,
|
|
27508
|
+
oldCompactTargetId: oldCompactDecision.targetId,
|
|
27509
|
+
oldCompactReason: oldCompactDecision.reason,
|
|
27510
|
+
formationValueScore: formed.evaluation.score,
|
|
27511
|
+
formationValueCategory: formed.evaluation.category,
|
|
27512
|
+
formationDurationMs: formed.pipeline.durationMs,
|
|
27513
|
+
compactDurationMs: oldCompactDecision.durationMs
|
|
27514
|
+
});
|
|
27515
|
+
}
|
|
27516
|
+
};
|
|
27517
|
+
shadowFormation().catch(() => {
|
|
27518
|
+
});
|
|
27519
|
+
}
|
|
27520
|
+
return {
|
|
27521
|
+
content: [
|
|
27522
|
+
{
|
|
27523
|
+
type: "text",
|
|
27524
|
+
text: `${action} observation #${obs.id} "${title}" (~${obs.tokens} tokens)
|
|
26854
27525
|
Entity: ${entityName} | Type: ${type} | Project: ${project.id}${obs.topicKey ? ` | Topic: ${obs.topicKey}` : ""}${compactAction}${compressionNote}${enrichment}${formationNote}${attributionWarning}`
|
|
26855
|
-
|
|
26856
|
-
|
|
27526
|
+
}
|
|
27527
|
+
]
|
|
27528
|
+
};
|
|
27529
|
+
});
|
|
27530
|
+
} catch (error) {
|
|
27531
|
+
return {
|
|
27532
|
+
content: [{
|
|
27533
|
+
type: "text",
|
|
27534
|
+
text: error instanceof Error ? error.message : "Failed to store memory."
|
|
27535
|
+
}],
|
|
27536
|
+
isError: true
|
|
26857
27537
|
};
|
|
26858
|
-
}
|
|
27538
|
+
}
|
|
26859
27539
|
}
|
|
26860
27540
|
);
|
|
26861
27541
|
server.registerTool(
|
|
@@ -26927,7 +27607,8 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
26927
27607
|
// Use scope: 'global' to explicitly search all projects.
|
|
26928
27608
|
projectId: scope === "global" ? void 0 : project.id,
|
|
26929
27609
|
status: status ?? "active",
|
|
26930
|
-
source
|
|
27610
|
+
source,
|
|
27611
|
+
reader: getObservationReader(scope === "global" ? "global" : "project")
|
|
26931
27612
|
});
|
|
26932
27613
|
const timeoutPromise = new Promise(
|
|
26933
27614
|
(_, reject) => setTimeout(() => reject(new Error(`Search timeout after ${TIMEOUT_MS}ms`)), TIMEOUT_MS)
|
|
@@ -26985,7 +27666,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26985
27666
|
const unresolved = requireResolvedProject("build graph context for the current project");
|
|
26986
27667
|
if (unresolved) return unresolved;
|
|
26987
27668
|
const { getObservationStore: getObservationStore2 } = await Promise.resolve().then(() => (init_obs_store(), obs_store_exports));
|
|
26988
|
-
const observations2 =
|
|
27669
|
+
const observations2 = filterReadableObservations(
|
|
27670
|
+
await getObservationStore2().loadByProject(project.id),
|
|
27671
|
+
getObservationReader()
|
|
27672
|
+
);
|
|
26989
27673
|
const packet = buildGraphContextPacket(observations2, {
|
|
26990
27674
|
projectId: project.id,
|
|
26991
27675
|
query,
|
|
@@ -27037,7 +27721,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27037
27721
|
Promise.resolve().then(() => (init_maintenance_jobs(), maintenance_jobs_exports)),
|
|
27038
27722
|
Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports))
|
|
27039
27723
|
]);
|
|
27040
|
-
const observations2 =
|
|
27724
|
+
const observations2 = filterReadableObservations(
|
|
27725
|
+
await getObservationStore2().loadByProject(project.id, { status: "active" }),
|
|
27726
|
+
getObservationReader()
|
|
27727
|
+
);
|
|
27041
27728
|
const context = await buildAutoProjectContext2({
|
|
27042
27729
|
project,
|
|
27043
27730
|
dataDir: projectDir2,
|
|
@@ -27127,7 +27814,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27127
27814
|
await store.init(projectDir2);
|
|
27128
27815
|
const codegraphConfig = getResolvedConfig2({ projectRoot: project.rootPath }).codegraph;
|
|
27129
27816
|
const exclude = codegraphConfig.excludePatterns;
|
|
27130
|
-
const observations2 =
|
|
27817
|
+
const observations2 = filterReadableObservations(
|
|
27818
|
+
await getObservationStore2().loadByProject(project.id, { status: "active" }),
|
|
27819
|
+
getObservationReader()
|
|
27820
|
+
);
|
|
27131
27821
|
observations2.reverse();
|
|
27132
27822
|
const basePack = assembleContextPackForTask2({
|
|
27133
27823
|
store,
|
|
@@ -27499,9 +28189,15 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27499
28189
|
}
|
|
27500
28190
|
},
|
|
27501
28191
|
async ({ ids, status }) => {
|
|
27502
|
-
const { resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28192
|
+
const { resolveObservations: resolveObservations2, getObservation: getObservation2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27503
28193
|
const safeIds = (Array.isArray(ids) ? ids : [ids]).map((id) => coerceNumber(id, 0)).filter((id) => id > 0);
|
|
27504
|
-
const
|
|
28194
|
+
const reader = getObservationReader();
|
|
28195
|
+
const authorizedIds = safeIds.filter((id) => {
|
|
28196
|
+
const observation = getObservation2(id, project.id);
|
|
28197
|
+
return observation ? canManageObservation(observation, reader) : false;
|
|
28198
|
+
});
|
|
28199
|
+
const result = await resolveObservations2(authorizedIds, status ?? "resolved");
|
|
28200
|
+
const deniedCount = safeIds.length - authorizedIds.length;
|
|
27505
28201
|
const parts = [];
|
|
27506
28202
|
if (result.resolved.length > 0) {
|
|
27507
28203
|
parts.push(`[OK] Resolved ${result.resolved.length} observation(s): #${result.resolved.join(", #")}`);
|
|
@@ -27509,6 +28205,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27509
28205
|
if (result.notFound.length > 0) {
|
|
27510
28206
|
parts.push(`[WARN] Not found: #${result.notFound.join(", #")}`);
|
|
27511
28207
|
}
|
|
28208
|
+
if (deniedCount > 0) {
|
|
28209
|
+
parts.push(`[WARN] Skipped ${deniedCount} observation(s) outside this session's write scope.`);
|
|
28210
|
+
}
|
|
27512
28211
|
parts.push('\nResolved memories are hidden from default search. Use status="all" to include them.');
|
|
27513
28212
|
parts.push('[STATS] Run `memorix_retention` with `action: "report"` to check remaining cleanup status.');
|
|
27514
28213
|
return {
|
|
@@ -27538,6 +28237,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27538
28237
|
async ({ entityName, decision, alternatives, rationale, constraints, expectedOutcome, risks, concepts, filesModified, relatedCommits, relatedEntities }) => {
|
|
27539
28238
|
const unresolved = requireResolvedProject("store reasoning in the current project");
|
|
27540
28239
|
if (unresolved) return unresolved;
|
|
28240
|
+
const reader = getObservationReader();
|
|
27541
28241
|
return withFreshIndex(async () => {
|
|
27542
28242
|
const narrativeParts = [rationale];
|
|
27543
28243
|
if (alternatives && alternatives.length > 0) {
|
|
@@ -27560,7 +28260,11 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27560
28260
|
]);
|
|
27561
28261
|
let reasoningAttributionWarning = "";
|
|
27562
28262
|
try {
|
|
27563
|
-
const attrCheck = await checkProjectAttribution(
|
|
28263
|
+
const attrCheck = await checkProjectAttribution(
|
|
28264
|
+
entityName,
|
|
28265
|
+
project.id,
|
|
28266
|
+
filterReadableObservations(getAllObservations(), reader)
|
|
28267
|
+
);
|
|
27564
28268
|
if (attrCheck.suspicious) {
|
|
27565
28269
|
reasoningAttributionWarning = `
|
|
27566
28270
|
[WARN] Attribution notice: entity "${entityName}" has 0 observations in "${project.id}" but ${attrCheck.count} in "${attrCheck.knownIn}" (confidence: ${attrCheck.confidence}). Verify the correct project is bound before storing.`;
|
|
@@ -27580,7 +28284,8 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27580
28284
|
relatedCommits,
|
|
27581
28285
|
relatedEntities,
|
|
27582
28286
|
sourceDetail: "explicit",
|
|
27583
|
-
createdByAgentId: currentAgentId
|
|
28287
|
+
createdByAgentId: currentAgentId,
|
|
28288
|
+
visibility: "project"
|
|
27584
28289
|
});
|
|
27585
28290
|
await graphManager.addObservations([
|
|
27586
28291
|
{ entityName, contents: [`[#${obs.id}] [REASONING] ${decision}`] }
|
|
@@ -27612,7 +28317,11 @@ Entity: ${entityName} | ${facts.length} facts | ${obs.tokens} tokens${reasoningA
|
|
|
27612
28317
|
const minCount = threshold ?? 2;
|
|
27613
28318
|
let entries;
|
|
27614
28319
|
try {
|
|
27615
|
-
entries = await auditProjectObservations(
|
|
28320
|
+
entries = await auditProjectObservations(
|
|
28321
|
+
project.id,
|
|
28322
|
+
await withFreshIndex(() => filterReadableObservations(getAllObservations(), getObservationReader())),
|
|
28323
|
+
minCount
|
|
28324
|
+
);
|
|
27616
28325
|
} catch (err) {
|
|
27617
28326
|
return {
|
|
27618
28327
|
content: [{
|
|
@@ -27677,7 +28386,8 @@ Entity: ${entityName} | ${facts.length} facts | ${obs.tokens} tokens${reasoningA
|
|
|
27677
28386
|
limit: safeLimit,
|
|
27678
28387
|
type: "reasoning",
|
|
27679
28388
|
projectId: scope === "global" ? void 0 : project.id,
|
|
27680
|
-
status: "active"
|
|
28389
|
+
status: "active",
|
|
28390
|
+
reader: getObservationReader(scope === "global" ? "global" : "project")
|
|
27681
28391
|
}));
|
|
27682
28392
|
if (result.entries.length === 0) {
|
|
27683
28393
|
return {
|
|
@@ -27702,7 +28412,11 @@ ${result.formatted}` }]
|
|
|
27702
28412
|
},
|
|
27703
28413
|
async ({ query, dryRun }) => {
|
|
27704
28414
|
const { getAllObservations: getAllObservations2, resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27705
|
-
const
|
|
28415
|
+
const reader = getObservationReader();
|
|
28416
|
+
const allObs = await withFreshIndex(() => filterReadableObservations(
|
|
28417
|
+
getAllObservations2().filter((o) => (o.status ?? "active") === "active" && o.projectId === project.id),
|
|
28418
|
+
reader
|
|
28419
|
+
).filter((observation) => canManageObservation(observation, reader)));
|
|
27706
28420
|
if (allObs.length < 2) {
|
|
27707
28421
|
return { content: [{ type: "text", text: "Not enough active memories to deduplicate." }] };
|
|
27708
28422
|
}
|
|
@@ -27716,7 +28430,7 @@ ${result.formatted}` }]
|
|
|
27716
28430
|
}
|
|
27717
28431
|
let candidates;
|
|
27718
28432
|
if (query) {
|
|
27719
|
-
const searchResult = await compactSearch({ query, limit: 20, projectId: project.id, status: "active" });
|
|
28433
|
+
const searchResult = await compactSearch({ query, limit: 20, projectId: project.id, status: "active", reader });
|
|
27720
28434
|
const idSet = new Set(searchResult.entries.map((e) => e.id));
|
|
27721
28435
|
candidates = allObs.filter((o) => idSet.has(o.id));
|
|
27722
28436
|
} else {
|
|
@@ -27806,7 +28520,8 @@ ${actions.join("\n")}`
|
|
|
27806
28520
|
safeAnchor,
|
|
27807
28521
|
project.id,
|
|
27808
28522
|
safeBefore,
|
|
27809
|
-
safeAfter
|
|
28523
|
+
safeAfter,
|
|
28524
|
+
getObservationReader()
|
|
27810
28525
|
);
|
|
27811
28526
|
return {
|
|
27812
28527
|
content: [
|
|
@@ -27840,12 +28555,14 @@ ${actions.join("\n")}`
|
|
|
27840
28555
|
const safeTypedRefs = coerceStringArray(typedRefs);
|
|
27841
28556
|
let result;
|
|
27842
28557
|
try {
|
|
28558
|
+
const hasCrossProjectRef = safeRefs.some((ref) => ref.projectId && ref.projectId !== project.id) || safeTypedRefs.some((ref) => ref.includes("@") && !ref.endsWith(`@${project.id}`));
|
|
28559
|
+
const reader = getObservationReader(hasCrossProjectRef ? "global" : "project");
|
|
27843
28560
|
if (safeTypedRefs.length > 0) {
|
|
27844
|
-
result = await compactDetail(safeTypedRefs);
|
|
28561
|
+
result = await compactDetail(safeTypedRefs, { reader });
|
|
27845
28562
|
} else if (safeRefs.length > 0) {
|
|
27846
|
-
result = await compactDetail(safeRefs);
|
|
28563
|
+
result = await compactDetail(safeRefs, { reader });
|
|
27847
28564
|
} else {
|
|
27848
|
-
result = await compactDetail(safeIds.map((id) => ({ id, projectId: project.id })));
|
|
28565
|
+
result = await compactDetail(safeIds.map((id) => ({ id, projectId: project.id })), { reader });
|
|
27849
28566
|
}
|
|
27850
28567
|
} catch (err) {
|
|
27851
28568
|
return { content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }], isError: true };
|
|
@@ -27871,11 +28588,15 @@ ${actions.join("\n")}`
|
|
|
27871
28588
|
},
|
|
27872
28589
|
async (args) => {
|
|
27873
28590
|
const action = args.action ?? "report";
|
|
27874
|
-
const { getRetentionSummary: getRetentionSummary2, getArchiveCandidates: getArchiveCandidates2, rankByRelevance: rankByRelevance2,
|
|
28591
|
+
const { getRetentionSummary: getRetentionSummary2, getArchiveCandidates: getArchiveCandidates2, rankByRelevance: rankByRelevance2, getRetentionZone: getRetentionZone2, explainRetention: explainRetention2 } = await Promise.resolve().then(() => (init_retention(), retention_exports));
|
|
27875
28592
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_orama_store(), orama_store_exports));
|
|
27876
28593
|
const { search: search2 } = await import("@orama/orama");
|
|
27877
|
-
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27878
|
-
const
|
|
28594
|
+
const { getAllObservations: getAllObservations2, resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28595
|
+
const reader = getObservationReader();
|
|
28596
|
+
const allObs = await withFreshIndex(() => filterReadableObservations(
|
|
28597
|
+
getAllObservations2().filter((observation) => observation.projectId === project.id),
|
|
28598
|
+
reader
|
|
28599
|
+
));
|
|
27879
28600
|
const accessMap = /* @__PURE__ */ new Map();
|
|
27880
28601
|
try {
|
|
27881
28602
|
const database = await getDb2();
|
|
@@ -27892,20 +28613,6 @@ ${actions.join("\n")}`
|
|
|
27892
28613
|
}
|
|
27893
28614
|
} catch {
|
|
27894
28615
|
}
|
|
27895
|
-
if (action === "archive") {
|
|
27896
|
-
const result = await archiveExpired2(projectDir2, void 0, accessMap, project.id);
|
|
27897
|
-
if (result.archived === 0) {
|
|
27898
|
-
return {
|
|
27899
|
-
content: [{ type: "text", text: "[OK] No expired observations to archive. All memories are within their retention period." }]
|
|
27900
|
-
};
|
|
27901
|
-
}
|
|
27902
|
-
return {
|
|
27903
|
-
content: [{ type: "text", text: `[ARCHIVED] Archived ${result.archived} expired observations (status set to 'archived' in-place)
|
|
27904
|
-
${result.remaining} active observations remaining.
|
|
27905
|
-
|
|
27906
|
-
Archived memories are hidden from default search but can be found with status: "all".` }]
|
|
27907
|
-
};
|
|
27908
|
-
}
|
|
27909
28616
|
const docs = allObs.map((obs) => ({
|
|
27910
28617
|
id: `obs-${obs.id}`,
|
|
27911
28618
|
observationId: obs.id,
|
|
@@ -27924,13 +28631,34 @@ Archived memories are hidden from default search but can be found with status: "
|
|
|
27924
28631
|
status: obs.status ?? "active",
|
|
27925
28632
|
source: obs.source ?? "agent",
|
|
27926
28633
|
sourceDetail: obs.sourceDetail ?? "",
|
|
27927
|
-
valueCategory: obs.valueCategory ?? ""
|
|
28634
|
+
valueCategory: obs.valueCategory ?? "",
|
|
28635
|
+
admissionState: obs.admissionState ?? "",
|
|
28636
|
+
admissionReason: obs.admissionReason ?? "",
|
|
28637
|
+
visibility: obs.visibility ?? "project",
|
|
28638
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
28639
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
27928
28640
|
}));
|
|
27929
28641
|
if (docs.length === 0) {
|
|
27930
28642
|
return {
|
|
27931
28643
|
content: [{ type: "text", text: "No observations found for this project." }]
|
|
27932
28644
|
};
|
|
27933
28645
|
}
|
|
28646
|
+
if (action === "archive") {
|
|
28647
|
+
const managedIds = new Set(
|
|
28648
|
+
allObs.filter((observation) => canManageObservation(observation, reader)).map((observation) => observation.id)
|
|
28649
|
+
);
|
|
28650
|
+
const candidates2 = getArchiveCandidates2(docs).filter((document) => managedIds.has(document.observationId));
|
|
28651
|
+
if (candidates2.length === 0) {
|
|
28652
|
+
return {
|
|
28653
|
+
content: [{ type: "text", text: "[OK] No expired memories in this session's write scope to archive." }]
|
|
28654
|
+
};
|
|
28655
|
+
}
|
|
28656
|
+
const result = await resolveObservations2(candidates2.map((document) => document.observationId), "archived");
|
|
28657
|
+
return {
|
|
28658
|
+
content: [{ type: "text", text: `[ARCHIVED] Archived ${result.resolved.length} expired observation(s) in this session's write scope.
|
|
28659
|
+
${Math.max(0, managedIds.size - result.resolved.length)} visible writable observations remaining.` }]
|
|
28660
|
+
};
|
|
28661
|
+
}
|
|
27934
28662
|
if (action === "stale") {
|
|
27935
28663
|
const staleDocs = docs.filter((d) => getRetentionZone2(d) === "stale");
|
|
27936
28664
|
if (staleDocs.length === 0) {
|
|
@@ -28251,7 +28979,10 @@ Archived memories are hidden from default search but can be found with status: "
|
|
|
28251
28979
|
const allObs = await withFreshIndex(() => getAllObservations2());
|
|
28252
28980
|
const scoped = scopeKnowledgeGraphToProject(
|
|
28253
28981
|
graph,
|
|
28254
|
-
|
|
28982
|
+
filterReadableObservations(
|
|
28983
|
+
allObs.filter((observation) => observation.projectId === project.id),
|
|
28984
|
+
getObservationReader()
|
|
28985
|
+
)
|
|
28255
28986
|
);
|
|
28256
28987
|
return { entities: scoped.entities, relations: scoped.relations };
|
|
28257
28988
|
}
|
|
@@ -28526,7 +29257,10 @@ ${skill2.content}` }]
|
|
|
28526
29257
|
};
|
|
28527
29258
|
}
|
|
28528
29259
|
const { getObservationStore: getStore } = await Promise.resolve().then(() => (init_obs_store(), obs_store_exports));
|
|
28529
|
-
const allObs =
|
|
29260
|
+
const allObs = filterReadableObservations(
|
|
29261
|
+
(await getStore().loadAll()).filter((observation) => observation.projectId === project.id),
|
|
29262
|
+
getObservationReader()
|
|
29263
|
+
);
|
|
28530
29264
|
const obsData = allObs.map((o) => ({
|
|
28531
29265
|
id: o.id || 0,
|
|
28532
29266
|
entityName: o.entityName || "unknown",
|
|
@@ -28625,7 +29359,11 @@ ${skill2.content}` }]
|
|
|
28625
29359
|
}
|
|
28626
29360
|
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28627
29361
|
const allObs = await withFreshIndex(() => getAllObservations2());
|
|
28628
|
-
const
|
|
29362
|
+
const reader = getObservationReader();
|
|
29363
|
+
const matched = filterReadableObservations(
|
|
29364
|
+
allObs.filter((observation) => observation.projectId === project.id && observationIds.includes(observation.id)),
|
|
29365
|
+
reader
|
|
29366
|
+
);
|
|
28629
29367
|
if (matched.length === 0) {
|
|
28630
29368
|
return { content: [{ type: "text", text: `No observations found for IDs: [${observationIds.join(", ")}]. Use \`memorix_search\` to find valid IDs.` }], isError: true };
|
|
28631
29369
|
}
|
|
@@ -28633,6 +29371,13 @@ ${skill2.content}` }]
|
|
|
28633
29371
|
if (nonActive.length > 0) {
|
|
28634
29372
|
return { content: [{ type: "text", text: `Cannot promote: ${nonActive.length} observation(s) are not active: ${nonActive.map((o) => `#${o.id} (${o.status})`).join(", ")}. Only active observations can be promoted to permanent knowledge.` }], isError: true };
|
|
28635
29373
|
}
|
|
29374
|
+
const nonProjectShared = matched.filter((observation) => resolveObservationVisibility(observation) !== "project");
|
|
29375
|
+
if (nonProjectShared.length > 0) {
|
|
29376
|
+
return {
|
|
29377
|
+
content: [{ type: "text", text: `Cannot promote private or team-scoped observations: ${nonProjectShared.map((observation) => `#${observation.id}`).join(", ")}. Promote only deliberate project-shared knowledge.` }],
|
|
29378
|
+
isError: true
|
|
29379
|
+
};
|
|
29380
|
+
}
|
|
28636
29381
|
const skill2 = await promoteToMiniSkill2(projectDir2, project.id, matched, { trigger, instruction, tags });
|
|
28637
29382
|
const lines = [
|
|
28638
29383
|
`[OK] Created mini-skill #${skill2.id}`,
|
|
@@ -28760,7 +29505,6 @@ Ensure the path points to a directory containing a .git folder (or a subdirector
|
|
|
28760
29505
|
const unresolved = requireResolvedProject("start a project session");
|
|
28761
29506
|
if (unresolved) return unresolved;
|
|
28762
29507
|
const { startSession: startSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
28763
|
-
const result = await startSession2(projectDir2, project.id, { sessionId, agent });
|
|
28764
29508
|
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)";
|
|
28765
29509
|
const shouldJoinTeam = !!joinTeam;
|
|
28766
29510
|
let registeredAgent = null;
|
|
@@ -28791,8 +29535,11 @@ Ensure the path points to a directory containing a .git folder (or a subdirector
|
|
|
28791
29535
|
const lastSeen = registeredAgent.last_seen_obs_generation;
|
|
28792
29536
|
const store = getObservationStore();
|
|
28793
29537
|
const currentGen = store.getGeneration();
|
|
28794
|
-
const projectObs = await withFreshIndex(() =>
|
|
28795
|
-
(
|
|
29538
|
+
const projectObs = await withFreshIndex(() => filterReadableObservations(
|
|
29539
|
+
getAllObservations().filter(
|
|
29540
|
+
(o) => o.projectId === project.id && (o.writeGeneration ?? 0) > lastSeen
|
|
29541
|
+
),
|
|
29542
|
+
getObservationReader()
|
|
28796
29543
|
));
|
|
28797
29544
|
const wm = computeWatermark2(lastSeen, currentGen, projectObs.length);
|
|
28798
29545
|
if (wm.newObservationCount > 0) {
|
|
@@ -28814,6 +29561,11 @@ Ensure the path points to a directory containing a .git folder (or a subdirector
|
|
|
28814
29561
|
}
|
|
28815
29562
|
} catch {
|
|
28816
29563
|
}
|
|
29564
|
+
const result = await startSession2(projectDir2, project.id, {
|
|
29565
|
+
sessionId,
|
|
29566
|
+
agent,
|
|
29567
|
+
reader: getObservationReader()
|
|
29568
|
+
});
|
|
28817
29569
|
const lines = [
|
|
28818
29570
|
`[OK] Session started: ${result.session.id}`,
|
|
28819
29571
|
`Project: ${project.name} (${project.id})`,
|
|
@@ -28936,7 +29688,7 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
28936
29688
|
async ({ limit }) => {
|
|
28937
29689
|
const safeLimit = limit != null ? coerceNumber(limit, 3) : 3;
|
|
28938
29690
|
const { getSessionContext: getSessionContext2, listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
28939
|
-
const context = await getSessionContext2(projectDir2, project.id, safeLimit);
|
|
29691
|
+
const context = await getSessionContext2(projectDir2, project.id, safeLimit, getObservationReader());
|
|
28940
29692
|
const sessions = await listSessions2(projectDir2, project.id);
|
|
28941
29693
|
const activeSessions = sessions.filter((s) => s.status === "active");
|
|
28942
29694
|
const completedSessions = sessions.filter((s) => s.status === "completed");
|
|
@@ -28961,7 +29713,7 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
28961
29713
|
"memorix_transfer",
|
|
28962
29714
|
{
|
|
28963
29715
|
title: "Transfer Memories",
|
|
28964
|
-
description: 'Export or import project memories. Action "export": export observations and sessions (JSON or Markdown). Action "import": import from a JSON export (re-assigns IDs, skips duplicate topicKeys).',
|
|
29716
|
+
description: 'Export or import project memories. Action "export": export observations visible to the current agent and project sessions (JSON or Markdown). Action "import": import from a JSON export (re-assigns IDs, skips duplicate topicKeys).',
|
|
28965
29717
|
inputSchema: {
|
|
28966
29718
|
action: z2.enum(["export", "import"]).describe("Operation: export or import"),
|
|
28967
29719
|
format: z2.enum(["json", "markdown"]).optional().describe("Export format (for export, default: json)"),
|
|
@@ -28972,10 +29724,10 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
28972
29724
|
if (action === "export") {
|
|
28973
29725
|
const { exportAsJson: exportAsJson2, exportAsMarkdown: exportAsMarkdown2 } = await Promise.resolve().then(() => (init_export_import(), export_import_exports));
|
|
28974
29726
|
if (format === "markdown") {
|
|
28975
|
-
const md = await exportAsMarkdown2(projectDir2, project.id);
|
|
29727
|
+
const md = await exportAsMarkdown2(projectDir2, project.id, getObservationReader());
|
|
28976
29728
|
return { content: [{ type: "text", text: md }] };
|
|
28977
29729
|
}
|
|
28978
|
-
const data = await exportAsJson2(projectDir2, project.id);
|
|
29730
|
+
const data = await exportAsJson2(projectDir2, project.id, getObservationReader());
|
|
28979
29731
|
const json = JSON.stringify(data, null, 2);
|
|
28980
29732
|
return {
|
|
28981
29733
|
content: [{
|
|
@@ -29172,8 +29924,6 @@ ${json}
|
|
|
29172
29924
|
}
|
|
29173
29925
|
}
|
|
29174
29926
|
);
|
|
29175
|
-
let teamStore;
|
|
29176
|
-
let initTeamStoreForProject;
|
|
29177
29927
|
if (teamFeaturesEnabled) {
|
|
29178
29928
|
const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
|
|
29179
29929
|
initTeamStoreForProject = initTeamStore2;
|
|
@@ -29446,7 +30196,7 @@ ${lines.join("\n")}` }] };
|
|
|
29446
30196
|
description: `Send, broadcast, or read messages between agents. Durable: messages survive restarts and reach inactive recipients. Action "send": direct message to one agent. Action "broadcast": message all agents. Action "inbox": read this agent's inbox.`,
|
|
29447
30197
|
inputSchema: {
|
|
29448
30198
|
action: z2.enum(["send", "broadcast", "inbox"]).describe("Operation to perform"),
|
|
29449
|
-
from: z2.string().optional().describe("
|
|
30199
|
+
from: z2.string().optional().describe("Your sender agent ID. Omit it to use this session identity."),
|
|
29450
30200
|
to: z2.string().optional().describe("Receiver agent ID (for send)"),
|
|
29451
30201
|
type: z2.enum(["request", "response", "info", "announcement", "contract", "error", "handoff"]).optional().describe("Message type (for send/broadcast)"),
|
|
29452
30202
|
content: z2.string().optional().describe("Message content (for send/broadcast)"),
|
|
@@ -29457,13 +30207,20 @@ ${lines.join("\n")}` }] };
|
|
|
29457
30207
|
}
|
|
29458
30208
|
},
|
|
29459
30209
|
async ({ action, from, to, type: msgType, content, agentId, markRead, toRole, handoffStatus }) => {
|
|
30210
|
+
const requireCurrentTeamAgent = () => {
|
|
30211
|
+
if (!currentAgentId) return null;
|
|
30212
|
+
const agent = teamStore.getAgent(currentAgentId);
|
|
30213
|
+
return agent?.project_id === project.id && agent.status === "active" ? agent : null;
|
|
30214
|
+
};
|
|
29460
30215
|
if (action === "send") {
|
|
29461
|
-
|
|
30216
|
+
const sender = requireCurrentTeamAgent();
|
|
30217
|
+
if (!sender || !msgType || !content) return { content: [{ type: "text", text: "[ERROR] active session identity, type, and content required for send" }], isError: true };
|
|
30218
|
+
if (from && from !== currentAgentId) return { content: [{ type: "text", text: "[ERROR] from must match the current session identity" }], isError: true };
|
|
29462
30219
|
if (!to && !toRole) return { content: [{ type: "text", text: "[ERROR] either to (agent ID) or toRole is required for send" }], isError: true };
|
|
29463
30220
|
if (content.length > 1e4) return { content: [{ type: "text", text: "[ERROR] Message too large (max 10KB)" }], isError: true };
|
|
29464
30221
|
const msg = teamStore.sendMessage({
|
|
29465
30222
|
projectId: project.id,
|
|
29466
|
-
senderAgentId:
|
|
30223
|
+
senderAgentId: currentAgentId,
|
|
29467
30224
|
recipientAgentId: to ?? null,
|
|
29468
30225
|
type: msgType,
|
|
29469
30226
|
content,
|
|
@@ -29475,11 +30232,13 @@ ${lines.join("\n")}` }] };
|
|
|
29475
30232
|
return { content: [{ type: "text", text: `Message sent (${msgType}) to ${target} | ID: ${msg.id.slice(0, 8)}\u2026${toRole ? ` [role: ${toRole}]` : ""}` }] };
|
|
29476
30233
|
}
|
|
29477
30234
|
if (action === "broadcast") {
|
|
29478
|
-
|
|
30235
|
+
const sender = requireCurrentTeamAgent();
|
|
30236
|
+
if (!sender || !msgType || !content) return { content: [{ type: "text", text: "[ERROR] active session identity, type, and content required for broadcast" }], isError: true };
|
|
30237
|
+
if (from && from !== currentAgentId) return { content: [{ type: "text", text: "[ERROR] from must match the current session identity" }], isError: true };
|
|
29479
30238
|
if (content.length > 1e4) return { content: [{ type: "text", text: "[ERROR] Message too large (max 10KB)" }], isError: true };
|
|
29480
30239
|
const msg = teamStore.sendMessage({
|
|
29481
30240
|
projectId: project.id,
|
|
29482
|
-
senderAgentId:
|
|
30241
|
+
senderAgentId: currentAgentId,
|
|
29483
30242
|
recipientAgentId: null,
|
|
29484
30243
|
type: msgType,
|
|
29485
30244
|
content
|
|
@@ -29487,8 +30246,12 @@ ${lines.join("\n")}` }] };
|
|
|
29487
30246
|
if ("error" in msg) return { content: [{ type: "text", text: `[ERROR] ${msg.error}` }], isError: true };
|
|
29488
30247
|
return { content: [{ type: "text", text: `Broadcast (${msgType}) | ID: ${msg.id.slice(0, 8)}\u2026` }] };
|
|
29489
30248
|
}
|
|
29490
|
-
const
|
|
29491
|
-
if (!
|
|
30249
|
+
const inboxAgent = requireCurrentTeamAgent();
|
|
30250
|
+
if (!inboxAgent) return { content: [{ type: "text", text: "[ERROR] active session identity required for inbox" }], isError: true };
|
|
30251
|
+
if (agentId && agentId !== currentAgentId || from && from !== currentAgentId) {
|
|
30252
|
+
return { content: [{ type: "text", text: "[ERROR] inbox access is limited to the current session identity" }], isError: true };
|
|
30253
|
+
}
|
|
30254
|
+
const inboxId = currentAgentId;
|
|
29492
30255
|
const inbox = teamStore.getInbox(project.id, inboxId);
|
|
29493
30256
|
const unread = teamStore.getUnreadCount(project.id, inboxId);
|
|
29494
30257
|
if (inbox.length === 0) return { content: [{ type: "text", text: "Inbox empty" }] };
|
|
@@ -29516,24 +30279,34 @@ ${lines.join("\n")}` }] };
|
|
|
29516
30279
|
},
|
|
29517
30280
|
async ({ agentId, markInboxRead }) => {
|
|
29518
30281
|
const { computeWatermark: computeWatermark2, computePoll: computePoll2 } = await Promise.resolve().then(() => (init_poll(), poll_exports));
|
|
30282
|
+
if (agentId && agentId !== currentAgentId) {
|
|
30283
|
+
return {
|
|
30284
|
+
content: [{ type: "text", text: "[ERROR] agentId must match the current session identity." }],
|
|
30285
|
+
isError: true
|
|
30286
|
+
};
|
|
30287
|
+
}
|
|
30288
|
+
const effectiveAgentId = currentAgentId;
|
|
29519
30289
|
let watermark = computeWatermark2(0, 0, 0);
|
|
29520
|
-
if (
|
|
29521
|
-
const agent = teamStore.getAgent(
|
|
30290
|
+
if (effectiveAgentId) {
|
|
30291
|
+
const agent = teamStore.getAgent(effectiveAgentId);
|
|
29522
30292
|
if (agent) {
|
|
29523
30293
|
const lastSeen = agent.last_seen_obs_generation;
|
|
29524
30294
|
const store = getObservationStore();
|
|
29525
30295
|
const currentGen = store.getGeneration();
|
|
29526
|
-
const projectObs = await withFreshIndex(() =>
|
|
29527
|
-
(
|
|
30296
|
+
const projectObs = await withFreshIndex(() => filterReadableObservations(
|
|
30297
|
+
getAllObservations().filter(
|
|
30298
|
+
(o) => o.projectId === project.id && (o.writeGeneration ?? 0) > lastSeen
|
|
30299
|
+
),
|
|
30300
|
+
getObservationReader()
|
|
29528
30301
|
));
|
|
29529
30302
|
watermark = computeWatermark2(lastSeen, currentGen, projectObs.length);
|
|
29530
|
-
teamStore.updateWatermark(
|
|
29531
|
-
teamStore.heartbeat(
|
|
30303
|
+
teamStore.updateWatermark(effectiveAgentId, currentGen);
|
|
30304
|
+
teamStore.heartbeat(effectiveAgentId);
|
|
29532
30305
|
}
|
|
29533
30306
|
}
|
|
29534
|
-
const poll = computePoll2(teamStore, project.id,
|
|
29535
|
-
if (markInboxRead &&
|
|
29536
|
-
teamStore.markAllRead(project.id,
|
|
30307
|
+
const poll = computePoll2(teamStore, project.id, effectiveAgentId ?? null, watermark);
|
|
30308
|
+
if (markInboxRead && effectiveAgentId) {
|
|
30309
|
+
teamStore.markAllRead(project.id, effectiveAgentId);
|
|
29537
30310
|
}
|
|
29538
30311
|
const lines = [];
|
|
29539
30312
|
if (poll.agent) {
|
|
@@ -29593,7 +30366,7 @@ ${lines.join("\n")}` }] };
|
|
|
29593
30366
|
"memorix_handoff",
|
|
29594
30367
|
{
|
|
29595
30368
|
title: "Team Handoff \u2014 Agent Context Transfer",
|
|
29596
|
-
description: "Create a structured handoff artifact when passing work to another agent. The handoff is stored as a durable observation (
|
|
30369
|
+
description: "Create a structured handoff artifact when passing work to another agent. A targeted handoff is visible only to its sender and recipient; a broadcast handoff is team-visible. The handoff is stored as a durable observation (immune to archival) and a notification message is sent to the recipient. Use this when completing a task and another agent should continue, or when you want to leave context for whoever works on this next.",
|
|
29597
30370
|
inputSchema: {
|
|
29598
30371
|
fromAgentId: z2.string().describe("Your agent ID (from team_manage join or session_start with joinTeam=true)"),
|
|
29599
30372
|
summary: z2.string().describe("Human-readable summary of what you did and what needs to happen next"),
|
|
@@ -29606,6 +30379,34 @@ ${lines.join("\n")}` }] };
|
|
|
29606
30379
|
},
|
|
29607
30380
|
async ({ fromAgentId, summary, context, toAgentId, taskId, filesModified, concepts }) => {
|
|
29608
30381
|
const { createHandoffArtifact: createHandoffArtifact2 } = await Promise.resolve().then(() => (init_handoff(), handoff_exports));
|
|
30382
|
+
if (!currentAgentId) {
|
|
30383
|
+
return {
|
|
30384
|
+
content: [{ type: "text", text: "Create a coordination identity first: call memorix_session_start with joinTeam=true." }],
|
|
30385
|
+
isError: true
|
|
30386
|
+
};
|
|
30387
|
+
}
|
|
30388
|
+
if (fromAgentId !== currentAgentId) {
|
|
30389
|
+
return {
|
|
30390
|
+
content: [{ type: "text", text: "fromAgentId must match the identity returned for this session. Memorix will not create a handoff on behalf of another agent." }],
|
|
30391
|
+
isError: true
|
|
30392
|
+
};
|
|
30393
|
+
}
|
|
30394
|
+
const sender = teamStore.getAgent(currentAgentId);
|
|
30395
|
+
if (!sender || sender.project_id !== project.id || sender.status !== "active") {
|
|
30396
|
+
return {
|
|
30397
|
+
content: [{ type: "text", text: "The current coordination identity is not an active member of this project." }],
|
|
30398
|
+
isError: true
|
|
30399
|
+
};
|
|
30400
|
+
}
|
|
30401
|
+
if (toAgentId) {
|
|
30402
|
+
const recipient = teamStore.getAgent(toAgentId);
|
|
30403
|
+
if (!recipient || recipient.project_id !== project.id) {
|
|
30404
|
+
return {
|
|
30405
|
+
content: [{ type: "text", text: "The handoff recipient must be an agent registered in the current project." }],
|
|
30406
|
+
isError: true
|
|
30407
|
+
};
|
|
30408
|
+
}
|
|
30409
|
+
}
|
|
29609
30410
|
const result = await createHandoffArtifact2(
|
|
29610
30411
|
{
|
|
29611
30412
|
projectId: project.id,
|