memorix 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +14 -2
- package/README.zh-CN.md +14 -2
- package/TEAM.md +86 -86
- package/dist/cli/index.js +15407 -13779
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +1321 -529
- 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 +16 -0
- package/dist/sdk.d.ts +7 -2
- package/dist/sdk.js +1349 -535
- 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/DESIGN_DECISIONS.md +357 -357
- package/docs/README.md +1 -1
- package/docs/dev-log/progress.txt +91 -11
- package/package.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/src/audit/index.ts +156 -156
- package/src/cli/command-guide.ts +192 -0
- package/src/cli/commands/audit-list.ts +89 -89
- package/src/cli/commands/audit.ts +9 -4
- package/src/cli/commands/background.ts +659 -659
- 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/formation.ts +48 -48
- package/src/cli/commands/git-hook-install.ts +111 -111
- package/src/cli/commands/handoff.ts +75 -61
- package/src/cli/commands/hooks-status.ts +63 -63
- package/src/cli/commands/identity.ts +116 -0
- package/src/cli/commands/ingest-commit.ts +153 -153
- package/src/cli/commands/ingest-image.ts +71 -69
- package/src/cli/commands/ingest-log.ts +180 -180
- package/src/cli/commands/ingest.ts +44 -44
- package/src/cli/commands/integrate-shared.ts +15 -15
- package/src/cli/commands/lock.ts +93 -92
- package/src/cli/commands/memory.ts +58 -21
- package/src/cli/commands/message.ts +123 -118
- package/src/cli/commands/operator-shared.ts +98 -3
- package/src/cli/commands/poll.ts +74 -64
- package/src/cli/commands/purge-all-memory.ts +85 -85
- package/src/cli/commands/purge-project-memory.ts +83 -83
- package/src/cli/commands/reasoning.ts +135 -121
- package/src/cli/commands/retention.ts +9 -4
- package/src/cli/commands/serve-http.ts +8 -2
- package/src/cli/commands/serve-shared.ts +118 -118
- package/src/cli/commands/session.ts +29 -3
- package/src/cli/commands/skills.ts +124 -119
- package/src/cli/commands/status.ts +4 -3
- package/src/cli/commands/task.ts +193 -184
- package/src/cli/commands/team.ts +14 -10
- package/src/cli/commands/transfer.ts +108 -55
- package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
- 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/ChatView.tsx +234 -234
- package/src/cli/tui/CommandBar.tsx +312 -312
- package/src/cli/tui/ContextRail.tsx +118 -118
- package/src/cli/tui/HeaderBar.tsx +72 -72
- package/src/cli/tui/LogoBanner.tsx +51 -51
- package/src/cli/tui/Sidebar.tsx +179 -179
- package/src/cli/tui/chat-service.ts +41 -18
- package/src/cli/tui/data.ts +23 -44
- package/src/cli/tui/index.ts +41 -41
- package/src/cli/tui/markdown-render.tsx +371 -371
- package/src/cli/tui/operator-context.ts +60 -0
- package/src/cli/tui/use-mouse.ts +157 -157
- package/src/cli/tui/useNavigation.ts +56 -56
- package/src/cli/tui/views/MemoryView.tsx +10 -8
- package/src/cli/update-checker.ts +211 -211
- package/src/cli/version.ts +7 -7
- package/src/cli/workbench.ts +1 -1
- package/src/codegraph/auto-context.ts +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/compact/token-budget.ts +74 -74
- package/src/dashboard/project-classification.ts +64 -64
- package/src/dashboard/server.ts +46 -9
- package/src/embedding/fastembed-provider.ts +142 -142
- package/src/embedding/transformers-provider.ts +111 -111
- package/src/git/extractor.ts +209 -209
- package/src/git/hooks-path.ts +85 -85
- package/src/hooks/admission.ts +117 -0
- package/src/hooks/handler.ts +98 -91
- package/src/hooks/pattern-detector.ts +173 -173
- package/src/hooks/significance-filter.ts +250 -250
- package/src/knowledge/context-assembly.ts +97 -0
- package/src/knowledge/workset.ts +179 -10
- package/src/llm/memory-manager.ts +328 -328
- package/src/llm/provider.ts +885 -885
- package/src/llm/quality.ts +248 -248
- package/src/memory/admission.ts +57 -0
- package/src/memory/attribution-guard.ts +249 -249
- package/src/memory/consolidation.ts +13 -2
- package/src/memory/disclosure-policy.ts +140 -135
- package/src/memory/entity-extractor.ts +197 -197
- package/src/memory/export-import.ts +11 -3
- package/src/memory/formation/evaluate.ts +217 -217
- package/src/memory/formation/extract.ts +361 -361
- package/src/memory/formation/index.ts +417 -417
- package/src/memory/formation/resolve.ts +344 -344
- package/src/memory/formation/types.ts +315 -315
- package/src/memory/freshness.ts +122 -122
- package/src/memory/graph-context.ts +8 -2
- package/src/memory/graph.ts +197 -197
- package/src/memory/observations.ts +162 -4
- package/src/memory/quality-audit.ts +2 -0
- package/src/memory/refs.ts +94 -94
- package/src/memory/retention.ts +22 -2
- package/src/memory/secret-filter.ts +79 -79
- package/src/memory/session.ts +5 -2
- package/src/memory/visibility.ts +80 -0
- package/src/multimodal/image-loader.ts +143 -143
- package/src/orchestrate/adapters/claude-stream.ts +192 -192
- package/src/orchestrate/adapters/claude.ts +111 -111
- package/src/orchestrate/adapters/codex-stream.ts +134 -134
- package/src/orchestrate/adapters/codex.ts +41 -41
- package/src/orchestrate/adapters/gemini-stream.ts +166 -166
- package/src/orchestrate/adapters/gemini.ts +42 -42
- package/src/orchestrate/adapters/index.ts +73 -73
- package/src/orchestrate/adapters/opencode-stream.ts +143 -143
- package/src/orchestrate/adapters/opencode.ts +47 -47
- package/src/orchestrate/adapters/spawn-helper.ts +286 -286
- package/src/orchestrate/adapters/types.ts +77 -77
- package/src/orchestrate/capability-router.ts +284 -284
- package/src/orchestrate/context-compact.ts +188 -188
- package/src/orchestrate/cost-tracker.ts +219 -219
- package/src/orchestrate/error-recovery.ts +191 -191
- package/src/orchestrate/evidence.ts +140 -140
- package/src/orchestrate/ledger.ts +110 -110
- package/src/orchestrate/memorix-bridge.ts +378 -340
- package/src/orchestrate/output-budget.ts +80 -80
- package/src/orchestrate/permission.ts +152 -152
- package/src/orchestrate/pipeline-trace.ts +131 -131
- package/src/orchestrate/prompt-builder.ts +155 -155
- package/src/orchestrate/ring-buffer.ts +37 -37
- package/src/orchestrate/task-graph.ts +389 -389
- package/src/orchestrate/worktree.ts +232 -232
- package/src/project/aliases.ts +374 -374
- package/src/project/detector.ts +268 -268
- package/src/rules/adapters/claude-code.ts +99 -99
- package/src/rules/adapters/codex.ts +97 -97
- package/src/rules/adapters/copilot.ts +124 -124
- package/src/rules/adapters/cursor.ts +114 -114
- package/src/rules/adapters/kiro.ts +126 -126
- package/src/rules/adapters/trae.ts +56 -56
- package/src/rules/adapters/windsurf.ts +83 -83
- package/src/rules/syncer.ts +235 -235
- package/src/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 +334 -304
- package/src/search/intent-detector.ts +289 -289
- package/src/search/query-expansion.ts +52 -52
- package/src/server/formation-timeout.ts +27 -27
- package/src/server.ts +260 -81
- package/src/skills/mini-skills.ts +386 -386
- package/src/store/chat-store.ts +119 -119
- package/src/store/graph-store.ts +249 -249
- package/src/store/mini-skill-store.ts +349 -349
- package/src/store/orama-store.ts +61 -6
- package/src/store/persistence-json.ts +212 -212
- package/src/store/persistence.ts +291 -291
- package/src/store/project-affinity.ts +195 -195
- package/src/store/sqlite-db.ts +23 -1
- package/src/store/sqlite-store.ts +12 -2
- package/src/team/event-bus.ts +76 -76
- package/src/team/file-locks.ts +173 -173
- package/src/team/handoff.ts +168 -161
- package/src/team/messages.ts +203 -203
- package/src/team/poll.ts +132 -132
- package/src/team/tasks.ts +211 -211
- package/src/types.ts +51 -0
- package/src/wiki/generator.ts +2 -0
- package/src/workspace/mcp-adapters/codex.ts +191 -191
- package/src/workspace/mcp-adapters/copilot.ts +105 -105
- package/src/workspace/mcp-adapters/cursor.ts +53 -53
- package/src/workspace/mcp-adapters/kiro.ts +64 -64
- package/src/workspace/mcp-adapters/opencode.ts +123 -123
- package/src/workspace/mcp-adapters/trae.ts +134 -134
- package/src/workspace/mcp-adapters/windsurf.ts +91 -91
- package/src/workspace/sanitizer.ts +60 -60
- package/src/workspace/workflow-sync.ts +131 -131
package/dist/sdk.js
CHANGED
|
@@ -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
|
}
|
|
@@ -15023,9 +15397,9 @@ async function getSessionContext(projectDir2, projectId, limit = 3) {
|
|
|
15023
15397
|
return true;
|
|
15024
15398
|
});
|
|
15025
15399
|
})() : 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);
|
|
15400
|
+
const l1HookObs = projectObs.filter((obs) => isEligibleForAutomaticDelivery(obs) && classifyLayer(obs) === "L1").sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, 3);
|
|
15027
15401
|
const l3GitCount = projectObs.filter((obs) => classifyLayer(obs) === "L3").length;
|
|
15028
|
-
const totalHookCount = projectObs.filter((obs) => classifyLayer(obs) === "L1").length;
|
|
15402
|
+
const totalHookCount = projectObs.filter((obs) => isEligibleForAutomaticDelivery(obs) && classifyLayer(obs) === "L1").length;
|
|
15029
15403
|
const activeEntities = [
|
|
15030
15404
|
...new Set(l2Obs.map((o) => o.entityName).filter((n) => !!n && n.trim().length > 0))
|
|
15031
15405
|
].slice(0, 5);
|
|
@@ -15151,6 +15525,7 @@ var init_session = __esm({
|
|
|
15151
15525
|
"src/memory/session.ts"() {
|
|
15152
15526
|
"use strict";
|
|
15153
15527
|
init_esm_shims();
|
|
15528
|
+
init_admission();
|
|
15154
15529
|
init_disclosure_policy();
|
|
15155
15530
|
init_aliases();
|
|
15156
15531
|
init_obs_store();
|
|
@@ -15303,27 +15678,80 @@ function workflowOutput(selection) {
|
|
|
15303
15678
|
cautions: selection.cautions.map((caution) => short(caution, 22))
|
|
15304
15679
|
};
|
|
15305
15680
|
}
|
|
15306
|
-
function appendLine(lines, candidate, maxTokens, omitted, omittedKind) {
|
|
15681
|
+
function appendLine(lines, candidate, maxTokens, omitted, omittedKind, selected, receiptSelection) {
|
|
15307
15682
|
const next = lines.length ? lines.join("\n") + "\n" + candidate : candidate;
|
|
15308
15683
|
if (countTextTokens(next) <= maxTokens) {
|
|
15309
15684
|
lines.push(candidate);
|
|
15685
|
+
if (receiptSelection) selected?.push(receiptSelection);
|
|
15310
15686
|
return true;
|
|
15311
15687
|
}
|
|
15312
15688
|
omitted.push(omittedKind);
|
|
15313
15689
|
return false;
|
|
15314
15690
|
}
|
|
15691
|
+
function freshnessForMemory(status) {
|
|
15692
|
+
if (status === "current" || status === "suspect" || status === "stale") return status;
|
|
15693
|
+
return "unknown";
|
|
15694
|
+
}
|
|
15695
|
+
function receiptOmissionKind(raw) {
|
|
15696
|
+
if (raw.includes("task")) return "task";
|
|
15697
|
+
if (raw.includes("fact")) return "current-fact";
|
|
15698
|
+
if (raw.includes("state")) return "code-state";
|
|
15699
|
+
if (raw.includes("semantic")) return "semantic-code";
|
|
15700
|
+
if (raw.includes("start")) return "start-here";
|
|
15701
|
+
if (raw.includes("memory")) return "memory";
|
|
15702
|
+
if (raw.includes("claim")) return "claim";
|
|
15703
|
+
if (raw.includes("knowledge-page")) return "knowledge-page";
|
|
15704
|
+
if (raw.includes("workflow")) return "workflow";
|
|
15705
|
+
if (raw.includes("verification")) return "verification";
|
|
15706
|
+
if (raw.includes("caution")) return "caution";
|
|
15707
|
+
return void 0;
|
|
15708
|
+
}
|
|
15709
|
+
function receiptOmissions(omitted, hiddenCautionMemoryCount) {
|
|
15710
|
+
const counts = /* @__PURE__ */ new Map();
|
|
15711
|
+
for (const raw of omitted) {
|
|
15712
|
+
const kind = receiptOmissionKind(raw);
|
|
15713
|
+
if (!kind || raw.endsWith("-heading")) continue;
|
|
15714
|
+
counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
15715
|
+
}
|
|
15716
|
+
const receipt = [...counts.entries()].map(([kind, count2]) => ({
|
|
15717
|
+
kind,
|
|
15718
|
+
reason: "token-budget",
|
|
15719
|
+
count: count2
|
|
15720
|
+
}));
|
|
15721
|
+
if (hiddenCautionMemoryCount > 0) {
|
|
15722
|
+
receipt.push({
|
|
15723
|
+
kind: "caution",
|
|
15724
|
+
reason: "hidden-by-task-lens",
|
|
15725
|
+
count: hiddenCautionMemoryCount
|
|
15726
|
+
});
|
|
15727
|
+
}
|
|
15728
|
+
return receipt;
|
|
15729
|
+
}
|
|
15730
|
+
function scheduledActions(cautions) {
|
|
15731
|
+
return cautions.filter((caution) => caution.kind === "codegraph-refresh-queued").map((caution) => short(caution.message, 28));
|
|
15732
|
+
}
|
|
15315
15733
|
function renderTaskWorksetPrompt(input) {
|
|
15316
15734
|
const maxTokens = input.budget?.maxTokens ?? 180;
|
|
15317
15735
|
const omitted = [];
|
|
15736
|
+
const selected = [];
|
|
15318
15737
|
const lines = ["Memorix Autopilot Brief"];
|
|
15319
15738
|
const task = short(input.task || "Continue the current task.", 34);
|
|
15320
|
-
appendLine(lines, "Task: " + task, maxTokens, omitted, "task-detail"
|
|
15739
|
+
appendLine(lines, "Task: " + task, maxTokens, omitted, "task-detail", selected, {
|
|
15740
|
+
kind: "task",
|
|
15741
|
+
reason: "current task supplied by the caller",
|
|
15742
|
+
trust: "source-backed"
|
|
15743
|
+
});
|
|
15321
15744
|
appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
|
|
15322
15745
|
if (input.cautions.length > 0 || input.cautionMemory.length > 0) {
|
|
15323
15746
|
appendLine(lines, "", maxTokens, omitted, "caution-heading");
|
|
15324
15747
|
appendLine(lines, "Cautions", maxTokens, omitted, "caution-heading");
|
|
15325
15748
|
for (const caution of input.cautions.slice(0, 6)) {
|
|
15326
|
-
appendLine(lines, "- " + short(caution.message, 22), maxTokens, omitted, "caution"
|
|
15749
|
+
appendLine(lines, "- " + short(caution.message, 22), maxTokens, omitted, "caution", selected, {
|
|
15750
|
+
kind: "caution",
|
|
15751
|
+
id: "caution:" + caution.kind,
|
|
15752
|
+
reason: "current project caution",
|
|
15753
|
+
trust: "source-backed"
|
|
15754
|
+
});
|
|
15327
15755
|
}
|
|
15328
15756
|
for (const memory of input.cautionMemory.slice(0, 3)) {
|
|
15329
15757
|
const location = memory.path ? memory.path + (memory.symbol ? "#" + memory.symbol : "") : "no current code location";
|
|
@@ -15333,7 +15761,15 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15333
15761
|
"- #" + memory.id + " " + memory.status + ": " + short(memory.title, 18) + " (" + location + reason + ")",
|
|
15334
15762
|
maxTokens,
|
|
15335
15763
|
omitted,
|
|
15336
|
-
"caution-memory"
|
|
15764
|
+
"caution-memory",
|
|
15765
|
+
selected,
|
|
15766
|
+
{
|
|
15767
|
+
kind: "memory",
|
|
15768
|
+
id: "memory:" + memory.id,
|
|
15769
|
+
reason: "task-relevant memory requiring source verification",
|
|
15770
|
+
freshness: freshnessForMemory(memory.status),
|
|
15771
|
+
trust: "historical"
|
|
15772
|
+
}
|
|
15337
15773
|
);
|
|
15338
15774
|
}
|
|
15339
15775
|
if (input.hiddenCautionMemoryCount > 0) {
|
|
@@ -15344,13 +15780,22 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15344
15780
|
appendLine(lines, "", maxTokens, omitted, "facts-heading");
|
|
15345
15781
|
appendLine(lines, "Current project facts", maxTokens, omitted, "facts-heading");
|
|
15346
15782
|
for (const fact of input.currentFacts.slice(0, 4)) {
|
|
15347
|
-
appendLine(lines, "- " + short(fact, 40), maxTokens, omitted, "current-fact"
|
|
15783
|
+
appendLine(lines, "- " + short(fact, 40), maxTokens, omitted, "current-fact", selected, {
|
|
15784
|
+
kind: "current-fact",
|
|
15785
|
+
reason: "current project state",
|
|
15786
|
+
trust: "source-backed"
|
|
15787
|
+
});
|
|
15348
15788
|
}
|
|
15349
15789
|
}
|
|
15350
15790
|
if (input.codeState) {
|
|
15351
15791
|
appendLine(lines, "", maxTokens, omitted, "state-heading");
|
|
15352
15792
|
appendLine(lines, "Project state", maxTokens, omitted, "state-heading");
|
|
15353
|
-
appendLine(lines, input.codeState, maxTokens, omitted, "code-state"
|
|
15793
|
+
appendLine(lines, input.codeState, maxTokens, omitted, "code-state", selected, {
|
|
15794
|
+
kind: "code-state",
|
|
15795
|
+
...input.provenance.snapshotId ? { id: "snapshot:" + input.provenance.snapshotId } : {},
|
|
15796
|
+
reason: "latest available Code State snapshot",
|
|
15797
|
+
trust: "source-backed"
|
|
15798
|
+
});
|
|
15354
15799
|
}
|
|
15355
15800
|
if (input.semanticCode && (input.semanticCode.entryPoints.length > 0 || input.semanticCode.relations.length > 0)) {
|
|
15356
15801
|
appendLine(lines, "", maxTokens, omitted, "semantic-code-heading");
|
|
@@ -15362,7 +15807,14 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15362
15807
|
"- " + location + ": " + short(relation.from.name, 12) + " " + short(relation.kind, 8) + " " + short(relation.to.name, 12),
|
|
15363
15808
|
maxTokens,
|
|
15364
15809
|
omitted,
|
|
15365
|
-
"semantic-relation"
|
|
15810
|
+
"semantic-relation",
|
|
15811
|
+
selected,
|
|
15812
|
+
{
|
|
15813
|
+
kind: "semantic-code",
|
|
15814
|
+
id: "code:" + location,
|
|
15815
|
+
reason: "validated optional semantic code relation",
|
|
15816
|
+
trust: "derived"
|
|
15817
|
+
}
|
|
15366
15818
|
);
|
|
15367
15819
|
}
|
|
15368
15820
|
if (input.semanticCode.relations.length === 0) {
|
|
@@ -15373,7 +15825,14 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15373
15825
|
"- " + location + ": " + short(entry.name, 16) + " (" + short(entry.kind, 8) + ")",
|
|
15374
15826
|
maxTokens,
|
|
15375
15827
|
omitted,
|
|
15376
|
-
"semantic-entry"
|
|
15828
|
+
"semantic-entry",
|
|
15829
|
+
selected,
|
|
15830
|
+
{
|
|
15831
|
+
kind: "semantic-code",
|
|
15832
|
+
id: "code:" + location,
|
|
15833
|
+
reason: "validated optional semantic code entry point",
|
|
15834
|
+
trust: "derived"
|
|
15835
|
+
}
|
|
15377
15836
|
);
|
|
15378
15837
|
}
|
|
15379
15838
|
}
|
|
@@ -15382,7 +15841,12 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15382
15841
|
appendLine(lines, "", maxTokens, omitted, "start-heading");
|
|
15383
15842
|
appendLine(lines, "Start here", maxTokens, omitted, "start-heading");
|
|
15384
15843
|
for (const source of input.startHere.slice(0, 5)) {
|
|
15385
|
-
appendLine(lines, "- " + source, maxTokens, omitted, "start-here"
|
|
15844
|
+
appendLine(lines, "- " + source, maxTokens, omitted, "start-here", selected, {
|
|
15845
|
+
kind: "start-here",
|
|
15846
|
+
id: "path:" + source,
|
|
15847
|
+
reason: "task-lensed starting point",
|
|
15848
|
+
trust: "derived"
|
|
15849
|
+
});
|
|
15386
15850
|
}
|
|
15387
15851
|
}
|
|
15388
15852
|
if (input.reliableMemory.length > 0) {
|
|
@@ -15395,7 +15859,15 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15395
15859
|
"- #" + memory.id + " " + memory.type + ": " + short(memory.title, 18) + " (" + location + ")",
|
|
15396
15860
|
maxTokens,
|
|
15397
15861
|
omitted,
|
|
15398
|
-
"reliable-memory"
|
|
15862
|
+
"reliable-memory",
|
|
15863
|
+
selected,
|
|
15864
|
+
{
|
|
15865
|
+
kind: "memory",
|
|
15866
|
+
id: "memory:" + memory.id,
|
|
15867
|
+
reason: "current code-bound memory",
|
|
15868
|
+
freshness: freshnessForMemory(memory.status),
|
|
15869
|
+
trust: "historical"
|
|
15870
|
+
}
|
|
15399
15871
|
);
|
|
15400
15872
|
}
|
|
15401
15873
|
}
|
|
@@ -15403,10 +15875,25 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15403
15875
|
appendLine(lines, "", maxTokens, omitted, "knowledge-heading");
|
|
15404
15876
|
appendLine(lines, "Project knowledge", maxTokens, omitted, "knowledge-heading");
|
|
15405
15877
|
for (const claim of input.claims.slice(0, 3)) {
|
|
15406
|
-
appendLine(lines, "- " + claim.assertion + " [" + claim.id + "]", maxTokens, omitted, "claim"
|
|
15878
|
+
appendLine(lines, "- " + claim.assertion + " [" + claim.id + "]", maxTokens, omitted, "claim", selected, {
|
|
15879
|
+
kind: "claim",
|
|
15880
|
+
id: "claim:" + claim.id,
|
|
15881
|
+
reason: "source-qualified task match",
|
|
15882
|
+
trust: "source-backed"
|
|
15883
|
+
});
|
|
15407
15884
|
}
|
|
15408
15885
|
for (const page of input.pages.slice(0, 2)) {
|
|
15409
|
-
|
|
15886
|
+
const supportsDeliveredClaim = page.claimIds.some((claimId) => selected.some((item) => item.kind === "claim" && item.id === "claim:" + claimId));
|
|
15887
|
+
if (!supportsDeliveredClaim) {
|
|
15888
|
+
omitted.push("knowledge-page-dependency");
|
|
15889
|
+
continue;
|
|
15890
|
+
}
|
|
15891
|
+
appendLine(lines, "- page: " + page.relativePath, maxTokens, omitted, "knowledge-page", selected, {
|
|
15892
|
+
kind: "knowledge-page",
|
|
15893
|
+
id: "page:" + page.id,
|
|
15894
|
+
reason: "approved page linked to a selected claim",
|
|
15895
|
+
trust: "source-backed"
|
|
15896
|
+
});
|
|
15410
15897
|
}
|
|
15411
15898
|
}
|
|
15412
15899
|
if (input.workflows.length > 0) {
|
|
@@ -15418,7 +15905,14 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15418
15905
|
"- " + workflow.title + ": " + workflow.firstPhase.title + " - " + workflow.firstPhase.instructions,
|
|
15419
15906
|
maxTokens,
|
|
15420
15907
|
omitted,
|
|
15421
|
-
"workflow"
|
|
15908
|
+
"workflow",
|
|
15909
|
+
selected,
|
|
15910
|
+
{
|
|
15911
|
+
kind: "workflow",
|
|
15912
|
+
id: "workflow:" + workflow.id,
|
|
15913
|
+
reason: "task-matching project workflow",
|
|
15914
|
+
trust: "source-backed"
|
|
15915
|
+
}
|
|
15422
15916
|
);
|
|
15423
15917
|
}
|
|
15424
15918
|
}
|
|
@@ -15426,16 +15920,23 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15426
15920
|
appendLine(lines, "", maxTokens, omitted, "verification-heading");
|
|
15427
15921
|
appendLine(lines, "Verify", maxTokens, omitted, "verification-heading");
|
|
15428
15922
|
for (const check of input.verification.slice(0, 4)) {
|
|
15429
|
-
appendLine(lines, "- " + short(check, 20), maxTokens, omitted, "verification"
|
|
15923
|
+
appendLine(lines, "- " + short(check, 20), maxTokens, omitted, "verification", selected, {
|
|
15924
|
+
kind: "verification",
|
|
15925
|
+
reason: "task-lensed verification guidance",
|
|
15926
|
+
trust: "derived"
|
|
15927
|
+
});
|
|
15430
15928
|
}
|
|
15431
15929
|
}
|
|
15432
15930
|
return {
|
|
15433
15931
|
prompt: lines.join("\n"),
|
|
15434
15932
|
tokenCount: countTextTokens(lines.join("\n")),
|
|
15435
|
-
omitted: unique(omitted)
|
|
15933
|
+
omitted: unique(omitted),
|
|
15934
|
+
omittedItems: omitted,
|
|
15935
|
+
selected
|
|
15436
15936
|
};
|
|
15437
15937
|
}
|
|
15438
15938
|
async function buildTaskWorkset(input) {
|
|
15939
|
+
const startedAt = Date.now();
|
|
15439
15940
|
const task = input.task?.trim() ?? "";
|
|
15440
15941
|
const maxTokens = Math.max(96, Math.min(Math.floor(input.maxTokens ?? 180), 320));
|
|
15441
15942
|
const cautions = [...input.runtimeCautions ?? [], ...snapshotCautions(input)];
|
|
@@ -15537,6 +16038,18 @@ async function buildTaskWorkset(input) {
|
|
|
15537
16038
|
...base,
|
|
15538
16039
|
budget: { maxTokens }
|
|
15539
16040
|
});
|
|
16041
|
+
const receipt = {
|
|
16042
|
+
version: "1.2.2",
|
|
16043
|
+
target: input.deliveryTarget ?? "project-context",
|
|
16044
|
+
elapsedMs: Math.max(0, Date.now() - startedAt),
|
|
16045
|
+
budget: {
|
|
16046
|
+
maxTokens,
|
|
16047
|
+
tokenCount: rendered.tokenCount
|
|
16048
|
+
},
|
|
16049
|
+
selected: rendered.selected,
|
|
16050
|
+
omitted: receiptOmissions(rendered.omittedItems, base.hiddenCautionMemoryCount),
|
|
16051
|
+
scheduledActions: scheduledActions(normalizedCautions)
|
|
16052
|
+
};
|
|
15540
16053
|
return {
|
|
15541
16054
|
...base,
|
|
15542
16055
|
budget: {
|
|
@@ -15544,6 +16057,7 @@ async function buildTaskWorkset(input) {
|
|
|
15544
16057
|
tokenCount: rendered.tokenCount,
|
|
15545
16058
|
omitted: rendered.omitted
|
|
15546
16059
|
},
|
|
16060
|
+
receipt,
|
|
15547
16061
|
prompt: rendered.prompt
|
|
15548
16062
|
};
|
|
15549
16063
|
}
|
|
@@ -16321,6 +16835,9 @@ import path19 from "path";
|
|
|
16321
16835
|
function activeProjectObservations(observations2, projectId) {
|
|
16322
16836
|
return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
|
|
16323
16837
|
}
|
|
16838
|
+
function deliveryEligibleProjectObservations(observations2, projectId) {
|
|
16839
|
+
return activeProjectObservations(observations2, projectId).filter((observation) => isEligibleForAutomaticDelivery(observation));
|
|
16840
|
+
}
|
|
16324
16841
|
function decideRefresh(input) {
|
|
16325
16842
|
if (input.mode === "never") {
|
|
16326
16843
|
return { performed: false, reason: "disabled", message: "Automatic project scan disabled." };
|
|
@@ -16395,13 +16912,27 @@ async function buildAutoProjectContext(input) {
|
|
|
16395
16912
|
activeProjectObservations(input.observations, input.project.id)
|
|
16396
16913
|
);
|
|
16397
16914
|
try {
|
|
16398
|
-
const {
|
|
16915
|
+
const { MaintenanceTargetStore: MaintenanceTargetStore2 } = await Promise.resolve().then(() => (init_maintenance_targets(), maintenance_targets_exports));
|
|
16916
|
+
new MaintenanceTargetStore2(input.dataDir).register({
|
|
16917
|
+
projectId: input.project.id,
|
|
16918
|
+
projectRoot: input.project.rootPath,
|
|
16919
|
+
dataDir: input.dataDir
|
|
16920
|
+
});
|
|
16921
|
+
const {
|
|
16922
|
+
enqueueClaimRequalification: enqueueClaimRequalification2,
|
|
16923
|
+
enqueueObservationQualification: enqueueObservationQualification2
|
|
16924
|
+
} = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
|
|
16399
16925
|
enqueueClaimRequalification2({
|
|
16400
16926
|
dataDir: input.dataDir,
|
|
16401
16927
|
projectId: input.project.id,
|
|
16402
16928
|
source: "foreground-refresh",
|
|
16403
16929
|
snapshotId: store.latestSnapshot(input.project.id)?.id
|
|
16404
16930
|
});
|
|
16931
|
+
enqueueObservationQualification2({
|
|
16932
|
+
dataDir: input.dataDir,
|
|
16933
|
+
projectId: input.project.id,
|
|
16934
|
+
source: "foreground-refresh"
|
|
16935
|
+
});
|
|
16405
16936
|
} catch {
|
|
16406
16937
|
}
|
|
16407
16938
|
refresh = { ...refresh, backfill };
|
|
@@ -16418,7 +16949,9 @@ async function buildAutoProjectContext(input) {
|
|
|
16418
16949
|
const explain = buildProjectContextExplain({
|
|
16419
16950
|
project: input.project,
|
|
16420
16951
|
store,
|
|
16421
|
-
|
|
16952
|
+
// Binding sees all active evidence, but automatic delivery sees only
|
|
16953
|
+
// evidence that has passed the control-plane admission gate.
|
|
16954
|
+
observations: deliveryEligibleProjectObservations(input.observations, input.project.id),
|
|
16422
16955
|
exclude
|
|
16423
16956
|
});
|
|
16424
16957
|
const overview = explain.overview;
|
|
@@ -16509,7 +17042,8 @@ async function buildAutoProjectContext(input) {
|
|
|
16509
17042
|
suspect: overview.freshness.suspect,
|
|
16510
17043
|
stale: overview.freshness.stale
|
|
16511
17044
|
},
|
|
16512
|
-
runtimeCautions
|
|
17045
|
+
runtimeCautions,
|
|
17046
|
+
...input.deliveryTarget ? { deliveryTarget: input.deliveryTarget } : {}
|
|
16513
17047
|
});
|
|
16514
17048
|
return {
|
|
16515
17049
|
project: input.project,
|
|
@@ -16743,6 +17277,7 @@ var init_auto_context = __esm({
|
|
|
16743
17277
|
init_external_provider();
|
|
16744
17278
|
init_project_context();
|
|
16745
17279
|
init_store();
|
|
17280
|
+
init_admission();
|
|
16746
17281
|
init_task_lens();
|
|
16747
17282
|
DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
16748
17283
|
}
|
|
@@ -16935,7 +17470,8 @@ async function attachTaskWorkset(input) {
|
|
|
16935
17470
|
suspect: input.pack.warnings.filter((warning) => warning.status === "suspect").length,
|
|
16936
17471
|
stale: input.pack.warnings.filter((warning) => warning.status === "stale").length
|
|
16937
17472
|
},
|
|
16938
|
-
runtimeCautions: input.runtimeCautions
|
|
17473
|
+
runtimeCautions: input.runtimeCautions,
|
|
17474
|
+
deliveryTarget: "context-pack"
|
|
16939
17475
|
});
|
|
16940
17476
|
return { ...input.pack, workset };
|
|
16941
17477
|
}
|
|
@@ -18347,11 +18883,14 @@ __export(export_import_exports, {
|
|
|
18347
18883
|
exportAsMarkdown: () => exportAsMarkdown,
|
|
18348
18884
|
importFromJson: () => importFromJson
|
|
18349
18885
|
});
|
|
18350
|
-
async function exportAsJson(projectDir2, projectId) {
|
|
18886
|
+
async function exportAsJson(projectDir2, projectId, reader) {
|
|
18351
18887
|
const store = getObservationStore();
|
|
18352
18888
|
const allObs = await store.loadAll();
|
|
18353
18889
|
const allSessions = await getSessionStore().loadAll();
|
|
18354
|
-
const projectObs =
|
|
18890
|
+
const projectObs = filterReadableObservations(
|
|
18891
|
+
allObs.filter((o) => o.projectId === projectId),
|
|
18892
|
+
reader
|
|
18893
|
+
);
|
|
18355
18894
|
const projectSessions = allSessions.filter((s) => s.projectId === projectId);
|
|
18356
18895
|
const typeBreakdown = {};
|
|
18357
18896
|
for (const obs of projectObs) {
|
|
@@ -18370,8 +18909,8 @@ async function exportAsJson(projectDir2, projectId) {
|
|
|
18370
18909
|
}
|
|
18371
18910
|
};
|
|
18372
18911
|
}
|
|
18373
|
-
async function exportAsMarkdown(projectDir2, projectId) {
|
|
18374
|
-
const data = await exportAsJson(projectDir2, projectId);
|
|
18912
|
+
async function exportAsMarkdown(projectDir2, projectId, reader) {
|
|
18913
|
+
const data = await exportAsJson(projectDir2, projectId, reader);
|
|
18375
18914
|
const lines = [];
|
|
18376
18915
|
lines.push(`# Memorix Export: ${projectId}`);
|
|
18377
18916
|
lines.push(`Exported: ${data.exportedAt}`);
|
|
@@ -18473,6 +19012,7 @@ var init_export_import = __esm({
|
|
|
18473
19012
|
"src/memory/export-import.ts"() {
|
|
18474
19013
|
"use strict";
|
|
18475
19014
|
init_esm_shims();
|
|
19015
|
+
init_visibility();
|
|
18476
19016
|
init_obs_store();
|
|
18477
19017
|
init_session_store();
|
|
18478
19018
|
OBSERVATION_ICONS2 = {
|
|
@@ -18649,6 +19189,7 @@ function isEligible(o, projectId) {
|
|
|
18649
19189
|
if (isCommandLog2(o)) return false;
|
|
18650
19190
|
if (isInactive(o)) return false;
|
|
18651
19191
|
if (isOtherProject(o, projectId)) return false;
|
|
19192
|
+
if (!isEligibleForKnowledgePromotion(o)) return false;
|
|
18652
19193
|
if (o.valueCategory === "ephemeral") return false;
|
|
18653
19194
|
if (!contextualHasSubstance(o)) return false;
|
|
18654
19195
|
return true;
|
|
@@ -18797,6 +19338,7 @@ var init_generator = __esm({
|
|
|
18797
19338
|
"src/wiki/generator.ts"() {
|
|
18798
19339
|
"use strict";
|
|
18799
19340
|
init_esm_shims();
|
|
19341
|
+
init_admission();
|
|
18800
19342
|
COMMAND_LOG_TITLE4 = /^(Ran:|Command:|Executed:)\s/i;
|
|
18801
19343
|
SECTION_DEFS = [
|
|
18802
19344
|
{
|
|
@@ -19083,6 +19625,9 @@ function sendError(res, message, status = 500) {
|
|
|
19083
19625
|
function filterByProject(items, projectId) {
|
|
19084
19626
|
return items.filter((item) => item.projectId === projectId);
|
|
19085
19627
|
}
|
|
19628
|
+
function filterDashboardObservations(observations2, projectId) {
|
|
19629
|
+
return filterReadableObservations(observations2, { projectId });
|
|
19630
|
+
}
|
|
19086
19631
|
function isActiveStatus(status) {
|
|
19087
19632
|
return (status ?? "active") === "active";
|
|
19088
19633
|
}
|
|
@@ -19130,7 +19675,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19130
19675
|
switch (apiPath) {
|
|
19131
19676
|
case "/projects": {
|
|
19132
19677
|
try {
|
|
19133
|
-
const allObs =
|
|
19678
|
+
const allObs = filterReadableObservations(
|
|
19679
|
+
await getObservationStore().loadAll(),
|
|
19680
|
+
{}
|
|
19681
|
+
);
|
|
19134
19682
|
const projectSet = /* @__PURE__ */ new Map();
|
|
19135
19683
|
for (const obs of allObs) {
|
|
19136
19684
|
if (!isActiveStatus(obs.status)) continue;
|
|
@@ -19179,13 +19727,19 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19179
19727
|
await initGraphStore(effectiveDataDir);
|
|
19180
19728
|
const gStore = getGraphStore();
|
|
19181
19729
|
const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
19182
|
-
const graphObs =
|
|
19730
|
+
const graphObs = filterDashboardObservations(
|
|
19731
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19732
|
+
effectiveProjectId
|
|
19733
|
+
);
|
|
19183
19734
|
const scoped = scopeKnowledgeGraphToProject(graph, graphObs);
|
|
19184
19735
|
sendJson(res, { entities: scoped.entities, relations: scoped.relations });
|
|
19185
19736
|
break;
|
|
19186
19737
|
}
|
|
19187
19738
|
case "/observations": {
|
|
19188
|
-
const observations2 =
|
|
19739
|
+
const observations2 = filterDashboardObservations(
|
|
19740
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19741
|
+
effectiveProjectId
|
|
19742
|
+
);
|
|
19189
19743
|
sendJson(res, observations2);
|
|
19190
19744
|
break;
|
|
19191
19745
|
}
|
|
@@ -19198,7 +19752,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19198
19752
|
case "/stats": {
|
|
19199
19753
|
await initGraphStore(effectiveDataDir);
|
|
19200
19754
|
const graph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
|
|
19201
|
-
const observations2 =
|
|
19755
|
+
const observations2 = filterDashboardObservations(
|
|
19756
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19757
|
+
effectiveProjectId
|
|
19758
|
+
);
|
|
19202
19759
|
const nextId2 = await getObservationStore().loadIdCounter();
|
|
19203
19760
|
const projectGraphCounts = computeProjectGraphCounts(graph.entities, graph.relations, observations2);
|
|
19204
19761
|
const typeCounts = {};
|
|
@@ -19316,7 +19873,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19316
19873
|
break;
|
|
19317
19874
|
}
|
|
19318
19875
|
case "/retention": {
|
|
19319
|
-
const observations2 =
|
|
19876
|
+
const observations2 = filterDashboardObservations(
|
|
19877
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19878
|
+
effectiveProjectId
|
|
19879
|
+
);
|
|
19320
19880
|
const now3 = Date.now();
|
|
19321
19881
|
const scored = observations2.filter((obs) => obs.type !== "probe").map((obs) => {
|
|
19322
19882
|
const age = now3 - new Date(obs.createdAt || now3).getTime();
|
|
@@ -19354,7 +19914,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19354
19914
|
const { generateKnowledgeBase: generateKnowledgeBase2 } = await Promise.resolve().then(() => (init_generator(), generator_exports));
|
|
19355
19915
|
const { initMiniSkillStore: initMiniSkillStore2, getMiniSkillStore: getMiniSkillStore2 } = await Promise.resolve().then(() => (init_mini_skill_store(), mini_skill_store_exports));
|
|
19356
19916
|
await initMiniSkillStore2(effectiveDataDir);
|
|
19357
|
-
const allObs =
|
|
19917
|
+
const allObs = filterDashboardObservations(
|
|
19918
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19919
|
+
effectiveProjectId
|
|
19920
|
+
);
|
|
19358
19921
|
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
19359
19922
|
const overview = generateKnowledgeBase2({
|
|
19360
19923
|
projectId: effectiveProjectId,
|
|
@@ -19370,7 +19933,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19370
19933
|
const { initGraphStore: initGraphStore2, getGraphStore: getGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
19371
19934
|
await initMiniSkillStore2(effectiveDataDir);
|
|
19372
19935
|
await initGraphStore2(effectiveDataDir);
|
|
19373
|
-
const allObs =
|
|
19936
|
+
const allObs = filterDashboardObservations(
|
|
19937
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19938
|
+
effectiveProjectId
|
|
19939
|
+
);
|
|
19374
19940
|
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
19375
19941
|
const fullGraph = { entities: getGraphStore2().loadEntities(), relations: getGraphStore2().loadRelations() };
|
|
19376
19942
|
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
@@ -19493,7 +20059,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19493
20059
|
break;
|
|
19494
20060
|
}
|
|
19495
20061
|
case "/identity": {
|
|
19496
|
-
const allObs = await getObservationStore().loadAll();
|
|
20062
|
+
const allObs = filterReadableObservations(await getObservationStore().loadAll(), {});
|
|
19497
20063
|
const allProjectIds = [...new Set(allObs.map((o) => o.projectId).filter(Boolean))];
|
|
19498
20064
|
let classifyProjectId2 = () => "real";
|
|
19499
20065
|
let isDirtyProjectId2 = () => false;
|
|
@@ -19578,6 +20144,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19578
20144
|
sendError(res, "Observation not found", 404);
|
|
19579
20145
|
} else if (matchObs.projectId !== effectiveProjectId) {
|
|
19580
20146
|
sendError(res, `Observation #${obsId} belongs to project "${matchObs.projectId}", not "${effectiveProjectId}"`, 403);
|
|
20147
|
+
} else if (!canManageObservation(matchObs, { projectId: effectiveProjectId })) {
|
|
20148
|
+
sendError(res, `Observation #${obsId} is not manageable from the unbound dashboard.`, 403);
|
|
19581
20149
|
} else {
|
|
19582
20150
|
await obsStore.remove(obsId);
|
|
19583
20151
|
try {
|
|
@@ -19599,7 +20167,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19599
20167
|
if (apiPath === "/export") {
|
|
19600
20168
|
await initGraphStore(effectiveDataDir);
|
|
19601
20169
|
const fullGraph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
|
|
19602
|
-
const observations2 =
|
|
20170
|
+
const observations2 = filterDashboardObservations(
|
|
20171
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
20172
|
+
effectiveProjectId
|
|
20173
|
+
);
|
|
19603
20174
|
const nextId2 = await getObservationStore().loadIdCounter();
|
|
19604
20175
|
const scoped = scopeKnowledgeGraphToProject(fullGraph, observations2);
|
|
19605
20176
|
const exportData = {
|
|
@@ -19923,6 +20494,7 @@ var init_server = __esm({
|
|
|
19923
20494
|
init_yaml_loader();
|
|
19924
20495
|
init_yaml_loader();
|
|
19925
20496
|
init_graph_scope();
|
|
20497
|
+
init_visibility();
|
|
19926
20498
|
MIME_TYPES = {
|
|
19927
20499
|
".html": "text/html; charset=utf-8",
|
|
19928
20500
|
".css": "text/css; charset=utf-8",
|
|
@@ -20700,6 +21272,11 @@ async function createHandoffArtifact(input, storeObservation2, teamStore) {
|
|
|
20700
21272
|
sourceDetail: "explicit",
|
|
20701
21273
|
valueCategory: "core",
|
|
20702
21274
|
// immune to retention archival
|
|
21275
|
+
// A direct handoff is not a project-wide memo. The sender retains access
|
|
21276
|
+
// and the explicit recipient receives a read grant. Broadcast handoffs
|
|
21277
|
+
// are deliberately visible only to active team members.
|
|
21278
|
+
visibility: input.toAgentId ? "personal" : "team",
|
|
21279
|
+
...input.toAgentId ? { sharedWithAgentIds: [input.toAgentId] } : {},
|
|
20703
21280
|
createdByAgentId: input.fromAgentId
|
|
20704
21281
|
});
|
|
20705
21282
|
try {
|
|
@@ -22405,6 +22982,7 @@ var init_hooks_path = __esm({
|
|
|
22405
22982
|
|
|
22406
22983
|
// src/sdk.ts
|
|
22407
22984
|
init_esm_shims();
|
|
22985
|
+
init_visibility();
|
|
22408
22986
|
|
|
22409
22987
|
// src/server.ts
|
|
22410
22988
|
init_esm_shims();
|
|
@@ -22610,6 +23188,7 @@ async function createAutoRelations(obs, extracted, graphManager) {
|
|
|
22610
23188
|
// src/server.ts
|
|
22611
23189
|
init_entity_extractor();
|
|
22612
23190
|
init_graph_scope();
|
|
23191
|
+
init_visibility();
|
|
22613
23192
|
|
|
22614
23193
|
// src/compact/engine.ts
|
|
22615
23194
|
init_esm_shims();
|
|
@@ -22735,7 +23314,15 @@ function formatTimeline(timeline) {
|
|
|
22735
23314
|
function formatObservationDetail(doc) {
|
|
22736
23315
|
const icon = getTypeIcon(doc.type);
|
|
22737
23316
|
const lines = [];
|
|
22738
|
-
const header = buildProvenanceHeader(
|
|
23317
|
+
const header = buildProvenanceHeader(
|
|
23318
|
+
doc.sourceDetail,
|
|
23319
|
+
doc.valueCategory,
|
|
23320
|
+
doc.admissionState,
|
|
23321
|
+
doc.admissionReason,
|
|
23322
|
+
doc.source,
|
|
23323
|
+
doc.commitHash,
|
|
23324
|
+
doc.relatedCommits
|
|
23325
|
+
);
|
|
22739
23326
|
if (header) {
|
|
22740
23327
|
lines.push(header);
|
|
22741
23328
|
lines.push("");
|
|
@@ -22770,7 +23357,7 @@ function formatObservationDetail(doc) {
|
|
|
22770
23357
|
}
|
|
22771
23358
|
return lines.join("\n");
|
|
22772
23359
|
}
|
|
22773
|
-
function buildProvenanceHeader(sourceDetail, valueCategory, source, commitHash, relatedCommits) {
|
|
23360
|
+
function buildProvenanceHeader(sourceDetail, valueCategory, admissionState, admissionReason, source, commitHash, relatedCommits) {
|
|
22774
23361
|
const sd = resolveSourceDetail(sourceDetail, source);
|
|
22775
23362
|
if (!sd) return "";
|
|
22776
23363
|
const label = sourceKindLabel(sd);
|
|
@@ -22781,11 +23368,21 @@ function buildProvenanceHeader(sourceDetail, valueCategory, source, commitHash,
|
|
|
22781
23368
|
if (verificationLine) {
|
|
22782
23369
|
lines.push(verificationLine);
|
|
22783
23370
|
}
|
|
22784
|
-
if (valueCategory === "core") {
|
|
23371
|
+
if (valueCategory === "core" && admissionState !== "candidate" && admissionState !== "ephemeral") {
|
|
22785
23372
|
lines.push("[CORE] Core \u2014 immune to decay");
|
|
23373
|
+
} else if (valueCategory === "core") {
|
|
23374
|
+
lines.push("[CORE] Core candidate \u2014 not durable until qualification completes");
|
|
22786
23375
|
} else if (valueCategory === "ephemeral") {
|
|
22787
23376
|
lines.push("[WARN] Ephemeral \u2014 short-lived signal");
|
|
22788
23377
|
}
|
|
23378
|
+
if (admissionState === "candidate") {
|
|
23379
|
+
lines.push("[PENDING] Candidate \u2014 excluded from automatic context until current Code Memory qualifies it");
|
|
23380
|
+
} else if (admissionState === "ephemeral") {
|
|
23381
|
+
lines.push("[TRACE] Ephemeral capture \u2014 excluded from automatic context");
|
|
23382
|
+
} else if (admissionState === "qualified") {
|
|
23383
|
+
lines.push("[QUALIFIED] Automatic evidence passed current Code Memory qualification");
|
|
23384
|
+
}
|
|
23385
|
+
if (admissionReason) lines.push(`Admission: ${redactCredentials(admissionReason)}`);
|
|
22789
23386
|
return lines.join("\n");
|
|
22790
23387
|
}
|
|
22791
23388
|
function sourceKindLabel(sd) {
|
|
@@ -22866,6 +23463,7 @@ init_obs_store();
|
|
|
22866
23463
|
init_mini_skill_store();
|
|
22867
23464
|
init_mini_skills();
|
|
22868
23465
|
init_secret_filter();
|
|
23466
|
+
init_visibility();
|
|
22869
23467
|
function normalizeMemoryBrowseQuery(query) {
|
|
22870
23468
|
const trimmed = query.trim();
|
|
22871
23469
|
if (!trimmed) return "";
|
|
@@ -22908,11 +23506,13 @@ async function compactSearch(options) {
|
|
|
22908
23506
|
);
|
|
22909
23507
|
let formatted = formatIndexTable(entries, searchOptions.query, !options.projectId);
|
|
22910
23508
|
if (entries.length === 0 && options.projectId) {
|
|
22911
|
-
const allObservations = getAllObservations();
|
|
23509
|
+
const allObservations = filterReadableObservations(getAllObservations(), options.reader);
|
|
22912
23510
|
const projectAliases = new Set(await resolveAliases(options.projectId).catch(() => [options.projectId]));
|
|
22913
23511
|
const projectHasStoredMemory = allObservations.some((obs) => obs.projectId && projectAliases.has(obs.projectId));
|
|
22914
23512
|
if (!projectHasStoredMemory) {
|
|
22915
|
-
formatted = `
|
|
23513
|
+
formatted = options.reader ? `No Memorix memories are available to this session yet.
|
|
23514
|
+
|
|
23515
|
+
The tool call worked, but this session has no project-visible memory to retrieve.` : `This project does not have any Memorix memories yet.
|
|
22916
23516
|
|
|
22917
23517
|
It looks like a fresh project: the tool call worked, but there is nothing stored to retrieve yet.
|
|
22918
23518
|
|
|
@@ -22926,8 +23526,8 @@ This project does have stored Memorix memories, but none matched the current que
|
|
|
22926
23526
|
const totalTokens = countTextTokens(formatted);
|
|
22927
23527
|
return { entries, formatted, totalTokens };
|
|
22928
23528
|
}
|
|
22929
|
-
async function compactTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3) {
|
|
22930
|
-
const result = await getTimeline(anchorId, projectId, depthBefore, depthAfter);
|
|
23529
|
+
async function compactTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3, reader) {
|
|
23530
|
+
const result = await getTimeline(anchorId, projectId, depthBefore, depthAfter, reader);
|
|
22931
23531
|
const timeline = {
|
|
22932
23532
|
anchorId,
|
|
22933
23533
|
anchorEntry: result.anchor,
|
|
@@ -22938,7 +23538,7 @@ async function compactTimeline(anchorId, projectId, depthBefore = 3, depthAfter
|
|
|
22938
23538
|
const totalTokens = countTextTokens(formatted);
|
|
22939
23539
|
return { timeline, formatted, totalTokens };
|
|
22940
23540
|
}
|
|
22941
|
-
async function compactDetail(idsOrRefs) {
|
|
23541
|
+
async function compactDetail(idsOrRefs, options = {}) {
|
|
22942
23542
|
const invalidRefs = [];
|
|
22943
23543
|
const parsedRefs = [];
|
|
22944
23544
|
for (let i = 0; i < idsOrRefs.length; i++) {
|
|
@@ -22995,7 +23595,7 @@ async function compactDetail(idsOrRefs) {
|
|
|
22995
23595
|
const missingRefs = [];
|
|
22996
23596
|
for (const ref of refs) {
|
|
22997
23597
|
const obs = getObservation(ref.id, ref.projectId);
|
|
22998
|
-
if (obs && (ref.projectId ? obs.projectId === ref.projectId : true)) {
|
|
23598
|
+
if (obs && (ref.projectId ? obs.projectId === ref.projectId : true) && canReadObservation(obs, options.reader)) {
|
|
22999
23599
|
documentMap.set(toRefKey(ref), {
|
|
23000
23600
|
id: makeOramaObservationId(obs.projectId, obs.id),
|
|
23001
23601
|
observationId: obs.id,
|
|
@@ -23014,7 +23614,12 @@ async function compactDetail(idsOrRefs) {
|
|
|
23014
23614
|
status: obs.status ?? "active",
|
|
23015
23615
|
source: obs.source ?? "agent",
|
|
23016
23616
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23017
|
-
valueCategory: obs.valueCategory ?? ""
|
|
23617
|
+
valueCategory: obs.valueCategory ?? "",
|
|
23618
|
+
admissionState: obs.admissionState ?? "",
|
|
23619
|
+
admissionReason: obs.admissionReason ?? "",
|
|
23620
|
+
visibility: obs.visibility ?? "project",
|
|
23621
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
23622
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
23018
23623
|
});
|
|
23019
23624
|
} else {
|
|
23020
23625
|
missingRefs.push(ref);
|
|
@@ -23024,17 +23629,17 @@ async function compactDetail(idsOrRefs) {
|
|
|
23024
23629
|
for (const ref of missingRefs) {
|
|
23025
23630
|
const fallbackDocs = await getObservationsByIds([ref.id], ref.projectId);
|
|
23026
23631
|
const doc = fallbackDocs[0];
|
|
23027
|
-
if (doc) {
|
|
23632
|
+
if (doc && canReadObservation(doc, options.reader)) {
|
|
23028
23633
|
documentMap.set(toRefKey(ref), doc);
|
|
23029
23634
|
continue;
|
|
23030
23635
|
}
|
|
23031
23636
|
const stored = await getPersistedObservation(ref);
|
|
23032
|
-
if (stored) {
|
|
23637
|
+
if (stored && canReadObservation(stored, options.reader)) {
|
|
23033
23638
|
documentMap.set(toRefKey(ref), observationToDocument(stored));
|
|
23034
23639
|
}
|
|
23035
23640
|
}
|
|
23036
23641
|
}
|
|
23037
|
-
const allObs = getAllObservations();
|
|
23642
|
+
const allObs = filterReadableObservations(getAllObservations(), options.reader);
|
|
23038
23643
|
const crossRefMap = /* @__PURE__ */ new Map();
|
|
23039
23644
|
for (const ref of refs) {
|
|
23040
23645
|
const obs = getObservation(ref.id, ref.projectId);
|
|
@@ -23148,7 +23753,12 @@ function observationToDocument(obs) {
|
|
|
23148
23753
|
status: obs.status ?? "active",
|
|
23149
23754
|
source: obs.source ?? "agent",
|
|
23150
23755
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23151
|
-
valueCategory: obs.valueCategory ?? ""
|
|
23756
|
+
valueCategory: obs.valueCategory ?? "",
|
|
23757
|
+
admissionState: obs.admissionState ?? "",
|
|
23758
|
+
admissionReason: obs.admissionReason ?? "",
|
|
23759
|
+
visibility: obs.visibility ?? "project",
|
|
23760
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
23761
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
23152
23762
|
};
|
|
23153
23763
|
}
|
|
23154
23764
|
function formatMiniSkillDetail(skill2, provenanceStatus) {
|
|
@@ -23267,7 +23877,9 @@ function auditMemoryQuality(observations2, options) {
|
|
|
23267
23877
|
status: obs.status ?? "active",
|
|
23268
23878
|
source: obs.source ?? "agent",
|
|
23269
23879
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23270
|
-
valueCategory: obs.valueCategory ?? ""
|
|
23880
|
+
valueCategory: obs.valueCategory ?? "",
|
|
23881
|
+
admissionState: obs.admissionState ?? "",
|
|
23882
|
+
admissionReason: obs.admissionReason ?? ""
|
|
23271
23883
|
}, options.referenceTime) !== "active").map((obs) => makeEntry(obs, "Outside active retention zone"));
|
|
23272
23884
|
const summary = {
|
|
23273
23885
|
total: scoped.length,
|
|
@@ -23309,6 +23921,7 @@ function auditMemoryQuality(observations2, options) {
|
|
|
23309
23921
|
|
|
23310
23922
|
// src/memory/graph-context.ts
|
|
23311
23923
|
init_retention();
|
|
23924
|
+
init_admission();
|
|
23312
23925
|
function formatGraphContextPrompt(packet) {
|
|
23313
23926
|
const lines = [
|
|
23314
23927
|
"## Memory Context Packet",
|
|
@@ -23466,7 +24079,9 @@ function scoreForPacket(obs, queryTokens, referenceTime, broadMemoryQuery) {
|
|
|
23466
24079
|
status: obs.status ?? "active",
|
|
23467
24080
|
source: obs.source ?? "agent",
|
|
23468
24081
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23469
|
-
valueCategory: obs.valueCategory ?? ""
|
|
24082
|
+
valueCategory: obs.valueCategory ?? "",
|
|
24083
|
+
admissionState: obs.admissionState ?? "",
|
|
24084
|
+
admissionReason: obs.admissionReason ?? ""
|
|
23470
24085
|
}, referenceTime) !== "active") {
|
|
23471
24086
|
score -= 2;
|
|
23472
24087
|
}
|
|
@@ -23560,8 +24175,11 @@ function buildGraphContextPacket(observations2, options) {
|
|
|
23560
24175
|
...audit.issues.orphans.map((entry) => entry.id),
|
|
23561
24176
|
...audit.issues.retentionCandidates.map((entry) => entry.id)
|
|
23562
24177
|
]);
|
|
23563
|
-
const
|
|
23564
|
-
|
|
24178
|
+
const deliveryEligible = observations2.filter(
|
|
24179
|
+
(obs) => obs.projectId === options.projectId && isEligibleForAutomaticDelivery(obs)
|
|
24180
|
+
);
|
|
24181
|
+
const filteredObservations = deliveryEligible.filter((obs) => !riskIds.has(obs.id));
|
|
24182
|
+
const baseObservations = filteredObservations.length > 0 ? filteredObservations : deliveryEligible;
|
|
23565
24183
|
const memories = pickMemories(baseObservations, options.projectId, options.query, options.limit ?? 5, referenceTime);
|
|
23566
24184
|
const entities = buildEntities(memories);
|
|
23567
24185
|
const entityNames = new Set(entities.map((entity) => entity.name));
|
|
@@ -26048,6 +26666,8 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
26048
26666
|
let projectResolutionError = null;
|
|
26049
26667
|
let explicitProjectBound = false;
|
|
26050
26668
|
let currentAgentId;
|
|
26669
|
+
let teamStore;
|
|
26670
|
+
let initTeamStoreForProject;
|
|
26051
26671
|
if (detectedProject) {
|
|
26052
26672
|
rawProject = detectedProject;
|
|
26053
26673
|
} else {
|
|
@@ -26295,7 +26915,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
26295
26915
|
};
|
|
26296
26916
|
const server = existingServer ?? new McpServer({
|
|
26297
26917
|
name: "memorix",
|
|
26298
|
-
version: true ? "1.2.
|
|
26918
|
+
version: true ? "1.2.2" : "1.0.1"
|
|
26299
26919
|
});
|
|
26300
26920
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
26301
26921
|
server.registerTool = ((name, ...args) => {
|
|
@@ -26315,11 +26935,26 @@ The path should point to a directory containing a .git folder.`
|
|
|
26315
26935
|
}
|
|
26316
26936
|
return originalRegisterTool(name, ...args);
|
|
26317
26937
|
});
|
|
26938
|
+
const getObservationReader = (scope = "project") => {
|
|
26939
|
+
let isTeamMember = false;
|
|
26940
|
+
if (teamFeaturesEnabled && currentAgentId) {
|
|
26941
|
+
try {
|
|
26942
|
+
const agent = teamStore.getAgent(currentAgentId);
|
|
26943
|
+
isTeamMember = agent?.project_id === project.id && agent.status === "active";
|
|
26944
|
+
} catch {
|
|
26945
|
+
}
|
|
26946
|
+
}
|
|
26947
|
+
return {
|
|
26948
|
+
...scope === "project" ? { projectId: project.id } : {},
|
|
26949
|
+
...currentAgentId ? { agentId: currentAgentId } : {},
|
|
26950
|
+
isTeamMember
|
|
26951
|
+
};
|
|
26952
|
+
};
|
|
26318
26953
|
server.registerTool(
|
|
26319
26954
|
"memorix_store",
|
|
26320
26955
|
{
|
|
26321
26956
|
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).
|
|
26957
|
+
description: "Store a new observation/memory. Automatically indexed for search. Use type to classify: gotcha ([GOTCHA] critical pitfall), decision ([DECISION] architecture choice), problem-solution ([FIX] bug fix), how-it-works ([INFO] explanation), what-changed ([CHANGE] change), discovery ([DISCOVERY] insight), why-it-exists ([WHY] rationale), trade-off ([TRADEOFF] compromise), session-request ([SESSION] original goal). Project visibility is the default. Personal or team visibility requires an explicitly joined coordination identity.",
|
|
26323
26958
|
inputSchema: {
|
|
26324
26959
|
entityName: z2.string().describe('The entity this observation belongs to (e.g., "auth-module", "port-config")'),
|
|
26325
26960
|
type: z2.enum(OBSERVATION_TYPES).describe("Observation type for classification"),
|
|
@@ -26337,361 +26972,214 @@ The path should point to a directory containing a .git folder.`
|
|
|
26337
26972
|
completion: z2.number().optional().describe("Completion percentage 0-100")
|
|
26338
26973
|
}).optional().describe("Progress tracking for task/feature observations"),
|
|
26339
26974
|
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")
|
|
26975
|
+
relatedEntities: z2.array(z2.string()).optional().describe("Other entity names this memory cross-references"),
|
|
26976
|
+
visibility: z2.enum(["personal", "project", "team"]).optional().describe(
|
|
26977
|
+
"Retrieval scope. Project is the normal shared default; personal/team require memorix_session_start with joinTeam=true."
|
|
26978
|
+
)
|
|
26341
26979
|
}
|
|
26342
26980
|
},
|
|
26343
|
-
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities }) => {
|
|
26981
|
+
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility }) => {
|
|
26344
26982
|
const unresolved = requireResolvedProject("store memory in the current project");
|
|
26345
26983
|
if (unresolved) return unresolved;
|
|
26346
|
-
|
|
26347
|
-
|
|
26348
|
-
|
|
26349
|
-
|
|
26350
|
-
|
|
26351
|
-
|
|
26352
|
-
|
|
26353
|
-
|
|
26354
|
-
|
|
26355
|
-
|
|
26356
|
-
|
|
26357
|
-
|
|
26358
|
-
|
|
26359
|
-
|
|
26360
|
-
|
|
26984
|
+
const requestedVisibility = visibility ?? "project";
|
|
26985
|
+
const reader = getObservationReader();
|
|
26986
|
+
if (requestedVisibility !== "project" && !currentAgentId) {
|
|
26987
|
+
return {
|
|
26988
|
+
content: [{ type: "text", text: "Personal or team memory requires memorix_session_start with joinTeam=true so Memorix can bind an owner." }],
|
|
26989
|
+
isError: true
|
|
26990
|
+
};
|
|
26991
|
+
}
|
|
26992
|
+
if (requestedVisibility === "team" && !reader.isTeamMember) {
|
|
26993
|
+
return {
|
|
26994
|
+
content: [{ type: "text", text: "Team memory requires an active coordination membership in the current project." }],
|
|
26995
|
+
isError: true
|
|
26996
|
+
};
|
|
26997
|
+
}
|
|
26998
|
+
try {
|
|
26999
|
+
return await withFreshIndex(async () => {
|
|
27000
|
+
let entityName = rawEntityName;
|
|
27001
|
+
let type = rawType;
|
|
27002
|
+
let title = rawTitle;
|
|
27003
|
+
let safeFacts = facts ? coerceStringArray(facts) : void 0;
|
|
27004
|
+
const safeFiles = filesModified ? coerceStringArray(filesModified) : void 0;
|
|
27005
|
+
const safeConcepts = concepts ? coerceStringArray(concepts) : void 0;
|
|
27006
|
+
let formationMode = "active";
|
|
27007
|
+
if (process.env.MEMORIX_FORMATION_MODE) {
|
|
27008
|
+
formationMode = process.env.MEMORIX_FORMATION_MODE;
|
|
27009
|
+
} else {
|
|
27010
|
+
try {
|
|
27011
|
+
const { getBehaviorConfig: getBehaviorConfig2 } = await Promise.resolve().then(() => (init_behavior(), behavior_exports));
|
|
27012
|
+
formationMode = getBehaviorConfig2({ projectRoot: project.rootPath }).formationMode;
|
|
27013
|
+
} catch {
|
|
27014
|
+
}
|
|
26361
27015
|
}
|
|
26362
|
-
|
|
26363
|
-
|
|
26364
|
-
|
|
26365
|
-
|
|
26366
|
-
|
|
26367
|
-
|
|
26368
|
-
|
|
26369
|
-
|
|
26370
|
-
|
|
26371
|
-
|
|
27016
|
+
const useFormation = formationMode === "active";
|
|
27017
|
+
let formationResult = null;
|
|
27018
|
+
let formationNote = "";
|
|
27019
|
+
if (useFormation && !topicKey && !progress) {
|
|
27020
|
+
let currentFormationStage = "setup";
|
|
27021
|
+
const completedFormationStages = {};
|
|
27022
|
+
const formationStartTime = Date.now();
|
|
27023
|
+
const onFormationStageEvent = (event) => {
|
|
27024
|
+
if (event.status === "start") {
|
|
27025
|
+
currentFormationStage = event.stage;
|
|
27026
|
+
return;
|
|
27027
|
+
}
|
|
26372
27028
|
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
|
|
27029
|
+
if (event.stageDurationMs !== void 0) {
|
|
27030
|
+
completedFormationStages[event.stage] = event.stageDurationMs;
|
|
27031
|
+
}
|
|
26415
27032
|
};
|
|
26416
|
-
|
|
26417
|
-
|
|
26418
|
-
|
|
26419
|
-
|
|
26420
|
-
|
|
26421
|
-
|
|
26422
|
-
|
|
26423
|
-
|
|
26424
|
-
|
|
26425
|
-
|
|
26426
|
-
|
|
26427
|
-
|
|
26428
|
-
|
|
26429
|
-
|
|
26430
|
-
|
|
27033
|
+
try {
|
|
27034
|
+
const formationConfig = {
|
|
27035
|
+
mode: "active",
|
|
27036
|
+
useLLM: isLLMEnabled(),
|
|
27037
|
+
minValueScore: 0.3,
|
|
27038
|
+
searchMemories: async (q, limit, pid) => {
|
|
27039
|
+
const result = await compactSearch({ query: q, limit, projectId: pid, status: "active", reader });
|
|
27040
|
+
if (result.entries.length === 0) return [];
|
|
27041
|
+
const details = await compactDetail(result.entries.map((e) => e.id), { reader });
|
|
27042
|
+
return details.documents.map((d, i) => ({
|
|
27043
|
+
id: Number(d.id.replace("obs-", "")),
|
|
27044
|
+
observationId: d.observationId,
|
|
27045
|
+
title: d.title,
|
|
27046
|
+
narrative: d.narrative,
|
|
27047
|
+
facts: d.facts,
|
|
27048
|
+
entityName: d.entityName,
|
|
27049
|
+
type: d.type,
|
|
27050
|
+
score: result.entries[i]?.score ?? 0
|
|
27051
|
+
}));
|
|
27052
|
+
},
|
|
27053
|
+
getObservation: (id) => {
|
|
27054
|
+
const o = getObservation(id);
|
|
27055
|
+
if (!o || o.projectId !== project.id || !canReadObservation(o, reader)) return null;
|
|
27056
|
+
return {
|
|
27057
|
+
id: o.id,
|
|
27058
|
+
entityName: o.entityName,
|
|
27059
|
+
type: o.type,
|
|
27060
|
+
title: o.title,
|
|
27061
|
+
narrative: o.narrative,
|
|
27062
|
+
facts: o.facts,
|
|
27063
|
+
topicKey: o.topicKey
|
|
27064
|
+
};
|
|
27065
|
+
},
|
|
27066
|
+
getEntityNames: () => graphManager.getEntityNames(),
|
|
27067
|
+
onStageEvent: onFormationStageEvent
|
|
27068
|
+
};
|
|
27069
|
+
formationResult = await withTimeout2(
|
|
27070
|
+
runFormation({
|
|
27071
|
+
entityName,
|
|
27072
|
+
type,
|
|
27073
|
+
title,
|
|
27074
|
+
narrative,
|
|
27075
|
+
facts: safeFacts,
|
|
27076
|
+
projectId: project.id,
|
|
27077
|
+
source: "explicit"
|
|
27078
|
+
}, formationConfig),
|
|
27079
|
+
FORMATION_TIMEOUT_MS,
|
|
27080
|
+
"Formation pipeline"
|
|
27081
|
+
);
|
|
27082
|
+
const modeIcon = "[FAST]";
|
|
27083
|
+
formationNote = `
|
|
26431
27084
|
${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
|
-
|
|
27085
|
+
if (formationResult.extraction.extractedFacts.length > 0) {
|
|
27086
|
+
formationNote += ` | +${formationResult.extraction.extractedFacts.length} facts`;
|
|
27087
|
+
}
|
|
27088
|
+
if (formationResult.extraction.titleImproved) formationNote += " | title\u2191";
|
|
27089
|
+
if (formationResult.extraction.entityResolved) formationNote += ` | entity\u2192${formationResult.entityName}`;
|
|
27090
|
+
if (formationResult.extraction.typeCorrected) formationNote += ` | type\u2192${formationResult.type}`;
|
|
27091
|
+
} catch (formationErr) {
|
|
27092
|
+
const isTimeout = formationErr instanceof Error && formationErr.message.includes("timed out");
|
|
27093
|
+
const elapsedMs = Date.now() - formationStartTime;
|
|
27094
|
+
const stageSummary = formatFormationStageDurations(completedFormationStages);
|
|
27095
|
+
console.error(
|
|
27096
|
+
`[memorix] Formation ${isTimeout ? "timed out" : "failed"} in memorix_store after ${elapsedMs}ms/${FORMATION_TIMEOUT_MS}ms at stage ${currentFormationStage}${stageSummary ? ` | completed: ${stageSummary}` : ""}`
|
|
27097
|
+
);
|
|
27098
|
+
if (!isTimeout && formationErr instanceof Error) {
|
|
27099
|
+
console.error(`[memorix] Formation error: ${formationErr.message}`);
|
|
27100
|
+
}
|
|
27101
|
+
formationNote = `
|
|
26449
27102
|
[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
27103
|
}
|
|
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
27104
|
}
|
|
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") {
|
|
27105
|
+
if (useFormation && formationResult && formationResult.resolution.action !== "new") {
|
|
27106
|
+
const { action: action2, targetId, reason } = formationResult.resolution;
|
|
27107
|
+
if (action2 === "merge" && targetId) {
|
|
27108
|
+
const targetObs = getObservation(targetId);
|
|
27109
|
+
if (targetObs && targetObs.projectId === project.id && canReadObservation(targetObs, reader)) {
|
|
27110
|
+
await storeObservation({
|
|
27111
|
+
entityName: targetObs.entityName,
|
|
27112
|
+
type: targetObs.type,
|
|
27113
|
+
title: formationResult.title,
|
|
27114
|
+
narrative: formationResult.narrative,
|
|
27115
|
+
facts: formationResult.facts,
|
|
27116
|
+
filesModified: safeFiles,
|
|
27117
|
+
concepts: safeConcepts,
|
|
27118
|
+
projectId: project.id,
|
|
27119
|
+
topicKey: targetObs.topicKey,
|
|
27120
|
+
progress,
|
|
27121
|
+
sourceDetail: "explicit",
|
|
27122
|
+
createdByAgentId: currentAgentId,
|
|
27123
|
+
visibility,
|
|
27124
|
+
visibilityReader: reader
|
|
27125
|
+
});
|
|
26568
27126
|
return {
|
|
26569
27127
|
content: [{
|
|
26570
27128
|
type: "text",
|
|
26571
|
-
text: `[
|
|
26572
|
-
Existing memory #${decision.targetId} already covers this.
|
|
26573
|
-
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
27129
|
+
text: `[UPDATED] Formation MERGE: merged into #${targetId} (${reason})${formationNote}`
|
|
26574
27130
|
}]
|
|
26575
27131
|
};
|
|
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
27132
|
}
|
|
26581
|
-
|
|
26582
|
-
|
|
26583
|
-
|
|
26584
|
-
|
|
26585
|
-
|
|
26586
|
-
|
|
27133
|
+
} else if (action2 === "evolve" && targetId) {
|
|
27134
|
+
const targetObs = getObservation(targetId);
|
|
27135
|
+
if (targetObs && targetObs.projectId === project.id && canReadObservation(targetObs, reader)) {
|
|
27136
|
+
await storeObservation({
|
|
27137
|
+
entityName: targetObs.entityName,
|
|
27138
|
+
type: targetObs.type,
|
|
27139
|
+
title: formationResult.title,
|
|
27140
|
+
narrative: formationResult.narrative,
|
|
27141
|
+
facts: formationResult.facts,
|
|
27142
|
+
filesModified: safeFiles,
|
|
27143
|
+
concepts: safeConcepts,
|
|
27144
|
+
projectId: project.id,
|
|
27145
|
+
topicKey: targetObs.topicKey,
|
|
27146
|
+
progress,
|
|
27147
|
+
sourceDetail: "explicit",
|
|
27148
|
+
createdByAgentId: currentAgentId,
|
|
27149
|
+
visibility,
|
|
27150
|
+
visibilityReader: reader
|
|
27151
|
+
});
|
|
27152
|
+
return {
|
|
27153
|
+
content: [{
|
|
27154
|
+
type: "text",
|
|
27155
|
+
text: `[UPDATED] Formation EVOLVE: evolved #${targetId} (${reason})${formationNote}`
|
|
27156
|
+
}]
|
|
27157
|
+
};
|
|
26587
27158
|
}
|
|
27159
|
+
} else if (action2 === "discard") {
|
|
27160
|
+
return {
|
|
27161
|
+
content: [{
|
|
27162
|
+
type: "text",
|
|
27163
|
+
text: `[SKIP] Formation DISCARD: ${reason}${formationNote}`
|
|
27164
|
+
}]
|
|
27165
|
+
};
|
|
26588
27166
|
}
|
|
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
27167
|
}
|
|
26635
|
-
|
|
26636
|
-
|
|
26637
|
-
|
|
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
|
-
}
|
|
26644
|
-
} catch {
|
|
26645
|
-
}
|
|
26646
|
-
const { observation: obs, upserted } = await storeObservation({
|
|
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;
|
|
27168
|
+
let compactAction = "";
|
|
27169
|
+
let compactMerged = false;
|
|
27170
|
+
if (!useFormation && !topicKey && !progress) {
|
|
26683
27171
|
try {
|
|
26684
|
-
const compactStart = Date.now();
|
|
26685
27172
|
const searchResult = await compactSearch({
|
|
26686
27173
|
query: `${title} ${narrative.substring(0, 200)}`,
|
|
26687
27174
|
limit: 5,
|
|
26688
27175
|
projectId: project.id,
|
|
26689
|
-
status: "active"
|
|
27176
|
+
status: "active",
|
|
27177
|
+
reader
|
|
26690
27178
|
});
|
|
26691
27179
|
const similarEntries = searchResult.entries.map((e) => e);
|
|
26692
27180
|
if (similarEntries.length > 0) {
|
|
26693
27181
|
const similarIds = similarEntries.map((e) => e.id);
|
|
26694
|
-
const details = await compactDetail(similarIds);
|
|
27182
|
+
const details = await compactDetail(similarIds, { reader });
|
|
26695
27183
|
const existingMemories = details.documents.map((d, i) => ({
|
|
26696
27184
|
id: d.observationId,
|
|
26697
27185
|
title: d.title,
|
|
@@ -26699,78 +27187,266 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
|
26699
27187
|
facts: d.facts,
|
|
26700
27188
|
score: similarEntries[i]?.score ?? 0
|
|
26701
27189
|
}));
|
|
26702
|
-
const decision = await
|
|
26703
|
-
|
|
26704
|
-
|
|
27190
|
+
const decision = await withTimeout2(
|
|
27191
|
+
compactOnWrite(
|
|
27192
|
+
{ title, narrative, facts: safeFacts ?? [] },
|
|
27193
|
+
existingMemories
|
|
27194
|
+
),
|
|
27195
|
+
COMPACT_ON_WRITE_TIMEOUT_MS,
|
|
27196
|
+
"Compact-on-write"
|
|
26705
27197
|
);
|
|
26706
|
-
|
|
26707
|
-
|
|
26708
|
-
|
|
26709
|
-
|
|
26710
|
-
|
|
26711
|
-
|
|
27198
|
+
if (decision.action === "UPDATE" && decision.targetId) {
|
|
27199
|
+
const targetObs = getObservation(decision.targetId);
|
|
27200
|
+
if (targetObs && targetObs.projectId === project.id && canReadObservation(targetObs, reader)) {
|
|
27201
|
+
await storeObservation({
|
|
27202
|
+
entityName: targetObs.entityName,
|
|
27203
|
+
type: targetObs.type,
|
|
27204
|
+
title: decision.mergedNarrative ? title : targetObs.title,
|
|
27205
|
+
narrative: decision.mergedNarrative ?? narrative,
|
|
27206
|
+
facts: decision.mergedFacts ?? safeFacts,
|
|
27207
|
+
filesModified: safeFiles,
|
|
27208
|
+
concepts: safeConcepts,
|
|
27209
|
+
projectId: project.id,
|
|
27210
|
+
topicKey: targetObs.topicKey,
|
|
27211
|
+
progress,
|
|
27212
|
+
sourceDetail: "explicit",
|
|
27213
|
+
createdByAgentId: currentAgentId,
|
|
27214
|
+
visibility,
|
|
27215
|
+
visibilityReader: reader
|
|
27216
|
+
});
|
|
27217
|
+
compactAction = `[UPDATED] Compact UPDATE: merged into #${decision.targetId} (${decision.reason})`;
|
|
27218
|
+
compactMerged = true;
|
|
27219
|
+
return {
|
|
27220
|
+
content: [{
|
|
27221
|
+
type: "text",
|
|
27222
|
+
text: `${compactAction}
|
|
27223
|
+
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
27224
|
+
}]
|
|
27225
|
+
};
|
|
27226
|
+
}
|
|
27227
|
+
} else if (decision.action === "NONE") {
|
|
27228
|
+
return {
|
|
27229
|
+
content: [{
|
|
27230
|
+
type: "text",
|
|
27231
|
+
text: `[SKIP] Compact SKIP: ${decision.reason}
|
|
27232
|
+
Existing memory #${decision.targetId} already covers this.
|
|
27233
|
+
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
27234
|
+
}]
|
|
27235
|
+
};
|
|
27236
|
+
} else if (decision.action === "DELETE" && decision.targetId) {
|
|
27237
|
+
const { resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27238
|
+
await resolveObservations2([decision.targetId], "resolved");
|
|
27239
|
+
compactAction = ` | Compact: resolved outdated #${decision.targetId}`;
|
|
27240
|
+
}
|
|
27241
|
+
if (decision.enrichedFacts && decision.enrichedFacts.length > 0) {
|
|
27242
|
+
const currentFacts = safeFacts ?? [];
|
|
27243
|
+
const newFacts = decision.enrichedFacts.filter((f) => !currentFacts.includes(f));
|
|
27244
|
+
if (newFacts.length > 0) {
|
|
27245
|
+
compactAction += ` | +${newFacts.length} LLM-extracted facts`;
|
|
27246
|
+
}
|
|
27247
|
+
}
|
|
26712
27248
|
}
|
|
26713
27249
|
} catch {
|
|
26714
27250
|
}
|
|
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
|
-
|
|
27251
|
+
}
|
|
27252
|
+
if (formationResult && formationResult.resolution.action === "new") {
|
|
27253
|
+
const llmFacts = formationResult.extraction.extractedFacts;
|
|
27254
|
+
if (llmFacts.length > 0) {
|
|
27255
|
+
const currentFacts = safeFacts ?? [];
|
|
27256
|
+
const currentLower = new Set(currentFacts.map((f) => f.toLowerCase().trim()));
|
|
27257
|
+
const newFacts = llmFacts.filter((f) => !currentLower.has(f.toLowerCase().trim()));
|
|
27258
|
+
if (newFacts.length > 0) {
|
|
27259
|
+
safeFacts = [...currentFacts, ...newFacts];
|
|
27260
|
+
}
|
|
27261
|
+
}
|
|
27262
|
+
if (formationResult.extraction.titleImproved && formationResult.title) {
|
|
27263
|
+
title = formationResult.title;
|
|
27264
|
+
}
|
|
27265
|
+
if (formationResult.extraction.typeCorrected && formationResult.type) {
|
|
27266
|
+
type = formationResult.type;
|
|
27267
|
+
}
|
|
27268
|
+
if (formationResult.extraction.entityResolved && formationResult.entityName) {
|
|
27269
|
+
entityName = formationResult.entityName;
|
|
27270
|
+
}
|
|
27271
|
+
}
|
|
27272
|
+
await graphManager.createEntities([
|
|
27273
|
+
{ name: entityName, entityType: "auto", observations: [] }
|
|
27274
|
+
]);
|
|
27275
|
+
let sessionId;
|
|
27276
|
+
try {
|
|
27277
|
+
const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
27278
|
+
const active = await getActiveSession2(projectDir2, project.id);
|
|
27279
|
+
if (active) sessionId = active.id;
|
|
27280
|
+
} catch {
|
|
27281
|
+
}
|
|
27282
|
+
let finalNarrative = narrative;
|
|
27283
|
+
let compressionNote = "";
|
|
27284
|
+
try {
|
|
27285
|
+
const { compressNarrative: compressNarrative2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
|
|
27286
|
+
const { compressed, saved, usedLLM } = await withTimeout2(
|
|
27287
|
+
compressNarrative2(narrative, safeFacts, type),
|
|
27288
|
+
COMPRESSION_TIMEOUT_MS,
|
|
27289
|
+
"Narrative compression"
|
|
26745
27290
|
);
|
|
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
|
-
});
|
|
27291
|
+
if (usedLLM && saved > 0) {
|
|
27292
|
+
finalNarrative = compressed;
|
|
27293
|
+
compressionNote = ` | compressed -${saved} tokens`;
|
|
26759
27294
|
}
|
|
26760
|
-
}
|
|
26761
|
-
|
|
27295
|
+
} catch {
|
|
27296
|
+
}
|
|
27297
|
+
let attributionWarning = "";
|
|
27298
|
+
try {
|
|
27299
|
+
const attrCheck = await checkProjectAttribution(
|
|
27300
|
+
entityName,
|
|
27301
|
+
project.id,
|
|
27302
|
+
filterReadableObservations(getAllObservations(), reader)
|
|
27303
|
+
);
|
|
27304
|
+
if (attrCheck.suspicious) {
|
|
27305
|
+
attributionWarning = `
|
|
27306
|
+
[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.`;
|
|
27307
|
+
}
|
|
27308
|
+
} catch {
|
|
27309
|
+
}
|
|
27310
|
+
const { observation: obs, upserted } = await storeObservation({
|
|
27311
|
+
entityName,
|
|
27312
|
+
type,
|
|
27313
|
+
title,
|
|
27314
|
+
narrative: finalNarrative,
|
|
27315
|
+
facts: safeFacts,
|
|
27316
|
+
filesModified: safeFiles,
|
|
27317
|
+
concepts: safeConcepts,
|
|
27318
|
+
projectId: project.id,
|
|
27319
|
+
topicKey,
|
|
27320
|
+
sessionId,
|
|
27321
|
+
progress,
|
|
27322
|
+
relatedCommits,
|
|
27323
|
+
relatedEntities,
|
|
27324
|
+
sourceDetail: "explicit",
|
|
27325
|
+
valueCategory: formationResult?.evaluation.category,
|
|
27326
|
+
createdByAgentId: currentAgentId,
|
|
27327
|
+
visibility,
|
|
27328
|
+
visibilityReader: reader
|
|
26762
27329
|
});
|
|
26763
|
-
|
|
26764
|
-
|
|
26765
|
-
|
|
26766
|
-
|
|
26767
|
-
|
|
26768
|
-
|
|
27330
|
+
await graphManager.addObservations([
|
|
27331
|
+
{ entityName, contents: [`[#${obs.id}] ${title}`] }
|
|
27332
|
+
]);
|
|
27333
|
+
const extracted = extractEntities([title, narrative, ...safeFacts ?? []].join(" "));
|
|
27334
|
+
const autoRelCount = await createAutoRelations(obs, extracted, graphManager);
|
|
27335
|
+
const enrichmentParts = [];
|
|
27336
|
+
const autoFiles = obs.filesModified.filter((f) => !(safeFiles ?? []).includes(f));
|
|
27337
|
+
const autoConcepts = obs.concepts.filter((c) => !(safeConcepts ?? []).includes(c));
|
|
27338
|
+
if (autoFiles.length > 0) enrichmentParts.push(`+${autoFiles.length} files extracted`);
|
|
27339
|
+
if (autoConcepts.length > 0) enrichmentParts.push(`+${autoConcepts.length} concepts enriched`);
|
|
27340
|
+
if (autoRelCount > 0) enrichmentParts.push(`+${autoRelCount} relations auto-created`);
|
|
27341
|
+
if (obs.hasCausalLanguage) enrichmentParts.push("causal language detected");
|
|
27342
|
+
if (upserted) enrichmentParts.push(`topic upserted (rev ${obs.revisionCount ?? 1})`);
|
|
27343
|
+
const enrichment = enrichmentParts.length > 0 ? `
|
|
27344
|
+
Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
27345
|
+
const action = upserted ? "[UPDATED] Updated" : "[OK] Stored";
|
|
27346
|
+
if (!useFormation && !topicKey && !progress) {
|
|
27347
|
+
const shadowFormation = async () => {
|
|
27348
|
+
let oldCompactDecision = null;
|
|
27349
|
+
try {
|
|
27350
|
+
const compactStart = Date.now();
|
|
27351
|
+
const searchResult = await compactSearch({
|
|
27352
|
+
query: `${title} ${narrative.substring(0, 200)}`,
|
|
27353
|
+
limit: 5,
|
|
27354
|
+
projectId: project.id,
|
|
27355
|
+
status: "active",
|
|
27356
|
+
reader
|
|
27357
|
+
});
|
|
27358
|
+
const similarEntries = searchResult.entries.map((e) => e);
|
|
27359
|
+
if (similarEntries.length > 0) {
|
|
27360
|
+
const similarIds = similarEntries.map((e) => e.id);
|
|
27361
|
+
const details = await compactDetail(similarIds, { reader });
|
|
27362
|
+
const existingMemories = details.documents.map((d, i) => ({
|
|
27363
|
+
id: d.observationId,
|
|
27364
|
+
title: d.title,
|
|
27365
|
+
narrative: d.narrative,
|
|
27366
|
+
facts: d.facts,
|
|
27367
|
+
score: similarEntries[i]?.score ?? 0
|
|
27368
|
+
}));
|
|
27369
|
+
const decision = await compactOnWrite(
|
|
27370
|
+
{ title, narrative, facts: safeFacts ?? [] },
|
|
27371
|
+
existingMemories
|
|
27372
|
+
);
|
|
27373
|
+
oldCompactDecision = {
|
|
27374
|
+
action: decision.action,
|
|
27375
|
+
targetId: decision.targetId,
|
|
27376
|
+
reason: decision.reason,
|
|
27377
|
+
durationMs: Date.now() - compactStart
|
|
27378
|
+
};
|
|
27379
|
+
}
|
|
27380
|
+
} catch {
|
|
27381
|
+
}
|
|
27382
|
+
const formationConfig = {
|
|
27383
|
+
mode: formationMode,
|
|
27384
|
+
useLLM: isLLMEnabled(),
|
|
27385
|
+
minValueScore: 0.3,
|
|
27386
|
+
searchMemories: async (q, limit, pid) => {
|
|
27387
|
+
const result = await compactSearch({ query: q, limit, projectId: pid, status: "active", reader });
|
|
27388
|
+
if (result.entries.length === 0) return [];
|
|
27389
|
+
const details = await compactDetail(result.entries.map((e) => e.id), { reader });
|
|
27390
|
+
return details.documents.map((d, i) => ({
|
|
27391
|
+
id: Number(d.id.replace("obs-", "")),
|
|
27392
|
+
observationId: d.observationId,
|
|
27393
|
+
title: d.title,
|
|
27394
|
+
narrative: d.narrative,
|
|
27395
|
+
facts: d.facts,
|
|
27396
|
+
entityName: d.entityName,
|
|
27397
|
+
type: d.type,
|
|
27398
|
+
score: result.entries[i]?.score ?? 0
|
|
27399
|
+
}));
|
|
27400
|
+
},
|
|
27401
|
+
getObservation: (id) => {
|
|
27402
|
+
const o = getObservation(id);
|
|
27403
|
+
if (!o || o.projectId !== project.id || !canReadObservation(o, reader)) return null;
|
|
27404
|
+
return { id: o.id, entityName: o.entityName, type: o.type, title: o.title, narrative: o.narrative, facts: o.facts, topicKey: o.topicKey };
|
|
27405
|
+
},
|
|
27406
|
+
getEntityNames: () => graphManager.getEntityNames()
|
|
27407
|
+
};
|
|
27408
|
+
const formed = await withTimeout2(
|
|
27409
|
+
runFormation({ entityName, type, title, narrative, facts: safeFacts, projectId: project.id, source: "explicit", topicKey }, formationConfig),
|
|
27410
|
+
FORMATION_TIMEOUT_MS,
|
|
27411
|
+
"Shadow formation"
|
|
27412
|
+
);
|
|
27413
|
+
const { recordBeforeAfterMetrics: recordBeforeAfterMetrics2 } = await Promise.resolve().then(() => (init_formation(), formation_exports));
|
|
27414
|
+
if (oldCompactDecision) {
|
|
27415
|
+
recordBeforeAfterMetrics2({
|
|
27416
|
+
formationAction: formed.resolution.action,
|
|
27417
|
+
formationTargetId: formed.resolution.targetId,
|
|
27418
|
+
oldCompactAction: oldCompactDecision.action,
|
|
27419
|
+
oldCompactTargetId: oldCompactDecision.targetId,
|
|
27420
|
+
oldCompactReason: oldCompactDecision.reason,
|
|
27421
|
+
formationValueScore: formed.evaluation.score,
|
|
27422
|
+
formationValueCategory: formed.evaluation.category,
|
|
27423
|
+
formationDurationMs: formed.pipeline.durationMs,
|
|
27424
|
+
compactDurationMs: oldCompactDecision.durationMs
|
|
27425
|
+
});
|
|
27426
|
+
}
|
|
27427
|
+
};
|
|
27428
|
+
shadowFormation().catch(() => {
|
|
27429
|
+
});
|
|
27430
|
+
}
|
|
27431
|
+
return {
|
|
27432
|
+
content: [
|
|
27433
|
+
{
|
|
27434
|
+
type: "text",
|
|
27435
|
+
text: `${action} observation #${obs.id} "${title}" (~${obs.tokens} tokens)
|
|
26769
27436
|
Entity: ${entityName} | Type: ${type} | Project: ${project.id}${obs.topicKey ? ` | Topic: ${obs.topicKey}` : ""}${compactAction}${compressionNote}${enrichment}${formationNote}${attributionWarning}`
|
|
26770
|
-
|
|
26771
|
-
|
|
27437
|
+
}
|
|
27438
|
+
]
|
|
27439
|
+
};
|
|
27440
|
+
});
|
|
27441
|
+
} catch (error) {
|
|
27442
|
+
return {
|
|
27443
|
+
content: [{
|
|
27444
|
+
type: "text",
|
|
27445
|
+
text: error instanceof Error ? error.message : "Failed to store memory."
|
|
27446
|
+
}],
|
|
27447
|
+
isError: true
|
|
26772
27448
|
};
|
|
26773
|
-
}
|
|
27449
|
+
}
|
|
26774
27450
|
}
|
|
26775
27451
|
);
|
|
26776
27452
|
server.registerTool(
|
|
@@ -26842,7 +27518,8 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
26842
27518
|
// Use scope: 'global' to explicitly search all projects.
|
|
26843
27519
|
projectId: scope === "global" ? void 0 : project.id,
|
|
26844
27520
|
status: status ?? "active",
|
|
26845
|
-
source
|
|
27521
|
+
source,
|
|
27522
|
+
reader: getObservationReader(scope === "global" ? "global" : "project")
|
|
26846
27523
|
});
|
|
26847
27524
|
const timeoutPromise = new Promise(
|
|
26848
27525
|
(_, reject) => setTimeout(() => reject(new Error(`Search timeout after ${TIMEOUT_MS}ms`)), TIMEOUT_MS)
|
|
@@ -26900,7 +27577,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26900
27577
|
const unresolved = requireResolvedProject("build graph context for the current project");
|
|
26901
27578
|
if (unresolved) return unresolved;
|
|
26902
27579
|
const { getObservationStore: getObservationStore2 } = await Promise.resolve().then(() => (init_obs_store(), obs_store_exports));
|
|
26903
|
-
const observations2 =
|
|
27580
|
+
const observations2 = filterReadableObservations(
|
|
27581
|
+
await getObservationStore2().loadByProject(project.id),
|
|
27582
|
+
getObservationReader()
|
|
27583
|
+
);
|
|
26904
27584
|
const packet = buildGraphContextPacket(observations2, {
|
|
26905
27585
|
projectId: project.id,
|
|
26906
27586
|
query,
|
|
@@ -26952,7 +27632,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26952
27632
|
Promise.resolve().then(() => (init_maintenance_jobs(), maintenance_jobs_exports)),
|
|
26953
27633
|
Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports))
|
|
26954
27634
|
]);
|
|
26955
|
-
const observations2 =
|
|
27635
|
+
const observations2 = filterReadableObservations(
|
|
27636
|
+
await getObservationStore2().loadByProject(project.id, { status: "active" }),
|
|
27637
|
+
getObservationReader()
|
|
27638
|
+
);
|
|
26956
27639
|
const context = await buildAutoProjectContext2({
|
|
26957
27640
|
project,
|
|
26958
27641
|
dataDir: projectDir2,
|
|
@@ -27042,7 +27725,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27042
27725
|
await store.init(projectDir2);
|
|
27043
27726
|
const codegraphConfig = getResolvedConfig2({ projectRoot: project.rootPath }).codegraph;
|
|
27044
27727
|
const exclude = codegraphConfig.excludePatterns;
|
|
27045
|
-
const observations2 =
|
|
27728
|
+
const observations2 = filterReadableObservations(
|
|
27729
|
+
await getObservationStore2().loadByProject(project.id, { status: "active" }),
|
|
27730
|
+
getObservationReader()
|
|
27731
|
+
);
|
|
27046
27732
|
observations2.reverse();
|
|
27047
27733
|
const basePack = assembleContextPackForTask2({
|
|
27048
27734
|
store,
|
|
@@ -27414,9 +28100,15 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27414
28100
|
}
|
|
27415
28101
|
},
|
|
27416
28102
|
async ({ ids, status }) => {
|
|
27417
|
-
const { resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28103
|
+
const { resolveObservations: resolveObservations2, getObservation: getObservation2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27418
28104
|
const safeIds = (Array.isArray(ids) ? ids : [ids]).map((id) => coerceNumber(id, 0)).filter((id) => id > 0);
|
|
27419
|
-
const
|
|
28105
|
+
const reader = getObservationReader();
|
|
28106
|
+
const authorizedIds = safeIds.filter((id) => {
|
|
28107
|
+
const observation = getObservation2(id, project.id);
|
|
28108
|
+
return observation ? canManageObservation(observation, reader) : false;
|
|
28109
|
+
});
|
|
28110
|
+
const result = await resolveObservations2(authorizedIds, status ?? "resolved");
|
|
28111
|
+
const deniedCount = safeIds.length - authorizedIds.length;
|
|
27420
28112
|
const parts = [];
|
|
27421
28113
|
if (result.resolved.length > 0) {
|
|
27422
28114
|
parts.push(`[OK] Resolved ${result.resolved.length} observation(s): #${result.resolved.join(", #")}`);
|
|
@@ -27424,6 +28116,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27424
28116
|
if (result.notFound.length > 0) {
|
|
27425
28117
|
parts.push(`[WARN] Not found: #${result.notFound.join(", #")}`);
|
|
27426
28118
|
}
|
|
28119
|
+
if (deniedCount > 0) {
|
|
28120
|
+
parts.push(`[WARN] Skipped ${deniedCount} observation(s) outside this session's write scope.`);
|
|
28121
|
+
}
|
|
27427
28122
|
parts.push('\nResolved memories are hidden from default search. Use status="all" to include them.');
|
|
27428
28123
|
parts.push('[STATS] Run `memorix_retention` with `action: "report"` to check remaining cleanup status.');
|
|
27429
28124
|
return {
|
|
@@ -27453,6 +28148,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27453
28148
|
async ({ entityName, decision, alternatives, rationale, constraints, expectedOutcome, risks, concepts, filesModified, relatedCommits, relatedEntities }) => {
|
|
27454
28149
|
const unresolved = requireResolvedProject("store reasoning in the current project");
|
|
27455
28150
|
if (unresolved) return unresolved;
|
|
28151
|
+
const reader = getObservationReader();
|
|
27456
28152
|
return withFreshIndex(async () => {
|
|
27457
28153
|
const narrativeParts = [rationale];
|
|
27458
28154
|
if (alternatives && alternatives.length > 0) {
|
|
@@ -27475,7 +28171,11 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27475
28171
|
]);
|
|
27476
28172
|
let reasoningAttributionWarning = "";
|
|
27477
28173
|
try {
|
|
27478
|
-
const attrCheck = await checkProjectAttribution(
|
|
28174
|
+
const attrCheck = await checkProjectAttribution(
|
|
28175
|
+
entityName,
|
|
28176
|
+
project.id,
|
|
28177
|
+
filterReadableObservations(getAllObservations(), reader)
|
|
28178
|
+
);
|
|
27479
28179
|
if (attrCheck.suspicious) {
|
|
27480
28180
|
reasoningAttributionWarning = `
|
|
27481
28181
|
[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 +28195,8 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27495
28195
|
relatedCommits,
|
|
27496
28196
|
relatedEntities,
|
|
27497
28197
|
sourceDetail: "explicit",
|
|
27498
|
-
createdByAgentId: currentAgentId
|
|
28198
|
+
createdByAgentId: currentAgentId,
|
|
28199
|
+
visibility: "project"
|
|
27499
28200
|
});
|
|
27500
28201
|
await graphManager.addObservations([
|
|
27501
28202
|
{ entityName, contents: [`[#${obs.id}] [REASONING] ${decision}`] }
|
|
@@ -27527,7 +28228,11 @@ Entity: ${entityName} | ${facts.length} facts | ${obs.tokens} tokens${reasoningA
|
|
|
27527
28228
|
const minCount = threshold ?? 2;
|
|
27528
28229
|
let entries;
|
|
27529
28230
|
try {
|
|
27530
|
-
entries = await auditProjectObservations(
|
|
28231
|
+
entries = await auditProjectObservations(
|
|
28232
|
+
project.id,
|
|
28233
|
+
await withFreshIndex(() => filterReadableObservations(getAllObservations(), getObservationReader())),
|
|
28234
|
+
minCount
|
|
28235
|
+
);
|
|
27531
28236
|
} catch (err) {
|
|
27532
28237
|
return {
|
|
27533
28238
|
content: [{
|
|
@@ -27592,7 +28297,8 @@ Entity: ${entityName} | ${facts.length} facts | ${obs.tokens} tokens${reasoningA
|
|
|
27592
28297
|
limit: safeLimit,
|
|
27593
28298
|
type: "reasoning",
|
|
27594
28299
|
projectId: scope === "global" ? void 0 : project.id,
|
|
27595
|
-
status: "active"
|
|
28300
|
+
status: "active",
|
|
28301
|
+
reader: getObservationReader(scope === "global" ? "global" : "project")
|
|
27596
28302
|
}));
|
|
27597
28303
|
if (result.entries.length === 0) {
|
|
27598
28304
|
return {
|
|
@@ -27617,7 +28323,11 @@ ${result.formatted}` }]
|
|
|
27617
28323
|
},
|
|
27618
28324
|
async ({ query, dryRun }) => {
|
|
27619
28325
|
const { getAllObservations: getAllObservations2, resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27620
|
-
const
|
|
28326
|
+
const reader = getObservationReader();
|
|
28327
|
+
const allObs = await withFreshIndex(() => filterReadableObservations(
|
|
28328
|
+
getAllObservations2().filter((o) => (o.status ?? "active") === "active" && o.projectId === project.id),
|
|
28329
|
+
reader
|
|
28330
|
+
).filter((observation) => canManageObservation(observation, reader)));
|
|
27621
28331
|
if (allObs.length < 2) {
|
|
27622
28332
|
return { content: [{ type: "text", text: "Not enough active memories to deduplicate." }] };
|
|
27623
28333
|
}
|
|
@@ -27631,7 +28341,7 @@ ${result.formatted}` }]
|
|
|
27631
28341
|
}
|
|
27632
28342
|
let candidates;
|
|
27633
28343
|
if (query) {
|
|
27634
|
-
const searchResult = await compactSearch({ query, limit: 20, projectId: project.id, status: "active" });
|
|
28344
|
+
const searchResult = await compactSearch({ query, limit: 20, projectId: project.id, status: "active", reader });
|
|
27635
28345
|
const idSet = new Set(searchResult.entries.map((e) => e.id));
|
|
27636
28346
|
candidates = allObs.filter((o) => idSet.has(o.id));
|
|
27637
28347
|
} else {
|
|
@@ -27721,7 +28431,8 @@ ${actions.join("\n")}`
|
|
|
27721
28431
|
safeAnchor,
|
|
27722
28432
|
project.id,
|
|
27723
28433
|
safeBefore,
|
|
27724
|
-
safeAfter
|
|
28434
|
+
safeAfter,
|
|
28435
|
+
getObservationReader()
|
|
27725
28436
|
);
|
|
27726
28437
|
return {
|
|
27727
28438
|
content: [
|
|
@@ -27755,12 +28466,14 @@ ${actions.join("\n")}`
|
|
|
27755
28466
|
const safeTypedRefs = coerceStringArray(typedRefs);
|
|
27756
28467
|
let result;
|
|
27757
28468
|
try {
|
|
28469
|
+
const hasCrossProjectRef = safeRefs.some((ref) => ref.projectId && ref.projectId !== project.id) || safeTypedRefs.some((ref) => ref.includes("@") && !ref.endsWith(`@${project.id}`));
|
|
28470
|
+
const reader = getObservationReader(hasCrossProjectRef ? "global" : "project");
|
|
27758
28471
|
if (safeTypedRefs.length > 0) {
|
|
27759
|
-
result = await compactDetail(safeTypedRefs);
|
|
28472
|
+
result = await compactDetail(safeTypedRefs, { reader });
|
|
27760
28473
|
} else if (safeRefs.length > 0) {
|
|
27761
|
-
result = await compactDetail(safeRefs);
|
|
28474
|
+
result = await compactDetail(safeRefs, { reader });
|
|
27762
28475
|
} else {
|
|
27763
|
-
result = await compactDetail(safeIds.map((id) => ({ id, projectId: project.id })));
|
|
28476
|
+
result = await compactDetail(safeIds.map((id) => ({ id, projectId: project.id })), { reader });
|
|
27764
28477
|
}
|
|
27765
28478
|
} catch (err) {
|
|
27766
28479
|
return { content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }], isError: true };
|
|
@@ -27786,11 +28499,15 @@ ${actions.join("\n")}`
|
|
|
27786
28499
|
},
|
|
27787
28500
|
async (args) => {
|
|
27788
28501
|
const action = args.action ?? "report";
|
|
27789
|
-
const { getRetentionSummary: getRetentionSummary2, getArchiveCandidates: getArchiveCandidates2, rankByRelevance: rankByRelevance2,
|
|
28502
|
+
const { getRetentionSummary: getRetentionSummary2, getArchiveCandidates: getArchiveCandidates2, rankByRelevance: rankByRelevance2, getRetentionZone: getRetentionZone2, explainRetention: explainRetention2 } = await Promise.resolve().then(() => (init_retention(), retention_exports));
|
|
27790
28503
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_orama_store(), orama_store_exports));
|
|
27791
28504
|
const { search: search2 } = await import("@orama/orama");
|
|
27792
|
-
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27793
|
-
const
|
|
28505
|
+
const { getAllObservations: getAllObservations2, resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28506
|
+
const reader = getObservationReader();
|
|
28507
|
+
const allObs = await withFreshIndex(() => filterReadableObservations(
|
|
28508
|
+
getAllObservations2().filter((observation) => observation.projectId === project.id),
|
|
28509
|
+
reader
|
|
28510
|
+
));
|
|
27794
28511
|
const accessMap = /* @__PURE__ */ new Map();
|
|
27795
28512
|
try {
|
|
27796
28513
|
const database = await getDb2();
|
|
@@ -27807,20 +28524,6 @@ ${actions.join("\n")}`
|
|
|
27807
28524
|
}
|
|
27808
28525
|
} catch {
|
|
27809
28526
|
}
|
|
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
28527
|
const docs = allObs.map((obs) => ({
|
|
27825
28528
|
id: `obs-${obs.id}`,
|
|
27826
28529
|
observationId: obs.id,
|
|
@@ -27839,13 +28542,34 @@ Archived memories are hidden from default search but can be found with status: "
|
|
|
27839
28542
|
status: obs.status ?? "active",
|
|
27840
28543
|
source: obs.source ?? "agent",
|
|
27841
28544
|
sourceDetail: obs.sourceDetail ?? "",
|
|
27842
|
-
valueCategory: obs.valueCategory ?? ""
|
|
28545
|
+
valueCategory: obs.valueCategory ?? "",
|
|
28546
|
+
admissionState: obs.admissionState ?? "",
|
|
28547
|
+
admissionReason: obs.admissionReason ?? "",
|
|
28548
|
+
visibility: obs.visibility ?? "project",
|
|
28549
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
28550
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
27843
28551
|
}));
|
|
27844
28552
|
if (docs.length === 0) {
|
|
27845
28553
|
return {
|
|
27846
28554
|
content: [{ type: "text", text: "No observations found for this project." }]
|
|
27847
28555
|
};
|
|
27848
28556
|
}
|
|
28557
|
+
if (action === "archive") {
|
|
28558
|
+
const managedIds = new Set(
|
|
28559
|
+
allObs.filter((observation) => canManageObservation(observation, reader)).map((observation) => observation.id)
|
|
28560
|
+
);
|
|
28561
|
+
const candidates2 = getArchiveCandidates2(docs).filter((document) => managedIds.has(document.observationId));
|
|
28562
|
+
if (candidates2.length === 0) {
|
|
28563
|
+
return {
|
|
28564
|
+
content: [{ type: "text", text: "[OK] No expired memories in this session's write scope to archive." }]
|
|
28565
|
+
};
|
|
28566
|
+
}
|
|
28567
|
+
const result = await resolveObservations2(candidates2.map((document) => document.observationId), "archived");
|
|
28568
|
+
return {
|
|
28569
|
+
content: [{ type: "text", text: `[ARCHIVED] Archived ${result.resolved.length} expired observation(s) in this session's write scope.
|
|
28570
|
+
${Math.max(0, managedIds.size - result.resolved.length)} visible writable observations remaining.` }]
|
|
28571
|
+
};
|
|
28572
|
+
}
|
|
27849
28573
|
if (action === "stale") {
|
|
27850
28574
|
const staleDocs = docs.filter((d) => getRetentionZone2(d) === "stale");
|
|
27851
28575
|
if (staleDocs.length === 0) {
|
|
@@ -28166,7 +28890,10 @@ Archived memories are hidden from default search but can be found with status: "
|
|
|
28166
28890
|
const allObs = await withFreshIndex(() => getAllObservations2());
|
|
28167
28891
|
const scoped = scopeKnowledgeGraphToProject(
|
|
28168
28892
|
graph,
|
|
28169
|
-
|
|
28893
|
+
filterReadableObservations(
|
|
28894
|
+
allObs.filter((observation) => observation.projectId === project.id),
|
|
28895
|
+
getObservationReader()
|
|
28896
|
+
)
|
|
28170
28897
|
);
|
|
28171
28898
|
return { entities: scoped.entities, relations: scoped.relations };
|
|
28172
28899
|
}
|
|
@@ -28441,7 +29168,10 @@ ${skill2.content}` }]
|
|
|
28441
29168
|
};
|
|
28442
29169
|
}
|
|
28443
29170
|
const { getObservationStore: getStore } = await Promise.resolve().then(() => (init_obs_store(), obs_store_exports));
|
|
28444
|
-
const allObs =
|
|
29171
|
+
const allObs = filterReadableObservations(
|
|
29172
|
+
(await getStore().loadAll()).filter((observation) => observation.projectId === project.id),
|
|
29173
|
+
getObservationReader()
|
|
29174
|
+
);
|
|
28445
29175
|
const obsData = allObs.map((o) => ({
|
|
28446
29176
|
id: o.id || 0,
|
|
28447
29177
|
entityName: o.entityName || "unknown",
|
|
@@ -28540,7 +29270,11 @@ ${skill2.content}` }]
|
|
|
28540
29270
|
}
|
|
28541
29271
|
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28542
29272
|
const allObs = await withFreshIndex(() => getAllObservations2());
|
|
28543
|
-
const
|
|
29273
|
+
const reader = getObservationReader();
|
|
29274
|
+
const matched = filterReadableObservations(
|
|
29275
|
+
allObs.filter((observation) => observation.projectId === project.id && observationIds.includes(observation.id)),
|
|
29276
|
+
reader
|
|
29277
|
+
);
|
|
28544
29278
|
if (matched.length === 0) {
|
|
28545
29279
|
return { content: [{ type: "text", text: `No observations found for IDs: [${observationIds.join(", ")}]. Use \`memorix_search\` to find valid IDs.` }], isError: true };
|
|
28546
29280
|
}
|
|
@@ -28548,6 +29282,13 @@ ${skill2.content}` }]
|
|
|
28548
29282
|
if (nonActive.length > 0) {
|
|
28549
29283
|
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
29284
|
}
|
|
29285
|
+
const nonProjectShared = matched.filter((observation) => resolveObservationVisibility(observation) !== "project");
|
|
29286
|
+
if (nonProjectShared.length > 0) {
|
|
29287
|
+
return {
|
|
29288
|
+
content: [{ type: "text", text: `Cannot promote private or team-scoped observations: ${nonProjectShared.map((observation) => `#${observation.id}`).join(", ")}. Promote only deliberate project-shared knowledge.` }],
|
|
29289
|
+
isError: true
|
|
29290
|
+
};
|
|
29291
|
+
}
|
|
28551
29292
|
const skill2 = await promoteToMiniSkill2(projectDir2, project.id, matched, { trigger, instruction, tags });
|
|
28552
29293
|
const lines = [
|
|
28553
29294
|
`[OK] Created mini-skill #${skill2.id}`,
|
|
@@ -28706,8 +29447,11 @@ Ensure the path points to a directory containing a .git folder (or a subdirector
|
|
|
28706
29447
|
const lastSeen = registeredAgent.last_seen_obs_generation;
|
|
28707
29448
|
const store = getObservationStore();
|
|
28708
29449
|
const currentGen = store.getGeneration();
|
|
28709
|
-
const projectObs = await withFreshIndex(() =>
|
|
28710
|
-
(
|
|
29450
|
+
const projectObs = await withFreshIndex(() => filterReadableObservations(
|
|
29451
|
+
getAllObservations().filter(
|
|
29452
|
+
(o) => o.projectId === project.id && (o.writeGeneration ?? 0) > lastSeen
|
|
29453
|
+
),
|
|
29454
|
+
getObservationReader()
|
|
28711
29455
|
));
|
|
28712
29456
|
const wm = computeWatermark2(lastSeen, currentGen, projectObs.length);
|
|
28713
29457
|
if (wm.newObservationCount > 0) {
|
|
@@ -28876,7 +29620,7 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
28876
29620
|
"memorix_transfer",
|
|
28877
29621
|
{
|
|
28878
29622
|
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).',
|
|
29623
|
+
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
29624
|
inputSchema: {
|
|
28881
29625
|
action: z2.enum(["export", "import"]).describe("Operation: export or import"),
|
|
28882
29626
|
format: z2.enum(["json", "markdown"]).optional().describe("Export format (for export, default: json)"),
|
|
@@ -28887,10 +29631,10 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
28887
29631
|
if (action === "export") {
|
|
28888
29632
|
const { exportAsJson: exportAsJson2, exportAsMarkdown: exportAsMarkdown2 } = await Promise.resolve().then(() => (init_export_import(), export_import_exports));
|
|
28889
29633
|
if (format === "markdown") {
|
|
28890
|
-
const md = await exportAsMarkdown2(projectDir2, project.id);
|
|
29634
|
+
const md = await exportAsMarkdown2(projectDir2, project.id, getObservationReader());
|
|
28891
29635
|
return { content: [{ type: "text", text: md }] };
|
|
28892
29636
|
}
|
|
28893
|
-
const data = await exportAsJson2(projectDir2, project.id);
|
|
29637
|
+
const data = await exportAsJson2(projectDir2, project.id, getObservationReader());
|
|
28894
29638
|
const json = JSON.stringify(data, null, 2);
|
|
28895
29639
|
return {
|
|
28896
29640
|
content: [{
|
|
@@ -29087,8 +29831,6 @@ ${json}
|
|
|
29087
29831
|
}
|
|
29088
29832
|
}
|
|
29089
29833
|
);
|
|
29090
|
-
let teamStore;
|
|
29091
|
-
let initTeamStoreForProject;
|
|
29092
29834
|
if (teamFeaturesEnabled) {
|
|
29093
29835
|
const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
|
|
29094
29836
|
initTeamStoreForProject = initTeamStore2;
|
|
@@ -29361,7 +30103,7 @@ ${lines.join("\n")}` }] };
|
|
|
29361
30103
|
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
30104
|
inputSchema: {
|
|
29363
30105
|
action: z2.enum(["send", "broadcast", "inbox"]).describe("Operation to perform"),
|
|
29364
|
-
from: z2.string().optional().describe("
|
|
30106
|
+
from: z2.string().optional().describe("Your sender agent ID. Omit it to use this session identity."),
|
|
29365
30107
|
to: z2.string().optional().describe("Receiver agent ID (for send)"),
|
|
29366
30108
|
type: z2.enum(["request", "response", "info", "announcement", "contract", "error", "handoff"]).optional().describe("Message type (for send/broadcast)"),
|
|
29367
30109
|
content: z2.string().optional().describe("Message content (for send/broadcast)"),
|
|
@@ -29372,13 +30114,20 @@ ${lines.join("\n")}` }] };
|
|
|
29372
30114
|
}
|
|
29373
30115
|
},
|
|
29374
30116
|
async ({ action, from, to, type: msgType, content, agentId, markRead, toRole, handoffStatus }) => {
|
|
30117
|
+
const requireCurrentTeamAgent = () => {
|
|
30118
|
+
if (!currentAgentId) return null;
|
|
30119
|
+
const agent = teamStore.getAgent(currentAgentId);
|
|
30120
|
+
return agent?.project_id === project.id && agent.status === "active" ? agent : null;
|
|
30121
|
+
};
|
|
29375
30122
|
if (action === "send") {
|
|
29376
|
-
|
|
30123
|
+
const sender = requireCurrentTeamAgent();
|
|
30124
|
+
if (!sender || !msgType || !content) return { content: [{ type: "text", text: "[ERROR] active session identity, type, and content required for send" }], isError: true };
|
|
30125
|
+
if (from && from !== currentAgentId) return { content: [{ type: "text", text: "[ERROR] from must match the current session identity" }], isError: true };
|
|
29377
30126
|
if (!to && !toRole) return { content: [{ type: "text", text: "[ERROR] either to (agent ID) or toRole is required for send" }], isError: true };
|
|
29378
30127
|
if (content.length > 1e4) return { content: [{ type: "text", text: "[ERROR] Message too large (max 10KB)" }], isError: true };
|
|
29379
30128
|
const msg = teamStore.sendMessage({
|
|
29380
30129
|
projectId: project.id,
|
|
29381
|
-
senderAgentId:
|
|
30130
|
+
senderAgentId: currentAgentId,
|
|
29382
30131
|
recipientAgentId: to ?? null,
|
|
29383
30132
|
type: msgType,
|
|
29384
30133
|
content,
|
|
@@ -29390,11 +30139,13 @@ ${lines.join("\n")}` }] };
|
|
|
29390
30139
|
return { content: [{ type: "text", text: `Message sent (${msgType}) to ${target} | ID: ${msg.id.slice(0, 8)}\u2026${toRole ? ` [role: ${toRole}]` : ""}` }] };
|
|
29391
30140
|
}
|
|
29392
30141
|
if (action === "broadcast") {
|
|
29393
|
-
|
|
30142
|
+
const sender = requireCurrentTeamAgent();
|
|
30143
|
+
if (!sender || !msgType || !content) return { content: [{ type: "text", text: "[ERROR] active session identity, type, and content required for broadcast" }], isError: true };
|
|
30144
|
+
if (from && from !== currentAgentId) return { content: [{ type: "text", text: "[ERROR] from must match the current session identity" }], isError: true };
|
|
29394
30145
|
if (content.length > 1e4) return { content: [{ type: "text", text: "[ERROR] Message too large (max 10KB)" }], isError: true };
|
|
29395
30146
|
const msg = teamStore.sendMessage({
|
|
29396
30147
|
projectId: project.id,
|
|
29397
|
-
senderAgentId:
|
|
30148
|
+
senderAgentId: currentAgentId,
|
|
29398
30149
|
recipientAgentId: null,
|
|
29399
30150
|
type: msgType,
|
|
29400
30151
|
content
|
|
@@ -29402,8 +30153,12 @@ ${lines.join("\n")}` }] };
|
|
|
29402
30153
|
if ("error" in msg) return { content: [{ type: "text", text: `[ERROR] ${msg.error}` }], isError: true };
|
|
29403
30154
|
return { content: [{ type: "text", text: `Broadcast (${msgType}) | ID: ${msg.id.slice(0, 8)}\u2026` }] };
|
|
29404
30155
|
}
|
|
29405
|
-
const
|
|
29406
|
-
if (!
|
|
30156
|
+
const inboxAgent = requireCurrentTeamAgent();
|
|
30157
|
+
if (!inboxAgent) return { content: [{ type: "text", text: "[ERROR] active session identity required for inbox" }], isError: true };
|
|
30158
|
+
if (agentId && agentId !== currentAgentId || from && from !== currentAgentId) {
|
|
30159
|
+
return { content: [{ type: "text", text: "[ERROR] inbox access is limited to the current session identity" }], isError: true };
|
|
30160
|
+
}
|
|
30161
|
+
const inboxId = currentAgentId;
|
|
29407
30162
|
const inbox = teamStore.getInbox(project.id, inboxId);
|
|
29408
30163
|
const unread = teamStore.getUnreadCount(project.id, inboxId);
|
|
29409
30164
|
if (inbox.length === 0) return { content: [{ type: "text", text: "Inbox empty" }] };
|
|
@@ -29431,24 +30186,34 @@ ${lines.join("\n")}` }] };
|
|
|
29431
30186
|
},
|
|
29432
30187
|
async ({ agentId, markInboxRead }) => {
|
|
29433
30188
|
const { computeWatermark: computeWatermark2, computePoll: computePoll2 } = await Promise.resolve().then(() => (init_poll(), poll_exports));
|
|
30189
|
+
if (agentId && agentId !== currentAgentId) {
|
|
30190
|
+
return {
|
|
30191
|
+
content: [{ type: "text", text: "[ERROR] agentId must match the current session identity." }],
|
|
30192
|
+
isError: true
|
|
30193
|
+
};
|
|
30194
|
+
}
|
|
30195
|
+
const effectiveAgentId = currentAgentId;
|
|
29434
30196
|
let watermark = computeWatermark2(0, 0, 0);
|
|
29435
|
-
if (
|
|
29436
|
-
const agent = teamStore.getAgent(
|
|
30197
|
+
if (effectiveAgentId) {
|
|
30198
|
+
const agent = teamStore.getAgent(effectiveAgentId);
|
|
29437
30199
|
if (agent) {
|
|
29438
30200
|
const lastSeen = agent.last_seen_obs_generation;
|
|
29439
30201
|
const store = getObservationStore();
|
|
29440
30202
|
const currentGen = store.getGeneration();
|
|
29441
|
-
const projectObs = await withFreshIndex(() =>
|
|
29442
|
-
(
|
|
30203
|
+
const projectObs = await withFreshIndex(() => filterReadableObservations(
|
|
30204
|
+
getAllObservations().filter(
|
|
30205
|
+
(o) => o.projectId === project.id && (o.writeGeneration ?? 0) > lastSeen
|
|
30206
|
+
),
|
|
30207
|
+
getObservationReader()
|
|
29443
30208
|
));
|
|
29444
30209
|
watermark = computeWatermark2(lastSeen, currentGen, projectObs.length);
|
|
29445
|
-
teamStore.updateWatermark(
|
|
29446
|
-
teamStore.heartbeat(
|
|
30210
|
+
teamStore.updateWatermark(effectiveAgentId, currentGen);
|
|
30211
|
+
teamStore.heartbeat(effectiveAgentId);
|
|
29447
30212
|
}
|
|
29448
30213
|
}
|
|
29449
|
-
const poll = computePoll2(teamStore, project.id,
|
|
29450
|
-
if (markInboxRead &&
|
|
29451
|
-
teamStore.markAllRead(project.id,
|
|
30214
|
+
const poll = computePoll2(teamStore, project.id, effectiveAgentId ?? null, watermark);
|
|
30215
|
+
if (markInboxRead && effectiveAgentId) {
|
|
30216
|
+
teamStore.markAllRead(project.id, effectiveAgentId);
|
|
29452
30217
|
}
|
|
29453
30218
|
const lines = [];
|
|
29454
30219
|
if (poll.agent) {
|
|
@@ -29508,7 +30273,7 @@ ${lines.join("\n")}` }] };
|
|
|
29508
30273
|
"memorix_handoff",
|
|
29509
30274
|
{
|
|
29510
30275
|
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 (
|
|
30276
|
+
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
30277
|
inputSchema: {
|
|
29513
30278
|
fromAgentId: z2.string().describe("Your agent ID (from team_manage join or session_start with joinTeam=true)"),
|
|
29514
30279
|
summary: z2.string().describe("Human-readable summary of what you did and what needs to happen next"),
|
|
@@ -29521,6 +30286,34 @@ ${lines.join("\n")}` }] };
|
|
|
29521
30286
|
},
|
|
29522
30287
|
async ({ fromAgentId, summary, context, toAgentId, taskId, filesModified, concepts }) => {
|
|
29523
30288
|
const { createHandoffArtifact: createHandoffArtifact2 } = await Promise.resolve().then(() => (init_handoff(), handoff_exports));
|
|
30289
|
+
if (!currentAgentId) {
|
|
30290
|
+
return {
|
|
30291
|
+
content: [{ type: "text", text: "Create a coordination identity first: call memorix_session_start with joinTeam=true." }],
|
|
30292
|
+
isError: true
|
|
30293
|
+
};
|
|
30294
|
+
}
|
|
30295
|
+
if (fromAgentId !== currentAgentId) {
|
|
30296
|
+
return {
|
|
30297
|
+
content: [{ type: "text", text: "fromAgentId must match the identity returned for this session. Memorix will not create a handoff on behalf of another agent." }],
|
|
30298
|
+
isError: true
|
|
30299
|
+
};
|
|
30300
|
+
}
|
|
30301
|
+
const sender = teamStore.getAgent(currentAgentId);
|
|
30302
|
+
if (!sender || sender.project_id !== project.id || sender.status !== "active") {
|
|
30303
|
+
return {
|
|
30304
|
+
content: [{ type: "text", text: "The current coordination identity is not an active member of this project." }],
|
|
30305
|
+
isError: true
|
|
30306
|
+
};
|
|
30307
|
+
}
|
|
30308
|
+
if (toAgentId) {
|
|
30309
|
+
const recipient = teamStore.getAgent(toAgentId);
|
|
30310
|
+
if (!recipient || recipient.project_id !== project.id) {
|
|
30311
|
+
return {
|
|
30312
|
+
content: [{ type: "text", text: "The handoff recipient must be an agent registered in the current project." }],
|
|
30313
|
+
isError: true
|
|
30314
|
+
};
|
|
30315
|
+
}
|
|
30316
|
+
}
|
|
29524
30317
|
const result = await createHandoffArtifact2(
|
|
29525
30318
|
{
|
|
29526
30319
|
projectId: project.id,
|
|
@@ -29760,6 +30553,8 @@ var MemoryClient = class {
|
|
|
29760
30553
|
_projectId;
|
|
29761
30554
|
_projectRoot;
|
|
29762
30555
|
_dataDir;
|
|
30556
|
+
/** SDK calls are unbound by default, so they see only project-shared records. */
|
|
30557
|
+
_reader;
|
|
29763
30558
|
_closed = false;
|
|
29764
30559
|
// Internal module references — loaded lazily to avoid top-level side effects
|
|
29765
30560
|
_observations;
|
|
@@ -29771,6 +30566,7 @@ var MemoryClient = class {
|
|
|
29771
30566
|
this._projectId = projectId;
|
|
29772
30567
|
this._projectRoot = projectRoot;
|
|
29773
30568
|
this._dataDir = dataDir;
|
|
30569
|
+
this._reader = { projectId };
|
|
29774
30570
|
}
|
|
29775
30571
|
/** The canonical project ID (derived from Git remote or local path) */
|
|
29776
30572
|
get projectId() {
|
|
@@ -29829,7 +30625,8 @@ var MemoryClient = class {
|
|
|
29829
30625
|
this._ensureOpen();
|
|
29830
30626
|
return this._observations.storeObservation({
|
|
29831
30627
|
...input,
|
|
29832
|
-
projectId: this._projectId
|
|
30628
|
+
projectId: this._projectId,
|
|
30629
|
+
visibilityReader: this._reader
|
|
29833
30630
|
});
|
|
29834
30631
|
}
|
|
29835
30632
|
/**
|
|
@@ -29853,7 +30650,8 @@ var MemoryClient = class {
|
|
|
29853
30650
|
limit: options.limit ?? 20,
|
|
29854
30651
|
type: options.type,
|
|
29855
30652
|
source: options.source,
|
|
29856
|
-
status: options.status === "all" ? void 0 : options.status ?? "active"
|
|
30653
|
+
status: options.status === "all" ? void 0 : options.status ?? "active",
|
|
30654
|
+
reader: this._reader
|
|
29857
30655
|
};
|
|
29858
30656
|
return this._oramaStore.searchObservations(searchOpts);
|
|
29859
30657
|
}
|
|
@@ -29864,7 +30662,8 @@ var MemoryClient = class {
|
|
|
29864
30662
|
this._ensureOpen();
|
|
29865
30663
|
await this._freshness.withFreshIndex(() => {
|
|
29866
30664
|
});
|
|
29867
|
-
|
|
30665
|
+
const observation = this._observations.getObservation(id, this._projectId);
|
|
30666
|
+
return observation && canReadObservation(observation, this._reader) ? observation : void 0;
|
|
29868
30667
|
}
|
|
29869
30668
|
/**
|
|
29870
30669
|
* Get all observations for this project.
|
|
@@ -29873,7 +30672,10 @@ var MemoryClient = class {
|
|
|
29873
30672
|
this._ensureOpen();
|
|
29874
30673
|
await this._freshness.withFreshIndex(() => {
|
|
29875
30674
|
});
|
|
29876
|
-
return
|
|
30675
|
+
return filterReadableObservations(
|
|
30676
|
+
this._observations.getProjectObservations(this._projectId),
|
|
30677
|
+
this._reader
|
|
30678
|
+
);
|
|
29877
30679
|
}
|
|
29878
30680
|
/**
|
|
29879
30681
|
* Get the total observation count for this project.
|
|
@@ -29882,7 +30684,10 @@ var MemoryClient = class {
|
|
|
29882
30684
|
this._ensureOpen();
|
|
29883
30685
|
await this._freshness.withFreshIndex(() => {
|
|
29884
30686
|
});
|
|
29885
|
-
return
|
|
30687
|
+
return filterReadableObservations(
|
|
30688
|
+
this._observations.getProjectObservations(this._projectId),
|
|
30689
|
+
this._reader
|
|
30690
|
+
).length;
|
|
29886
30691
|
}
|
|
29887
30692
|
/**
|
|
29888
30693
|
* Mark observations as resolved or archived.
|
|
@@ -29898,7 +30703,16 @@ var MemoryClient = class {
|
|
|
29898
30703
|
*/
|
|
29899
30704
|
async resolve(ids, status = "resolved") {
|
|
29900
30705
|
this._ensureOpen();
|
|
29901
|
-
|
|
30706
|
+
const manageableIds = ids.filter((id) => {
|
|
30707
|
+
const observation = this._observations.getObservation(id, this._projectId);
|
|
30708
|
+
return observation && canManageObservation(observation, this._reader);
|
|
30709
|
+
});
|
|
30710
|
+
const deniedIds = ids.filter((id) => !manageableIds.includes(id));
|
|
30711
|
+
const result = await this._observations.resolveObservations(manageableIds, status);
|
|
30712
|
+
return {
|
|
30713
|
+
resolved: result.resolved,
|
|
30714
|
+
notFound: [.../* @__PURE__ */ new Set([...result.notFound, ...deniedIds])]
|
|
30715
|
+
};
|
|
29902
30716
|
}
|
|
29903
30717
|
/**
|
|
29904
30718
|
* Release resources (close SQLite handle, reset index).
|