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/sdk.js
CHANGED
|
@@ -32,6 +32,58 @@ var init_esm_shims = __esm({
|
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
+
// src/memory/visibility.ts
|
|
36
|
+
function resolveObservationVisibility(record) {
|
|
37
|
+
switch (record.visibility) {
|
|
38
|
+
case "personal":
|
|
39
|
+
case "team":
|
|
40
|
+
case "project":
|
|
41
|
+
return record.visibility;
|
|
42
|
+
default:
|
|
43
|
+
return "project";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function sharedAgentIds(record) {
|
|
47
|
+
if (Array.isArray(record.sharedWithAgentIds)) return record.sharedWithAgentIds;
|
|
48
|
+
if (typeof record.sharedWithAgentIds !== "string" || !record.sharedWithAgentIds) return [];
|
|
49
|
+
try {
|
|
50
|
+
const parsed = JSON.parse(record.sharedWithAgentIds);
|
|
51
|
+
return Array.isArray(parsed) ? parsed.filter((id) => typeof id === "string") : [];
|
|
52
|
+
} catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function canReadObservation(record, reader) {
|
|
57
|
+
if (!reader) return true;
|
|
58
|
+
const sameProject = reader.projectId === record.projectId;
|
|
59
|
+
const visibility = resolveObservationVisibility(record);
|
|
60
|
+
if (visibility === "project") return !reader.projectId || sameProject;
|
|
61
|
+
if (!sameProject || !reader.agentId) return false;
|
|
62
|
+
if (visibility === "team") return reader.isTeamMember === true;
|
|
63
|
+
return record.createdByAgentId === reader.agentId || sharedAgentIds(record).includes(reader.agentId);
|
|
64
|
+
}
|
|
65
|
+
function canManageObservation(record, reader) {
|
|
66
|
+
if (!reader) return true;
|
|
67
|
+
if (!reader.projectId || reader.projectId !== record.projectId) return false;
|
|
68
|
+
switch (resolveObservationVisibility(record)) {
|
|
69
|
+
case "project":
|
|
70
|
+
return true;
|
|
71
|
+
case "team":
|
|
72
|
+
return reader.isTeamMember === true;
|
|
73
|
+
case "personal":
|
|
74
|
+
return Boolean(reader.agentId && record.createdByAgentId === reader.agentId);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function filterReadableObservations(observations2, reader) {
|
|
78
|
+
return reader ? observations2.filter((observation) => canReadObservation(observation, reader)) : [...observations2];
|
|
79
|
+
}
|
|
80
|
+
var init_visibility = __esm({
|
|
81
|
+
"src/memory/visibility.ts"() {
|
|
82
|
+
"use strict";
|
|
83
|
+
init_esm_shims();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
35
87
|
// src/store/bun-sqlite-compat.ts
|
|
36
88
|
import { createRequire } from "module";
|
|
37
89
|
function loadSqlite() {
|
|
@@ -208,7 +260,11 @@ CREATE TABLE IF NOT EXISTS observations (
|
|
|
208
260
|
relatedCommits TEXT,
|
|
209
261
|
relatedEntities TEXT,
|
|
210
262
|
sourceDetail TEXT,
|
|
211
|
-
valueCategory TEXT
|
|
263
|
+
valueCategory TEXT,
|
|
264
|
+
admissionState TEXT,
|
|
265
|
+
admissionReason TEXT,
|
|
266
|
+
visibility TEXT,
|
|
267
|
+
sharedWithAgentIds TEXT
|
|
212
268
|
);
|
|
213
269
|
`;
|
|
214
270
|
CREATE_MINI_SKILLS_TABLE = `
|
|
@@ -631,6 +687,8 @@ CREATE INDEX IF NOT EXISTS idx_observations_projectId ON observations(projectId)
|
|
|
631
687
|
CREATE INDEX IF NOT EXISTS idx_observations_topicKey ON observations(projectId, topicKey);
|
|
632
688
|
CREATE INDEX IF NOT EXISTS idx_observations_status ON observations(status);
|
|
633
689
|
CREATE INDEX IF NOT EXISTS idx_observations_project_status_id ON observations(projectId, status, id);
|
|
690
|
+
CREATE INDEX IF NOT EXISTS idx_observations_project_admission ON observations(projectId, status, admissionState, id);
|
|
691
|
+
CREATE INDEX IF NOT EXISTS idx_observations_project_visibility ON observations(projectId, status, visibility, id);
|
|
634
692
|
CREATE INDEX IF NOT EXISTS idx_mini_skills_projectId ON mini_skills(projectId);
|
|
635
693
|
CREATE INDEX IF NOT EXISTS idx_sessions_projectId ON sessions(projectId);
|
|
636
694
|
CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(projectId, status);
|
|
@@ -675,6 +733,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
|
675
733
|
WHERE status IN ('pending', 'running', 'retry');
|
|
676
734
|
`;
|
|
677
735
|
SCHEMA_MIGRATIONS = [
|
|
736
|
+
{
|
|
737
|
+
id: "1.2.2-observation-admission",
|
|
738
|
+
apply: (db2) => {
|
|
739
|
+
addColumnIfMissing(db2, "observations", "admissionState", "admissionState TEXT");
|
|
740
|
+
addColumnIfMissing(db2, "observations", "admissionReason", "admissionReason TEXT");
|
|
741
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_observations_project_admission ON observations(projectId, status, admissionState, id)");
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
id: "1.2.2-observation-visibility",
|
|
746
|
+
apply: (db2) => {
|
|
747
|
+
addColumnIfMissing(db2, "observations", "visibility", "visibility TEXT");
|
|
748
|
+
addColumnIfMissing(db2, "observations", "sharedWithAgentIds", "sharedWithAgentIds TEXT");
|
|
749
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_observations_project_visibility ON observations(projectId, status, visibility, id)");
|
|
750
|
+
}
|
|
751
|
+
},
|
|
678
752
|
{
|
|
679
753
|
id: "1.2-code-state-snapshots",
|
|
680
754
|
apply: (db2) => {
|
|
@@ -5349,7 +5423,8 @@ __export(orama_store_exports, {
|
|
|
5349
5423
|
makeOramaObservationId: () => makeOramaObservationId,
|
|
5350
5424
|
removeObservation: () => removeObservation,
|
|
5351
5425
|
resetDb: () => resetDb,
|
|
5352
|
-
searchObservations: () => searchObservations
|
|
5426
|
+
searchObservations: () => searchObservations,
|
|
5427
|
+
updateObservationMetadata: () => updateObservationMetadata
|
|
5353
5428
|
});
|
|
5354
5429
|
import { create, insert as insert2, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
5355
5430
|
function getLastSearchMode(projectId) {
|
|
@@ -5427,6 +5502,11 @@ async function initializeDb(options, generation) {
|
|
|
5427
5502
|
source: "string",
|
|
5428
5503
|
sourceDetail: "string",
|
|
5429
5504
|
valueCategory: "string",
|
|
5505
|
+
admissionState: "string",
|
|
5506
|
+
admissionReason: "string",
|
|
5507
|
+
visibility: "string",
|
|
5508
|
+
createdByAgentId: "string",
|
|
5509
|
+
sharedWithAgentIds: "string",
|
|
5430
5510
|
documentType: "string",
|
|
5431
5511
|
knowledgeLayer: "string"
|
|
5432
5512
|
};
|
|
@@ -5565,6 +5645,13 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
5565
5645
|
lastAccessedAt: obs.lastAccessedAt || "",
|
|
5566
5646
|
status: obs.status ?? "active",
|
|
5567
5647
|
source: obs.source || "agent",
|
|
5648
|
+
sourceDetail: obs.sourceDetail ?? "",
|
|
5649
|
+
valueCategory: obs.valueCategory ?? "",
|
|
5650
|
+
admissionState: obs.admissionState ?? "",
|
|
5651
|
+
admissionReason: obs.admissionReason ?? "",
|
|
5652
|
+
visibility: obs.visibility ?? "project",
|
|
5653
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
5654
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
5568
5655
|
documentType: "observation",
|
|
5569
5656
|
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
5570
5657
|
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
@@ -5598,6 +5685,16 @@ async function insertObservation(doc) {
|
|
|
5598
5685
|
await insert2(database, doc);
|
|
5599
5686
|
rememberObservationDoc(doc);
|
|
5600
5687
|
}
|
|
5688
|
+
async function updateObservationMetadata(projectId, observationId2, patch) {
|
|
5689
|
+
const database = await getDb();
|
|
5690
|
+
const id = makeOramaObservationId(projectId, observationId2);
|
|
5691
|
+
const existing = getByID(database, id);
|
|
5692
|
+
if (!existing) return false;
|
|
5693
|
+
const next = { ...existing, ...patch };
|
|
5694
|
+
await update(database, id, next);
|
|
5695
|
+
rememberObservationDoc(next);
|
|
5696
|
+
return true;
|
|
5697
|
+
}
|
|
5601
5698
|
async function removeObservation(oramaId) {
|
|
5602
5699
|
const database = await getDb();
|
|
5603
5700
|
await remove2(database, oramaId);
|
|
@@ -5751,7 +5848,7 @@ async function searchObservations(options) {
|
|
|
5751
5848
|
if (!projectIds) return true;
|
|
5752
5849
|
const doc = hit.document;
|
|
5753
5850
|
return projectIds.includes(doc.projectId);
|
|
5754
|
-
}).filter((hit) => {
|
|
5851
|
+
}).filter((hit) => canReadObservation(hit.document, options.reader)).filter((hit) => {
|
|
5755
5852
|
if (statusFilter === "all") return true;
|
|
5756
5853
|
const doc = hit.document;
|
|
5757
5854
|
return (doc.status || "active") === statusFilter;
|
|
@@ -5782,6 +5879,8 @@ async function searchObservations(options) {
|
|
|
5782
5879
|
source: doc.source || "agent",
|
|
5783
5880
|
sourceDetail: doc.sourceDetail || void 0,
|
|
5784
5881
|
valueCategory: doc.valueCategory || void 0,
|
|
5882
|
+
admissionState: doc.admissionState || void 0,
|
|
5883
|
+
visibility: doc.visibility || void 0,
|
|
5785
5884
|
entityName: doc.entityName || void 0,
|
|
5786
5885
|
documentType: doc.documentType || "observation",
|
|
5787
5886
|
knowledgeLayer: doc.knowledgeLayer || "project-truth",
|
|
@@ -5825,6 +5924,12 @@ async function searchObservations(options) {
|
|
|
5825
5924
|
score: isCommandStyleEntry(entry.title) ? entry.score * 0.3 : entry.score
|
|
5826
5925
|
}));
|
|
5827
5926
|
}
|
|
5927
|
+
if (hasQuery) {
|
|
5928
|
+
const qualifiedEntries = intermediate.filter(
|
|
5929
|
+
(entry) => entry.admissionState !== "candidate" && entry.admissionState !== "ephemeral"
|
|
5930
|
+
);
|
|
5931
|
+
if (qualifiedEntries.length > 0) intermediate = qualifiedEntries;
|
|
5932
|
+
}
|
|
5828
5933
|
if (intentResult?.preferChronological) {
|
|
5829
5934
|
intermediate.sort((a, b) => new Date(b.rawTime).getTime() - new Date(a.rawTime).getTime());
|
|
5830
5935
|
} else {
|
|
@@ -5968,7 +6073,8 @@ async function searchObservations(options) {
|
|
|
5968
6073
|
}
|
|
5969
6074
|
let entries = intermediate.map(({ rawTime: _, _isCommandLog: _c, ...rest }) => rest);
|
|
5970
6075
|
for (const hit of results.hits) {
|
|
5971
|
-
|
|
6076
|
+
const doc = hit.document;
|
|
6077
|
+
if (canReadObservation(doc, options.reader)) rememberObservationDoc(doc);
|
|
5972
6078
|
}
|
|
5973
6079
|
if (hasQuery && originalQuery) {
|
|
5974
6080
|
const queryLower = originalQuery.toLowerCase();
|
|
@@ -6012,7 +6118,8 @@ async function searchObservations(options) {
|
|
|
6012
6118
|
entries = applyTokenBudget(entries, options.maxTokens);
|
|
6013
6119
|
}
|
|
6014
6120
|
if (options.trackAccess !== false) {
|
|
6015
|
-
const
|
|
6121
|
+
const returnedKeys = new Set(entries.map((entry) => makeEntryKey(entry.projectId, entry.id)));
|
|
6122
|
+
const hitDocs = results.hits.map((hit) => ({ id: hit.id, doc: hit.document })).filter(({ doc }) => returnedKeys.has(makeEntryKey(doc.projectId, doc.observationId)));
|
|
6016
6123
|
recordAccessBatch(hitDocs).catch(() => {
|
|
6017
6124
|
});
|
|
6018
6125
|
}
|
|
@@ -6044,11 +6151,11 @@ async function getObservationsByIds(ids, projectId) {
|
|
|
6044
6151
|
}
|
|
6045
6152
|
return results;
|
|
6046
6153
|
}
|
|
6047
|
-
async function getTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3) {
|
|
6154
|
+
async function getTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3, reader) {
|
|
6048
6155
|
const { withFreshIndex: withFreshIndex2 } = await Promise.resolve().then(() => (init_freshness(), freshness_exports));
|
|
6049
6156
|
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
6050
6157
|
const rawObs = await withFreshIndex2(() => getAllObservations2());
|
|
6051
|
-
const allObs = projectId ? rawObs.filter((o) => o.projectId === projectId) : rawObs;
|
|
6158
|
+
const allObs = (projectId ? rawObs.filter((o) => o.projectId === projectId) : rawObs).filter((observation) => canReadObservation(observation, reader));
|
|
6052
6159
|
const sorted = allObs.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
6053
6160
|
const anchorIndex = sorted.findIndex((o) => o.id === anchorId);
|
|
6054
6161
|
if (anchorIndex === -1) {
|
|
@@ -6065,7 +6172,9 @@ async function getTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3)
|
|
|
6065
6172
|
tokens: obs.tokens,
|
|
6066
6173
|
source: obs.source || void 0,
|
|
6067
6174
|
sourceDetail: obs.sourceDetail || void 0,
|
|
6068
|
-
valueCategory: obs.valueCategory || void 0
|
|
6175
|
+
valueCategory: obs.valueCategory || void 0,
|
|
6176
|
+
admissionState: obs.admissionState || void 0,
|
|
6177
|
+
visibility: obs.visibility || void 0
|
|
6069
6178
|
};
|
|
6070
6179
|
};
|
|
6071
6180
|
const before = sorted.slice(Math.max(0, anchorIndex - depthBefore), anchorIndex).map(toIndexEntry);
|
|
@@ -6133,6 +6242,7 @@ var init_orama_store = __esm({
|
|
|
6133
6242
|
init_esm_shims();
|
|
6134
6243
|
init_types();
|
|
6135
6244
|
init_mini_skills();
|
|
6245
|
+
init_visibility();
|
|
6136
6246
|
init_provider();
|
|
6137
6247
|
init_project_affinity();
|
|
6138
6248
|
init_intent_detector();
|
|
@@ -6189,6 +6299,10 @@ function obsToRow(obs) {
|
|
|
6189
6299
|
relatedEntities: obs.relatedEntities ? JSON.stringify(obs.relatedEntities) : null,
|
|
6190
6300
|
sourceDetail: obs.sourceDetail ?? null,
|
|
6191
6301
|
valueCategory: obs.valueCategory ?? null,
|
|
6302
|
+
admissionState: obs.admissionState ?? null,
|
|
6303
|
+
admissionReason: obs.admissionReason ?? null,
|
|
6304
|
+
visibility: obs.visibility ?? null,
|
|
6305
|
+
sharedWithAgentIds: obs.sharedWithAgentIds ? JSON.stringify(obs.sharedWithAgentIds) : null,
|
|
6192
6306
|
createdByAgentId: obs.createdByAgentId ?? null,
|
|
6193
6307
|
writeGeneration: obs.writeGeneration ?? 0
|
|
6194
6308
|
};
|
|
@@ -6219,6 +6333,10 @@ function rowToObs(row) {
|
|
|
6219
6333
|
...row.relatedEntities ? { relatedEntities: safeJsonParse3(row.relatedEntities, []) } : {},
|
|
6220
6334
|
...row.sourceDetail ? { sourceDetail: row.sourceDetail } : {},
|
|
6221
6335
|
...row.valueCategory ? { valueCategory: row.valueCategory } : {},
|
|
6336
|
+
...row.admissionState ? { admissionState: row.admissionState } : {},
|
|
6337
|
+
...row.admissionReason ? { admissionReason: row.admissionReason } : {},
|
|
6338
|
+
...row.visibility ? { visibility: row.visibility } : {},
|
|
6339
|
+
...row.sharedWithAgentIds ? { sharedWithAgentIds: safeJsonParse3(row.sharedWithAgentIds, []) } : {},
|
|
6222
6340
|
...row.createdByAgentId ? { createdByAgentId: row.createdByAgentId } : {},
|
|
6223
6341
|
...row.writeGeneration ? { writeGeneration: row.writeGeneration } : {}
|
|
6224
6342
|
};
|
|
@@ -6270,12 +6388,14 @@ var init_sqlite_store = __esm({
|
|
|
6270
6388
|
(id, entityName, type, title, narrative, facts, filesModified, concepts, tokens,
|
|
6271
6389
|
createdAt, updatedAt, projectId, hasCausalLanguage, topicKey, revisionCount,
|
|
6272
6390
|
sessionId, status, progress, source, commitHash, relatedCommits, relatedEntities,
|
|
6273
|
-
sourceDetail, valueCategory,
|
|
6391
|
+
sourceDetail, valueCategory, admissionState, admissionReason, visibility, sharedWithAgentIds,
|
|
6392
|
+
createdByAgentId, writeGeneration)
|
|
6274
6393
|
VALUES
|
|
6275
6394
|
(@id, @entityName, @type, @title, @narrative, @facts, @filesModified, @concepts, @tokens,
|
|
6276
6395
|
@createdAt, @updatedAt, @projectId, @hasCausalLanguage, @topicKey, @revisionCount,
|
|
6277
6396
|
@sessionId, @status, @progress, @source, @commitHash, @relatedCommits, @relatedEntities,
|
|
6278
|
-
@sourceDetail, @valueCategory, @
|
|
6397
|
+
@sourceDetail, @valueCategory, @admissionState, @admissionReason, @visibility, @sharedWithAgentIds,
|
|
6398
|
+
@createdByAgentId, @writeGeneration)
|
|
6279
6399
|
`);
|
|
6280
6400
|
this.stmtUpdate = this.stmtInsert;
|
|
6281
6401
|
this.stmtSetStatus = this.db.prepare(`UPDATE observations SET status = ? WHERE id = ?`);
|
|
@@ -6827,6 +6947,7 @@ var init_maintenance_jobs = __esm({
|
|
|
6827
6947
|
"retention-archive",
|
|
6828
6948
|
"consolidation",
|
|
6829
6949
|
"codegraph-refresh",
|
|
6950
|
+
"observation-qualify",
|
|
6830
6951
|
"claim-derive",
|
|
6831
6952
|
"claim-requalification",
|
|
6832
6953
|
"knowledge-compile",
|
|
@@ -7185,7 +7306,8 @@ __export(lifecycle_exports, {
|
|
|
7185
7306
|
enqueueClaimDerivation: () => enqueueClaimDerivation,
|
|
7186
7307
|
enqueueClaimRequalification: () => enqueueClaimRequalification,
|
|
7187
7308
|
enqueueCodegraphRefresh: () => enqueueCodegraphRefresh,
|
|
7188
|
-
enqueueKnowledgeFollowups: () => enqueueKnowledgeFollowups
|
|
7309
|
+
enqueueKnowledgeFollowups: () => enqueueKnowledgeFollowups,
|
|
7310
|
+
enqueueObservationQualification: () => enqueueObservationQualification
|
|
7189
7311
|
});
|
|
7190
7312
|
function queueFor(input) {
|
|
7191
7313
|
return input.queue ?? new MaintenanceJobStore(input.dataDir);
|
|
@@ -7220,6 +7342,14 @@ function enqueueClaimDerivation(input) {
|
|
|
7220
7342
|
payload: { observationId: input.observationId }
|
|
7221
7343
|
});
|
|
7222
7344
|
}
|
|
7345
|
+
function enqueueObservationQualification(input) {
|
|
7346
|
+
queueFor(input).enqueue({
|
|
7347
|
+
projectId: input.projectId,
|
|
7348
|
+
kind: "observation-qualify",
|
|
7349
|
+
dedupeKey: "observation-qualify",
|
|
7350
|
+
payload: { source: input.source, limit: 100 }
|
|
7351
|
+
});
|
|
7352
|
+
}
|
|
7223
7353
|
function enqueueKnowledgeFollowups(input) {
|
|
7224
7354
|
const queue = queueFor(input);
|
|
7225
7355
|
const payload = {
|
|
@@ -8084,6 +8214,7 @@ __export(observations_exports, {
|
|
|
8084
8214
|
resolveObservations: () => resolveObservations,
|
|
8085
8215
|
storeObservation: () => storeObservation,
|
|
8086
8216
|
suggestTopicKey: () => suggestTopicKey,
|
|
8217
|
+
updateObservationAdmission: () => updateObservationAdmission,
|
|
8087
8218
|
withFreshObservations: () => withFreshObservations
|
|
8088
8219
|
});
|
|
8089
8220
|
function logEmbeddingFailureOnce(key, message) {
|
|
@@ -8150,6 +8281,18 @@ function queueClaimDerivation(observation) {
|
|
|
8150
8281
|
} catch {
|
|
8151
8282
|
}
|
|
8152
8283
|
}
|
|
8284
|
+
function queueObservationQualification(observation) {
|
|
8285
|
+
const dataDir = projectDir;
|
|
8286
|
+
if (!dataDir || observation.admissionState !== "candidate") return;
|
|
8287
|
+
try {
|
|
8288
|
+
enqueueObservationQualification({
|
|
8289
|
+
dataDir,
|
|
8290
|
+
projectId: observation.projectId,
|
|
8291
|
+
source: "automatic-capture:" + observation.id
|
|
8292
|
+
});
|
|
8293
|
+
} catch {
|
|
8294
|
+
}
|
|
8295
|
+
}
|
|
8153
8296
|
function isVectorCompatibleWithCurrentIndex(embedding) {
|
|
8154
8297
|
if (!embedding) return false;
|
|
8155
8298
|
const vectorDimensions = getVectorDimensions();
|
|
@@ -8205,6 +8348,9 @@ async function storeObservation(input) {
|
|
|
8205
8348
|
(o) => o.topicKey === input.topicKey && o.projectId === input.projectId
|
|
8206
8349
|
);
|
|
8207
8350
|
if (existing) {
|
|
8351
|
+
if (input.visibilityReader && !canManageObservation(existing, input.visibilityReader)) {
|
|
8352
|
+
throw new Error("Cannot update a memory outside this session's write scope.");
|
|
8353
|
+
}
|
|
8208
8354
|
return { observation: await upsertObservation(existing, input, now3), upserted: true };
|
|
8209
8355
|
}
|
|
8210
8356
|
}
|
|
@@ -8241,6 +8387,9 @@ async function storeObservation(input) {
|
|
|
8241
8387
|
if (input.topicKey) {
|
|
8242
8388
|
const diskExisting = await tx.findByTopicKey(input.projectId, input.topicKey);
|
|
8243
8389
|
if (diskExisting) {
|
|
8390
|
+
if (input.visibilityReader && !canManageObservation(diskExisting, input.visibilityReader)) {
|
|
8391
|
+
throw new Error("Cannot update a memory outside this session's write scope.");
|
|
8392
|
+
}
|
|
8244
8393
|
upsertedInsideLock = true;
|
|
8245
8394
|
observation = diskExisting;
|
|
8246
8395
|
return;
|
|
@@ -8271,6 +8420,10 @@ async function storeObservation(input) {
|
|
|
8271
8420
|
relatedEntities: input.relatedEntities,
|
|
8272
8421
|
sourceDetail: input.sourceDetail,
|
|
8273
8422
|
valueCategory: input.valueCategory,
|
|
8423
|
+
admissionState: input.admissionState,
|
|
8424
|
+
admissionReason: input.admissionReason ? sanitizeCredentials(input.admissionReason) : void 0,
|
|
8425
|
+
visibility: input.visibility ?? "project",
|
|
8426
|
+
sharedWithAgentIds: input.sharedWithAgentIds,
|
|
8274
8427
|
createdByAgentId: input.createdByAgentId,
|
|
8275
8428
|
// Predict the generation that atomic() will commit after this callback.
|
|
8276
8429
|
// bumpGeneration() runs after fn(tx) returns, incrementing by 1.
|
|
@@ -8317,6 +8470,10 @@ async function storeObservation(input) {
|
|
|
8317
8470
|
relatedEntities: input.relatedEntities,
|
|
8318
8471
|
sourceDetail: input.sourceDetail,
|
|
8319
8472
|
valueCategory: input.valueCategory,
|
|
8473
|
+
admissionState: input.admissionState,
|
|
8474
|
+
admissionReason: input.admissionReason ? sanitizeCredentials(input.admissionReason) : void 0,
|
|
8475
|
+
visibility: input.visibility ?? "project",
|
|
8476
|
+
sharedWithAgentIds: input.sharedWithAgentIds,
|
|
8320
8477
|
createdByAgentId: input.createdByAgentId,
|
|
8321
8478
|
writeGeneration: 0
|
|
8322
8479
|
};
|
|
@@ -8340,7 +8497,12 @@ async function storeObservation(input) {
|
|
|
8340
8497
|
status: "active",
|
|
8341
8498
|
source: input.source ?? "agent",
|
|
8342
8499
|
sourceDetail: input.sourceDetail ?? "",
|
|
8343
|
-
valueCategory: input.valueCategory ?? ""
|
|
8500
|
+
valueCategory: input.valueCategory ?? "",
|
|
8501
|
+
admissionState: input.admissionState ?? "",
|
|
8502
|
+
admissionReason: input.admissionReason ? sanitizeCredentials(input.admissionReason) : "",
|
|
8503
|
+
visibility: input.visibility ?? "project",
|
|
8504
|
+
createdByAgentId: input.createdByAgentId ?? "",
|
|
8505
|
+
sharedWithAgentIds: JSON.stringify(input.sharedWithAgentIds ?? [])
|
|
8344
8506
|
};
|
|
8345
8507
|
await insertObservation(doc);
|
|
8346
8508
|
};
|
|
@@ -8349,7 +8511,10 @@ async function storeObservation(input) {
|
|
|
8349
8511
|
return { observation: await upsertObservation(observation, input, now3), upserted: true };
|
|
8350
8512
|
}
|
|
8351
8513
|
await bindObservationCodeRefsBestEffort(observation);
|
|
8352
|
-
|
|
8514
|
+
queueObservationQualification(observation);
|
|
8515
|
+
if (resolveObservationVisibility(observation) === "project") {
|
|
8516
|
+
queueClaimDerivation(observation);
|
|
8517
|
+
}
|
|
8353
8518
|
const obsId = observation.id;
|
|
8354
8519
|
vectorMissingIds.add(obsId);
|
|
8355
8520
|
const searchableText = [input.title, input.narrative, ...input.facts ?? []].join(" ");
|
|
@@ -8422,6 +8587,10 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8422
8587
|
if (input.progress) existing.progress = input.progress;
|
|
8423
8588
|
if (input.sourceDetail !== void 0) existing.sourceDetail = input.sourceDetail;
|
|
8424
8589
|
if (input.valueCategory !== void 0) existing.valueCategory = input.valueCategory;
|
|
8590
|
+
if (input.admissionState !== void 0) existing.admissionState = input.admissionState;
|
|
8591
|
+
if (input.admissionReason !== void 0) existing.admissionReason = sanitizeCredentials(input.admissionReason);
|
|
8592
|
+
if (input.visibility !== void 0) existing.visibility = input.visibility;
|
|
8593
|
+
if (input.sharedWithAgentIds !== void 0) existing.sharedWithAgentIds = input.sharedWithAgentIds;
|
|
8425
8594
|
const doc = {
|
|
8426
8595
|
id: makeOramaObservationId(existing.projectId, existing.id),
|
|
8427
8596
|
observationId: existing.id,
|
|
@@ -8440,7 +8609,12 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8440
8609
|
status: "active",
|
|
8441
8610
|
source: existing.source ?? "agent",
|
|
8442
8611
|
sourceDetail: existing.sourceDetail ?? "",
|
|
8443
|
-
valueCategory: existing.valueCategory ?? ""
|
|
8612
|
+
valueCategory: existing.valueCategory ?? "",
|
|
8613
|
+
admissionState: existing.admissionState ?? "",
|
|
8614
|
+
admissionReason: existing.admissionReason ?? "",
|
|
8615
|
+
visibility: existing.visibility ?? "project",
|
|
8616
|
+
createdByAgentId: existing.createdByAgentId ?? "",
|
|
8617
|
+
sharedWithAgentIds: JSON.stringify(existing.sharedWithAgentIds ?? [])
|
|
8444
8618
|
};
|
|
8445
8619
|
const oramaId = makeOramaObservationId(existing.projectId, existing.id);
|
|
8446
8620
|
try {
|
|
@@ -8463,7 +8637,10 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8463
8637
|
await store.update(existing);
|
|
8464
8638
|
}
|
|
8465
8639
|
await bindObservationCodeRefsBestEffort(existing);
|
|
8466
|
-
|
|
8640
|
+
queueObservationQualification(existing);
|
|
8641
|
+
if (resolveObservationVisibility(existing) === "project") {
|
|
8642
|
+
queueClaimDerivation(existing);
|
|
8643
|
+
}
|
|
8467
8644
|
const searchableText = [input.title, input.narrative, ...input.facts ?? []].join(" ");
|
|
8468
8645
|
const obsId = existing.id;
|
|
8469
8646
|
vectorMissingIds.add(obsId);
|
|
@@ -8502,6 +8679,56 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8502
8679
|
function getObservation(id, projectId) {
|
|
8503
8680
|
return observations.find((o) => o.id === id && (projectId ? o.projectId === projectId : true));
|
|
8504
8681
|
}
|
|
8682
|
+
async function updateObservationAdmission(input) {
|
|
8683
|
+
await ensureFreshObservations();
|
|
8684
|
+
const cached = observations.find(
|
|
8685
|
+
(observation) => observation.id === input.observationId && (!input.projectId || observation.projectId === input.projectId)
|
|
8686
|
+
);
|
|
8687
|
+
if (!cached) return void 0;
|
|
8688
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
8689
|
+
const admissionReason = sanitizeCredentials(input.admissionReason).slice(0, 240);
|
|
8690
|
+
const apply = (current) => {
|
|
8691
|
+
if (input.projectId && current.projectId !== input.projectId) return void 0;
|
|
8692
|
+
if (input.expectedState && current.admissionState !== input.expectedState) return void 0;
|
|
8693
|
+
return {
|
|
8694
|
+
...current,
|
|
8695
|
+
admissionState: input.admissionState,
|
|
8696
|
+
admissionReason,
|
|
8697
|
+
...input.visibility !== void 0 ? { visibility: input.visibility } : {},
|
|
8698
|
+
updatedAt: now3
|
|
8699
|
+
};
|
|
8700
|
+
};
|
|
8701
|
+
let updated;
|
|
8702
|
+
if (projectDir) {
|
|
8703
|
+
const store = getObservationStore();
|
|
8704
|
+
await store.atomic(async (tx) => {
|
|
8705
|
+
const current = await tx.getById(input.observationId);
|
|
8706
|
+
if (!current) return;
|
|
8707
|
+
const next = apply(current);
|
|
8708
|
+
if (!next) return;
|
|
8709
|
+
await tx.update(next);
|
|
8710
|
+
updated = next;
|
|
8711
|
+
});
|
|
8712
|
+
} else {
|
|
8713
|
+
updated = apply(cached);
|
|
8714
|
+
}
|
|
8715
|
+
if (!updated) return void 0;
|
|
8716
|
+
observations = observations.map(
|
|
8717
|
+
(observation) => observation.id === updated.id && observation.projectId === updated.projectId ? updated : observation
|
|
8718
|
+
);
|
|
8719
|
+
try {
|
|
8720
|
+
await updateObservationMetadata(updated.projectId, updated.id, {
|
|
8721
|
+
admissionState: updated.admissionState,
|
|
8722
|
+
admissionReason: updated.admissionReason,
|
|
8723
|
+
...updated.visibility !== void 0 ? { visibility: updated.visibility } : {}
|
|
8724
|
+
});
|
|
8725
|
+
} catch {
|
|
8726
|
+
}
|
|
8727
|
+
if (updated.admissionState === "qualified" && resolveObservationVisibility(updated) === "project") {
|
|
8728
|
+
queueClaimDerivation(updated);
|
|
8729
|
+
}
|
|
8730
|
+
return updated;
|
|
8731
|
+
}
|
|
8505
8732
|
async function resolveObservations(ids, status = "resolved") {
|
|
8506
8733
|
const resolved = [];
|
|
8507
8734
|
const notFound = [];
|
|
@@ -8541,7 +8768,12 @@ async function resolveObservations(ids, status = "resolved") {
|
|
|
8541
8768
|
status,
|
|
8542
8769
|
source: obs.source ?? "agent",
|
|
8543
8770
|
sourceDetail: obs.sourceDetail ?? "",
|
|
8544
|
-
valueCategory: obs.valueCategory ?? ""
|
|
8771
|
+
valueCategory: obs.valueCategory ?? "",
|
|
8772
|
+
admissionState: obs.admissionState ?? "",
|
|
8773
|
+
admissionReason: obs.admissionReason ?? "",
|
|
8774
|
+
visibility: obs.visibility ?? "project",
|
|
8775
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
8776
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
8545
8777
|
};
|
|
8546
8778
|
await insertObservation(doc);
|
|
8547
8779
|
const obsId = obs.id;
|
|
@@ -8671,6 +8903,11 @@ async function reindexObservations() {
|
|
|
8671
8903
|
source: obs.source ?? "agent",
|
|
8672
8904
|
sourceDetail: obs.sourceDetail ?? "",
|
|
8673
8905
|
valueCategory: obs.valueCategory ?? "",
|
|
8906
|
+
admissionState: obs.admissionState ?? "",
|
|
8907
|
+
admissionReason: obs.admissionReason ?? "",
|
|
8908
|
+
visibility: obs.visibility ?? "project",
|
|
8909
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
8910
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
8674
8911
|
...compatibleEmbedding ? { embedding: compatibleEmbedding } : {}
|
|
8675
8912
|
};
|
|
8676
8913
|
await insertObservation(doc);
|
|
@@ -8743,6 +8980,7 @@ async function probeSearchIndex(projectId) {
|
|
|
8743
8980
|
await searchObservations({
|
|
8744
8981
|
query: "semantic memory retrieval status",
|
|
8745
8982
|
projectId,
|
|
8983
|
+
reader: { projectId },
|
|
8746
8984
|
limit: 1,
|
|
8747
8985
|
status: "all",
|
|
8748
8986
|
trackAccess: false
|
|
@@ -8808,6 +9046,11 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
8808
9046
|
source: obs.source ?? "agent",
|
|
8809
9047
|
sourceDetail: obs.sourceDetail ?? "",
|
|
8810
9048
|
valueCategory: obs.valueCategory ?? "",
|
|
9049
|
+
admissionState: obs.admissionState ?? "",
|
|
9050
|
+
admissionReason: obs.admissionReason ?? "",
|
|
9051
|
+
visibility: obs.visibility ?? "project",
|
|
9052
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
9053
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
8811
9054
|
embedding
|
|
8812
9055
|
};
|
|
8813
9056
|
await insertObservation(doc);
|
|
@@ -8849,6 +9092,7 @@ var init_observations = __esm({
|
|
|
8849
9092
|
init_provider();
|
|
8850
9093
|
init_secret_filter();
|
|
8851
9094
|
init_lifecycle();
|
|
9095
|
+
init_visibility();
|
|
8852
9096
|
observations = [];
|
|
8853
9097
|
nextId = 1;
|
|
8854
9098
|
projectDir = null;
|
|
@@ -9128,8 +9372,9 @@ function resolveSourceDetail(sourceDetail, source) {
|
|
|
9128
9372
|
return void 0;
|
|
9129
9373
|
}
|
|
9130
9374
|
function classifyLayer(fields) {
|
|
9131
|
-
const { valueCategory } = fields;
|
|
9375
|
+
const { valueCategory, admissionState } = fields;
|
|
9132
9376
|
const sd = resolveSourceDetail(fields.sourceDetail, fields.source);
|
|
9377
|
+
if (admissionState === "candidate" || admissionState === "ephemeral") return "L1";
|
|
9133
9378
|
if (valueCategory === "core") return "L2";
|
|
9134
9379
|
if (sd === "hook") return "L1";
|
|
9135
9380
|
if (sd === "git-ingest") return "L3";
|
|
@@ -9187,6 +9432,7 @@ function getEffectiveRetentionDays(doc) {
|
|
|
9187
9432
|
}
|
|
9188
9433
|
function isImmune(doc) {
|
|
9189
9434
|
if (doc.type === "probe") return false;
|
|
9435
|
+
if (doc.admissionState === "candidate" || doc.admissionState === "ephemeral") return false;
|
|
9190
9436
|
if (doc.valueCategory === "core") return true;
|
|
9191
9437
|
const importance = getImportanceLevel(doc);
|
|
9192
9438
|
if (importance === "critical") return true;
|
|
@@ -9196,6 +9442,7 @@ function isImmune(doc) {
|
|
|
9196
9442
|
}
|
|
9197
9443
|
function getImmunityReason(doc) {
|
|
9198
9444
|
if (doc.type === "probe") return null;
|
|
9445
|
+
if (doc.admissionState === "candidate" || doc.admissionState === "ephemeral") return null;
|
|
9199
9446
|
if (doc.valueCategory === "core") return "core valueCategory (formation-classified)";
|
|
9200
9447
|
const importance = getImportanceLevel(doc);
|
|
9201
9448
|
if (importance === "critical") return "critical importance";
|
|
@@ -9328,7 +9575,9 @@ function toRetentionDocument(obs, accessMap) {
|
|
|
9328
9575
|
status: obs.status ?? "active",
|
|
9329
9576
|
source: obs.source ?? "agent",
|
|
9330
9577
|
sourceDetail: obs.sourceDetail ?? "",
|
|
9331
|
-
valueCategory: obs.valueCategory ?? ""
|
|
9578
|
+
valueCategory: obs.valueCategory ?? "",
|
|
9579
|
+
admissionState: obs.admissionState ?? "",
|
|
9580
|
+
admissionReason: obs.admissionReason ?? ""
|
|
9332
9581
|
};
|
|
9333
9582
|
}
|
|
9334
9583
|
async function archiveExpiredBatch(_projectDir, options) {
|
|
@@ -9342,7 +9591,7 @@ async function archiveExpiredBatch(_projectDir, options) {
|
|
|
9342
9591
|
});
|
|
9343
9592
|
const hasMore = page.length > limit;
|
|
9344
9593
|
const scanned = hasMore ? page.slice(0, limit) : page;
|
|
9345
|
-
const candidateIds = scanned.filter((observation) => getRetentionZone(
|
|
9594
|
+
const candidateIds = scanned.filter((observation) => !options.reader || canManageObservation(observation, options.reader)).filter((observation) => getRetentionZone(
|
|
9346
9595
|
toRetentionDocument(observation, options.accessMap),
|
|
9347
9596
|
options.referenceTime
|
|
9348
9597
|
) === "archive-candidate").map((observation) => observation.id);
|
|
@@ -9355,7 +9604,7 @@ async function archiveExpiredBatch(_projectDir, options) {
|
|
|
9355
9604
|
const nextCursor = hasMore && scanned.length > 0 ? scanned[scanned.length - 1].id : void 0;
|
|
9356
9605
|
return nextCursor === void 0 ? { archived, scanned: scanned.length } : { archived, scanned: scanned.length, nextCursor };
|
|
9357
9606
|
}
|
|
9358
|
-
async function archiveExpired(projectDir2, referenceTime, accessMap, projectId) {
|
|
9607
|
+
async function archiveExpired(projectDir2, referenceTime, accessMap, projectId, reader) {
|
|
9359
9608
|
const store = getObservationStore();
|
|
9360
9609
|
if (projectId) {
|
|
9361
9610
|
let afterId;
|
|
@@ -9365,12 +9614,14 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId)
|
|
|
9365
9614
|
projectId,
|
|
9366
9615
|
afterId,
|
|
9367
9616
|
referenceTime,
|
|
9368
|
-
accessMap
|
|
9617
|
+
accessMap,
|
|
9618
|
+
reader
|
|
9369
9619
|
});
|
|
9370
9620
|
archived += batch.archived;
|
|
9371
9621
|
afterId = batch.nextCursor;
|
|
9372
9622
|
} while (afterId !== void 0);
|
|
9373
|
-
const
|
|
9623
|
+
const remainingObservations = await store.loadByProject(projectId, { status: "active" });
|
|
9624
|
+
const remaining = reader ? remainingObservations.filter((observation) => canManageObservation(observation, reader)).length : remainingObservations.length;
|
|
9374
9625
|
return { archived, remaining };
|
|
9375
9626
|
}
|
|
9376
9627
|
return await store.atomic(async (tx) => {
|
|
@@ -9400,6 +9651,7 @@ var init_retention = __esm({
|
|
|
9400
9651
|
"use strict";
|
|
9401
9652
|
init_esm_shims();
|
|
9402
9653
|
init_obs_store();
|
|
9654
|
+
init_visibility();
|
|
9403
9655
|
RETENTION_DAYS = {
|
|
9404
9656
|
critical: 365,
|
|
9405
9657
|
high: 180,
|
|
@@ -9435,6 +9687,49 @@ var init_retention = __esm({
|
|
|
9435
9687
|
}
|
|
9436
9688
|
});
|
|
9437
9689
|
|
|
9690
|
+
// src/memory/admission.ts
|
|
9691
|
+
var admission_exports = {};
|
|
9692
|
+
__export(admission_exports, {
|
|
9693
|
+
isCandidateObservation: () => isCandidateObservation,
|
|
9694
|
+
isEligibleForAutomaticDelivery: () => isEligibleForAutomaticDelivery,
|
|
9695
|
+
isEligibleForKnowledgePromotion: () => isEligibleForKnowledgePromotion,
|
|
9696
|
+
qualifyCandidateFromCurrentCode: () => qualifyCandidateFromCurrentCode
|
|
9697
|
+
});
|
|
9698
|
+
function isEligibleForAutomaticDelivery(observation) {
|
|
9699
|
+
return observation.admissionState !== "ephemeral" && observation.admissionState !== "candidate";
|
|
9700
|
+
}
|
|
9701
|
+
function isCandidateObservation(observation) {
|
|
9702
|
+
return observation.admissionState === "candidate";
|
|
9703
|
+
}
|
|
9704
|
+
function isEligibleForKnowledgePromotion(observation) {
|
|
9705
|
+
return isEligibleForAutomaticDelivery(observation) && observation.valueCategory !== "ephemeral" && resolveObservationVisibility(observation) === "project";
|
|
9706
|
+
}
|
|
9707
|
+
function qualifyCandidateFromCurrentCode(input) {
|
|
9708
|
+
if (!isCandidateObservation(input.observation)) return void 0;
|
|
9709
|
+
if (input.observation.valueCategory === "ephemeral") return void 0;
|
|
9710
|
+
if (input.currentCodeReferenceCount <= 0) return void 0;
|
|
9711
|
+
const typeLabel = DURABLE_AUTOMATIC_TYPES.has(input.observation.type) ? "high-value automatic record" : "automatic record";
|
|
9712
|
+
return {
|
|
9713
|
+
admissionState: "qualified",
|
|
9714
|
+
admissionReason: `${typeLabel} qualified against ${input.currentCodeReferenceCount} current Code Memory reference(s)`
|
|
9715
|
+
};
|
|
9716
|
+
}
|
|
9717
|
+
var DURABLE_AUTOMATIC_TYPES;
|
|
9718
|
+
var init_admission = __esm({
|
|
9719
|
+
"src/memory/admission.ts"() {
|
|
9720
|
+
"use strict";
|
|
9721
|
+
init_esm_shims();
|
|
9722
|
+
init_visibility();
|
|
9723
|
+
DURABLE_AUTOMATIC_TYPES = /* @__PURE__ */ new Set([
|
|
9724
|
+
"decision",
|
|
9725
|
+
"gotcha",
|
|
9726
|
+
"problem-solution",
|
|
9727
|
+
"trade-off",
|
|
9728
|
+
"why-it-exists"
|
|
9729
|
+
]);
|
|
9730
|
+
}
|
|
9731
|
+
});
|
|
9732
|
+
|
|
9438
9733
|
// src/workspace/workflow-sync.ts
|
|
9439
9734
|
import matter9 from "gray-matter";
|
|
9440
9735
|
var WorkflowSyncer;
|
|
@@ -10835,6 +11130,7 @@ var init_isolated_maintenance = __esm({
|
|
|
10835
11130
|
"retention-archive",
|
|
10836
11131
|
"consolidation",
|
|
10837
11132
|
"codegraph-refresh",
|
|
11133
|
+
"observation-qualify",
|
|
10838
11134
|
"claim-derive",
|
|
10839
11135
|
"claim-requalification",
|
|
10840
11136
|
"knowledge-compile",
|
|
@@ -11270,9 +11566,10 @@ async function loadConsolidationPage(projectId, options) {
|
|
|
11270
11566
|
return hasMore && observations2.length > 0 ? { observations: observations2, nextCursor: observations2[observations2.length - 1].id } : { observations: observations2 };
|
|
11271
11567
|
}
|
|
11272
11568
|
function findClusters(observations2, threshold) {
|
|
11273
|
-
|
|
11569
|
+
const eligible = observations2.filter(isEligibleForAutomaticDelivery).filter((observation) => resolveObservationVisibility(observation) === "project");
|
|
11570
|
+
if (eligible.length < MIN_CLUSTER_SIZE) return [];
|
|
11274
11571
|
const groups = /* @__PURE__ */ new Map();
|
|
11275
|
-
for (const obs of
|
|
11572
|
+
for (const obs of eligible) {
|
|
11276
11573
|
const key = `${obs.entityName}::${obs.type}`;
|
|
11277
11574
|
const group = groups.get(key) ?? [];
|
|
11278
11575
|
group.push(obs);
|
|
@@ -11409,6 +11706,8 @@ var init_consolidation = __esm({
|
|
|
11409
11706
|
"use strict";
|
|
11410
11707
|
init_esm_shims();
|
|
11411
11708
|
init_obs_store();
|
|
11709
|
+
init_admission();
|
|
11710
|
+
init_visibility();
|
|
11412
11711
|
DEFAULT_SIMILARITY_THRESHOLD = 0.45;
|
|
11413
11712
|
HIGH_VALUE_SIMILARITY_THRESHOLD = 0.85;
|
|
11414
11713
|
HIGH_VALUE_TYPES = /* @__PURE__ */ new Set(["gotcha", "decision", "trade-off", "reasoning", "problem-solution"]);
|
|
@@ -14427,6 +14726,15 @@ function claimDerivationCursor(payload) {
|
|
|
14427
14726
|
const value = payload.cursor;
|
|
14428
14727
|
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
14429
14728
|
}
|
|
14729
|
+
function observationQualificationBatchSize(payload) {
|
|
14730
|
+
const value = payload.limit;
|
|
14731
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE;
|
|
14732
|
+
return Math.min(500, Math.max(1, Math.floor(value)));
|
|
14733
|
+
}
|
|
14734
|
+
function observationQualificationCursor(payload) {
|
|
14735
|
+
const value = payload.cursor;
|
|
14736
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
14737
|
+
}
|
|
14430
14738
|
function observationId(payload) {
|
|
14431
14739
|
const value = payload.observationId;
|
|
14432
14740
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : void 0;
|
|
@@ -14605,6 +14913,71 @@ function createProjectMaintenanceHandler(projectId, projectDir2, projectRoot, op
|
|
|
14605
14913
|
...snapshot?.id ? { snapshotId: snapshot.id } : {},
|
|
14606
14914
|
...options.maintenanceQueue ? { queue: options.maintenanceQueue } : {}
|
|
14607
14915
|
});
|
|
14916
|
+
enqueueObservationQualification({
|
|
14917
|
+
projectId,
|
|
14918
|
+
dataDir: projectDir2,
|
|
14919
|
+
source: "codegraph-refresh",
|
|
14920
|
+
...options.maintenanceQueue ? { queue: options.maintenanceQueue } : {}
|
|
14921
|
+
});
|
|
14922
|
+
return { action: "complete" };
|
|
14923
|
+
}
|
|
14924
|
+
if (job.kind === "observation-qualify") {
|
|
14925
|
+
const [
|
|
14926
|
+
{ CodeGraphStore: CodeGraphStore2 },
|
|
14927
|
+
{ bindObservationToCode: bindObservationToCode2 },
|
|
14928
|
+
{ getObservationStore: getObservationStore2 },
|
|
14929
|
+
{ qualifyCandidateFromCurrentCode: qualifyCandidateFromCurrentCode2 },
|
|
14930
|
+
{ updateObservationAdmission: updateObservationAdmission2 }
|
|
14931
|
+
] = await Promise.all([
|
|
14932
|
+
Promise.resolve().then(() => (init_store(), store_exports)),
|
|
14933
|
+
Promise.resolve().then(() => (init_binder(), binder_exports)),
|
|
14934
|
+
Promise.resolve().then(() => (init_obs_store(), obs_store_exports)),
|
|
14935
|
+
Promise.resolve().then(() => (init_admission(), admission_exports)),
|
|
14936
|
+
Promise.resolve().then(() => (init_observations(), observations_exports))
|
|
14937
|
+
]);
|
|
14938
|
+
const codeStore = new CodeGraphStore2();
|
|
14939
|
+
await codeStore.init(projectDir2);
|
|
14940
|
+
const limit = observationQualificationBatchSize(job.payload);
|
|
14941
|
+
const page = await getObservationStore2().loadByProject(projectId, {
|
|
14942
|
+
status: "active",
|
|
14943
|
+
afterId: observationQualificationCursor(job.payload),
|
|
14944
|
+
limit: limit + 1
|
|
14945
|
+
});
|
|
14946
|
+
const hasMore = page.length > limit;
|
|
14947
|
+
const observations2 = hasMore ? page.slice(0, limit) : page;
|
|
14948
|
+
const indexedAtMs = Date.parse(codeStore.status(projectId).indexedAt ?? "");
|
|
14949
|
+
for (const observation of observations2) {
|
|
14950
|
+
if (observation.admissionState !== "candidate") continue;
|
|
14951
|
+
if (!Number.isFinite(indexedAtMs) || indexedAtMs < Date.parse(observation.createdAt)) continue;
|
|
14952
|
+
await bindObservationToCode2(codeStore, observation);
|
|
14953
|
+
const currentCodeReferenceCount = codeStore.listObservationRefs(projectId, observation.id).filter((reference) => reference.status === "current").length;
|
|
14954
|
+
const qualification = qualifyCandidateFromCurrentCode2({
|
|
14955
|
+
observation,
|
|
14956
|
+
currentCodeReferenceCount
|
|
14957
|
+
});
|
|
14958
|
+
if (!qualification) continue;
|
|
14959
|
+
await updateObservationAdmission2({
|
|
14960
|
+
observationId: observation.id,
|
|
14961
|
+
projectId,
|
|
14962
|
+
expectedState: "candidate",
|
|
14963
|
+
// Automatic capture earns project visibility only after a current
|
|
14964
|
+
// Code Memory link exists. Explicit/private records retain scope.
|
|
14965
|
+
...observation.sourceDetail === "hook" ? { visibility: "project" } : {},
|
|
14966
|
+
...qualification
|
|
14967
|
+
});
|
|
14968
|
+
}
|
|
14969
|
+
if (hasMore && observations2.length > 0) {
|
|
14970
|
+
return {
|
|
14971
|
+
action: "reschedule",
|
|
14972
|
+
delayMs: 0,
|
|
14973
|
+
resetAttempts: true,
|
|
14974
|
+
payload: {
|
|
14975
|
+
...job.payload,
|
|
14976
|
+
cursor: observations2[observations2.length - 1].id,
|
|
14977
|
+
limit
|
|
14978
|
+
}
|
|
14979
|
+
};
|
|
14980
|
+
}
|
|
14608
14981
|
return { action: "complete" };
|
|
14609
14982
|
}
|
|
14610
14983
|
if (job.kind === "claim-derive") {
|
|
@@ -14770,7 +15143,7 @@ function createProjectMaintenanceDispatcher(projectId, projectDir2, projectRoot,
|
|
|
14770
15143
|
return isolatedRunner({ job, projectRoot, dataDir: projectDir2 });
|
|
14771
15144
|
};
|
|
14772
15145
|
}
|
|
14773
|
-
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;
|
|
15146
|
+
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;
|
|
14774
15147
|
var init_project_maintenance = __esm({
|
|
14775
15148
|
"src/runtime/project-maintenance.ts"() {
|
|
14776
15149
|
"use strict";
|
|
@@ -14782,6 +15155,7 @@ var init_project_maintenance = __esm({
|
|
|
14782
15155
|
DEFAULT_CONSOLIDATION_BATCH_SIZE = 200;
|
|
14783
15156
|
DEFAULT_CODEGRAPH_MAX_FILES = 5e3;
|
|
14784
15157
|
DEFAULT_CLAIM_DERIVATION_BATCH_SIZE = 100;
|
|
15158
|
+
DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE = 100;
|
|
14785
15159
|
VECTOR_RETRY_DELAY_MS = 5e3;
|
|
14786
15160
|
DEDUP_PER_PAIR_TIMEOUT_MS = 5e3;
|
|
14787
15161
|
}
|
|
@@ -14972,7 +15346,7 @@ async function startSession(projectDir2, projectId, opts) {
|
|
|
14972
15346
|
status: "active",
|
|
14973
15347
|
agent: opts?.agent
|
|
14974
15348
|
};
|
|
14975
|
-
const previousContext = await getSessionContext(projectDir2, projectId);
|
|
15349
|
+
const previousContext = await getSessionContext(projectDir2, projectId, 3, opts?.reader);
|
|
14976
15350
|
const sessionStore = getSessionStore();
|
|
14977
15351
|
const aliasSet = await resolveProjectIds(projectId);
|
|
14978
15352
|
await sessionStore.atomicRolloverInsert(session, [...aliasSet], now3);
|
|
@@ -14990,23 +15364,27 @@ async function endSession(projectDir2, sessionId, summary) {
|
|
|
14990
15364
|
await sessionStore.update(session);
|
|
14991
15365
|
return session;
|
|
14992
15366
|
}
|
|
14993
|
-
async function getSessionContext(projectDir2, projectId, limit = 3) {
|
|
15367
|
+
async function getSessionContext(projectDir2, projectId, limit = 3, reader) {
|
|
14994
15368
|
const aliasSet = await resolveProjectIds(projectId);
|
|
14995
15369
|
const [sessions, allObs] = await Promise.all([
|
|
14996
15370
|
loadAliasSessions(aliasSet),
|
|
14997
15371
|
loadAliasActiveObservations(aliasSet)
|
|
14998
15372
|
]);
|
|
15373
|
+
const readableObs = reader ? allObs.filter((observation) => {
|
|
15374
|
+
const observationReader = reader.projectId && aliasSet.has(observation.projectId) ? { ...reader, projectId: observation.projectId } : reader;
|
|
15375
|
+
return canReadObservation(observation, observationReader);
|
|
15376
|
+
}) : allObs;
|
|
14999
15377
|
const isNoisySummary = (summary) => {
|
|
15000
15378
|
if (!summary) return false;
|
|
15001
15379
|
return NOISE_PATTERNS2.some((p) => p.test(summary)) || SYSTEM_SELF_PATTERNS.some((p) => p.test(summary));
|
|
15002
15380
|
};
|
|
15003
15381
|
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);
|
|
15004
|
-
if (projectSessions.length === 0 &&
|
|
15382
|
+
if (projectSessions.length === 0 && readableObs.length === 0) {
|
|
15005
15383
|
return "";
|
|
15006
15384
|
}
|
|
15007
15385
|
const lines = [];
|
|
15008
15386
|
const projectTokens = tokenizeProjectId(projectId);
|
|
15009
|
-
const projectObs =
|
|
15387
|
+
const projectObs = readableObs.filter((obs) => !isNoiseObservation(obs) && !isSystemSelfObservation(obs));
|
|
15010
15388
|
const l2Scored = projectObs.filter((obs) => PRIORITY_TYPES.has(obs.type) && classifyLayer(obs) === "L2").map((obs) => ({ obs, score: scoreObservationForSessionContext(obs, projectTokens) })).sort((a, b) => {
|
|
15011
15389
|
if (b.score !== a.score) return b.score - a.score;
|
|
15012
15390
|
return new Date(b.obs.createdAt).getTime() - new Date(a.obs.createdAt).getTime();
|
|
@@ -15023,16 +15401,16 @@ async function getSessionContext(projectDir2, projectId, limit = 3) {
|
|
|
15023
15401
|
return true;
|
|
15024
15402
|
});
|
|
15025
15403
|
})() : l2Scored).slice(0, 5).map(({ obs }) => obs);
|
|
15026
|
-
const l1HookObs = projectObs.filter((obs) => classifyLayer(obs) === "L1").sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, 3);
|
|
15404
|
+
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);
|
|
15027
15405
|
const l3GitCount = projectObs.filter((obs) => classifyLayer(obs) === "L3").length;
|
|
15028
|
-
const totalHookCount = projectObs.filter((obs) => classifyLayer(obs) === "L1").length;
|
|
15406
|
+
const totalHookCount = projectObs.filter((obs) => isEligibleForAutomaticDelivery(obs) && classifyLayer(obs) === "L1").length;
|
|
15029
15407
|
const activeEntities = [
|
|
15030
15408
|
...new Set(l2Obs.map((o) => o.entityName).filter((n) => !!n && n.trim().length > 0))
|
|
15031
15409
|
].slice(0, 5);
|
|
15032
15410
|
const hasL1Content = l1HookObs.length > 0 || l3GitCount > 0;
|
|
15033
15411
|
if (hasL1Content) {
|
|
15034
15412
|
let graphNeighbors = [];
|
|
15035
|
-
if (activeEntities.length > 0) {
|
|
15413
|
+
if (!reader && activeEntities.length > 0) {
|
|
15036
15414
|
try {
|
|
15037
15415
|
const graphMgr = new KnowledgeGraphManager(projectDir2);
|
|
15038
15416
|
await graphMgr.init();
|
|
@@ -15151,12 +15529,14 @@ var init_session = __esm({
|
|
|
15151
15529
|
"src/memory/session.ts"() {
|
|
15152
15530
|
"use strict";
|
|
15153
15531
|
init_esm_shims();
|
|
15532
|
+
init_admission();
|
|
15154
15533
|
init_disclosure_policy();
|
|
15155
15534
|
init_aliases();
|
|
15156
15535
|
init_obs_store();
|
|
15157
15536
|
init_session_store();
|
|
15158
15537
|
init_graph();
|
|
15159
15538
|
init_secret_filter();
|
|
15539
|
+
init_visibility();
|
|
15160
15540
|
PRIORITY_TYPES = /* @__PURE__ */ new Set(["gotcha", "decision", "problem-solution", "trade-off", "discovery"]);
|
|
15161
15541
|
TYPE_EMOJI = {
|
|
15162
15542
|
"gotcha": "[DISCOVERY]",
|
|
@@ -15303,27 +15683,80 @@ function workflowOutput(selection) {
|
|
|
15303
15683
|
cautions: selection.cautions.map((caution) => short(caution, 22))
|
|
15304
15684
|
};
|
|
15305
15685
|
}
|
|
15306
|
-
function appendLine(lines, candidate, maxTokens, omitted, omittedKind) {
|
|
15686
|
+
function appendLine(lines, candidate, maxTokens, omitted, omittedKind, selected, receiptSelection) {
|
|
15307
15687
|
const next = lines.length ? lines.join("\n") + "\n" + candidate : candidate;
|
|
15308
15688
|
if (countTextTokens(next) <= maxTokens) {
|
|
15309
15689
|
lines.push(candidate);
|
|
15690
|
+
if (receiptSelection) selected?.push(receiptSelection);
|
|
15310
15691
|
return true;
|
|
15311
15692
|
}
|
|
15312
15693
|
omitted.push(omittedKind);
|
|
15313
15694
|
return false;
|
|
15314
15695
|
}
|
|
15696
|
+
function freshnessForMemory(status) {
|
|
15697
|
+
if (status === "current" || status === "suspect" || status === "stale") return status;
|
|
15698
|
+
return "unknown";
|
|
15699
|
+
}
|
|
15700
|
+
function receiptOmissionKind(raw) {
|
|
15701
|
+
if (raw.includes("task")) return "task";
|
|
15702
|
+
if (raw.includes("fact")) return "current-fact";
|
|
15703
|
+
if (raw.includes("state")) return "code-state";
|
|
15704
|
+
if (raw.includes("semantic")) return "semantic-code";
|
|
15705
|
+
if (raw.includes("start")) return "start-here";
|
|
15706
|
+
if (raw.includes("memory")) return "memory";
|
|
15707
|
+
if (raw.includes("claim")) return "claim";
|
|
15708
|
+
if (raw.includes("knowledge-page")) return "knowledge-page";
|
|
15709
|
+
if (raw.includes("workflow")) return "workflow";
|
|
15710
|
+
if (raw.includes("verification")) return "verification";
|
|
15711
|
+
if (raw.includes("caution")) return "caution";
|
|
15712
|
+
return void 0;
|
|
15713
|
+
}
|
|
15714
|
+
function receiptOmissions(omitted, hiddenCautionMemoryCount) {
|
|
15715
|
+
const counts = /* @__PURE__ */ new Map();
|
|
15716
|
+
for (const raw of omitted) {
|
|
15717
|
+
const kind = receiptOmissionKind(raw);
|
|
15718
|
+
if (!kind || raw.endsWith("-heading")) continue;
|
|
15719
|
+
counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
15720
|
+
}
|
|
15721
|
+
const receipt = [...counts.entries()].map(([kind, count2]) => ({
|
|
15722
|
+
kind,
|
|
15723
|
+
reason: "token-budget",
|
|
15724
|
+
count: count2
|
|
15725
|
+
}));
|
|
15726
|
+
if (hiddenCautionMemoryCount > 0) {
|
|
15727
|
+
receipt.push({
|
|
15728
|
+
kind: "caution",
|
|
15729
|
+
reason: "hidden-by-task-lens",
|
|
15730
|
+
count: hiddenCautionMemoryCount
|
|
15731
|
+
});
|
|
15732
|
+
}
|
|
15733
|
+
return receipt;
|
|
15734
|
+
}
|
|
15735
|
+
function scheduledActions(cautions) {
|
|
15736
|
+
return cautions.filter((caution) => caution.kind === "codegraph-refresh-queued").map((caution) => short(caution.message, 28));
|
|
15737
|
+
}
|
|
15315
15738
|
function renderTaskWorksetPrompt(input) {
|
|
15316
15739
|
const maxTokens = input.budget?.maxTokens ?? 180;
|
|
15317
15740
|
const omitted = [];
|
|
15741
|
+
const selected = [];
|
|
15318
15742
|
const lines = ["Memorix Autopilot Brief"];
|
|
15319
15743
|
const task = short(input.task || "Continue the current task.", 34);
|
|
15320
|
-
appendLine(lines, "Task: " + task, maxTokens, omitted, "task-detail"
|
|
15744
|
+
appendLine(lines, "Task: " + task, maxTokens, omitted, "task-detail", selected, {
|
|
15745
|
+
kind: "task",
|
|
15746
|
+
reason: "current task supplied by the caller",
|
|
15747
|
+
trust: "source-backed"
|
|
15748
|
+
});
|
|
15321
15749
|
appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
|
|
15322
15750
|
if (input.cautions.length > 0 || input.cautionMemory.length > 0) {
|
|
15323
15751
|
appendLine(lines, "", maxTokens, omitted, "caution-heading");
|
|
15324
15752
|
appendLine(lines, "Cautions", maxTokens, omitted, "caution-heading");
|
|
15325
15753
|
for (const caution of input.cautions.slice(0, 6)) {
|
|
15326
|
-
appendLine(lines, "- " + short(caution.message, 22), maxTokens, omitted, "caution"
|
|
15754
|
+
appendLine(lines, "- " + short(caution.message, 22), maxTokens, omitted, "caution", selected, {
|
|
15755
|
+
kind: "caution",
|
|
15756
|
+
id: "caution:" + caution.kind,
|
|
15757
|
+
reason: "current project caution",
|
|
15758
|
+
trust: "source-backed"
|
|
15759
|
+
});
|
|
15327
15760
|
}
|
|
15328
15761
|
for (const memory of input.cautionMemory.slice(0, 3)) {
|
|
15329
15762
|
const location = memory.path ? memory.path + (memory.symbol ? "#" + memory.symbol : "") : "no current code location";
|
|
@@ -15333,7 +15766,15 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15333
15766
|
"- #" + memory.id + " " + memory.status + ": " + short(memory.title, 18) + " (" + location + reason + ")",
|
|
15334
15767
|
maxTokens,
|
|
15335
15768
|
omitted,
|
|
15336
|
-
"caution-memory"
|
|
15769
|
+
"caution-memory",
|
|
15770
|
+
selected,
|
|
15771
|
+
{
|
|
15772
|
+
kind: "memory",
|
|
15773
|
+
id: "memory:" + memory.id,
|
|
15774
|
+
reason: "task-relevant memory requiring source verification",
|
|
15775
|
+
freshness: freshnessForMemory(memory.status),
|
|
15776
|
+
trust: "historical"
|
|
15777
|
+
}
|
|
15337
15778
|
);
|
|
15338
15779
|
}
|
|
15339
15780
|
if (input.hiddenCautionMemoryCount > 0) {
|
|
@@ -15344,13 +15785,22 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15344
15785
|
appendLine(lines, "", maxTokens, omitted, "facts-heading");
|
|
15345
15786
|
appendLine(lines, "Current project facts", maxTokens, omitted, "facts-heading");
|
|
15346
15787
|
for (const fact of input.currentFacts.slice(0, 4)) {
|
|
15347
|
-
appendLine(lines, "- " + short(fact, 40), maxTokens, omitted, "current-fact"
|
|
15788
|
+
appendLine(lines, "- " + short(fact, 40), maxTokens, omitted, "current-fact", selected, {
|
|
15789
|
+
kind: "current-fact",
|
|
15790
|
+
reason: "current project state",
|
|
15791
|
+
trust: "source-backed"
|
|
15792
|
+
});
|
|
15348
15793
|
}
|
|
15349
15794
|
}
|
|
15350
15795
|
if (input.codeState) {
|
|
15351
15796
|
appendLine(lines, "", maxTokens, omitted, "state-heading");
|
|
15352
15797
|
appendLine(lines, "Project state", maxTokens, omitted, "state-heading");
|
|
15353
|
-
appendLine(lines, input.codeState, maxTokens, omitted, "code-state"
|
|
15798
|
+
appendLine(lines, input.codeState, maxTokens, omitted, "code-state", selected, {
|
|
15799
|
+
kind: "code-state",
|
|
15800
|
+
...input.provenance.snapshotId ? { id: "snapshot:" + input.provenance.snapshotId } : {},
|
|
15801
|
+
reason: "latest available Code State snapshot",
|
|
15802
|
+
trust: "source-backed"
|
|
15803
|
+
});
|
|
15354
15804
|
}
|
|
15355
15805
|
if (input.semanticCode && (input.semanticCode.entryPoints.length > 0 || input.semanticCode.relations.length > 0)) {
|
|
15356
15806
|
appendLine(lines, "", maxTokens, omitted, "semantic-code-heading");
|
|
@@ -15362,7 +15812,14 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15362
15812
|
"- " + location + ": " + short(relation.from.name, 12) + " " + short(relation.kind, 8) + " " + short(relation.to.name, 12),
|
|
15363
15813
|
maxTokens,
|
|
15364
15814
|
omitted,
|
|
15365
|
-
"semantic-relation"
|
|
15815
|
+
"semantic-relation",
|
|
15816
|
+
selected,
|
|
15817
|
+
{
|
|
15818
|
+
kind: "semantic-code",
|
|
15819
|
+
id: "code:" + location,
|
|
15820
|
+
reason: "validated optional semantic code relation",
|
|
15821
|
+
trust: "derived"
|
|
15822
|
+
}
|
|
15366
15823
|
);
|
|
15367
15824
|
}
|
|
15368
15825
|
if (input.semanticCode.relations.length === 0) {
|
|
@@ -15373,7 +15830,14 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15373
15830
|
"- " + location + ": " + short(entry.name, 16) + " (" + short(entry.kind, 8) + ")",
|
|
15374
15831
|
maxTokens,
|
|
15375
15832
|
omitted,
|
|
15376
|
-
"semantic-entry"
|
|
15833
|
+
"semantic-entry",
|
|
15834
|
+
selected,
|
|
15835
|
+
{
|
|
15836
|
+
kind: "semantic-code",
|
|
15837
|
+
id: "code:" + location,
|
|
15838
|
+
reason: "validated optional semantic code entry point",
|
|
15839
|
+
trust: "derived"
|
|
15840
|
+
}
|
|
15377
15841
|
);
|
|
15378
15842
|
}
|
|
15379
15843
|
}
|
|
@@ -15382,7 +15846,12 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15382
15846
|
appendLine(lines, "", maxTokens, omitted, "start-heading");
|
|
15383
15847
|
appendLine(lines, "Start here", maxTokens, omitted, "start-heading");
|
|
15384
15848
|
for (const source of input.startHere.slice(0, 5)) {
|
|
15385
|
-
appendLine(lines, "- " + source, maxTokens, omitted, "start-here"
|
|
15849
|
+
appendLine(lines, "- " + source, maxTokens, omitted, "start-here", selected, {
|
|
15850
|
+
kind: "start-here",
|
|
15851
|
+
id: "path:" + source,
|
|
15852
|
+
reason: "task-lensed starting point",
|
|
15853
|
+
trust: "derived"
|
|
15854
|
+
});
|
|
15386
15855
|
}
|
|
15387
15856
|
}
|
|
15388
15857
|
if (input.reliableMemory.length > 0) {
|
|
@@ -15395,7 +15864,15 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15395
15864
|
"- #" + memory.id + " " + memory.type + ": " + short(memory.title, 18) + " (" + location + ")",
|
|
15396
15865
|
maxTokens,
|
|
15397
15866
|
omitted,
|
|
15398
|
-
"reliable-memory"
|
|
15867
|
+
"reliable-memory",
|
|
15868
|
+
selected,
|
|
15869
|
+
{
|
|
15870
|
+
kind: "memory",
|
|
15871
|
+
id: "memory:" + memory.id,
|
|
15872
|
+
reason: "current code-bound memory",
|
|
15873
|
+
freshness: freshnessForMemory(memory.status),
|
|
15874
|
+
trust: "historical"
|
|
15875
|
+
}
|
|
15399
15876
|
);
|
|
15400
15877
|
}
|
|
15401
15878
|
}
|
|
@@ -15403,10 +15880,25 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15403
15880
|
appendLine(lines, "", maxTokens, omitted, "knowledge-heading");
|
|
15404
15881
|
appendLine(lines, "Project knowledge", maxTokens, omitted, "knowledge-heading");
|
|
15405
15882
|
for (const claim of input.claims.slice(0, 3)) {
|
|
15406
|
-
appendLine(lines, "- " + claim.assertion + " [" + claim.id + "]", maxTokens, omitted, "claim"
|
|
15883
|
+
appendLine(lines, "- " + claim.assertion + " [" + claim.id + "]", maxTokens, omitted, "claim", selected, {
|
|
15884
|
+
kind: "claim",
|
|
15885
|
+
id: "claim:" + claim.id,
|
|
15886
|
+
reason: "source-qualified task match",
|
|
15887
|
+
trust: "source-backed"
|
|
15888
|
+
});
|
|
15407
15889
|
}
|
|
15408
15890
|
for (const page of input.pages.slice(0, 2)) {
|
|
15409
|
-
|
|
15891
|
+
const supportsDeliveredClaim = page.claimIds.some((claimId) => selected.some((item) => item.kind === "claim" && item.id === "claim:" + claimId));
|
|
15892
|
+
if (!supportsDeliveredClaim) {
|
|
15893
|
+
omitted.push("knowledge-page-dependency");
|
|
15894
|
+
continue;
|
|
15895
|
+
}
|
|
15896
|
+
appendLine(lines, "- page: " + page.relativePath, maxTokens, omitted, "knowledge-page", selected, {
|
|
15897
|
+
kind: "knowledge-page",
|
|
15898
|
+
id: "page:" + page.id,
|
|
15899
|
+
reason: "approved page linked to a selected claim",
|
|
15900
|
+
trust: "source-backed"
|
|
15901
|
+
});
|
|
15410
15902
|
}
|
|
15411
15903
|
}
|
|
15412
15904
|
if (input.workflows.length > 0) {
|
|
@@ -15418,7 +15910,14 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15418
15910
|
"- " + workflow.title + ": " + workflow.firstPhase.title + " - " + workflow.firstPhase.instructions,
|
|
15419
15911
|
maxTokens,
|
|
15420
15912
|
omitted,
|
|
15421
|
-
"workflow"
|
|
15913
|
+
"workflow",
|
|
15914
|
+
selected,
|
|
15915
|
+
{
|
|
15916
|
+
kind: "workflow",
|
|
15917
|
+
id: "workflow:" + workflow.id,
|
|
15918
|
+
reason: "task-matching project workflow",
|
|
15919
|
+
trust: "source-backed"
|
|
15920
|
+
}
|
|
15422
15921
|
);
|
|
15423
15922
|
}
|
|
15424
15923
|
}
|
|
@@ -15426,16 +15925,23 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15426
15925
|
appendLine(lines, "", maxTokens, omitted, "verification-heading");
|
|
15427
15926
|
appendLine(lines, "Verify", maxTokens, omitted, "verification-heading");
|
|
15428
15927
|
for (const check of input.verification.slice(0, 4)) {
|
|
15429
|
-
appendLine(lines, "- " + short(check, 20), maxTokens, omitted, "verification"
|
|
15928
|
+
appendLine(lines, "- " + short(check, 20), maxTokens, omitted, "verification", selected, {
|
|
15929
|
+
kind: "verification",
|
|
15930
|
+
reason: "task-lensed verification guidance",
|
|
15931
|
+
trust: "derived"
|
|
15932
|
+
});
|
|
15430
15933
|
}
|
|
15431
15934
|
}
|
|
15432
15935
|
return {
|
|
15433
15936
|
prompt: lines.join("\n"),
|
|
15434
15937
|
tokenCount: countTextTokens(lines.join("\n")),
|
|
15435
|
-
omitted: unique(omitted)
|
|
15938
|
+
omitted: unique(omitted),
|
|
15939
|
+
omittedItems: omitted,
|
|
15940
|
+
selected
|
|
15436
15941
|
};
|
|
15437
15942
|
}
|
|
15438
15943
|
async function buildTaskWorkset(input) {
|
|
15944
|
+
const startedAt = Date.now();
|
|
15439
15945
|
const task = input.task?.trim() ?? "";
|
|
15440
15946
|
const maxTokens = Math.max(96, Math.min(Math.floor(input.maxTokens ?? 180), 320));
|
|
15441
15947
|
const cautions = [...input.runtimeCautions ?? [], ...snapshotCautions(input)];
|
|
@@ -15537,6 +16043,18 @@ async function buildTaskWorkset(input) {
|
|
|
15537
16043
|
...base,
|
|
15538
16044
|
budget: { maxTokens }
|
|
15539
16045
|
});
|
|
16046
|
+
const receipt = {
|
|
16047
|
+
version: "1.2.2",
|
|
16048
|
+
target: input.deliveryTarget ?? "project-context",
|
|
16049
|
+
elapsedMs: Math.max(0, Date.now() - startedAt),
|
|
16050
|
+
budget: {
|
|
16051
|
+
maxTokens,
|
|
16052
|
+
tokenCount: rendered.tokenCount
|
|
16053
|
+
},
|
|
16054
|
+
selected: rendered.selected,
|
|
16055
|
+
omitted: receiptOmissions(rendered.omittedItems, base.hiddenCautionMemoryCount),
|
|
16056
|
+
scheduledActions: scheduledActions(normalizedCautions)
|
|
16057
|
+
};
|
|
15540
16058
|
return {
|
|
15541
16059
|
...base,
|
|
15542
16060
|
budget: {
|
|
@@ -15544,6 +16062,7 @@ async function buildTaskWorkset(input) {
|
|
|
15544
16062
|
tokenCount: rendered.tokenCount,
|
|
15545
16063
|
omitted: rendered.omitted
|
|
15546
16064
|
},
|
|
16065
|
+
receipt,
|
|
15547
16066
|
prompt: rendered.prompt
|
|
15548
16067
|
};
|
|
15549
16068
|
}
|
|
@@ -16321,6 +16840,9 @@ import path19 from "path";
|
|
|
16321
16840
|
function activeProjectObservations(observations2, projectId) {
|
|
16322
16841
|
return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
|
|
16323
16842
|
}
|
|
16843
|
+
function deliveryEligibleProjectObservations(observations2, projectId) {
|
|
16844
|
+
return activeProjectObservations(observations2, projectId).filter((observation) => isEligibleForAutomaticDelivery(observation));
|
|
16845
|
+
}
|
|
16324
16846
|
function decideRefresh(input) {
|
|
16325
16847
|
if (input.mode === "never") {
|
|
16326
16848
|
return { performed: false, reason: "disabled", message: "Automatic project scan disabled." };
|
|
@@ -16395,13 +16917,27 @@ async function buildAutoProjectContext(input) {
|
|
|
16395
16917
|
activeProjectObservations(input.observations, input.project.id)
|
|
16396
16918
|
);
|
|
16397
16919
|
try {
|
|
16398
|
-
const {
|
|
16920
|
+
const { MaintenanceTargetStore: MaintenanceTargetStore2 } = await Promise.resolve().then(() => (init_maintenance_targets(), maintenance_targets_exports));
|
|
16921
|
+
new MaintenanceTargetStore2(input.dataDir).register({
|
|
16922
|
+
projectId: input.project.id,
|
|
16923
|
+
projectRoot: input.project.rootPath,
|
|
16924
|
+
dataDir: input.dataDir
|
|
16925
|
+
});
|
|
16926
|
+
const {
|
|
16927
|
+
enqueueClaimRequalification: enqueueClaimRequalification2,
|
|
16928
|
+
enqueueObservationQualification: enqueueObservationQualification2
|
|
16929
|
+
} = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
|
|
16399
16930
|
enqueueClaimRequalification2({
|
|
16400
16931
|
dataDir: input.dataDir,
|
|
16401
16932
|
projectId: input.project.id,
|
|
16402
16933
|
source: "foreground-refresh",
|
|
16403
16934
|
snapshotId: store.latestSnapshot(input.project.id)?.id
|
|
16404
16935
|
});
|
|
16936
|
+
enqueueObservationQualification2({
|
|
16937
|
+
dataDir: input.dataDir,
|
|
16938
|
+
projectId: input.project.id,
|
|
16939
|
+
source: "foreground-refresh"
|
|
16940
|
+
});
|
|
16405
16941
|
} catch {
|
|
16406
16942
|
}
|
|
16407
16943
|
refresh = { ...refresh, backfill };
|
|
@@ -16418,7 +16954,9 @@ async function buildAutoProjectContext(input) {
|
|
|
16418
16954
|
const explain = buildProjectContextExplain({
|
|
16419
16955
|
project: input.project,
|
|
16420
16956
|
store,
|
|
16421
|
-
|
|
16957
|
+
// Binding sees all active evidence, but automatic delivery sees only
|
|
16958
|
+
// evidence that has passed the control-plane admission gate.
|
|
16959
|
+
observations: deliveryEligibleProjectObservations(input.observations, input.project.id),
|
|
16422
16960
|
exclude
|
|
16423
16961
|
});
|
|
16424
16962
|
const overview = explain.overview;
|
|
@@ -16509,7 +17047,8 @@ async function buildAutoProjectContext(input) {
|
|
|
16509
17047
|
suspect: overview.freshness.suspect,
|
|
16510
17048
|
stale: overview.freshness.stale
|
|
16511
17049
|
},
|
|
16512
|
-
runtimeCautions
|
|
17050
|
+
runtimeCautions,
|
|
17051
|
+
...input.deliveryTarget ? { deliveryTarget: input.deliveryTarget } : {}
|
|
16513
17052
|
});
|
|
16514
17053
|
return {
|
|
16515
17054
|
project: input.project,
|
|
@@ -16743,6 +17282,7 @@ var init_auto_context = __esm({
|
|
|
16743
17282
|
init_external_provider();
|
|
16744
17283
|
init_project_context();
|
|
16745
17284
|
init_store();
|
|
17285
|
+
init_admission();
|
|
16746
17286
|
init_task_lens();
|
|
16747
17287
|
DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
16748
17288
|
}
|
|
@@ -16935,7 +17475,8 @@ async function attachTaskWorkset(input) {
|
|
|
16935
17475
|
suspect: input.pack.warnings.filter((warning) => warning.status === "suspect").length,
|
|
16936
17476
|
stale: input.pack.warnings.filter((warning) => warning.status === "stale").length
|
|
16937
17477
|
},
|
|
16938
|
-
runtimeCautions: input.runtimeCautions
|
|
17478
|
+
runtimeCautions: input.runtimeCautions,
|
|
17479
|
+
deliveryTarget: "context-pack"
|
|
16939
17480
|
});
|
|
16940
17481
|
return { ...input.pack, workset };
|
|
16941
17482
|
}
|
|
@@ -18347,11 +18888,14 @@ __export(export_import_exports, {
|
|
|
18347
18888
|
exportAsMarkdown: () => exportAsMarkdown,
|
|
18348
18889
|
importFromJson: () => importFromJson
|
|
18349
18890
|
});
|
|
18350
|
-
async function exportAsJson(projectDir2, projectId) {
|
|
18891
|
+
async function exportAsJson(projectDir2, projectId, reader) {
|
|
18351
18892
|
const store = getObservationStore();
|
|
18352
18893
|
const allObs = await store.loadAll();
|
|
18353
18894
|
const allSessions = await getSessionStore().loadAll();
|
|
18354
|
-
const projectObs =
|
|
18895
|
+
const projectObs = filterReadableObservations(
|
|
18896
|
+
allObs.filter((o) => o.projectId === projectId),
|
|
18897
|
+
reader
|
|
18898
|
+
);
|
|
18355
18899
|
const projectSessions = allSessions.filter((s) => s.projectId === projectId);
|
|
18356
18900
|
const typeBreakdown = {};
|
|
18357
18901
|
for (const obs of projectObs) {
|
|
@@ -18370,8 +18914,8 @@ async function exportAsJson(projectDir2, projectId) {
|
|
|
18370
18914
|
}
|
|
18371
18915
|
};
|
|
18372
18916
|
}
|
|
18373
|
-
async function exportAsMarkdown(projectDir2, projectId) {
|
|
18374
|
-
const data = await exportAsJson(projectDir2, projectId);
|
|
18917
|
+
async function exportAsMarkdown(projectDir2, projectId, reader) {
|
|
18918
|
+
const data = await exportAsJson(projectDir2, projectId, reader);
|
|
18375
18919
|
const lines = [];
|
|
18376
18920
|
lines.push(`# Memorix Export: ${projectId}`);
|
|
18377
18921
|
lines.push(`Exported: ${data.exportedAt}`);
|
|
@@ -18473,6 +19017,7 @@ var init_export_import = __esm({
|
|
|
18473
19017
|
"src/memory/export-import.ts"() {
|
|
18474
19018
|
"use strict";
|
|
18475
19019
|
init_esm_shims();
|
|
19020
|
+
init_visibility();
|
|
18476
19021
|
init_obs_store();
|
|
18477
19022
|
init_session_store();
|
|
18478
19023
|
OBSERVATION_ICONS2 = {
|
|
@@ -18649,6 +19194,7 @@ function isEligible(o, projectId) {
|
|
|
18649
19194
|
if (isCommandLog2(o)) return false;
|
|
18650
19195
|
if (isInactive(o)) return false;
|
|
18651
19196
|
if (isOtherProject(o, projectId)) return false;
|
|
19197
|
+
if (!isEligibleForKnowledgePromotion(o)) return false;
|
|
18652
19198
|
if (o.valueCategory === "ephemeral") return false;
|
|
18653
19199
|
if (!contextualHasSubstance(o)) return false;
|
|
18654
19200
|
return true;
|
|
@@ -18797,6 +19343,7 @@ var init_generator = __esm({
|
|
|
18797
19343
|
"src/wiki/generator.ts"() {
|
|
18798
19344
|
"use strict";
|
|
18799
19345
|
init_esm_shims();
|
|
19346
|
+
init_admission();
|
|
18800
19347
|
COMMAND_LOG_TITLE4 = /^(Ran:|Command:|Executed:)\s/i;
|
|
18801
19348
|
SECTION_DEFS = [
|
|
18802
19349
|
{
|
|
@@ -19083,6 +19630,9 @@ function sendError(res, message, status = 500) {
|
|
|
19083
19630
|
function filterByProject(items, projectId) {
|
|
19084
19631
|
return items.filter((item) => item.projectId === projectId);
|
|
19085
19632
|
}
|
|
19633
|
+
function filterDashboardObservations(observations2, projectId) {
|
|
19634
|
+
return filterReadableObservations(observations2, { projectId });
|
|
19635
|
+
}
|
|
19086
19636
|
function isActiveStatus(status) {
|
|
19087
19637
|
return (status ?? "active") === "active";
|
|
19088
19638
|
}
|
|
@@ -19130,7 +19680,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19130
19680
|
switch (apiPath) {
|
|
19131
19681
|
case "/projects": {
|
|
19132
19682
|
try {
|
|
19133
|
-
const allObs =
|
|
19683
|
+
const allObs = filterReadableObservations(
|
|
19684
|
+
await getObservationStore().loadAll(),
|
|
19685
|
+
{}
|
|
19686
|
+
);
|
|
19134
19687
|
const projectSet = /* @__PURE__ */ new Map();
|
|
19135
19688
|
for (const obs of allObs) {
|
|
19136
19689
|
if (!isActiveStatus(obs.status)) continue;
|
|
@@ -19179,13 +19732,19 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19179
19732
|
await initGraphStore(effectiveDataDir);
|
|
19180
19733
|
const gStore = getGraphStore();
|
|
19181
19734
|
const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
19182
|
-
const graphObs =
|
|
19735
|
+
const graphObs = filterDashboardObservations(
|
|
19736
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19737
|
+
effectiveProjectId
|
|
19738
|
+
);
|
|
19183
19739
|
const scoped = scopeKnowledgeGraphToProject(graph, graphObs);
|
|
19184
19740
|
sendJson(res, { entities: scoped.entities, relations: scoped.relations });
|
|
19185
19741
|
break;
|
|
19186
19742
|
}
|
|
19187
19743
|
case "/observations": {
|
|
19188
|
-
const observations2 =
|
|
19744
|
+
const observations2 = filterDashboardObservations(
|
|
19745
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19746
|
+
effectiveProjectId
|
|
19747
|
+
);
|
|
19189
19748
|
sendJson(res, observations2);
|
|
19190
19749
|
break;
|
|
19191
19750
|
}
|
|
@@ -19198,7 +19757,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19198
19757
|
case "/stats": {
|
|
19199
19758
|
await initGraphStore(effectiveDataDir);
|
|
19200
19759
|
const graph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
|
|
19201
|
-
const observations2 =
|
|
19760
|
+
const observations2 = filterDashboardObservations(
|
|
19761
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19762
|
+
effectiveProjectId
|
|
19763
|
+
);
|
|
19202
19764
|
const nextId2 = await getObservationStore().loadIdCounter();
|
|
19203
19765
|
const projectGraphCounts = computeProjectGraphCounts(graph.entities, graph.relations, observations2);
|
|
19204
19766
|
const typeCounts = {};
|
|
@@ -19316,7 +19878,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19316
19878
|
break;
|
|
19317
19879
|
}
|
|
19318
19880
|
case "/retention": {
|
|
19319
|
-
const observations2 =
|
|
19881
|
+
const observations2 = filterDashboardObservations(
|
|
19882
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19883
|
+
effectiveProjectId
|
|
19884
|
+
);
|
|
19320
19885
|
const now3 = Date.now();
|
|
19321
19886
|
const scored = observations2.filter((obs) => obs.type !== "probe").map((obs) => {
|
|
19322
19887
|
const age = now3 - new Date(obs.createdAt || now3).getTime();
|
|
@@ -19354,7 +19919,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19354
19919
|
const { generateKnowledgeBase: generateKnowledgeBase2 } = await Promise.resolve().then(() => (init_generator(), generator_exports));
|
|
19355
19920
|
const { initMiniSkillStore: initMiniSkillStore2, getMiniSkillStore: getMiniSkillStore2 } = await Promise.resolve().then(() => (init_mini_skill_store(), mini_skill_store_exports));
|
|
19356
19921
|
await initMiniSkillStore2(effectiveDataDir);
|
|
19357
|
-
const allObs =
|
|
19922
|
+
const allObs = filterDashboardObservations(
|
|
19923
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19924
|
+
effectiveProjectId
|
|
19925
|
+
);
|
|
19358
19926
|
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
19359
19927
|
const overview = generateKnowledgeBase2({
|
|
19360
19928
|
projectId: effectiveProjectId,
|
|
@@ -19370,7 +19938,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19370
19938
|
const { initGraphStore: initGraphStore2, getGraphStore: getGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
19371
19939
|
await initMiniSkillStore2(effectiveDataDir);
|
|
19372
19940
|
await initGraphStore2(effectiveDataDir);
|
|
19373
|
-
const allObs =
|
|
19941
|
+
const allObs = filterDashboardObservations(
|
|
19942
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19943
|
+
effectiveProjectId
|
|
19944
|
+
);
|
|
19374
19945
|
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
19375
19946
|
const fullGraph = { entities: getGraphStore2().loadEntities(), relations: getGraphStore2().loadRelations() };
|
|
19376
19947
|
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
@@ -19493,7 +20064,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19493
20064
|
break;
|
|
19494
20065
|
}
|
|
19495
20066
|
case "/identity": {
|
|
19496
|
-
const allObs = await getObservationStore().loadAll();
|
|
20067
|
+
const allObs = filterReadableObservations(await getObservationStore().loadAll(), {});
|
|
19497
20068
|
const allProjectIds = [...new Set(allObs.map((o) => o.projectId).filter(Boolean))];
|
|
19498
20069
|
let classifyProjectId2 = () => "real";
|
|
19499
20070
|
let isDirtyProjectId2 = () => false;
|
|
@@ -19578,6 +20149,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19578
20149
|
sendError(res, "Observation not found", 404);
|
|
19579
20150
|
} else if (matchObs.projectId !== effectiveProjectId) {
|
|
19580
20151
|
sendError(res, `Observation #${obsId} belongs to project "${matchObs.projectId}", not "${effectiveProjectId}"`, 403);
|
|
20152
|
+
} else if (!canManageObservation(matchObs, { projectId: effectiveProjectId })) {
|
|
20153
|
+
sendError(res, `Observation #${obsId} is not manageable from the unbound dashboard.`, 403);
|
|
19581
20154
|
} else {
|
|
19582
20155
|
await obsStore.remove(obsId);
|
|
19583
20156
|
try {
|
|
@@ -19599,7 +20172,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19599
20172
|
if (apiPath === "/export") {
|
|
19600
20173
|
await initGraphStore(effectiveDataDir);
|
|
19601
20174
|
const fullGraph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
|
|
19602
|
-
const observations2 =
|
|
20175
|
+
const observations2 = filterDashboardObservations(
|
|
20176
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
20177
|
+
effectiveProjectId
|
|
20178
|
+
);
|
|
19603
20179
|
const nextId2 = await getObservationStore().loadIdCounter();
|
|
19604
20180
|
const scoped = scopeKnowledgeGraphToProject(fullGraph, observations2);
|
|
19605
20181
|
const exportData = {
|
|
@@ -19923,6 +20499,7 @@ var init_server = __esm({
|
|
|
19923
20499
|
init_yaml_loader();
|
|
19924
20500
|
init_yaml_loader();
|
|
19925
20501
|
init_graph_scope();
|
|
20502
|
+
init_visibility();
|
|
19926
20503
|
MIME_TYPES = {
|
|
19927
20504
|
".html": "text/html; charset=utf-8",
|
|
19928
20505
|
".css": "text/css; charset=utf-8",
|
|
@@ -20700,6 +21277,11 @@ async function createHandoffArtifact(input, storeObservation2, teamStore) {
|
|
|
20700
21277
|
sourceDetail: "explicit",
|
|
20701
21278
|
valueCategory: "core",
|
|
20702
21279
|
// immune to retention archival
|
|
21280
|
+
// A direct handoff is not a project-wide memo. The sender retains access
|
|
21281
|
+
// and the explicit recipient receives a read grant. Broadcast handoffs
|
|
21282
|
+
// are deliberately visible only to active team members.
|
|
21283
|
+
visibility: input.toAgentId ? "personal" : "team",
|
|
21284
|
+
...input.toAgentId ? { sharedWithAgentIds: [input.toAgentId] } : {},
|
|
20703
21285
|
createdByAgentId: input.fromAgentId
|
|
20704
21286
|
});
|
|
20705
21287
|
try {
|
|
@@ -22405,6 +22987,7 @@ var init_hooks_path = __esm({
|
|
|
22405
22987
|
|
|
22406
22988
|
// src/sdk.ts
|
|
22407
22989
|
init_esm_shims();
|
|
22990
|
+
init_visibility();
|
|
22408
22991
|
|
|
22409
22992
|
// src/server.ts
|
|
22410
22993
|
init_esm_shims();
|
|
@@ -22610,6 +23193,7 @@ async function createAutoRelations(obs, extracted, graphManager) {
|
|
|
22610
23193
|
// src/server.ts
|
|
22611
23194
|
init_entity_extractor();
|
|
22612
23195
|
init_graph_scope();
|
|
23196
|
+
init_visibility();
|
|
22613
23197
|
|
|
22614
23198
|
// src/compact/engine.ts
|
|
22615
23199
|
init_esm_shims();
|
|
@@ -22735,7 +23319,15 @@ function formatTimeline(timeline) {
|
|
|
22735
23319
|
function formatObservationDetail(doc) {
|
|
22736
23320
|
const icon = getTypeIcon(doc.type);
|
|
22737
23321
|
const lines = [];
|
|
22738
|
-
const header = buildProvenanceHeader(
|
|
23322
|
+
const header = buildProvenanceHeader(
|
|
23323
|
+
doc.sourceDetail,
|
|
23324
|
+
doc.valueCategory,
|
|
23325
|
+
doc.admissionState,
|
|
23326
|
+
doc.admissionReason,
|
|
23327
|
+
doc.source,
|
|
23328
|
+
doc.commitHash,
|
|
23329
|
+
doc.relatedCommits
|
|
23330
|
+
);
|
|
22739
23331
|
if (header) {
|
|
22740
23332
|
lines.push(header);
|
|
22741
23333
|
lines.push("");
|
|
@@ -22770,7 +23362,7 @@ function formatObservationDetail(doc) {
|
|
|
22770
23362
|
}
|
|
22771
23363
|
return lines.join("\n");
|
|
22772
23364
|
}
|
|
22773
|
-
function buildProvenanceHeader(sourceDetail, valueCategory, source, commitHash, relatedCommits) {
|
|
23365
|
+
function buildProvenanceHeader(sourceDetail, valueCategory, admissionState, admissionReason, source, commitHash, relatedCommits) {
|
|
22774
23366
|
const sd = resolveSourceDetail(sourceDetail, source);
|
|
22775
23367
|
if (!sd) return "";
|
|
22776
23368
|
const label = sourceKindLabel(sd);
|
|
@@ -22781,11 +23373,21 @@ function buildProvenanceHeader(sourceDetail, valueCategory, source, commitHash,
|
|
|
22781
23373
|
if (verificationLine) {
|
|
22782
23374
|
lines.push(verificationLine);
|
|
22783
23375
|
}
|
|
22784
|
-
if (valueCategory === "core") {
|
|
23376
|
+
if (valueCategory === "core" && admissionState !== "candidate" && admissionState !== "ephemeral") {
|
|
22785
23377
|
lines.push("[CORE] Core \u2014 immune to decay");
|
|
23378
|
+
} else if (valueCategory === "core") {
|
|
23379
|
+
lines.push("[CORE] Core candidate \u2014 not durable until qualification completes");
|
|
22786
23380
|
} else if (valueCategory === "ephemeral") {
|
|
22787
23381
|
lines.push("[WARN] Ephemeral \u2014 short-lived signal");
|
|
22788
23382
|
}
|
|
23383
|
+
if (admissionState === "candidate") {
|
|
23384
|
+
lines.push("[PENDING] Candidate \u2014 excluded from automatic context until current Code Memory qualifies it");
|
|
23385
|
+
} else if (admissionState === "ephemeral") {
|
|
23386
|
+
lines.push("[TRACE] Ephemeral capture \u2014 excluded from automatic context");
|
|
23387
|
+
} else if (admissionState === "qualified") {
|
|
23388
|
+
lines.push("[QUALIFIED] Automatic evidence passed current Code Memory qualification");
|
|
23389
|
+
}
|
|
23390
|
+
if (admissionReason) lines.push(`Admission: ${redactCredentials(admissionReason)}`);
|
|
22789
23391
|
return lines.join("\n");
|
|
22790
23392
|
}
|
|
22791
23393
|
function sourceKindLabel(sd) {
|
|
@@ -22866,6 +23468,7 @@ init_obs_store();
|
|
|
22866
23468
|
init_mini_skill_store();
|
|
22867
23469
|
init_mini_skills();
|
|
22868
23470
|
init_secret_filter();
|
|
23471
|
+
init_visibility();
|
|
22869
23472
|
function normalizeMemoryBrowseQuery(query) {
|
|
22870
23473
|
const trimmed = query.trim();
|
|
22871
23474
|
if (!trimmed) return "";
|
|
@@ -22908,11 +23511,13 @@ async function compactSearch(options) {
|
|
|
22908
23511
|
);
|
|
22909
23512
|
let formatted = formatIndexTable(entries, searchOptions.query, !options.projectId);
|
|
22910
23513
|
if (entries.length === 0 && options.projectId) {
|
|
22911
|
-
const allObservations = getAllObservations();
|
|
23514
|
+
const allObservations = filterReadableObservations(getAllObservations(), options.reader);
|
|
22912
23515
|
const projectAliases = new Set(await resolveAliases(options.projectId).catch(() => [options.projectId]));
|
|
22913
23516
|
const projectHasStoredMemory = allObservations.some((obs) => obs.projectId && projectAliases.has(obs.projectId));
|
|
22914
23517
|
if (!projectHasStoredMemory) {
|
|
22915
|
-
formatted = `
|
|
23518
|
+
formatted = options.reader ? `No Memorix memories are available to this session yet.
|
|
23519
|
+
|
|
23520
|
+
The tool call worked, but this session has no project-visible memory to retrieve.` : `This project does not have any Memorix memories yet.
|
|
22916
23521
|
|
|
22917
23522
|
It looks like a fresh project: the tool call worked, but there is nothing stored to retrieve yet.
|
|
22918
23523
|
|
|
@@ -22926,8 +23531,8 @@ This project does have stored Memorix memories, but none matched the current que
|
|
|
22926
23531
|
const totalTokens = countTextTokens(formatted);
|
|
22927
23532
|
return { entries, formatted, totalTokens };
|
|
22928
23533
|
}
|
|
22929
|
-
async function compactTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3) {
|
|
22930
|
-
const result = await getTimeline(anchorId, projectId, depthBefore, depthAfter);
|
|
23534
|
+
async function compactTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3, reader) {
|
|
23535
|
+
const result = await getTimeline(anchorId, projectId, depthBefore, depthAfter, reader);
|
|
22931
23536
|
const timeline = {
|
|
22932
23537
|
anchorId,
|
|
22933
23538
|
anchorEntry: result.anchor,
|
|
@@ -22938,7 +23543,7 @@ async function compactTimeline(anchorId, projectId, depthBefore = 3, depthAfter
|
|
|
22938
23543
|
const totalTokens = countTextTokens(formatted);
|
|
22939
23544
|
return { timeline, formatted, totalTokens };
|
|
22940
23545
|
}
|
|
22941
|
-
async function compactDetail(idsOrRefs) {
|
|
23546
|
+
async function compactDetail(idsOrRefs, options = {}) {
|
|
22942
23547
|
const invalidRefs = [];
|
|
22943
23548
|
const parsedRefs = [];
|
|
22944
23549
|
for (let i = 0; i < idsOrRefs.length; i++) {
|
|
@@ -22995,7 +23600,7 @@ async function compactDetail(idsOrRefs) {
|
|
|
22995
23600
|
const missingRefs = [];
|
|
22996
23601
|
for (const ref of refs) {
|
|
22997
23602
|
const obs = getObservation(ref.id, ref.projectId);
|
|
22998
|
-
if (obs && (ref.projectId ? obs.projectId === ref.projectId : true)) {
|
|
23603
|
+
if (obs && (ref.projectId ? obs.projectId === ref.projectId : true) && canReadObservation(obs, options.reader)) {
|
|
22999
23604
|
documentMap.set(toRefKey(ref), {
|
|
23000
23605
|
id: makeOramaObservationId(obs.projectId, obs.id),
|
|
23001
23606
|
observationId: obs.id,
|
|
@@ -23014,7 +23619,12 @@ async function compactDetail(idsOrRefs) {
|
|
|
23014
23619
|
status: obs.status ?? "active",
|
|
23015
23620
|
source: obs.source ?? "agent",
|
|
23016
23621
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23017
|
-
valueCategory: obs.valueCategory ?? ""
|
|
23622
|
+
valueCategory: obs.valueCategory ?? "",
|
|
23623
|
+
admissionState: obs.admissionState ?? "",
|
|
23624
|
+
admissionReason: obs.admissionReason ?? "",
|
|
23625
|
+
visibility: obs.visibility ?? "project",
|
|
23626
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
23627
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
23018
23628
|
});
|
|
23019
23629
|
} else {
|
|
23020
23630
|
missingRefs.push(ref);
|
|
@@ -23024,17 +23634,17 @@ async function compactDetail(idsOrRefs) {
|
|
|
23024
23634
|
for (const ref of missingRefs) {
|
|
23025
23635
|
const fallbackDocs = await getObservationsByIds([ref.id], ref.projectId);
|
|
23026
23636
|
const doc = fallbackDocs[0];
|
|
23027
|
-
if (doc) {
|
|
23637
|
+
if (doc && canReadObservation(doc, options.reader)) {
|
|
23028
23638
|
documentMap.set(toRefKey(ref), doc);
|
|
23029
23639
|
continue;
|
|
23030
23640
|
}
|
|
23031
23641
|
const stored = await getPersistedObservation(ref);
|
|
23032
|
-
if (stored) {
|
|
23642
|
+
if (stored && canReadObservation(stored, options.reader)) {
|
|
23033
23643
|
documentMap.set(toRefKey(ref), observationToDocument(stored));
|
|
23034
23644
|
}
|
|
23035
23645
|
}
|
|
23036
23646
|
}
|
|
23037
|
-
const allObs = getAllObservations();
|
|
23647
|
+
const allObs = filterReadableObservations(getAllObservations(), options.reader);
|
|
23038
23648
|
const crossRefMap = /* @__PURE__ */ new Map();
|
|
23039
23649
|
for (const ref of refs) {
|
|
23040
23650
|
const obs = getObservation(ref.id, ref.projectId);
|
|
@@ -23148,7 +23758,12 @@ function observationToDocument(obs) {
|
|
|
23148
23758
|
status: obs.status ?? "active",
|
|
23149
23759
|
source: obs.source ?? "agent",
|
|
23150
23760
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23151
|
-
valueCategory: obs.valueCategory ?? ""
|
|
23761
|
+
valueCategory: obs.valueCategory ?? "",
|
|
23762
|
+
admissionState: obs.admissionState ?? "",
|
|
23763
|
+
admissionReason: obs.admissionReason ?? "",
|
|
23764
|
+
visibility: obs.visibility ?? "project",
|
|
23765
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
23766
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
23152
23767
|
};
|
|
23153
23768
|
}
|
|
23154
23769
|
function formatMiniSkillDetail(skill2, provenanceStatus) {
|
|
@@ -23267,7 +23882,9 @@ function auditMemoryQuality(observations2, options) {
|
|
|
23267
23882
|
status: obs.status ?? "active",
|
|
23268
23883
|
source: obs.source ?? "agent",
|
|
23269
23884
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23270
|
-
valueCategory: obs.valueCategory ?? ""
|
|
23885
|
+
valueCategory: obs.valueCategory ?? "",
|
|
23886
|
+
admissionState: obs.admissionState ?? "",
|
|
23887
|
+
admissionReason: obs.admissionReason ?? ""
|
|
23271
23888
|
}, options.referenceTime) !== "active").map((obs) => makeEntry(obs, "Outside active retention zone"));
|
|
23272
23889
|
const summary = {
|
|
23273
23890
|
total: scoped.length,
|
|
@@ -23309,6 +23926,7 @@ function auditMemoryQuality(observations2, options) {
|
|
|
23309
23926
|
|
|
23310
23927
|
// src/memory/graph-context.ts
|
|
23311
23928
|
init_retention();
|
|
23929
|
+
init_admission();
|
|
23312
23930
|
function formatGraphContextPrompt(packet) {
|
|
23313
23931
|
const lines = [
|
|
23314
23932
|
"## Memory Context Packet",
|
|
@@ -23466,7 +24084,9 @@ function scoreForPacket(obs, queryTokens, referenceTime, broadMemoryQuery) {
|
|
|
23466
24084
|
status: obs.status ?? "active",
|
|
23467
24085
|
source: obs.source ?? "agent",
|
|
23468
24086
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23469
|
-
valueCategory: obs.valueCategory ?? ""
|
|
24087
|
+
valueCategory: obs.valueCategory ?? "",
|
|
24088
|
+
admissionState: obs.admissionState ?? "",
|
|
24089
|
+
admissionReason: obs.admissionReason ?? ""
|
|
23470
24090
|
}, referenceTime) !== "active") {
|
|
23471
24091
|
score -= 2;
|
|
23472
24092
|
}
|
|
@@ -23560,8 +24180,11 @@ function buildGraphContextPacket(observations2, options) {
|
|
|
23560
24180
|
...audit.issues.orphans.map((entry) => entry.id),
|
|
23561
24181
|
...audit.issues.retentionCandidates.map((entry) => entry.id)
|
|
23562
24182
|
]);
|
|
23563
|
-
const
|
|
23564
|
-
|
|
24183
|
+
const deliveryEligible = observations2.filter(
|
|
24184
|
+
(obs) => obs.projectId === options.projectId && isEligibleForAutomaticDelivery(obs)
|
|
24185
|
+
);
|
|
24186
|
+
const filteredObservations = deliveryEligible.filter((obs) => !riskIds.has(obs.id));
|
|
24187
|
+
const baseObservations = filteredObservations.length > 0 ? filteredObservations : deliveryEligible;
|
|
23565
24188
|
const memories = pickMemories(baseObservations, options.projectId, options.query, options.limit ?? 5, referenceTime);
|
|
23566
24189
|
const entities = buildEntities(memories);
|
|
23567
24190
|
const entityNames = new Set(entities.map((entity) => entity.name));
|
|
@@ -26048,6 +26671,8 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
26048
26671
|
let projectResolutionError = null;
|
|
26049
26672
|
let explicitProjectBound = false;
|
|
26050
26673
|
let currentAgentId;
|
|
26674
|
+
let teamStore;
|
|
26675
|
+
let initTeamStoreForProject;
|
|
26051
26676
|
if (detectedProject) {
|
|
26052
26677
|
rawProject = detectedProject;
|
|
26053
26678
|
} else {
|
|
@@ -26295,7 +26920,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
26295
26920
|
};
|
|
26296
26921
|
const server = existingServer ?? new McpServer({
|
|
26297
26922
|
name: "memorix",
|
|
26298
|
-
version: true ? "1.2.
|
|
26923
|
+
version: true ? "1.2.3" : "1.0.1"
|
|
26299
26924
|
});
|
|
26300
26925
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
26301
26926
|
server.registerTool = ((name, ...args) => {
|
|
@@ -26315,11 +26940,26 @@ The path should point to a directory containing a .git folder.`
|
|
|
26315
26940
|
}
|
|
26316
26941
|
return originalRegisterTool(name, ...args);
|
|
26317
26942
|
});
|
|
26943
|
+
const getObservationReader = (scope = "project") => {
|
|
26944
|
+
let isTeamMember = false;
|
|
26945
|
+
if (teamFeaturesEnabled && currentAgentId) {
|
|
26946
|
+
try {
|
|
26947
|
+
const agent = teamStore.getAgent(currentAgentId);
|
|
26948
|
+
isTeamMember = agent?.project_id === project.id && agent.status === "active";
|
|
26949
|
+
} catch {
|
|
26950
|
+
}
|
|
26951
|
+
}
|
|
26952
|
+
return {
|
|
26953
|
+
...scope === "project" ? { projectId: project.id } : {},
|
|
26954
|
+
...currentAgentId ? { agentId: currentAgentId } : {},
|
|
26955
|
+
isTeamMember
|
|
26956
|
+
};
|
|
26957
|
+
};
|
|
26318
26958
|
server.registerTool(
|
|
26319
26959
|
"memorix_store",
|
|
26320
26960
|
{
|
|
26321
26961
|
title: "Store Memory",
|
|
26322
|
-
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).
|
|
26962
|
+
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.",
|
|
26323
26963
|
inputSchema: {
|
|
26324
26964
|
entityName: z2.string().describe('The entity this observation belongs to (e.g., "auth-module", "port-config")'),
|
|
26325
26965
|
type: z2.enum(OBSERVATION_TYPES).describe("Observation type for classification"),
|
|
@@ -26337,361 +26977,214 @@ The path should point to a directory containing a .git folder.`
|
|
|
26337
26977
|
completion: z2.number().optional().describe("Completion percentage 0-100")
|
|
26338
26978
|
}).optional().describe("Progress tracking for task/feature observations"),
|
|
26339
26979
|
relatedCommits: z2.array(z2.string()).optional().describe("Git commit hashes this memory relates to (links ground truth \u2194 reasoning)"),
|
|
26340
|
-
relatedEntities: z2.array(z2.string()).optional().describe("Other entity names this memory cross-references")
|
|
26980
|
+
relatedEntities: z2.array(z2.string()).optional().describe("Other entity names this memory cross-references"),
|
|
26981
|
+
visibility: z2.enum(["personal", "project", "team"]).optional().describe(
|
|
26982
|
+
"Retrieval scope. Project is the normal shared default; personal/team require memorix_session_start with joinTeam=true."
|
|
26983
|
+
)
|
|
26341
26984
|
}
|
|
26342
26985
|
},
|
|
26343
|
-
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities }) => {
|
|
26986
|
+
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility }) => {
|
|
26344
26987
|
const unresolved = requireResolvedProject("store memory in the current project");
|
|
26345
26988
|
if (unresolved) return unresolved;
|
|
26346
|
-
|
|
26347
|
-
|
|
26348
|
-
|
|
26349
|
-
|
|
26350
|
-
|
|
26351
|
-
|
|
26352
|
-
|
|
26353
|
-
|
|
26354
|
-
|
|
26355
|
-
|
|
26356
|
-
|
|
26357
|
-
|
|
26358
|
-
|
|
26359
|
-
|
|
26360
|
-
|
|
26989
|
+
const requestedVisibility = visibility ?? "project";
|
|
26990
|
+
const reader = getObservationReader();
|
|
26991
|
+
if (requestedVisibility !== "project" && !currentAgentId) {
|
|
26992
|
+
return {
|
|
26993
|
+
content: [{ type: "text", text: "Personal or team memory requires memorix_session_start with joinTeam=true so Memorix can bind an owner." }],
|
|
26994
|
+
isError: true
|
|
26995
|
+
};
|
|
26996
|
+
}
|
|
26997
|
+
if (requestedVisibility === "team" && !reader.isTeamMember) {
|
|
26998
|
+
return {
|
|
26999
|
+
content: [{ type: "text", text: "Team memory requires an active coordination membership in the current project." }],
|
|
27000
|
+
isError: true
|
|
27001
|
+
};
|
|
27002
|
+
}
|
|
27003
|
+
try {
|
|
27004
|
+
return await withFreshIndex(async () => {
|
|
27005
|
+
let entityName = rawEntityName;
|
|
27006
|
+
let type = rawType;
|
|
27007
|
+
let title = rawTitle;
|
|
27008
|
+
let safeFacts = facts ? coerceStringArray(facts) : void 0;
|
|
27009
|
+
const safeFiles = filesModified ? coerceStringArray(filesModified) : void 0;
|
|
27010
|
+
const safeConcepts = concepts ? coerceStringArray(concepts) : void 0;
|
|
27011
|
+
let formationMode = "active";
|
|
27012
|
+
if (process.env.MEMORIX_FORMATION_MODE) {
|
|
27013
|
+
formationMode = process.env.MEMORIX_FORMATION_MODE;
|
|
27014
|
+
} else {
|
|
27015
|
+
try {
|
|
27016
|
+
const { getBehaviorConfig: getBehaviorConfig2 } = await Promise.resolve().then(() => (init_behavior(), behavior_exports));
|
|
27017
|
+
formationMode = getBehaviorConfig2({ projectRoot: project.rootPath }).formationMode;
|
|
27018
|
+
} catch {
|
|
27019
|
+
}
|
|
26361
27020
|
}
|
|
26362
|
-
|
|
26363
|
-
|
|
26364
|
-
|
|
26365
|
-
|
|
26366
|
-
|
|
26367
|
-
|
|
26368
|
-
|
|
26369
|
-
|
|
26370
|
-
|
|
26371
|
-
|
|
27021
|
+
const useFormation = formationMode === "active";
|
|
27022
|
+
let formationResult = null;
|
|
27023
|
+
let formationNote = "";
|
|
27024
|
+
if (useFormation && !topicKey && !progress) {
|
|
27025
|
+
let currentFormationStage = "setup";
|
|
27026
|
+
const completedFormationStages = {};
|
|
27027
|
+
const formationStartTime = Date.now();
|
|
27028
|
+
const onFormationStageEvent = (event) => {
|
|
27029
|
+
if (event.status === "start") {
|
|
27030
|
+
currentFormationStage = event.stage;
|
|
27031
|
+
return;
|
|
27032
|
+
}
|
|
26372
27033
|
currentFormationStage = event.stage;
|
|
26373
|
-
|
|
26374
|
-
|
|
26375
|
-
|
|
26376
|
-
if (event.stageDurationMs !== void 0) {
|
|
26377
|
-
completedFormationStages[event.stage] = event.stageDurationMs;
|
|
26378
|
-
}
|
|
26379
|
-
};
|
|
26380
|
-
try {
|
|
26381
|
-
const formationConfig = {
|
|
26382
|
-
mode: "active",
|
|
26383
|
-
useLLM: isLLMEnabled(),
|
|
26384
|
-
minValueScore: 0.3,
|
|
26385
|
-
searchMemories: async (q, limit, pid) => {
|
|
26386
|
-
const result = await compactSearch({ query: q, limit, projectId: pid, status: "active" });
|
|
26387
|
-
if (result.entries.length === 0) return [];
|
|
26388
|
-
const details = await compactDetail(result.entries.map((e) => e.id));
|
|
26389
|
-
return details.documents.map((d, i) => ({
|
|
26390
|
-
id: Number(d.id.replace("obs-", "")),
|
|
26391
|
-
observationId: d.observationId,
|
|
26392
|
-
title: d.title,
|
|
26393
|
-
narrative: d.narrative,
|
|
26394
|
-
facts: d.facts,
|
|
26395
|
-
entityName: d.entityName,
|
|
26396
|
-
type: d.type,
|
|
26397
|
-
score: result.entries[i]?.score ?? 0
|
|
26398
|
-
}));
|
|
26399
|
-
},
|
|
26400
|
-
getObservation: (id) => {
|
|
26401
|
-
const o = getObservation(id);
|
|
26402
|
-
if (!o) return null;
|
|
26403
|
-
return {
|
|
26404
|
-
id: o.id,
|
|
26405
|
-
entityName: o.entityName,
|
|
26406
|
-
type: o.type,
|
|
26407
|
-
title: o.title,
|
|
26408
|
-
narrative: o.narrative,
|
|
26409
|
-
facts: o.facts,
|
|
26410
|
-
topicKey: o.topicKey
|
|
26411
|
-
};
|
|
26412
|
-
},
|
|
26413
|
-
getEntityNames: () => graphManager.getEntityNames(),
|
|
26414
|
-
onStageEvent: onFormationStageEvent
|
|
27034
|
+
if (event.stageDurationMs !== void 0) {
|
|
27035
|
+
completedFormationStages[event.stage] = event.stageDurationMs;
|
|
27036
|
+
}
|
|
26415
27037
|
};
|
|
26416
|
-
|
|
26417
|
-
|
|
26418
|
-
|
|
26419
|
-
|
|
26420
|
-
|
|
26421
|
-
|
|
26422
|
-
|
|
26423
|
-
|
|
26424
|
-
|
|
26425
|
-
|
|
26426
|
-
|
|
26427
|
-
|
|
26428
|
-
|
|
26429
|
-
|
|
26430
|
-
|
|
27038
|
+
try {
|
|
27039
|
+
const formationConfig = {
|
|
27040
|
+
mode: "active",
|
|
27041
|
+
useLLM: isLLMEnabled(),
|
|
27042
|
+
minValueScore: 0.3,
|
|
27043
|
+
searchMemories: async (q, limit, pid) => {
|
|
27044
|
+
const result = await compactSearch({ query: q, limit, projectId: pid, status: "active", reader });
|
|
27045
|
+
if (result.entries.length === 0) return [];
|
|
27046
|
+
const details = await compactDetail(result.entries.map((e) => e.id), { reader });
|
|
27047
|
+
return details.documents.map((d, i) => ({
|
|
27048
|
+
id: Number(d.id.replace("obs-", "")),
|
|
27049
|
+
observationId: d.observationId,
|
|
27050
|
+
title: d.title,
|
|
27051
|
+
narrative: d.narrative,
|
|
27052
|
+
facts: d.facts,
|
|
27053
|
+
entityName: d.entityName,
|
|
27054
|
+
type: d.type,
|
|
27055
|
+
score: result.entries[i]?.score ?? 0
|
|
27056
|
+
}));
|
|
27057
|
+
},
|
|
27058
|
+
getObservation: (id) => {
|
|
27059
|
+
const o = getObservation(id);
|
|
27060
|
+
if (!o || o.projectId !== project.id || !canReadObservation(o, reader)) return null;
|
|
27061
|
+
return {
|
|
27062
|
+
id: o.id,
|
|
27063
|
+
entityName: o.entityName,
|
|
27064
|
+
type: o.type,
|
|
27065
|
+
title: o.title,
|
|
27066
|
+
narrative: o.narrative,
|
|
27067
|
+
facts: o.facts,
|
|
27068
|
+
topicKey: o.topicKey
|
|
27069
|
+
};
|
|
27070
|
+
},
|
|
27071
|
+
getEntityNames: () => graphManager.getEntityNames(),
|
|
27072
|
+
onStageEvent: onFormationStageEvent
|
|
27073
|
+
};
|
|
27074
|
+
formationResult = await withTimeout2(
|
|
27075
|
+
runFormation({
|
|
27076
|
+
entityName,
|
|
27077
|
+
type,
|
|
27078
|
+
title,
|
|
27079
|
+
narrative,
|
|
27080
|
+
facts: safeFacts,
|
|
27081
|
+
projectId: project.id,
|
|
27082
|
+
source: "explicit"
|
|
27083
|
+
}, formationConfig),
|
|
27084
|
+
FORMATION_TIMEOUT_MS,
|
|
27085
|
+
"Formation pipeline"
|
|
27086
|
+
);
|
|
27087
|
+
const modeIcon = "[FAST]";
|
|
27088
|
+
formationNote = `
|
|
26431
27089
|
${modeIcon} Formation[active]: ${formationResult.evaluation.category} (${formationResult.evaluation.score.toFixed(2)}) | ${formationResult.resolution.action} | ${formationResult.pipeline.durationMs}ms`;
|
|
26432
|
-
|
|
26433
|
-
|
|
26434
|
-
|
|
26435
|
-
|
|
26436
|
-
|
|
26437
|
-
|
|
26438
|
-
|
|
26439
|
-
|
|
26440
|
-
|
|
26441
|
-
|
|
26442
|
-
|
|
26443
|
-
|
|
26444
|
-
|
|
26445
|
-
|
|
26446
|
-
|
|
26447
|
-
|
|
26448
|
-
|
|
27090
|
+
if (formationResult.extraction.extractedFacts.length > 0) {
|
|
27091
|
+
formationNote += ` | +${formationResult.extraction.extractedFacts.length} facts`;
|
|
27092
|
+
}
|
|
27093
|
+
if (formationResult.extraction.titleImproved) formationNote += " | title\u2191";
|
|
27094
|
+
if (formationResult.extraction.entityResolved) formationNote += ` | entity\u2192${formationResult.entityName}`;
|
|
27095
|
+
if (formationResult.extraction.typeCorrected) formationNote += ` | type\u2192${formationResult.type}`;
|
|
27096
|
+
} catch (formationErr) {
|
|
27097
|
+
const isTimeout = formationErr instanceof Error && formationErr.message.includes("timed out");
|
|
27098
|
+
const elapsedMs = Date.now() - formationStartTime;
|
|
27099
|
+
const stageSummary = formatFormationStageDurations(completedFormationStages);
|
|
27100
|
+
console.error(
|
|
27101
|
+
`[memorix] Formation ${isTimeout ? "timed out" : "failed"} in memorix_store after ${elapsedMs}ms/${FORMATION_TIMEOUT_MS}ms at stage ${currentFormationStage}${stageSummary ? ` | completed: ${stageSummary}` : ""}`
|
|
27102
|
+
);
|
|
27103
|
+
if (!isTimeout && formationErr instanceof Error) {
|
|
27104
|
+
console.error(`[memorix] Formation error: ${formationErr.message}`);
|
|
27105
|
+
}
|
|
27106
|
+
formationNote = `
|
|
26449
27107
|
[WARN] Formation ${isTimeout ? "timed out" : "failed"} \u2014 storing base observation without enrichment`;
|
|
26450
|
-
}
|
|
26451
|
-
}
|
|
26452
|
-
if (useFormation && formationResult && formationResult.resolution.action !== "new") {
|
|
26453
|
-
const { action: action2, targetId, reason } = formationResult.resolution;
|
|
26454
|
-
if (action2 === "merge" && targetId) {
|
|
26455
|
-
const targetObs = getObservation(targetId);
|
|
26456
|
-
if (targetObs) {
|
|
26457
|
-
await storeObservation({
|
|
26458
|
-
entityName: targetObs.entityName,
|
|
26459
|
-
type: targetObs.type,
|
|
26460
|
-
title: formationResult.title,
|
|
26461
|
-
narrative: formationResult.narrative,
|
|
26462
|
-
facts: formationResult.facts,
|
|
26463
|
-
filesModified: safeFiles,
|
|
26464
|
-
concepts: safeConcepts,
|
|
26465
|
-
projectId: project.id,
|
|
26466
|
-
topicKey: targetObs.topicKey,
|
|
26467
|
-
progress,
|
|
26468
|
-
sourceDetail: "explicit",
|
|
26469
|
-
createdByAgentId: currentAgentId
|
|
26470
|
-
});
|
|
26471
|
-
return {
|
|
26472
|
-
content: [{
|
|
26473
|
-
type: "text",
|
|
26474
|
-
text: `[UPDATED] Formation MERGE: merged into #${targetId} (${reason})${formationNote}`
|
|
26475
|
-
}]
|
|
26476
|
-
};
|
|
26477
27108
|
}
|
|
26478
|
-
} else if (action2 === "evolve" && targetId) {
|
|
26479
|
-
const targetObs = getObservation(targetId);
|
|
26480
|
-
if (targetObs) {
|
|
26481
|
-
await storeObservation({
|
|
26482
|
-
entityName: targetObs.entityName,
|
|
26483
|
-
type: targetObs.type,
|
|
26484
|
-
title: formationResult.title,
|
|
26485
|
-
narrative: formationResult.narrative,
|
|
26486
|
-
facts: formationResult.facts,
|
|
26487
|
-
filesModified: safeFiles,
|
|
26488
|
-
concepts: safeConcepts,
|
|
26489
|
-
projectId: project.id,
|
|
26490
|
-
topicKey: targetObs.topicKey,
|
|
26491
|
-
progress,
|
|
26492
|
-
sourceDetail: "explicit",
|
|
26493
|
-
createdByAgentId: currentAgentId
|
|
26494
|
-
});
|
|
26495
|
-
return {
|
|
26496
|
-
content: [{
|
|
26497
|
-
type: "text",
|
|
26498
|
-
text: `[UPDATED] Formation EVOLVE: evolved #${targetId} (${reason})${formationNote}`
|
|
26499
|
-
}]
|
|
26500
|
-
};
|
|
26501
|
-
}
|
|
26502
|
-
} else if (action2 === "discard") {
|
|
26503
|
-
return {
|
|
26504
|
-
content: [{
|
|
26505
|
-
type: "text",
|
|
26506
|
-
text: `[SKIP] Formation DISCARD: ${reason}${formationNote}`
|
|
26507
|
-
}]
|
|
26508
|
-
};
|
|
26509
27109
|
}
|
|
26510
|
-
|
|
26511
|
-
|
|
26512
|
-
|
|
26513
|
-
|
|
26514
|
-
|
|
26515
|
-
|
|
26516
|
-
|
|
26517
|
-
|
|
26518
|
-
|
|
26519
|
-
|
|
26520
|
-
|
|
26521
|
-
|
|
26522
|
-
|
|
26523
|
-
|
|
26524
|
-
|
|
26525
|
-
|
|
26526
|
-
|
|
26527
|
-
|
|
26528
|
-
|
|
26529
|
-
|
|
26530
|
-
|
|
26531
|
-
}));
|
|
26532
|
-
const decision = await withTimeout2(
|
|
26533
|
-
compactOnWrite(
|
|
26534
|
-
{ title, narrative, facts: safeFacts ?? [] },
|
|
26535
|
-
existingMemories
|
|
26536
|
-
),
|
|
26537
|
-
COMPACT_ON_WRITE_TIMEOUT_MS,
|
|
26538
|
-
"Compact-on-write"
|
|
26539
|
-
);
|
|
26540
|
-
if (decision.action === "UPDATE" && decision.targetId) {
|
|
26541
|
-
const targetObs = getObservation(decision.targetId);
|
|
26542
|
-
if (targetObs) {
|
|
26543
|
-
await storeObservation({
|
|
26544
|
-
entityName: targetObs.entityName,
|
|
26545
|
-
type: targetObs.type,
|
|
26546
|
-
title: decision.mergedNarrative ? title : targetObs.title,
|
|
26547
|
-
narrative: decision.mergedNarrative ?? narrative,
|
|
26548
|
-
facts: decision.mergedFacts ?? safeFacts,
|
|
26549
|
-
filesModified: safeFiles,
|
|
26550
|
-
concepts: safeConcepts,
|
|
26551
|
-
projectId: project.id,
|
|
26552
|
-
topicKey: targetObs.topicKey,
|
|
26553
|
-
progress,
|
|
26554
|
-
sourceDetail: "explicit",
|
|
26555
|
-
createdByAgentId: currentAgentId
|
|
26556
|
-
});
|
|
26557
|
-
compactAction = `[UPDATED] Compact UPDATE: merged into #${decision.targetId} (${decision.reason})`;
|
|
26558
|
-
compactMerged = true;
|
|
26559
|
-
return {
|
|
26560
|
-
content: [{
|
|
26561
|
-
type: "text",
|
|
26562
|
-
text: `${compactAction}
|
|
26563
|
-
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
26564
|
-
}]
|
|
26565
|
-
};
|
|
26566
|
-
}
|
|
26567
|
-
} else if (decision.action === "NONE") {
|
|
27110
|
+
if (useFormation && formationResult && formationResult.resolution.action !== "new") {
|
|
27111
|
+
const { action: action2, targetId, reason } = formationResult.resolution;
|
|
27112
|
+
if (action2 === "merge" && targetId) {
|
|
27113
|
+
const targetObs = getObservation(targetId);
|
|
27114
|
+
if (targetObs && targetObs.projectId === project.id && canReadObservation(targetObs, reader)) {
|
|
27115
|
+
await storeObservation({
|
|
27116
|
+
entityName: targetObs.entityName,
|
|
27117
|
+
type: targetObs.type,
|
|
27118
|
+
title: formationResult.title,
|
|
27119
|
+
narrative: formationResult.narrative,
|
|
27120
|
+
facts: formationResult.facts,
|
|
27121
|
+
filesModified: safeFiles,
|
|
27122
|
+
concepts: safeConcepts,
|
|
27123
|
+
projectId: project.id,
|
|
27124
|
+
topicKey: targetObs.topicKey,
|
|
27125
|
+
progress,
|
|
27126
|
+
sourceDetail: "explicit",
|
|
27127
|
+
createdByAgentId: currentAgentId,
|
|
27128
|
+
visibility,
|
|
27129
|
+
visibilityReader: reader
|
|
27130
|
+
});
|
|
26568
27131
|
return {
|
|
26569
27132
|
content: [{
|
|
26570
27133
|
type: "text",
|
|
26571
|
-
text: `[
|
|
26572
|
-
Existing memory #${decision.targetId} already covers this.
|
|
26573
|
-
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
27134
|
+
text: `[UPDATED] Formation MERGE: merged into #${targetId} (${reason})${formationNote}`
|
|
26574
27135
|
}]
|
|
26575
27136
|
};
|
|
26576
|
-
} else if (decision.action === "DELETE" && decision.targetId) {
|
|
26577
|
-
const { resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
26578
|
-
await resolveObservations2([decision.targetId], "resolved");
|
|
26579
|
-
compactAction = ` | Compact: resolved outdated #${decision.targetId}`;
|
|
26580
27137
|
}
|
|
26581
|
-
|
|
26582
|
-
|
|
26583
|
-
|
|
26584
|
-
|
|
26585
|
-
|
|
26586
|
-
|
|
27138
|
+
} else if (action2 === "evolve" && targetId) {
|
|
27139
|
+
const targetObs = getObservation(targetId);
|
|
27140
|
+
if (targetObs && targetObs.projectId === project.id && canReadObservation(targetObs, reader)) {
|
|
27141
|
+
await storeObservation({
|
|
27142
|
+
entityName: targetObs.entityName,
|
|
27143
|
+
type: targetObs.type,
|
|
27144
|
+
title: formationResult.title,
|
|
27145
|
+
narrative: formationResult.narrative,
|
|
27146
|
+
facts: formationResult.facts,
|
|
27147
|
+
filesModified: safeFiles,
|
|
27148
|
+
concepts: safeConcepts,
|
|
27149
|
+
projectId: project.id,
|
|
27150
|
+
topicKey: targetObs.topicKey,
|
|
27151
|
+
progress,
|
|
27152
|
+
sourceDetail: "explicit",
|
|
27153
|
+
createdByAgentId: currentAgentId,
|
|
27154
|
+
visibility,
|
|
27155
|
+
visibilityReader: reader
|
|
27156
|
+
});
|
|
27157
|
+
return {
|
|
27158
|
+
content: [{
|
|
27159
|
+
type: "text",
|
|
27160
|
+
text: `[UPDATED] Formation EVOLVE: evolved #${targetId} (${reason})${formationNote}`
|
|
27161
|
+
}]
|
|
27162
|
+
};
|
|
26587
27163
|
}
|
|
27164
|
+
} else if (action2 === "discard") {
|
|
27165
|
+
return {
|
|
27166
|
+
content: [{
|
|
27167
|
+
type: "text",
|
|
27168
|
+
text: `[SKIP] Formation DISCARD: ${reason}${formationNote}`
|
|
27169
|
+
}]
|
|
27170
|
+
};
|
|
26588
27171
|
}
|
|
26589
|
-
} catch {
|
|
26590
|
-
}
|
|
26591
|
-
}
|
|
26592
|
-
if (formationResult && formationResult.resolution.action === "new") {
|
|
26593
|
-
const llmFacts = formationResult.extraction.extractedFacts;
|
|
26594
|
-
if (llmFacts.length > 0) {
|
|
26595
|
-
const currentFacts = safeFacts ?? [];
|
|
26596
|
-
const currentLower = new Set(currentFacts.map((f) => f.toLowerCase().trim()));
|
|
26597
|
-
const newFacts = llmFacts.filter((f) => !currentLower.has(f.toLowerCase().trim()));
|
|
26598
|
-
if (newFacts.length > 0) {
|
|
26599
|
-
safeFacts = [...currentFacts, ...newFacts];
|
|
26600
|
-
}
|
|
26601
|
-
}
|
|
26602
|
-
if (formationResult.extraction.titleImproved && formationResult.title) {
|
|
26603
|
-
title = formationResult.title;
|
|
26604
|
-
}
|
|
26605
|
-
if (formationResult.extraction.typeCorrected && formationResult.type) {
|
|
26606
|
-
type = formationResult.type;
|
|
26607
|
-
}
|
|
26608
|
-
if (formationResult.extraction.entityResolved && formationResult.entityName) {
|
|
26609
|
-
entityName = formationResult.entityName;
|
|
26610
|
-
}
|
|
26611
|
-
}
|
|
26612
|
-
await graphManager.createEntities([
|
|
26613
|
-
{ name: entityName, entityType: "auto", observations: [] }
|
|
26614
|
-
]);
|
|
26615
|
-
let sessionId;
|
|
26616
|
-
try {
|
|
26617
|
-
const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
26618
|
-
const active = await getActiveSession2(projectDir2, project.id);
|
|
26619
|
-
if (active) sessionId = active.id;
|
|
26620
|
-
} catch {
|
|
26621
|
-
}
|
|
26622
|
-
let finalNarrative = narrative;
|
|
26623
|
-
let compressionNote = "";
|
|
26624
|
-
try {
|
|
26625
|
-
const { compressNarrative: compressNarrative2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
|
|
26626
|
-
const { compressed, saved, usedLLM } = await withTimeout2(
|
|
26627
|
-
compressNarrative2(narrative, safeFacts, type),
|
|
26628
|
-
COMPRESSION_TIMEOUT_MS,
|
|
26629
|
-
"Narrative compression"
|
|
26630
|
-
);
|
|
26631
|
-
if (usedLLM && saved > 0) {
|
|
26632
|
-
finalNarrative = compressed;
|
|
26633
|
-
compressionNote = ` | compressed -${saved} tokens`;
|
|
26634
|
-
}
|
|
26635
|
-
} catch {
|
|
26636
|
-
}
|
|
26637
|
-
let attributionWarning = "";
|
|
26638
|
-
try {
|
|
26639
|
-
const attrCheck = await checkProjectAttribution(entityName, project.id, getAllObservations());
|
|
26640
|
-
if (attrCheck.suspicious) {
|
|
26641
|
-
attributionWarning = `
|
|
26642
|
-
[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.`;
|
|
26643
27172
|
}
|
|
26644
|
-
|
|
26645
|
-
|
|
26646
|
-
|
|
26647
|
-
entityName,
|
|
26648
|
-
type,
|
|
26649
|
-
title,
|
|
26650
|
-
narrative: finalNarrative,
|
|
26651
|
-
facts: safeFacts,
|
|
26652
|
-
filesModified: safeFiles,
|
|
26653
|
-
concepts: safeConcepts,
|
|
26654
|
-
projectId: project.id,
|
|
26655
|
-
topicKey,
|
|
26656
|
-
sessionId,
|
|
26657
|
-
progress,
|
|
26658
|
-
relatedCommits,
|
|
26659
|
-
relatedEntities,
|
|
26660
|
-
sourceDetail: "explicit",
|
|
26661
|
-
valueCategory: formationResult?.evaluation.category,
|
|
26662
|
-
createdByAgentId: currentAgentId
|
|
26663
|
-
});
|
|
26664
|
-
await graphManager.addObservations([
|
|
26665
|
-
{ entityName, contents: [`[#${obs.id}] ${title}`] }
|
|
26666
|
-
]);
|
|
26667
|
-
const extracted = extractEntities([title, narrative, ...safeFacts ?? []].join(" "));
|
|
26668
|
-
const autoRelCount = await createAutoRelations(obs, extracted, graphManager);
|
|
26669
|
-
const enrichmentParts = [];
|
|
26670
|
-
const autoFiles = obs.filesModified.filter((f) => !(safeFiles ?? []).includes(f));
|
|
26671
|
-
const autoConcepts = obs.concepts.filter((c) => !(safeConcepts ?? []).includes(c));
|
|
26672
|
-
if (autoFiles.length > 0) enrichmentParts.push(`+${autoFiles.length} files extracted`);
|
|
26673
|
-
if (autoConcepts.length > 0) enrichmentParts.push(`+${autoConcepts.length} concepts enriched`);
|
|
26674
|
-
if (autoRelCount > 0) enrichmentParts.push(`+${autoRelCount} relations auto-created`);
|
|
26675
|
-
if (obs.hasCausalLanguage) enrichmentParts.push("causal language detected");
|
|
26676
|
-
if (upserted) enrichmentParts.push(`topic upserted (rev ${obs.revisionCount ?? 1})`);
|
|
26677
|
-
const enrichment = enrichmentParts.length > 0 ? `
|
|
26678
|
-
Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
26679
|
-
const action = upserted ? "[UPDATED] Updated" : "[OK] Stored";
|
|
26680
|
-
if (!useFormation && !topicKey && !progress) {
|
|
26681
|
-
const shadowFormation = async () => {
|
|
26682
|
-
let oldCompactDecision = null;
|
|
27173
|
+
let compactAction = "";
|
|
27174
|
+
let compactMerged = false;
|
|
27175
|
+
if (!useFormation && !topicKey && !progress) {
|
|
26683
27176
|
try {
|
|
26684
|
-
const compactStart = Date.now();
|
|
26685
27177
|
const searchResult = await compactSearch({
|
|
26686
27178
|
query: `${title} ${narrative.substring(0, 200)}`,
|
|
26687
27179
|
limit: 5,
|
|
26688
27180
|
projectId: project.id,
|
|
26689
|
-
status: "active"
|
|
27181
|
+
status: "active",
|
|
27182
|
+
reader
|
|
26690
27183
|
});
|
|
26691
27184
|
const similarEntries = searchResult.entries.map((e) => e);
|
|
26692
27185
|
if (similarEntries.length > 0) {
|
|
26693
27186
|
const similarIds = similarEntries.map((e) => e.id);
|
|
26694
|
-
const details = await compactDetail(similarIds);
|
|
27187
|
+
const details = await compactDetail(similarIds, { reader });
|
|
26695
27188
|
const existingMemories = details.documents.map((d, i) => ({
|
|
26696
27189
|
id: d.observationId,
|
|
26697
27190
|
title: d.title,
|
|
@@ -26699,78 +27192,266 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
|
26699
27192
|
facts: d.facts,
|
|
26700
27193
|
score: similarEntries[i]?.score ?? 0
|
|
26701
27194
|
}));
|
|
26702
|
-
const decision = await
|
|
26703
|
-
|
|
26704
|
-
|
|
27195
|
+
const decision = await withTimeout2(
|
|
27196
|
+
compactOnWrite(
|
|
27197
|
+
{ title, narrative, facts: safeFacts ?? [] },
|
|
27198
|
+
existingMemories
|
|
27199
|
+
),
|
|
27200
|
+
COMPACT_ON_WRITE_TIMEOUT_MS,
|
|
27201
|
+
"Compact-on-write"
|
|
26705
27202
|
);
|
|
26706
|
-
|
|
26707
|
-
|
|
26708
|
-
|
|
26709
|
-
|
|
26710
|
-
|
|
26711
|
-
|
|
27203
|
+
if (decision.action === "UPDATE" && decision.targetId) {
|
|
27204
|
+
const targetObs = getObservation(decision.targetId);
|
|
27205
|
+
if (targetObs && targetObs.projectId === project.id && canReadObservation(targetObs, reader)) {
|
|
27206
|
+
await storeObservation({
|
|
27207
|
+
entityName: targetObs.entityName,
|
|
27208
|
+
type: targetObs.type,
|
|
27209
|
+
title: decision.mergedNarrative ? title : targetObs.title,
|
|
27210
|
+
narrative: decision.mergedNarrative ?? narrative,
|
|
27211
|
+
facts: decision.mergedFacts ?? safeFacts,
|
|
27212
|
+
filesModified: safeFiles,
|
|
27213
|
+
concepts: safeConcepts,
|
|
27214
|
+
projectId: project.id,
|
|
27215
|
+
topicKey: targetObs.topicKey,
|
|
27216
|
+
progress,
|
|
27217
|
+
sourceDetail: "explicit",
|
|
27218
|
+
createdByAgentId: currentAgentId,
|
|
27219
|
+
visibility,
|
|
27220
|
+
visibilityReader: reader
|
|
27221
|
+
});
|
|
27222
|
+
compactAction = `[UPDATED] Compact UPDATE: merged into #${decision.targetId} (${decision.reason})`;
|
|
27223
|
+
compactMerged = true;
|
|
27224
|
+
return {
|
|
27225
|
+
content: [{
|
|
27226
|
+
type: "text",
|
|
27227
|
+
text: `${compactAction}
|
|
27228
|
+
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
27229
|
+
}]
|
|
27230
|
+
};
|
|
27231
|
+
}
|
|
27232
|
+
} else if (decision.action === "NONE") {
|
|
27233
|
+
return {
|
|
27234
|
+
content: [{
|
|
27235
|
+
type: "text",
|
|
27236
|
+
text: `[SKIP] Compact SKIP: ${decision.reason}
|
|
27237
|
+
Existing memory #${decision.targetId} already covers this.
|
|
27238
|
+
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
27239
|
+
}]
|
|
27240
|
+
};
|
|
27241
|
+
} else if (decision.action === "DELETE" && decision.targetId) {
|
|
27242
|
+
const { resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27243
|
+
await resolveObservations2([decision.targetId], "resolved");
|
|
27244
|
+
compactAction = ` | Compact: resolved outdated #${decision.targetId}`;
|
|
27245
|
+
}
|
|
27246
|
+
if (decision.enrichedFacts && decision.enrichedFacts.length > 0) {
|
|
27247
|
+
const currentFacts = safeFacts ?? [];
|
|
27248
|
+
const newFacts = decision.enrichedFacts.filter((f) => !currentFacts.includes(f));
|
|
27249
|
+
if (newFacts.length > 0) {
|
|
27250
|
+
compactAction += ` | +${newFacts.length} LLM-extracted facts`;
|
|
27251
|
+
}
|
|
27252
|
+
}
|
|
26712
27253
|
}
|
|
26713
27254
|
} catch {
|
|
26714
27255
|
}
|
|
26715
|
-
|
|
26716
|
-
|
|
26717
|
-
|
|
26718
|
-
|
|
26719
|
-
|
|
26720
|
-
|
|
26721
|
-
|
|
26722
|
-
|
|
26723
|
-
|
|
26724
|
-
|
|
26725
|
-
|
|
26726
|
-
|
|
26727
|
-
|
|
26728
|
-
|
|
26729
|
-
|
|
26730
|
-
|
|
26731
|
-
|
|
26732
|
-
|
|
26733
|
-
|
|
26734
|
-
|
|
26735
|
-
|
|
26736
|
-
|
|
26737
|
-
|
|
26738
|
-
|
|
26739
|
-
|
|
26740
|
-
|
|
26741
|
-
const
|
|
26742
|
-
|
|
26743
|
-
|
|
26744
|
-
|
|
27256
|
+
}
|
|
27257
|
+
if (formationResult && formationResult.resolution.action === "new") {
|
|
27258
|
+
const llmFacts = formationResult.extraction.extractedFacts;
|
|
27259
|
+
if (llmFacts.length > 0) {
|
|
27260
|
+
const currentFacts = safeFacts ?? [];
|
|
27261
|
+
const currentLower = new Set(currentFacts.map((f) => f.toLowerCase().trim()));
|
|
27262
|
+
const newFacts = llmFacts.filter((f) => !currentLower.has(f.toLowerCase().trim()));
|
|
27263
|
+
if (newFacts.length > 0) {
|
|
27264
|
+
safeFacts = [...currentFacts, ...newFacts];
|
|
27265
|
+
}
|
|
27266
|
+
}
|
|
27267
|
+
if (formationResult.extraction.titleImproved && formationResult.title) {
|
|
27268
|
+
title = formationResult.title;
|
|
27269
|
+
}
|
|
27270
|
+
if (formationResult.extraction.typeCorrected && formationResult.type) {
|
|
27271
|
+
type = formationResult.type;
|
|
27272
|
+
}
|
|
27273
|
+
if (formationResult.extraction.entityResolved && formationResult.entityName) {
|
|
27274
|
+
entityName = formationResult.entityName;
|
|
27275
|
+
}
|
|
27276
|
+
}
|
|
27277
|
+
await graphManager.createEntities([
|
|
27278
|
+
{ name: entityName, entityType: "auto", observations: [] }
|
|
27279
|
+
]);
|
|
27280
|
+
let sessionId;
|
|
27281
|
+
try {
|
|
27282
|
+
const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
27283
|
+
const active = await getActiveSession2(projectDir2, project.id);
|
|
27284
|
+
if (active) sessionId = active.id;
|
|
27285
|
+
} catch {
|
|
27286
|
+
}
|
|
27287
|
+
let finalNarrative = narrative;
|
|
27288
|
+
let compressionNote = "";
|
|
27289
|
+
try {
|
|
27290
|
+
const { compressNarrative: compressNarrative2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
|
|
27291
|
+
const { compressed, saved, usedLLM } = await withTimeout2(
|
|
27292
|
+
compressNarrative2(narrative, safeFacts, type),
|
|
27293
|
+
COMPRESSION_TIMEOUT_MS,
|
|
27294
|
+
"Narrative compression"
|
|
26745
27295
|
);
|
|
26746
|
-
|
|
26747
|
-
|
|
26748
|
-
|
|
26749
|
-
formationAction: formed.resolution.action,
|
|
26750
|
-
formationTargetId: formed.resolution.targetId,
|
|
26751
|
-
oldCompactAction: oldCompactDecision.action,
|
|
26752
|
-
oldCompactTargetId: oldCompactDecision.targetId,
|
|
26753
|
-
oldCompactReason: oldCompactDecision.reason,
|
|
26754
|
-
formationValueScore: formed.evaluation.score,
|
|
26755
|
-
formationValueCategory: formed.evaluation.category,
|
|
26756
|
-
formationDurationMs: formed.pipeline.durationMs,
|
|
26757
|
-
compactDurationMs: oldCompactDecision.durationMs
|
|
26758
|
-
});
|
|
27296
|
+
if (usedLLM && saved > 0) {
|
|
27297
|
+
finalNarrative = compressed;
|
|
27298
|
+
compressionNote = ` | compressed -${saved} tokens`;
|
|
26759
27299
|
}
|
|
26760
|
-
}
|
|
26761
|
-
|
|
27300
|
+
} catch {
|
|
27301
|
+
}
|
|
27302
|
+
let attributionWarning = "";
|
|
27303
|
+
try {
|
|
27304
|
+
const attrCheck = await checkProjectAttribution(
|
|
27305
|
+
entityName,
|
|
27306
|
+
project.id,
|
|
27307
|
+
filterReadableObservations(getAllObservations(), reader)
|
|
27308
|
+
);
|
|
27309
|
+
if (attrCheck.suspicious) {
|
|
27310
|
+
attributionWarning = `
|
|
27311
|
+
[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.`;
|
|
27312
|
+
}
|
|
27313
|
+
} catch {
|
|
27314
|
+
}
|
|
27315
|
+
const { observation: obs, upserted } = await storeObservation({
|
|
27316
|
+
entityName,
|
|
27317
|
+
type,
|
|
27318
|
+
title,
|
|
27319
|
+
narrative: finalNarrative,
|
|
27320
|
+
facts: safeFacts,
|
|
27321
|
+
filesModified: safeFiles,
|
|
27322
|
+
concepts: safeConcepts,
|
|
27323
|
+
projectId: project.id,
|
|
27324
|
+
topicKey,
|
|
27325
|
+
sessionId,
|
|
27326
|
+
progress,
|
|
27327
|
+
relatedCommits,
|
|
27328
|
+
relatedEntities,
|
|
27329
|
+
sourceDetail: "explicit",
|
|
27330
|
+
valueCategory: formationResult?.evaluation.category,
|
|
27331
|
+
createdByAgentId: currentAgentId,
|
|
27332
|
+
visibility,
|
|
27333
|
+
visibilityReader: reader
|
|
26762
27334
|
});
|
|
26763
|
-
|
|
26764
|
-
|
|
26765
|
-
|
|
26766
|
-
|
|
26767
|
-
|
|
26768
|
-
|
|
27335
|
+
await graphManager.addObservations([
|
|
27336
|
+
{ entityName, contents: [`[#${obs.id}] ${title}`] }
|
|
27337
|
+
]);
|
|
27338
|
+
const extracted = extractEntities([title, narrative, ...safeFacts ?? []].join(" "));
|
|
27339
|
+
const autoRelCount = await createAutoRelations(obs, extracted, graphManager);
|
|
27340
|
+
const enrichmentParts = [];
|
|
27341
|
+
const autoFiles = obs.filesModified.filter((f) => !(safeFiles ?? []).includes(f));
|
|
27342
|
+
const autoConcepts = obs.concepts.filter((c) => !(safeConcepts ?? []).includes(c));
|
|
27343
|
+
if (autoFiles.length > 0) enrichmentParts.push(`+${autoFiles.length} files extracted`);
|
|
27344
|
+
if (autoConcepts.length > 0) enrichmentParts.push(`+${autoConcepts.length} concepts enriched`);
|
|
27345
|
+
if (autoRelCount > 0) enrichmentParts.push(`+${autoRelCount} relations auto-created`);
|
|
27346
|
+
if (obs.hasCausalLanguage) enrichmentParts.push("causal language detected");
|
|
27347
|
+
if (upserted) enrichmentParts.push(`topic upserted (rev ${obs.revisionCount ?? 1})`);
|
|
27348
|
+
const enrichment = enrichmentParts.length > 0 ? `
|
|
27349
|
+
Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
27350
|
+
const action = upserted ? "[UPDATED] Updated" : "[OK] Stored";
|
|
27351
|
+
if (!useFormation && !topicKey && !progress) {
|
|
27352
|
+
const shadowFormation = async () => {
|
|
27353
|
+
let oldCompactDecision = null;
|
|
27354
|
+
try {
|
|
27355
|
+
const compactStart = Date.now();
|
|
27356
|
+
const searchResult = await compactSearch({
|
|
27357
|
+
query: `${title} ${narrative.substring(0, 200)}`,
|
|
27358
|
+
limit: 5,
|
|
27359
|
+
projectId: project.id,
|
|
27360
|
+
status: "active",
|
|
27361
|
+
reader
|
|
27362
|
+
});
|
|
27363
|
+
const similarEntries = searchResult.entries.map((e) => e);
|
|
27364
|
+
if (similarEntries.length > 0) {
|
|
27365
|
+
const similarIds = similarEntries.map((e) => e.id);
|
|
27366
|
+
const details = await compactDetail(similarIds, { reader });
|
|
27367
|
+
const existingMemories = details.documents.map((d, i) => ({
|
|
27368
|
+
id: d.observationId,
|
|
27369
|
+
title: d.title,
|
|
27370
|
+
narrative: d.narrative,
|
|
27371
|
+
facts: d.facts,
|
|
27372
|
+
score: similarEntries[i]?.score ?? 0
|
|
27373
|
+
}));
|
|
27374
|
+
const decision = await compactOnWrite(
|
|
27375
|
+
{ title, narrative, facts: safeFacts ?? [] },
|
|
27376
|
+
existingMemories
|
|
27377
|
+
);
|
|
27378
|
+
oldCompactDecision = {
|
|
27379
|
+
action: decision.action,
|
|
27380
|
+
targetId: decision.targetId,
|
|
27381
|
+
reason: decision.reason,
|
|
27382
|
+
durationMs: Date.now() - compactStart
|
|
27383
|
+
};
|
|
27384
|
+
}
|
|
27385
|
+
} catch {
|
|
27386
|
+
}
|
|
27387
|
+
const formationConfig = {
|
|
27388
|
+
mode: formationMode,
|
|
27389
|
+
useLLM: isLLMEnabled(),
|
|
27390
|
+
minValueScore: 0.3,
|
|
27391
|
+
searchMemories: async (q, limit, pid) => {
|
|
27392
|
+
const result = await compactSearch({ query: q, limit, projectId: pid, status: "active", reader });
|
|
27393
|
+
if (result.entries.length === 0) return [];
|
|
27394
|
+
const details = await compactDetail(result.entries.map((e) => e.id), { reader });
|
|
27395
|
+
return details.documents.map((d, i) => ({
|
|
27396
|
+
id: Number(d.id.replace("obs-", "")),
|
|
27397
|
+
observationId: d.observationId,
|
|
27398
|
+
title: d.title,
|
|
27399
|
+
narrative: d.narrative,
|
|
27400
|
+
facts: d.facts,
|
|
27401
|
+
entityName: d.entityName,
|
|
27402
|
+
type: d.type,
|
|
27403
|
+
score: result.entries[i]?.score ?? 0
|
|
27404
|
+
}));
|
|
27405
|
+
},
|
|
27406
|
+
getObservation: (id) => {
|
|
27407
|
+
const o = getObservation(id);
|
|
27408
|
+
if (!o || o.projectId !== project.id || !canReadObservation(o, reader)) return null;
|
|
27409
|
+
return { id: o.id, entityName: o.entityName, type: o.type, title: o.title, narrative: o.narrative, facts: o.facts, topicKey: o.topicKey };
|
|
27410
|
+
},
|
|
27411
|
+
getEntityNames: () => graphManager.getEntityNames()
|
|
27412
|
+
};
|
|
27413
|
+
const formed = await withTimeout2(
|
|
27414
|
+
runFormation({ entityName, type, title, narrative, facts: safeFacts, projectId: project.id, source: "explicit", topicKey }, formationConfig),
|
|
27415
|
+
FORMATION_TIMEOUT_MS,
|
|
27416
|
+
"Shadow formation"
|
|
27417
|
+
);
|
|
27418
|
+
const { recordBeforeAfterMetrics: recordBeforeAfterMetrics2 } = await Promise.resolve().then(() => (init_formation(), formation_exports));
|
|
27419
|
+
if (oldCompactDecision) {
|
|
27420
|
+
recordBeforeAfterMetrics2({
|
|
27421
|
+
formationAction: formed.resolution.action,
|
|
27422
|
+
formationTargetId: formed.resolution.targetId,
|
|
27423
|
+
oldCompactAction: oldCompactDecision.action,
|
|
27424
|
+
oldCompactTargetId: oldCompactDecision.targetId,
|
|
27425
|
+
oldCompactReason: oldCompactDecision.reason,
|
|
27426
|
+
formationValueScore: formed.evaluation.score,
|
|
27427
|
+
formationValueCategory: formed.evaluation.category,
|
|
27428
|
+
formationDurationMs: formed.pipeline.durationMs,
|
|
27429
|
+
compactDurationMs: oldCompactDecision.durationMs
|
|
27430
|
+
});
|
|
27431
|
+
}
|
|
27432
|
+
};
|
|
27433
|
+
shadowFormation().catch(() => {
|
|
27434
|
+
});
|
|
27435
|
+
}
|
|
27436
|
+
return {
|
|
27437
|
+
content: [
|
|
27438
|
+
{
|
|
27439
|
+
type: "text",
|
|
27440
|
+
text: `${action} observation #${obs.id} "${title}" (~${obs.tokens} tokens)
|
|
26769
27441
|
Entity: ${entityName} | Type: ${type} | Project: ${project.id}${obs.topicKey ? ` | Topic: ${obs.topicKey}` : ""}${compactAction}${compressionNote}${enrichment}${formationNote}${attributionWarning}`
|
|
26770
|
-
|
|
26771
|
-
|
|
27442
|
+
}
|
|
27443
|
+
]
|
|
27444
|
+
};
|
|
27445
|
+
});
|
|
27446
|
+
} catch (error) {
|
|
27447
|
+
return {
|
|
27448
|
+
content: [{
|
|
27449
|
+
type: "text",
|
|
27450
|
+
text: error instanceof Error ? error.message : "Failed to store memory."
|
|
27451
|
+
}],
|
|
27452
|
+
isError: true
|
|
26772
27453
|
};
|
|
26773
|
-
}
|
|
27454
|
+
}
|
|
26774
27455
|
}
|
|
26775
27456
|
);
|
|
26776
27457
|
server.registerTool(
|
|
@@ -26842,7 +27523,8 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
26842
27523
|
// Use scope: 'global' to explicitly search all projects.
|
|
26843
27524
|
projectId: scope === "global" ? void 0 : project.id,
|
|
26844
27525
|
status: status ?? "active",
|
|
26845
|
-
source
|
|
27526
|
+
source,
|
|
27527
|
+
reader: getObservationReader(scope === "global" ? "global" : "project")
|
|
26846
27528
|
});
|
|
26847
27529
|
const timeoutPromise = new Promise(
|
|
26848
27530
|
(_, reject) => setTimeout(() => reject(new Error(`Search timeout after ${TIMEOUT_MS}ms`)), TIMEOUT_MS)
|
|
@@ -26900,7 +27582,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26900
27582
|
const unresolved = requireResolvedProject("build graph context for the current project");
|
|
26901
27583
|
if (unresolved) return unresolved;
|
|
26902
27584
|
const { getObservationStore: getObservationStore2 } = await Promise.resolve().then(() => (init_obs_store(), obs_store_exports));
|
|
26903
|
-
const observations2 =
|
|
27585
|
+
const observations2 = filterReadableObservations(
|
|
27586
|
+
await getObservationStore2().loadByProject(project.id),
|
|
27587
|
+
getObservationReader()
|
|
27588
|
+
);
|
|
26904
27589
|
const packet = buildGraphContextPacket(observations2, {
|
|
26905
27590
|
projectId: project.id,
|
|
26906
27591
|
query,
|
|
@@ -26952,7 +27637,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26952
27637
|
Promise.resolve().then(() => (init_maintenance_jobs(), maintenance_jobs_exports)),
|
|
26953
27638
|
Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports))
|
|
26954
27639
|
]);
|
|
26955
|
-
const observations2 =
|
|
27640
|
+
const observations2 = filterReadableObservations(
|
|
27641
|
+
await getObservationStore2().loadByProject(project.id, { status: "active" }),
|
|
27642
|
+
getObservationReader()
|
|
27643
|
+
);
|
|
26956
27644
|
const context = await buildAutoProjectContext2({
|
|
26957
27645
|
project,
|
|
26958
27646
|
dataDir: projectDir2,
|
|
@@ -27042,7 +27730,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27042
27730
|
await store.init(projectDir2);
|
|
27043
27731
|
const codegraphConfig = getResolvedConfig2({ projectRoot: project.rootPath }).codegraph;
|
|
27044
27732
|
const exclude = codegraphConfig.excludePatterns;
|
|
27045
|
-
const observations2 =
|
|
27733
|
+
const observations2 = filterReadableObservations(
|
|
27734
|
+
await getObservationStore2().loadByProject(project.id, { status: "active" }),
|
|
27735
|
+
getObservationReader()
|
|
27736
|
+
);
|
|
27046
27737
|
observations2.reverse();
|
|
27047
27738
|
const basePack = assembleContextPackForTask2({
|
|
27048
27739
|
store,
|
|
@@ -27414,9 +28105,15 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27414
28105
|
}
|
|
27415
28106
|
},
|
|
27416
28107
|
async ({ ids, status }) => {
|
|
27417
|
-
const { resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28108
|
+
const { resolveObservations: resolveObservations2, getObservation: getObservation2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27418
28109
|
const safeIds = (Array.isArray(ids) ? ids : [ids]).map((id) => coerceNumber(id, 0)).filter((id) => id > 0);
|
|
27419
|
-
const
|
|
28110
|
+
const reader = getObservationReader();
|
|
28111
|
+
const authorizedIds = safeIds.filter((id) => {
|
|
28112
|
+
const observation = getObservation2(id, project.id);
|
|
28113
|
+
return observation ? canManageObservation(observation, reader) : false;
|
|
28114
|
+
});
|
|
28115
|
+
const result = await resolveObservations2(authorizedIds, status ?? "resolved");
|
|
28116
|
+
const deniedCount = safeIds.length - authorizedIds.length;
|
|
27420
28117
|
const parts = [];
|
|
27421
28118
|
if (result.resolved.length > 0) {
|
|
27422
28119
|
parts.push(`[OK] Resolved ${result.resolved.length} observation(s): #${result.resolved.join(", #")}`);
|
|
@@ -27424,6 +28121,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27424
28121
|
if (result.notFound.length > 0) {
|
|
27425
28122
|
parts.push(`[WARN] Not found: #${result.notFound.join(", #")}`);
|
|
27426
28123
|
}
|
|
28124
|
+
if (deniedCount > 0) {
|
|
28125
|
+
parts.push(`[WARN] Skipped ${deniedCount} observation(s) outside this session's write scope.`);
|
|
28126
|
+
}
|
|
27427
28127
|
parts.push('\nResolved memories are hidden from default search. Use status="all" to include them.');
|
|
27428
28128
|
parts.push('[STATS] Run `memorix_retention` with `action: "report"` to check remaining cleanup status.');
|
|
27429
28129
|
return {
|
|
@@ -27453,6 +28153,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27453
28153
|
async ({ entityName, decision, alternatives, rationale, constraints, expectedOutcome, risks, concepts, filesModified, relatedCommits, relatedEntities }) => {
|
|
27454
28154
|
const unresolved = requireResolvedProject("store reasoning in the current project");
|
|
27455
28155
|
if (unresolved) return unresolved;
|
|
28156
|
+
const reader = getObservationReader();
|
|
27456
28157
|
return withFreshIndex(async () => {
|
|
27457
28158
|
const narrativeParts = [rationale];
|
|
27458
28159
|
if (alternatives && alternatives.length > 0) {
|
|
@@ -27475,7 +28176,11 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27475
28176
|
]);
|
|
27476
28177
|
let reasoningAttributionWarning = "";
|
|
27477
28178
|
try {
|
|
27478
|
-
const attrCheck = await checkProjectAttribution(
|
|
28179
|
+
const attrCheck = await checkProjectAttribution(
|
|
28180
|
+
entityName,
|
|
28181
|
+
project.id,
|
|
28182
|
+
filterReadableObservations(getAllObservations(), reader)
|
|
28183
|
+
);
|
|
27479
28184
|
if (attrCheck.suspicious) {
|
|
27480
28185
|
reasoningAttributionWarning = `
|
|
27481
28186
|
[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.`;
|
|
@@ -27495,7 +28200,8 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27495
28200
|
relatedCommits,
|
|
27496
28201
|
relatedEntities,
|
|
27497
28202
|
sourceDetail: "explicit",
|
|
27498
|
-
createdByAgentId: currentAgentId
|
|
28203
|
+
createdByAgentId: currentAgentId,
|
|
28204
|
+
visibility: "project"
|
|
27499
28205
|
});
|
|
27500
28206
|
await graphManager.addObservations([
|
|
27501
28207
|
{ entityName, contents: [`[#${obs.id}] [REASONING] ${decision}`] }
|
|
@@ -27527,7 +28233,11 @@ Entity: ${entityName} | ${facts.length} facts | ${obs.tokens} tokens${reasoningA
|
|
|
27527
28233
|
const minCount = threshold ?? 2;
|
|
27528
28234
|
let entries;
|
|
27529
28235
|
try {
|
|
27530
|
-
entries = await auditProjectObservations(
|
|
28236
|
+
entries = await auditProjectObservations(
|
|
28237
|
+
project.id,
|
|
28238
|
+
await withFreshIndex(() => filterReadableObservations(getAllObservations(), getObservationReader())),
|
|
28239
|
+
minCount
|
|
28240
|
+
);
|
|
27531
28241
|
} catch (err) {
|
|
27532
28242
|
return {
|
|
27533
28243
|
content: [{
|
|
@@ -27592,7 +28302,8 @@ Entity: ${entityName} | ${facts.length} facts | ${obs.tokens} tokens${reasoningA
|
|
|
27592
28302
|
limit: safeLimit,
|
|
27593
28303
|
type: "reasoning",
|
|
27594
28304
|
projectId: scope === "global" ? void 0 : project.id,
|
|
27595
|
-
status: "active"
|
|
28305
|
+
status: "active",
|
|
28306
|
+
reader: getObservationReader(scope === "global" ? "global" : "project")
|
|
27596
28307
|
}));
|
|
27597
28308
|
if (result.entries.length === 0) {
|
|
27598
28309
|
return {
|
|
@@ -27617,7 +28328,11 @@ ${result.formatted}` }]
|
|
|
27617
28328
|
},
|
|
27618
28329
|
async ({ query, dryRun }) => {
|
|
27619
28330
|
const { getAllObservations: getAllObservations2, resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27620
|
-
const
|
|
28331
|
+
const reader = getObservationReader();
|
|
28332
|
+
const allObs = await withFreshIndex(() => filterReadableObservations(
|
|
28333
|
+
getAllObservations2().filter((o) => (o.status ?? "active") === "active" && o.projectId === project.id),
|
|
28334
|
+
reader
|
|
28335
|
+
).filter((observation) => canManageObservation(observation, reader)));
|
|
27621
28336
|
if (allObs.length < 2) {
|
|
27622
28337
|
return { content: [{ type: "text", text: "Not enough active memories to deduplicate." }] };
|
|
27623
28338
|
}
|
|
@@ -27631,7 +28346,7 @@ ${result.formatted}` }]
|
|
|
27631
28346
|
}
|
|
27632
28347
|
let candidates;
|
|
27633
28348
|
if (query) {
|
|
27634
|
-
const searchResult = await compactSearch({ query, limit: 20, projectId: project.id, status: "active" });
|
|
28349
|
+
const searchResult = await compactSearch({ query, limit: 20, projectId: project.id, status: "active", reader });
|
|
27635
28350
|
const idSet = new Set(searchResult.entries.map((e) => e.id));
|
|
27636
28351
|
candidates = allObs.filter((o) => idSet.has(o.id));
|
|
27637
28352
|
} else {
|
|
@@ -27721,7 +28436,8 @@ ${actions.join("\n")}`
|
|
|
27721
28436
|
safeAnchor,
|
|
27722
28437
|
project.id,
|
|
27723
28438
|
safeBefore,
|
|
27724
|
-
safeAfter
|
|
28439
|
+
safeAfter,
|
|
28440
|
+
getObservationReader()
|
|
27725
28441
|
);
|
|
27726
28442
|
return {
|
|
27727
28443
|
content: [
|
|
@@ -27755,12 +28471,14 @@ ${actions.join("\n")}`
|
|
|
27755
28471
|
const safeTypedRefs = coerceStringArray(typedRefs);
|
|
27756
28472
|
let result;
|
|
27757
28473
|
try {
|
|
28474
|
+
const hasCrossProjectRef = safeRefs.some((ref) => ref.projectId && ref.projectId !== project.id) || safeTypedRefs.some((ref) => ref.includes("@") && !ref.endsWith(`@${project.id}`));
|
|
28475
|
+
const reader = getObservationReader(hasCrossProjectRef ? "global" : "project");
|
|
27758
28476
|
if (safeTypedRefs.length > 0) {
|
|
27759
|
-
result = await compactDetail(safeTypedRefs);
|
|
28477
|
+
result = await compactDetail(safeTypedRefs, { reader });
|
|
27760
28478
|
} else if (safeRefs.length > 0) {
|
|
27761
|
-
result = await compactDetail(safeRefs);
|
|
28479
|
+
result = await compactDetail(safeRefs, { reader });
|
|
27762
28480
|
} else {
|
|
27763
|
-
result = await compactDetail(safeIds.map((id) => ({ id, projectId: project.id })));
|
|
28481
|
+
result = await compactDetail(safeIds.map((id) => ({ id, projectId: project.id })), { reader });
|
|
27764
28482
|
}
|
|
27765
28483
|
} catch (err) {
|
|
27766
28484
|
return { content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }], isError: true };
|
|
@@ -27786,11 +28504,15 @@ ${actions.join("\n")}`
|
|
|
27786
28504
|
},
|
|
27787
28505
|
async (args) => {
|
|
27788
28506
|
const action = args.action ?? "report";
|
|
27789
|
-
const { getRetentionSummary: getRetentionSummary2, getArchiveCandidates: getArchiveCandidates2, rankByRelevance: rankByRelevance2,
|
|
28507
|
+
const { getRetentionSummary: getRetentionSummary2, getArchiveCandidates: getArchiveCandidates2, rankByRelevance: rankByRelevance2, getRetentionZone: getRetentionZone2, explainRetention: explainRetention2 } = await Promise.resolve().then(() => (init_retention(), retention_exports));
|
|
27790
28508
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_orama_store(), orama_store_exports));
|
|
27791
28509
|
const { search: search2 } = await import("@orama/orama");
|
|
27792
|
-
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27793
|
-
const
|
|
28510
|
+
const { getAllObservations: getAllObservations2, resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28511
|
+
const reader = getObservationReader();
|
|
28512
|
+
const allObs = await withFreshIndex(() => filterReadableObservations(
|
|
28513
|
+
getAllObservations2().filter((observation) => observation.projectId === project.id),
|
|
28514
|
+
reader
|
|
28515
|
+
));
|
|
27794
28516
|
const accessMap = /* @__PURE__ */ new Map();
|
|
27795
28517
|
try {
|
|
27796
28518
|
const database = await getDb2();
|
|
@@ -27807,20 +28529,6 @@ ${actions.join("\n")}`
|
|
|
27807
28529
|
}
|
|
27808
28530
|
} catch {
|
|
27809
28531
|
}
|
|
27810
|
-
if (action === "archive") {
|
|
27811
|
-
const result = await archiveExpired2(projectDir2, void 0, accessMap, project.id);
|
|
27812
|
-
if (result.archived === 0) {
|
|
27813
|
-
return {
|
|
27814
|
-
content: [{ type: "text", text: "[OK] No expired observations to archive. All memories are within their retention period." }]
|
|
27815
|
-
};
|
|
27816
|
-
}
|
|
27817
|
-
return {
|
|
27818
|
-
content: [{ type: "text", text: `[ARCHIVED] Archived ${result.archived} expired observations (status set to 'archived' in-place)
|
|
27819
|
-
${result.remaining} active observations remaining.
|
|
27820
|
-
|
|
27821
|
-
Archived memories are hidden from default search but can be found with status: "all".` }]
|
|
27822
|
-
};
|
|
27823
|
-
}
|
|
27824
28532
|
const docs = allObs.map((obs) => ({
|
|
27825
28533
|
id: `obs-${obs.id}`,
|
|
27826
28534
|
observationId: obs.id,
|
|
@@ -27839,13 +28547,34 @@ Archived memories are hidden from default search but can be found with status: "
|
|
|
27839
28547
|
status: obs.status ?? "active",
|
|
27840
28548
|
source: obs.source ?? "agent",
|
|
27841
28549
|
sourceDetail: obs.sourceDetail ?? "",
|
|
27842
|
-
valueCategory: obs.valueCategory ?? ""
|
|
28550
|
+
valueCategory: obs.valueCategory ?? "",
|
|
28551
|
+
admissionState: obs.admissionState ?? "",
|
|
28552
|
+
admissionReason: obs.admissionReason ?? "",
|
|
28553
|
+
visibility: obs.visibility ?? "project",
|
|
28554
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
28555
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
27843
28556
|
}));
|
|
27844
28557
|
if (docs.length === 0) {
|
|
27845
28558
|
return {
|
|
27846
28559
|
content: [{ type: "text", text: "No observations found for this project." }]
|
|
27847
28560
|
};
|
|
27848
28561
|
}
|
|
28562
|
+
if (action === "archive") {
|
|
28563
|
+
const managedIds = new Set(
|
|
28564
|
+
allObs.filter((observation) => canManageObservation(observation, reader)).map((observation) => observation.id)
|
|
28565
|
+
);
|
|
28566
|
+
const candidates2 = getArchiveCandidates2(docs).filter((document) => managedIds.has(document.observationId));
|
|
28567
|
+
if (candidates2.length === 0) {
|
|
28568
|
+
return {
|
|
28569
|
+
content: [{ type: "text", text: "[OK] No expired memories in this session's write scope to archive." }]
|
|
28570
|
+
};
|
|
28571
|
+
}
|
|
28572
|
+
const result = await resolveObservations2(candidates2.map((document) => document.observationId), "archived");
|
|
28573
|
+
return {
|
|
28574
|
+
content: [{ type: "text", text: `[ARCHIVED] Archived ${result.resolved.length} expired observation(s) in this session's write scope.
|
|
28575
|
+
${Math.max(0, managedIds.size - result.resolved.length)} visible writable observations remaining.` }]
|
|
28576
|
+
};
|
|
28577
|
+
}
|
|
27849
28578
|
if (action === "stale") {
|
|
27850
28579
|
const staleDocs = docs.filter((d) => getRetentionZone2(d) === "stale");
|
|
27851
28580
|
if (staleDocs.length === 0) {
|
|
@@ -28166,7 +28895,10 @@ Archived memories are hidden from default search but can be found with status: "
|
|
|
28166
28895
|
const allObs = await withFreshIndex(() => getAllObservations2());
|
|
28167
28896
|
const scoped = scopeKnowledgeGraphToProject(
|
|
28168
28897
|
graph,
|
|
28169
|
-
|
|
28898
|
+
filterReadableObservations(
|
|
28899
|
+
allObs.filter((observation) => observation.projectId === project.id),
|
|
28900
|
+
getObservationReader()
|
|
28901
|
+
)
|
|
28170
28902
|
);
|
|
28171
28903
|
return { entities: scoped.entities, relations: scoped.relations };
|
|
28172
28904
|
}
|
|
@@ -28441,7 +29173,10 @@ ${skill2.content}` }]
|
|
|
28441
29173
|
};
|
|
28442
29174
|
}
|
|
28443
29175
|
const { getObservationStore: getStore } = await Promise.resolve().then(() => (init_obs_store(), obs_store_exports));
|
|
28444
|
-
const allObs =
|
|
29176
|
+
const allObs = filterReadableObservations(
|
|
29177
|
+
(await getStore().loadAll()).filter((observation) => observation.projectId === project.id),
|
|
29178
|
+
getObservationReader()
|
|
29179
|
+
);
|
|
28445
29180
|
const obsData = allObs.map((o) => ({
|
|
28446
29181
|
id: o.id || 0,
|
|
28447
29182
|
entityName: o.entityName || "unknown",
|
|
@@ -28540,7 +29275,11 @@ ${skill2.content}` }]
|
|
|
28540
29275
|
}
|
|
28541
29276
|
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28542
29277
|
const allObs = await withFreshIndex(() => getAllObservations2());
|
|
28543
|
-
const
|
|
29278
|
+
const reader = getObservationReader();
|
|
29279
|
+
const matched = filterReadableObservations(
|
|
29280
|
+
allObs.filter((observation) => observation.projectId === project.id && observationIds.includes(observation.id)),
|
|
29281
|
+
reader
|
|
29282
|
+
);
|
|
28544
29283
|
if (matched.length === 0) {
|
|
28545
29284
|
return { content: [{ type: "text", text: `No observations found for IDs: [${observationIds.join(", ")}]. Use \`memorix_search\` to find valid IDs.` }], isError: true };
|
|
28546
29285
|
}
|
|
@@ -28548,6 +29287,13 @@ ${skill2.content}` }]
|
|
|
28548
29287
|
if (nonActive.length > 0) {
|
|
28549
29288
|
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 };
|
|
28550
29289
|
}
|
|
29290
|
+
const nonProjectShared = matched.filter((observation) => resolveObservationVisibility(observation) !== "project");
|
|
29291
|
+
if (nonProjectShared.length > 0) {
|
|
29292
|
+
return {
|
|
29293
|
+
content: [{ type: "text", text: `Cannot promote private or team-scoped observations: ${nonProjectShared.map((observation) => `#${observation.id}`).join(", ")}. Promote only deliberate project-shared knowledge.` }],
|
|
29294
|
+
isError: true
|
|
29295
|
+
};
|
|
29296
|
+
}
|
|
28551
29297
|
const skill2 = await promoteToMiniSkill2(projectDir2, project.id, matched, { trigger, instruction, tags });
|
|
28552
29298
|
const lines = [
|
|
28553
29299
|
`[OK] Created mini-skill #${skill2.id}`,
|
|
@@ -28675,7 +29421,6 @@ Ensure the path points to a directory containing a .git folder (or a subdirector
|
|
|
28675
29421
|
const unresolved = requireResolvedProject("start a project session");
|
|
28676
29422
|
if (unresolved) return unresolved;
|
|
28677
29423
|
const { startSession: startSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
28678
|
-
const result = await startSession2(projectDir2, project.id, { sessionId, agent });
|
|
28679
29424
|
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)";
|
|
28680
29425
|
const shouldJoinTeam = !!joinTeam;
|
|
28681
29426
|
let registeredAgent = null;
|
|
@@ -28706,8 +29451,11 @@ Ensure the path points to a directory containing a .git folder (or a subdirector
|
|
|
28706
29451
|
const lastSeen = registeredAgent.last_seen_obs_generation;
|
|
28707
29452
|
const store = getObservationStore();
|
|
28708
29453
|
const currentGen = store.getGeneration();
|
|
28709
|
-
const projectObs = await withFreshIndex(() =>
|
|
28710
|
-
(
|
|
29454
|
+
const projectObs = await withFreshIndex(() => filterReadableObservations(
|
|
29455
|
+
getAllObservations().filter(
|
|
29456
|
+
(o) => o.projectId === project.id && (o.writeGeneration ?? 0) > lastSeen
|
|
29457
|
+
),
|
|
29458
|
+
getObservationReader()
|
|
28711
29459
|
));
|
|
28712
29460
|
const wm = computeWatermark2(lastSeen, currentGen, projectObs.length);
|
|
28713
29461
|
if (wm.newObservationCount > 0) {
|
|
@@ -28729,6 +29477,11 @@ Ensure the path points to a directory containing a .git folder (or a subdirector
|
|
|
28729
29477
|
}
|
|
28730
29478
|
} catch {
|
|
28731
29479
|
}
|
|
29480
|
+
const result = await startSession2(projectDir2, project.id, {
|
|
29481
|
+
sessionId,
|
|
29482
|
+
agent,
|
|
29483
|
+
reader: getObservationReader()
|
|
29484
|
+
});
|
|
28732
29485
|
const lines = [
|
|
28733
29486
|
`[OK] Session started: ${result.session.id}`,
|
|
28734
29487
|
`Project: ${project.name} (${project.id})`,
|
|
@@ -28851,7 +29604,7 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
28851
29604
|
async ({ limit }) => {
|
|
28852
29605
|
const safeLimit = limit != null ? coerceNumber(limit, 3) : 3;
|
|
28853
29606
|
const { getSessionContext: getSessionContext2, listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
28854
|
-
const context = await getSessionContext2(projectDir2, project.id, safeLimit);
|
|
29607
|
+
const context = await getSessionContext2(projectDir2, project.id, safeLimit, getObservationReader());
|
|
28855
29608
|
const sessions = await listSessions2(projectDir2, project.id);
|
|
28856
29609
|
const activeSessions = sessions.filter((s) => s.status === "active");
|
|
28857
29610
|
const completedSessions = sessions.filter((s) => s.status === "completed");
|
|
@@ -28876,7 +29629,7 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
28876
29629
|
"memorix_transfer",
|
|
28877
29630
|
{
|
|
28878
29631
|
title: "Transfer Memories",
|
|
28879
|
-
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).',
|
|
29632
|
+
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).',
|
|
28880
29633
|
inputSchema: {
|
|
28881
29634
|
action: z2.enum(["export", "import"]).describe("Operation: export or import"),
|
|
28882
29635
|
format: z2.enum(["json", "markdown"]).optional().describe("Export format (for export, default: json)"),
|
|
@@ -28887,10 +29640,10 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
28887
29640
|
if (action === "export") {
|
|
28888
29641
|
const { exportAsJson: exportAsJson2, exportAsMarkdown: exportAsMarkdown2 } = await Promise.resolve().then(() => (init_export_import(), export_import_exports));
|
|
28889
29642
|
if (format === "markdown") {
|
|
28890
|
-
const md = await exportAsMarkdown2(projectDir2, project.id);
|
|
29643
|
+
const md = await exportAsMarkdown2(projectDir2, project.id, getObservationReader());
|
|
28891
29644
|
return { content: [{ type: "text", text: md }] };
|
|
28892
29645
|
}
|
|
28893
|
-
const data = await exportAsJson2(projectDir2, project.id);
|
|
29646
|
+
const data = await exportAsJson2(projectDir2, project.id, getObservationReader());
|
|
28894
29647
|
const json = JSON.stringify(data, null, 2);
|
|
28895
29648
|
return {
|
|
28896
29649
|
content: [{
|
|
@@ -29087,8 +29840,6 @@ ${json}
|
|
|
29087
29840
|
}
|
|
29088
29841
|
}
|
|
29089
29842
|
);
|
|
29090
|
-
let teamStore;
|
|
29091
|
-
let initTeamStoreForProject;
|
|
29092
29843
|
if (teamFeaturesEnabled) {
|
|
29093
29844
|
const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
|
|
29094
29845
|
initTeamStoreForProject = initTeamStore2;
|
|
@@ -29361,7 +30112,7 @@ ${lines.join("\n")}` }] };
|
|
|
29361
30112
|
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.`,
|
|
29362
30113
|
inputSchema: {
|
|
29363
30114
|
action: z2.enum(["send", "broadcast", "inbox"]).describe("Operation to perform"),
|
|
29364
|
-
from: z2.string().optional().describe("
|
|
30115
|
+
from: z2.string().optional().describe("Your sender agent ID. Omit it to use this session identity."),
|
|
29365
30116
|
to: z2.string().optional().describe("Receiver agent ID (for send)"),
|
|
29366
30117
|
type: z2.enum(["request", "response", "info", "announcement", "contract", "error", "handoff"]).optional().describe("Message type (for send/broadcast)"),
|
|
29367
30118
|
content: z2.string().optional().describe("Message content (for send/broadcast)"),
|
|
@@ -29372,13 +30123,20 @@ ${lines.join("\n")}` }] };
|
|
|
29372
30123
|
}
|
|
29373
30124
|
},
|
|
29374
30125
|
async ({ action, from, to, type: msgType, content, agentId, markRead, toRole, handoffStatus }) => {
|
|
30126
|
+
const requireCurrentTeamAgent = () => {
|
|
30127
|
+
if (!currentAgentId) return null;
|
|
30128
|
+
const agent = teamStore.getAgent(currentAgentId);
|
|
30129
|
+
return agent?.project_id === project.id && agent.status === "active" ? agent : null;
|
|
30130
|
+
};
|
|
29375
30131
|
if (action === "send") {
|
|
29376
|
-
|
|
30132
|
+
const sender = requireCurrentTeamAgent();
|
|
30133
|
+
if (!sender || !msgType || !content) return { content: [{ type: "text", text: "[ERROR] active session identity, type, and content required for send" }], isError: true };
|
|
30134
|
+
if (from && from !== currentAgentId) return { content: [{ type: "text", text: "[ERROR] from must match the current session identity" }], isError: true };
|
|
29377
30135
|
if (!to && !toRole) return { content: [{ type: "text", text: "[ERROR] either to (agent ID) or toRole is required for send" }], isError: true };
|
|
29378
30136
|
if (content.length > 1e4) return { content: [{ type: "text", text: "[ERROR] Message too large (max 10KB)" }], isError: true };
|
|
29379
30137
|
const msg = teamStore.sendMessage({
|
|
29380
30138
|
projectId: project.id,
|
|
29381
|
-
senderAgentId:
|
|
30139
|
+
senderAgentId: currentAgentId,
|
|
29382
30140
|
recipientAgentId: to ?? null,
|
|
29383
30141
|
type: msgType,
|
|
29384
30142
|
content,
|
|
@@ -29390,11 +30148,13 @@ ${lines.join("\n")}` }] };
|
|
|
29390
30148
|
return { content: [{ type: "text", text: `Message sent (${msgType}) to ${target} | ID: ${msg.id.slice(0, 8)}\u2026${toRole ? ` [role: ${toRole}]` : ""}` }] };
|
|
29391
30149
|
}
|
|
29392
30150
|
if (action === "broadcast") {
|
|
29393
|
-
|
|
30151
|
+
const sender = requireCurrentTeamAgent();
|
|
30152
|
+
if (!sender || !msgType || !content) return { content: [{ type: "text", text: "[ERROR] active session identity, type, and content required for broadcast" }], isError: true };
|
|
30153
|
+
if (from && from !== currentAgentId) return { content: [{ type: "text", text: "[ERROR] from must match the current session identity" }], isError: true };
|
|
29394
30154
|
if (content.length > 1e4) return { content: [{ type: "text", text: "[ERROR] Message too large (max 10KB)" }], isError: true };
|
|
29395
30155
|
const msg = teamStore.sendMessage({
|
|
29396
30156
|
projectId: project.id,
|
|
29397
|
-
senderAgentId:
|
|
30157
|
+
senderAgentId: currentAgentId,
|
|
29398
30158
|
recipientAgentId: null,
|
|
29399
30159
|
type: msgType,
|
|
29400
30160
|
content
|
|
@@ -29402,8 +30162,12 @@ ${lines.join("\n")}` }] };
|
|
|
29402
30162
|
if ("error" in msg) return { content: [{ type: "text", text: `[ERROR] ${msg.error}` }], isError: true };
|
|
29403
30163
|
return { content: [{ type: "text", text: `Broadcast (${msgType}) | ID: ${msg.id.slice(0, 8)}\u2026` }] };
|
|
29404
30164
|
}
|
|
29405
|
-
const
|
|
29406
|
-
if (!
|
|
30165
|
+
const inboxAgent = requireCurrentTeamAgent();
|
|
30166
|
+
if (!inboxAgent) return { content: [{ type: "text", text: "[ERROR] active session identity required for inbox" }], isError: true };
|
|
30167
|
+
if (agentId && agentId !== currentAgentId || from && from !== currentAgentId) {
|
|
30168
|
+
return { content: [{ type: "text", text: "[ERROR] inbox access is limited to the current session identity" }], isError: true };
|
|
30169
|
+
}
|
|
30170
|
+
const inboxId = currentAgentId;
|
|
29407
30171
|
const inbox = teamStore.getInbox(project.id, inboxId);
|
|
29408
30172
|
const unread = teamStore.getUnreadCount(project.id, inboxId);
|
|
29409
30173
|
if (inbox.length === 0) return { content: [{ type: "text", text: "Inbox empty" }] };
|
|
@@ -29431,24 +30195,34 @@ ${lines.join("\n")}` }] };
|
|
|
29431
30195
|
},
|
|
29432
30196
|
async ({ agentId, markInboxRead }) => {
|
|
29433
30197
|
const { computeWatermark: computeWatermark2, computePoll: computePoll2 } = await Promise.resolve().then(() => (init_poll(), poll_exports));
|
|
30198
|
+
if (agentId && agentId !== currentAgentId) {
|
|
30199
|
+
return {
|
|
30200
|
+
content: [{ type: "text", text: "[ERROR] agentId must match the current session identity." }],
|
|
30201
|
+
isError: true
|
|
30202
|
+
};
|
|
30203
|
+
}
|
|
30204
|
+
const effectiveAgentId = currentAgentId;
|
|
29434
30205
|
let watermark = computeWatermark2(0, 0, 0);
|
|
29435
|
-
if (
|
|
29436
|
-
const agent = teamStore.getAgent(
|
|
30206
|
+
if (effectiveAgentId) {
|
|
30207
|
+
const agent = teamStore.getAgent(effectiveAgentId);
|
|
29437
30208
|
if (agent) {
|
|
29438
30209
|
const lastSeen = agent.last_seen_obs_generation;
|
|
29439
30210
|
const store = getObservationStore();
|
|
29440
30211
|
const currentGen = store.getGeneration();
|
|
29441
|
-
const projectObs = await withFreshIndex(() =>
|
|
29442
|
-
(
|
|
30212
|
+
const projectObs = await withFreshIndex(() => filterReadableObservations(
|
|
30213
|
+
getAllObservations().filter(
|
|
30214
|
+
(o) => o.projectId === project.id && (o.writeGeneration ?? 0) > lastSeen
|
|
30215
|
+
),
|
|
30216
|
+
getObservationReader()
|
|
29443
30217
|
));
|
|
29444
30218
|
watermark = computeWatermark2(lastSeen, currentGen, projectObs.length);
|
|
29445
|
-
teamStore.updateWatermark(
|
|
29446
|
-
teamStore.heartbeat(
|
|
30219
|
+
teamStore.updateWatermark(effectiveAgentId, currentGen);
|
|
30220
|
+
teamStore.heartbeat(effectiveAgentId);
|
|
29447
30221
|
}
|
|
29448
30222
|
}
|
|
29449
|
-
const poll = computePoll2(teamStore, project.id,
|
|
29450
|
-
if (markInboxRead &&
|
|
29451
|
-
teamStore.markAllRead(project.id,
|
|
30223
|
+
const poll = computePoll2(teamStore, project.id, effectiveAgentId ?? null, watermark);
|
|
30224
|
+
if (markInboxRead && effectiveAgentId) {
|
|
30225
|
+
teamStore.markAllRead(project.id, effectiveAgentId);
|
|
29452
30226
|
}
|
|
29453
30227
|
const lines = [];
|
|
29454
30228
|
if (poll.agent) {
|
|
@@ -29508,7 +30282,7 @@ ${lines.join("\n")}` }] };
|
|
|
29508
30282
|
"memorix_handoff",
|
|
29509
30283
|
{
|
|
29510
30284
|
title: "Team Handoff \u2014 Agent Context Transfer",
|
|
29511
|
-
description: "Create a structured handoff artifact when passing work to another agent. The handoff is stored as a durable observation (
|
|
30285
|
+
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.",
|
|
29512
30286
|
inputSchema: {
|
|
29513
30287
|
fromAgentId: z2.string().describe("Your agent ID (from team_manage join or session_start with joinTeam=true)"),
|
|
29514
30288
|
summary: z2.string().describe("Human-readable summary of what you did and what needs to happen next"),
|
|
@@ -29521,6 +30295,34 @@ ${lines.join("\n")}` }] };
|
|
|
29521
30295
|
},
|
|
29522
30296
|
async ({ fromAgentId, summary, context, toAgentId, taskId, filesModified, concepts }) => {
|
|
29523
30297
|
const { createHandoffArtifact: createHandoffArtifact2 } = await Promise.resolve().then(() => (init_handoff(), handoff_exports));
|
|
30298
|
+
if (!currentAgentId) {
|
|
30299
|
+
return {
|
|
30300
|
+
content: [{ type: "text", text: "Create a coordination identity first: call memorix_session_start with joinTeam=true." }],
|
|
30301
|
+
isError: true
|
|
30302
|
+
};
|
|
30303
|
+
}
|
|
30304
|
+
if (fromAgentId !== currentAgentId) {
|
|
30305
|
+
return {
|
|
30306
|
+
content: [{ type: "text", text: "fromAgentId must match the identity returned for this session. Memorix will not create a handoff on behalf of another agent." }],
|
|
30307
|
+
isError: true
|
|
30308
|
+
};
|
|
30309
|
+
}
|
|
30310
|
+
const sender = teamStore.getAgent(currentAgentId);
|
|
30311
|
+
if (!sender || sender.project_id !== project.id || sender.status !== "active") {
|
|
30312
|
+
return {
|
|
30313
|
+
content: [{ type: "text", text: "The current coordination identity is not an active member of this project." }],
|
|
30314
|
+
isError: true
|
|
30315
|
+
};
|
|
30316
|
+
}
|
|
30317
|
+
if (toAgentId) {
|
|
30318
|
+
const recipient = teamStore.getAgent(toAgentId);
|
|
30319
|
+
if (!recipient || recipient.project_id !== project.id) {
|
|
30320
|
+
return {
|
|
30321
|
+
content: [{ type: "text", text: "The handoff recipient must be an agent registered in the current project." }],
|
|
30322
|
+
isError: true
|
|
30323
|
+
};
|
|
30324
|
+
}
|
|
30325
|
+
}
|
|
29524
30326
|
const result = await createHandoffArtifact2(
|
|
29525
30327
|
{
|
|
29526
30328
|
projectId: project.id,
|
|
@@ -29760,6 +30562,8 @@ var MemoryClient = class {
|
|
|
29760
30562
|
_projectId;
|
|
29761
30563
|
_projectRoot;
|
|
29762
30564
|
_dataDir;
|
|
30565
|
+
/** SDK calls are unbound by default, so they see only project-shared records. */
|
|
30566
|
+
_reader;
|
|
29763
30567
|
_closed = false;
|
|
29764
30568
|
// Internal module references — loaded lazily to avoid top-level side effects
|
|
29765
30569
|
_observations;
|
|
@@ -29771,6 +30575,7 @@ var MemoryClient = class {
|
|
|
29771
30575
|
this._projectId = projectId;
|
|
29772
30576
|
this._projectRoot = projectRoot;
|
|
29773
30577
|
this._dataDir = dataDir;
|
|
30578
|
+
this._reader = { projectId };
|
|
29774
30579
|
}
|
|
29775
30580
|
/** The canonical project ID (derived from Git remote or local path) */
|
|
29776
30581
|
get projectId() {
|
|
@@ -29829,7 +30634,8 @@ var MemoryClient = class {
|
|
|
29829
30634
|
this._ensureOpen();
|
|
29830
30635
|
return this._observations.storeObservation({
|
|
29831
30636
|
...input,
|
|
29832
|
-
projectId: this._projectId
|
|
30637
|
+
projectId: this._projectId,
|
|
30638
|
+
visibilityReader: this._reader
|
|
29833
30639
|
});
|
|
29834
30640
|
}
|
|
29835
30641
|
/**
|
|
@@ -29853,7 +30659,8 @@ var MemoryClient = class {
|
|
|
29853
30659
|
limit: options.limit ?? 20,
|
|
29854
30660
|
type: options.type,
|
|
29855
30661
|
source: options.source,
|
|
29856
|
-
status: options.status === "all" ? void 0 : options.status ?? "active"
|
|
30662
|
+
status: options.status === "all" ? void 0 : options.status ?? "active",
|
|
30663
|
+
reader: this._reader
|
|
29857
30664
|
};
|
|
29858
30665
|
return this._oramaStore.searchObservations(searchOpts);
|
|
29859
30666
|
}
|
|
@@ -29864,7 +30671,8 @@ var MemoryClient = class {
|
|
|
29864
30671
|
this._ensureOpen();
|
|
29865
30672
|
await this._freshness.withFreshIndex(() => {
|
|
29866
30673
|
});
|
|
29867
|
-
|
|
30674
|
+
const observation = this._observations.getObservation(id, this._projectId);
|
|
30675
|
+
return observation && canReadObservation(observation, this._reader) ? observation : void 0;
|
|
29868
30676
|
}
|
|
29869
30677
|
/**
|
|
29870
30678
|
* Get all observations for this project.
|
|
@@ -29873,7 +30681,10 @@ var MemoryClient = class {
|
|
|
29873
30681
|
this._ensureOpen();
|
|
29874
30682
|
await this._freshness.withFreshIndex(() => {
|
|
29875
30683
|
});
|
|
29876
|
-
return
|
|
30684
|
+
return filterReadableObservations(
|
|
30685
|
+
this._observations.getProjectObservations(this._projectId),
|
|
30686
|
+
this._reader
|
|
30687
|
+
);
|
|
29877
30688
|
}
|
|
29878
30689
|
/**
|
|
29879
30690
|
* Get the total observation count for this project.
|
|
@@ -29882,7 +30693,10 @@ var MemoryClient = class {
|
|
|
29882
30693
|
this._ensureOpen();
|
|
29883
30694
|
await this._freshness.withFreshIndex(() => {
|
|
29884
30695
|
});
|
|
29885
|
-
return
|
|
30696
|
+
return filterReadableObservations(
|
|
30697
|
+
this._observations.getProjectObservations(this._projectId),
|
|
30698
|
+
this._reader
|
|
30699
|
+
).length;
|
|
29886
30700
|
}
|
|
29887
30701
|
/**
|
|
29888
30702
|
* Mark observations as resolved or archived.
|
|
@@ -29898,7 +30712,16 @@ var MemoryClient = class {
|
|
|
29898
30712
|
*/
|
|
29899
30713
|
async resolve(ids, status = "resolved") {
|
|
29900
30714
|
this._ensureOpen();
|
|
29901
|
-
|
|
30715
|
+
const manageableIds = ids.filter((id) => {
|
|
30716
|
+
const observation = this._observations.getObservation(id, this._projectId);
|
|
30717
|
+
return observation && canManageObservation(observation, this._reader);
|
|
30718
|
+
});
|
|
30719
|
+
const deniedIds = ids.filter((id) => !manageableIds.includes(id));
|
|
30720
|
+
const result = await this._observations.resolveObservations(manageableIds, status);
|
|
30721
|
+
return {
|
|
30722
|
+
resolved: result.resolved,
|
|
30723
|
+
notFound: [.../* @__PURE__ */ new Set([...result.notFound, ...deniedIds])]
|
|
30724
|
+
};
|
|
29902
30725
|
}
|
|
29903
30726
|
/**
|
|
29904
30727
|
* Release resources (close SQLite handle, reset index).
|