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