memorix 1.2.0 → 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 +30 -1
- package/README.md +18 -4
- package/README.zh-CN.md +18 -4
- package/TEAM.md +86 -86
- package/dist/cli/index.js +15919 -14055
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +1997 -1021
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.d.ts +1 -1
- package/dist/maintenance-runner.js +8481 -8005
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +30 -1
- package/dist/sdk.d.ts +7 -2
- package/dist/sdk.js +2022 -1024
- 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 +27 -5
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/DEVELOPMENT.md +4 -0
- package/docs/README.md +1 -1
- package/docs/SETUP.md +7 -1
- package/docs/dev-log/progress.txt +91 -11
- package/docs/knowledge/workflows/memorix-release.md +57 -0
- 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 +17 -8
- 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/knowledge.ts +40 -0
- 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 +22 -43
- package/src/cli/commands/serve-shared.ts +118 -118
- package/src/cli/commands/session.ts +29 -3
- package/src/cli/commands/setup.ts +9 -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 +34 -17
- package/src/codegraph/context-pack.ts +1 -0
- package/src/codegraph/current-facts.ts +19 -1
- package/src/codegraph/project-context.ts +2 -0
- package/src/codegraph/task-lens.ts +49 -5
- 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 +58 -52
- 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/claims.ts +51 -1
- package/src/knowledge/context-assembly.ts +97 -0
- package/src/knowledge/types.ts +1 -0
- package/src/knowledge/workflows.ts +34 -3
- 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/auto-relations.ts +21 -0
- 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-scope.ts +46 -0
- 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/verify-gate.ts +33 -10
- 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 +334 -93
- package/src/skills/mini-skills.ts +386 -386
- package/src/store/chat-store.ts +119 -119
- package/src/store/graph-store.ts +249 -249
- package/src/store/mini-skill-store.ts +349 -349
- package/src/store/orama-store.ts +61 -6
- package/src/store/persistence-json.ts +212 -212
- package/src/store/persistence.ts +291 -291
- package/src/store/project-affinity.ts +195 -195
- package/src/store/sqlite-db.ts +23 -1
- package/src/store/sqlite-store.ts +12 -2
- package/src/team/event-bus.ts +76 -76
- package/src/team/file-locks.ts +173 -173
- package/src/team/handoff.ts +168 -161
- package/src/team/messages.ts +203 -203
- package/src/team/poll.ts +132 -132
- package/src/team/tasks.ts +211 -211
- package/src/types.ts +51 -0
- package/src/wiki/generator.ts +2 -0
- package/src/workspace/mcp-adapters/codex.ts +191 -191
- package/src/workspace/mcp-adapters/copilot.ts +105 -105
- package/src/workspace/mcp-adapters/cursor.ts +53 -53
- package/src/workspace/mcp-adapters/kiro.ts +64 -64
- package/src/workspace/mcp-adapters/opencode.ts +123 -123
- package/src/workspace/mcp-adapters/trae.ts +134 -134
- package/src/workspace/mcp-adapters/windsurf.ts +91 -91
- package/src/workspace/sanitizer.ts +60 -60
- package/src/workspace/workflow-sync.ts +131 -131
package/dist/sdk.js
CHANGED
|
@@ -32,6 +32,58 @@ var init_esm_shims = __esm({
|
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
+
// src/memory/visibility.ts
|
|
36
|
+
function resolveObservationVisibility(record) {
|
|
37
|
+
switch (record.visibility) {
|
|
38
|
+
case "personal":
|
|
39
|
+
case "team":
|
|
40
|
+
case "project":
|
|
41
|
+
return record.visibility;
|
|
42
|
+
default:
|
|
43
|
+
return "project";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function sharedAgentIds(record) {
|
|
47
|
+
if (Array.isArray(record.sharedWithAgentIds)) return record.sharedWithAgentIds;
|
|
48
|
+
if (typeof record.sharedWithAgentIds !== "string" || !record.sharedWithAgentIds) return [];
|
|
49
|
+
try {
|
|
50
|
+
const parsed = JSON.parse(record.sharedWithAgentIds);
|
|
51
|
+
return Array.isArray(parsed) ? parsed.filter((id) => typeof id === "string") : [];
|
|
52
|
+
} catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function canReadObservation(record, reader) {
|
|
57
|
+
if (!reader) return true;
|
|
58
|
+
const sameProject = reader.projectId === record.projectId;
|
|
59
|
+
const visibility = resolveObservationVisibility(record);
|
|
60
|
+
if (visibility === "project") return !reader.projectId || sameProject;
|
|
61
|
+
if (!sameProject || !reader.agentId) return false;
|
|
62
|
+
if (visibility === "team") return reader.isTeamMember === true;
|
|
63
|
+
return record.createdByAgentId === reader.agentId || sharedAgentIds(record).includes(reader.agentId);
|
|
64
|
+
}
|
|
65
|
+
function canManageObservation(record, reader) {
|
|
66
|
+
if (!reader) return true;
|
|
67
|
+
if (!reader.projectId || reader.projectId !== record.projectId) return false;
|
|
68
|
+
switch (resolveObservationVisibility(record)) {
|
|
69
|
+
case "project":
|
|
70
|
+
return true;
|
|
71
|
+
case "team":
|
|
72
|
+
return reader.isTeamMember === true;
|
|
73
|
+
case "personal":
|
|
74
|
+
return Boolean(reader.agentId && record.createdByAgentId === reader.agentId);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function filterReadableObservations(observations2, reader) {
|
|
78
|
+
return reader ? observations2.filter((observation) => canReadObservation(observation, reader)) : [...observations2];
|
|
79
|
+
}
|
|
80
|
+
var init_visibility = __esm({
|
|
81
|
+
"src/memory/visibility.ts"() {
|
|
82
|
+
"use strict";
|
|
83
|
+
init_esm_shims();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
35
87
|
// src/store/bun-sqlite-compat.ts
|
|
36
88
|
import { createRequire } from "module";
|
|
37
89
|
function loadSqlite() {
|
|
@@ -208,7 +260,11 @@ CREATE TABLE IF NOT EXISTS observations (
|
|
|
208
260
|
relatedCommits TEXT,
|
|
209
261
|
relatedEntities TEXT,
|
|
210
262
|
sourceDetail TEXT,
|
|
211
|
-
valueCategory TEXT
|
|
263
|
+
valueCategory TEXT,
|
|
264
|
+
admissionState TEXT,
|
|
265
|
+
admissionReason TEXT,
|
|
266
|
+
visibility TEXT,
|
|
267
|
+
sharedWithAgentIds TEXT
|
|
212
268
|
);
|
|
213
269
|
`;
|
|
214
270
|
CREATE_MINI_SKILLS_TABLE = `
|
|
@@ -631,6 +687,8 @@ CREATE INDEX IF NOT EXISTS idx_observations_projectId ON observations(projectId)
|
|
|
631
687
|
CREATE INDEX IF NOT EXISTS idx_observations_topicKey ON observations(projectId, topicKey);
|
|
632
688
|
CREATE INDEX IF NOT EXISTS idx_observations_status ON observations(status);
|
|
633
689
|
CREATE INDEX IF NOT EXISTS idx_observations_project_status_id ON observations(projectId, status, id);
|
|
690
|
+
CREATE INDEX IF NOT EXISTS idx_observations_project_admission ON observations(projectId, status, admissionState, id);
|
|
691
|
+
CREATE INDEX IF NOT EXISTS idx_observations_project_visibility ON observations(projectId, status, visibility, id);
|
|
634
692
|
CREATE INDEX IF NOT EXISTS idx_mini_skills_projectId ON mini_skills(projectId);
|
|
635
693
|
CREATE INDEX IF NOT EXISTS idx_sessions_projectId ON sessions(projectId);
|
|
636
694
|
CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(projectId, status);
|
|
@@ -675,6 +733,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
|
675
733
|
WHERE status IN ('pending', 'running', 'retry');
|
|
676
734
|
`;
|
|
677
735
|
SCHEMA_MIGRATIONS = [
|
|
736
|
+
{
|
|
737
|
+
id: "1.2.2-observation-admission",
|
|
738
|
+
apply: (db2) => {
|
|
739
|
+
addColumnIfMissing(db2, "observations", "admissionState", "admissionState TEXT");
|
|
740
|
+
addColumnIfMissing(db2, "observations", "admissionReason", "admissionReason TEXT");
|
|
741
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_observations_project_admission ON observations(projectId, status, admissionState, id)");
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
id: "1.2.2-observation-visibility",
|
|
746
|
+
apply: (db2) => {
|
|
747
|
+
addColumnIfMissing(db2, "observations", "visibility", "visibility TEXT");
|
|
748
|
+
addColumnIfMissing(db2, "observations", "sharedWithAgentIds", "sharedWithAgentIds TEXT");
|
|
749
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_observations_project_visibility ON observations(projectId, status, visibility, id)");
|
|
750
|
+
}
|
|
751
|
+
},
|
|
678
752
|
{
|
|
679
753
|
id: "1.2-code-state-snapshots",
|
|
680
754
|
apply: (db2) => {
|
|
@@ -5349,7 +5423,8 @@ __export(orama_store_exports, {
|
|
|
5349
5423
|
makeOramaObservationId: () => makeOramaObservationId,
|
|
5350
5424
|
removeObservation: () => removeObservation,
|
|
5351
5425
|
resetDb: () => resetDb,
|
|
5352
|
-
searchObservations: () => searchObservations
|
|
5426
|
+
searchObservations: () => searchObservations,
|
|
5427
|
+
updateObservationMetadata: () => updateObservationMetadata
|
|
5353
5428
|
});
|
|
5354
5429
|
import { create, insert as insert2, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
5355
5430
|
function getLastSearchMode(projectId) {
|
|
@@ -5427,6 +5502,11 @@ async function initializeDb(options, generation) {
|
|
|
5427
5502
|
source: "string",
|
|
5428
5503
|
sourceDetail: "string",
|
|
5429
5504
|
valueCategory: "string",
|
|
5505
|
+
admissionState: "string",
|
|
5506
|
+
admissionReason: "string",
|
|
5507
|
+
visibility: "string",
|
|
5508
|
+
createdByAgentId: "string",
|
|
5509
|
+
sharedWithAgentIds: "string",
|
|
5430
5510
|
documentType: "string",
|
|
5431
5511
|
knowledgeLayer: "string"
|
|
5432
5512
|
};
|
|
@@ -5565,6 +5645,13 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
5565
5645
|
lastAccessedAt: obs.lastAccessedAt || "",
|
|
5566
5646
|
status: obs.status ?? "active",
|
|
5567
5647
|
source: obs.source || "agent",
|
|
5648
|
+
sourceDetail: obs.sourceDetail ?? "",
|
|
5649
|
+
valueCategory: obs.valueCategory ?? "",
|
|
5650
|
+
admissionState: obs.admissionState ?? "",
|
|
5651
|
+
admissionReason: obs.admissionReason ?? "",
|
|
5652
|
+
visibility: obs.visibility ?? "project",
|
|
5653
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
5654
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
5568
5655
|
documentType: "observation",
|
|
5569
5656
|
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
5570
5657
|
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
@@ -5598,6 +5685,16 @@ async function insertObservation(doc) {
|
|
|
5598
5685
|
await insert2(database, doc);
|
|
5599
5686
|
rememberObservationDoc(doc);
|
|
5600
5687
|
}
|
|
5688
|
+
async function updateObservationMetadata(projectId, observationId2, patch) {
|
|
5689
|
+
const database = await getDb();
|
|
5690
|
+
const id = makeOramaObservationId(projectId, observationId2);
|
|
5691
|
+
const existing = getByID(database, id);
|
|
5692
|
+
if (!existing) return false;
|
|
5693
|
+
const next = { ...existing, ...patch };
|
|
5694
|
+
await update(database, id, next);
|
|
5695
|
+
rememberObservationDoc(next);
|
|
5696
|
+
return true;
|
|
5697
|
+
}
|
|
5601
5698
|
async function removeObservation(oramaId) {
|
|
5602
5699
|
const database = await getDb();
|
|
5603
5700
|
await remove2(database, oramaId);
|
|
@@ -5751,7 +5848,7 @@ async function searchObservations(options) {
|
|
|
5751
5848
|
if (!projectIds) return true;
|
|
5752
5849
|
const doc = hit.document;
|
|
5753
5850
|
return projectIds.includes(doc.projectId);
|
|
5754
|
-
}).filter((hit) => {
|
|
5851
|
+
}).filter((hit) => canReadObservation(hit.document, options.reader)).filter((hit) => {
|
|
5755
5852
|
if (statusFilter === "all") return true;
|
|
5756
5853
|
const doc = hit.document;
|
|
5757
5854
|
return (doc.status || "active") === statusFilter;
|
|
@@ -5782,6 +5879,8 @@ async function searchObservations(options) {
|
|
|
5782
5879
|
source: doc.source || "agent",
|
|
5783
5880
|
sourceDetail: doc.sourceDetail || void 0,
|
|
5784
5881
|
valueCategory: doc.valueCategory || void 0,
|
|
5882
|
+
admissionState: doc.admissionState || void 0,
|
|
5883
|
+
visibility: doc.visibility || void 0,
|
|
5785
5884
|
entityName: doc.entityName || void 0,
|
|
5786
5885
|
documentType: doc.documentType || "observation",
|
|
5787
5886
|
knowledgeLayer: doc.knowledgeLayer || "project-truth",
|
|
@@ -5825,6 +5924,12 @@ async function searchObservations(options) {
|
|
|
5825
5924
|
score: isCommandStyleEntry(entry.title) ? entry.score * 0.3 : entry.score
|
|
5826
5925
|
}));
|
|
5827
5926
|
}
|
|
5927
|
+
if (hasQuery) {
|
|
5928
|
+
const qualifiedEntries = intermediate.filter(
|
|
5929
|
+
(entry) => entry.admissionState !== "candidate" && entry.admissionState !== "ephemeral"
|
|
5930
|
+
);
|
|
5931
|
+
if (qualifiedEntries.length > 0) intermediate = qualifiedEntries;
|
|
5932
|
+
}
|
|
5828
5933
|
if (intentResult?.preferChronological) {
|
|
5829
5934
|
intermediate.sort((a, b) => new Date(b.rawTime).getTime() - new Date(a.rawTime).getTime());
|
|
5830
5935
|
} else {
|
|
@@ -5968,7 +6073,8 @@ async function searchObservations(options) {
|
|
|
5968
6073
|
}
|
|
5969
6074
|
let entries = intermediate.map(({ rawTime: _, _isCommandLog: _c, ...rest }) => rest);
|
|
5970
6075
|
for (const hit of results.hits) {
|
|
5971
|
-
|
|
6076
|
+
const doc = hit.document;
|
|
6077
|
+
if (canReadObservation(doc, options.reader)) rememberObservationDoc(doc);
|
|
5972
6078
|
}
|
|
5973
6079
|
if (hasQuery && originalQuery) {
|
|
5974
6080
|
const queryLower = originalQuery.toLowerCase();
|
|
@@ -6012,7 +6118,8 @@ async function searchObservations(options) {
|
|
|
6012
6118
|
entries = applyTokenBudget(entries, options.maxTokens);
|
|
6013
6119
|
}
|
|
6014
6120
|
if (options.trackAccess !== false) {
|
|
6015
|
-
const
|
|
6121
|
+
const returnedKeys = new Set(entries.map((entry) => makeEntryKey(entry.projectId, entry.id)));
|
|
6122
|
+
const hitDocs = results.hits.map((hit) => ({ id: hit.id, doc: hit.document })).filter(({ doc }) => returnedKeys.has(makeEntryKey(doc.projectId, doc.observationId)));
|
|
6016
6123
|
recordAccessBatch(hitDocs).catch(() => {
|
|
6017
6124
|
});
|
|
6018
6125
|
}
|
|
@@ -6044,11 +6151,11 @@ async function getObservationsByIds(ids, projectId) {
|
|
|
6044
6151
|
}
|
|
6045
6152
|
return results;
|
|
6046
6153
|
}
|
|
6047
|
-
async function getTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3) {
|
|
6154
|
+
async function getTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3, reader) {
|
|
6048
6155
|
const { withFreshIndex: withFreshIndex2 } = await Promise.resolve().then(() => (init_freshness(), freshness_exports));
|
|
6049
6156
|
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
6050
6157
|
const rawObs = await withFreshIndex2(() => getAllObservations2());
|
|
6051
|
-
const allObs = projectId ? rawObs.filter((o) => o.projectId === projectId) : rawObs;
|
|
6158
|
+
const allObs = (projectId ? rawObs.filter((o) => o.projectId === projectId) : rawObs).filter((observation) => canReadObservation(observation, reader));
|
|
6052
6159
|
const sorted = allObs.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
6053
6160
|
const anchorIndex = sorted.findIndex((o) => o.id === anchorId);
|
|
6054
6161
|
if (anchorIndex === -1) {
|
|
@@ -6065,7 +6172,9 @@ async function getTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3)
|
|
|
6065
6172
|
tokens: obs.tokens,
|
|
6066
6173
|
source: obs.source || void 0,
|
|
6067
6174
|
sourceDetail: obs.sourceDetail || void 0,
|
|
6068
|
-
valueCategory: obs.valueCategory || void 0
|
|
6175
|
+
valueCategory: obs.valueCategory || void 0,
|
|
6176
|
+
admissionState: obs.admissionState || void 0,
|
|
6177
|
+
visibility: obs.visibility || void 0
|
|
6069
6178
|
};
|
|
6070
6179
|
};
|
|
6071
6180
|
const before = sorted.slice(Math.max(0, anchorIndex - depthBefore), anchorIndex).map(toIndexEntry);
|
|
@@ -6133,6 +6242,7 @@ var init_orama_store = __esm({
|
|
|
6133
6242
|
init_esm_shims();
|
|
6134
6243
|
init_types();
|
|
6135
6244
|
init_mini_skills();
|
|
6245
|
+
init_visibility();
|
|
6136
6246
|
init_provider();
|
|
6137
6247
|
init_project_affinity();
|
|
6138
6248
|
init_intent_detector();
|
|
@@ -6189,6 +6299,10 @@ function obsToRow(obs) {
|
|
|
6189
6299
|
relatedEntities: obs.relatedEntities ? JSON.stringify(obs.relatedEntities) : null,
|
|
6190
6300
|
sourceDetail: obs.sourceDetail ?? null,
|
|
6191
6301
|
valueCategory: obs.valueCategory ?? null,
|
|
6302
|
+
admissionState: obs.admissionState ?? null,
|
|
6303
|
+
admissionReason: obs.admissionReason ?? null,
|
|
6304
|
+
visibility: obs.visibility ?? null,
|
|
6305
|
+
sharedWithAgentIds: obs.sharedWithAgentIds ? JSON.stringify(obs.sharedWithAgentIds) : null,
|
|
6192
6306
|
createdByAgentId: obs.createdByAgentId ?? null,
|
|
6193
6307
|
writeGeneration: obs.writeGeneration ?? 0
|
|
6194
6308
|
};
|
|
@@ -6219,6 +6333,10 @@ function rowToObs(row) {
|
|
|
6219
6333
|
...row.relatedEntities ? { relatedEntities: safeJsonParse3(row.relatedEntities, []) } : {},
|
|
6220
6334
|
...row.sourceDetail ? { sourceDetail: row.sourceDetail } : {},
|
|
6221
6335
|
...row.valueCategory ? { valueCategory: row.valueCategory } : {},
|
|
6336
|
+
...row.admissionState ? { admissionState: row.admissionState } : {},
|
|
6337
|
+
...row.admissionReason ? { admissionReason: row.admissionReason } : {},
|
|
6338
|
+
...row.visibility ? { visibility: row.visibility } : {},
|
|
6339
|
+
...row.sharedWithAgentIds ? { sharedWithAgentIds: safeJsonParse3(row.sharedWithAgentIds, []) } : {},
|
|
6222
6340
|
...row.createdByAgentId ? { createdByAgentId: row.createdByAgentId } : {},
|
|
6223
6341
|
...row.writeGeneration ? { writeGeneration: row.writeGeneration } : {}
|
|
6224
6342
|
};
|
|
@@ -6270,12 +6388,14 @@ var init_sqlite_store = __esm({
|
|
|
6270
6388
|
(id, entityName, type, title, narrative, facts, filesModified, concepts, tokens,
|
|
6271
6389
|
createdAt, updatedAt, projectId, hasCausalLanguage, topicKey, revisionCount,
|
|
6272
6390
|
sessionId, status, progress, source, commitHash, relatedCommits, relatedEntities,
|
|
6273
|
-
sourceDetail, valueCategory,
|
|
6391
|
+
sourceDetail, valueCategory, admissionState, admissionReason, visibility, sharedWithAgentIds,
|
|
6392
|
+
createdByAgentId, writeGeneration)
|
|
6274
6393
|
VALUES
|
|
6275
6394
|
(@id, @entityName, @type, @title, @narrative, @facts, @filesModified, @concepts, @tokens,
|
|
6276
6395
|
@createdAt, @updatedAt, @projectId, @hasCausalLanguage, @topicKey, @revisionCount,
|
|
6277
6396
|
@sessionId, @status, @progress, @source, @commitHash, @relatedCommits, @relatedEntities,
|
|
6278
|
-
@sourceDetail, @valueCategory, @
|
|
6397
|
+
@sourceDetail, @valueCategory, @admissionState, @admissionReason, @visibility, @sharedWithAgentIds,
|
|
6398
|
+
@createdByAgentId, @writeGeneration)
|
|
6279
6399
|
`);
|
|
6280
6400
|
this.stmtUpdate = this.stmtInsert;
|
|
6281
6401
|
this.stmtSetStatus = this.db.prepare(`UPDATE observations SET status = ? WHERE id = ?`);
|
|
@@ -6827,6 +6947,7 @@ var init_maintenance_jobs = __esm({
|
|
|
6827
6947
|
"retention-archive",
|
|
6828
6948
|
"consolidation",
|
|
6829
6949
|
"codegraph-refresh",
|
|
6950
|
+
"observation-qualify",
|
|
6830
6951
|
"claim-derive",
|
|
6831
6952
|
"claim-requalification",
|
|
6832
6953
|
"knowledge-compile",
|
|
@@ -7185,7 +7306,8 @@ __export(lifecycle_exports, {
|
|
|
7185
7306
|
enqueueClaimDerivation: () => enqueueClaimDerivation,
|
|
7186
7307
|
enqueueClaimRequalification: () => enqueueClaimRequalification,
|
|
7187
7308
|
enqueueCodegraphRefresh: () => enqueueCodegraphRefresh,
|
|
7188
|
-
enqueueKnowledgeFollowups: () => enqueueKnowledgeFollowups
|
|
7309
|
+
enqueueKnowledgeFollowups: () => enqueueKnowledgeFollowups,
|
|
7310
|
+
enqueueObservationQualification: () => enqueueObservationQualification
|
|
7189
7311
|
});
|
|
7190
7312
|
function queueFor(input) {
|
|
7191
7313
|
return input.queue ?? new MaintenanceJobStore(input.dataDir);
|
|
@@ -7220,6 +7342,14 @@ function enqueueClaimDerivation(input) {
|
|
|
7220
7342
|
payload: { observationId: input.observationId }
|
|
7221
7343
|
});
|
|
7222
7344
|
}
|
|
7345
|
+
function enqueueObservationQualification(input) {
|
|
7346
|
+
queueFor(input).enqueue({
|
|
7347
|
+
projectId: input.projectId,
|
|
7348
|
+
kind: "observation-qualify",
|
|
7349
|
+
dedupeKey: "observation-qualify",
|
|
7350
|
+
payload: { source: input.source, limit: 100 }
|
|
7351
|
+
});
|
|
7352
|
+
}
|
|
7223
7353
|
function enqueueKnowledgeFollowups(input) {
|
|
7224
7354
|
const queue = queueFor(input);
|
|
7225
7355
|
const payload = {
|
|
@@ -8084,6 +8214,7 @@ __export(observations_exports, {
|
|
|
8084
8214
|
resolveObservations: () => resolveObservations,
|
|
8085
8215
|
storeObservation: () => storeObservation,
|
|
8086
8216
|
suggestTopicKey: () => suggestTopicKey,
|
|
8217
|
+
updateObservationAdmission: () => updateObservationAdmission,
|
|
8087
8218
|
withFreshObservations: () => withFreshObservations
|
|
8088
8219
|
});
|
|
8089
8220
|
function logEmbeddingFailureOnce(key, message) {
|
|
@@ -8150,6 +8281,18 @@ function queueClaimDerivation(observation) {
|
|
|
8150
8281
|
} catch {
|
|
8151
8282
|
}
|
|
8152
8283
|
}
|
|
8284
|
+
function queueObservationQualification(observation) {
|
|
8285
|
+
const dataDir = projectDir;
|
|
8286
|
+
if (!dataDir || observation.admissionState !== "candidate") return;
|
|
8287
|
+
try {
|
|
8288
|
+
enqueueObservationQualification({
|
|
8289
|
+
dataDir,
|
|
8290
|
+
projectId: observation.projectId,
|
|
8291
|
+
source: "automatic-capture:" + observation.id
|
|
8292
|
+
});
|
|
8293
|
+
} catch {
|
|
8294
|
+
}
|
|
8295
|
+
}
|
|
8153
8296
|
function isVectorCompatibleWithCurrentIndex(embedding) {
|
|
8154
8297
|
if (!embedding) return false;
|
|
8155
8298
|
const vectorDimensions = getVectorDimensions();
|
|
@@ -8205,6 +8348,9 @@ async function storeObservation(input) {
|
|
|
8205
8348
|
(o) => o.topicKey === input.topicKey && o.projectId === input.projectId
|
|
8206
8349
|
);
|
|
8207
8350
|
if (existing) {
|
|
8351
|
+
if (input.visibilityReader && !canManageObservation(existing, input.visibilityReader)) {
|
|
8352
|
+
throw new Error("Cannot update a memory outside this session's write scope.");
|
|
8353
|
+
}
|
|
8208
8354
|
return { observation: await upsertObservation(existing, input, now3), upserted: true };
|
|
8209
8355
|
}
|
|
8210
8356
|
}
|
|
@@ -8241,6 +8387,9 @@ async function storeObservation(input) {
|
|
|
8241
8387
|
if (input.topicKey) {
|
|
8242
8388
|
const diskExisting = await tx.findByTopicKey(input.projectId, input.topicKey);
|
|
8243
8389
|
if (diskExisting) {
|
|
8390
|
+
if (input.visibilityReader && !canManageObservation(diskExisting, input.visibilityReader)) {
|
|
8391
|
+
throw new Error("Cannot update a memory outside this session's write scope.");
|
|
8392
|
+
}
|
|
8244
8393
|
upsertedInsideLock = true;
|
|
8245
8394
|
observation = diskExisting;
|
|
8246
8395
|
return;
|
|
@@ -8271,6 +8420,10 @@ async function storeObservation(input) {
|
|
|
8271
8420
|
relatedEntities: input.relatedEntities,
|
|
8272
8421
|
sourceDetail: input.sourceDetail,
|
|
8273
8422
|
valueCategory: input.valueCategory,
|
|
8423
|
+
admissionState: input.admissionState,
|
|
8424
|
+
admissionReason: input.admissionReason ? sanitizeCredentials(input.admissionReason) : void 0,
|
|
8425
|
+
visibility: input.visibility ?? "project",
|
|
8426
|
+
sharedWithAgentIds: input.sharedWithAgentIds,
|
|
8274
8427
|
createdByAgentId: input.createdByAgentId,
|
|
8275
8428
|
// Predict the generation that atomic() will commit after this callback.
|
|
8276
8429
|
// bumpGeneration() runs after fn(tx) returns, incrementing by 1.
|
|
@@ -8317,6 +8470,10 @@ async function storeObservation(input) {
|
|
|
8317
8470
|
relatedEntities: input.relatedEntities,
|
|
8318
8471
|
sourceDetail: input.sourceDetail,
|
|
8319
8472
|
valueCategory: input.valueCategory,
|
|
8473
|
+
admissionState: input.admissionState,
|
|
8474
|
+
admissionReason: input.admissionReason ? sanitizeCredentials(input.admissionReason) : void 0,
|
|
8475
|
+
visibility: input.visibility ?? "project",
|
|
8476
|
+
sharedWithAgentIds: input.sharedWithAgentIds,
|
|
8320
8477
|
createdByAgentId: input.createdByAgentId,
|
|
8321
8478
|
writeGeneration: 0
|
|
8322
8479
|
};
|
|
@@ -8340,7 +8497,12 @@ async function storeObservation(input) {
|
|
|
8340
8497
|
status: "active",
|
|
8341
8498
|
source: input.source ?? "agent",
|
|
8342
8499
|
sourceDetail: input.sourceDetail ?? "",
|
|
8343
|
-
valueCategory: input.valueCategory ?? ""
|
|
8500
|
+
valueCategory: input.valueCategory ?? "",
|
|
8501
|
+
admissionState: input.admissionState ?? "",
|
|
8502
|
+
admissionReason: input.admissionReason ? sanitizeCredentials(input.admissionReason) : "",
|
|
8503
|
+
visibility: input.visibility ?? "project",
|
|
8504
|
+
createdByAgentId: input.createdByAgentId ?? "",
|
|
8505
|
+
sharedWithAgentIds: JSON.stringify(input.sharedWithAgentIds ?? [])
|
|
8344
8506
|
};
|
|
8345
8507
|
await insertObservation(doc);
|
|
8346
8508
|
};
|
|
@@ -8349,7 +8511,10 @@ async function storeObservation(input) {
|
|
|
8349
8511
|
return { observation: await upsertObservation(observation, input, now3), upserted: true };
|
|
8350
8512
|
}
|
|
8351
8513
|
await bindObservationCodeRefsBestEffort(observation);
|
|
8352
|
-
|
|
8514
|
+
queueObservationQualification(observation);
|
|
8515
|
+
if (resolveObservationVisibility(observation) === "project") {
|
|
8516
|
+
queueClaimDerivation(observation);
|
|
8517
|
+
}
|
|
8353
8518
|
const obsId = observation.id;
|
|
8354
8519
|
vectorMissingIds.add(obsId);
|
|
8355
8520
|
const searchableText = [input.title, input.narrative, ...input.facts ?? []].join(" ");
|
|
@@ -8422,6 +8587,10 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8422
8587
|
if (input.progress) existing.progress = input.progress;
|
|
8423
8588
|
if (input.sourceDetail !== void 0) existing.sourceDetail = input.sourceDetail;
|
|
8424
8589
|
if (input.valueCategory !== void 0) existing.valueCategory = input.valueCategory;
|
|
8590
|
+
if (input.admissionState !== void 0) existing.admissionState = input.admissionState;
|
|
8591
|
+
if (input.admissionReason !== void 0) existing.admissionReason = sanitizeCredentials(input.admissionReason);
|
|
8592
|
+
if (input.visibility !== void 0) existing.visibility = input.visibility;
|
|
8593
|
+
if (input.sharedWithAgentIds !== void 0) existing.sharedWithAgentIds = input.sharedWithAgentIds;
|
|
8425
8594
|
const doc = {
|
|
8426
8595
|
id: makeOramaObservationId(existing.projectId, existing.id),
|
|
8427
8596
|
observationId: existing.id,
|
|
@@ -8440,7 +8609,12 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8440
8609
|
status: "active",
|
|
8441
8610
|
source: existing.source ?? "agent",
|
|
8442
8611
|
sourceDetail: existing.sourceDetail ?? "",
|
|
8443
|
-
valueCategory: existing.valueCategory ?? ""
|
|
8612
|
+
valueCategory: existing.valueCategory ?? "",
|
|
8613
|
+
admissionState: existing.admissionState ?? "",
|
|
8614
|
+
admissionReason: existing.admissionReason ?? "",
|
|
8615
|
+
visibility: existing.visibility ?? "project",
|
|
8616
|
+
createdByAgentId: existing.createdByAgentId ?? "",
|
|
8617
|
+
sharedWithAgentIds: JSON.stringify(existing.sharedWithAgentIds ?? [])
|
|
8444
8618
|
};
|
|
8445
8619
|
const oramaId = makeOramaObservationId(existing.projectId, existing.id);
|
|
8446
8620
|
try {
|
|
@@ -8463,7 +8637,10 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8463
8637
|
await store.update(existing);
|
|
8464
8638
|
}
|
|
8465
8639
|
await bindObservationCodeRefsBestEffort(existing);
|
|
8466
|
-
|
|
8640
|
+
queueObservationQualification(existing);
|
|
8641
|
+
if (resolveObservationVisibility(existing) === "project") {
|
|
8642
|
+
queueClaimDerivation(existing);
|
|
8643
|
+
}
|
|
8467
8644
|
const searchableText = [input.title, input.narrative, ...input.facts ?? []].join(" ");
|
|
8468
8645
|
const obsId = existing.id;
|
|
8469
8646
|
vectorMissingIds.add(obsId);
|
|
@@ -8502,6 +8679,56 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8502
8679
|
function getObservation(id, projectId) {
|
|
8503
8680
|
return observations.find((o) => o.id === id && (projectId ? o.projectId === projectId : true));
|
|
8504
8681
|
}
|
|
8682
|
+
async function updateObservationAdmission(input) {
|
|
8683
|
+
await ensureFreshObservations();
|
|
8684
|
+
const cached = observations.find(
|
|
8685
|
+
(observation) => observation.id === input.observationId && (!input.projectId || observation.projectId === input.projectId)
|
|
8686
|
+
);
|
|
8687
|
+
if (!cached) return void 0;
|
|
8688
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
8689
|
+
const admissionReason = sanitizeCredentials(input.admissionReason).slice(0, 240);
|
|
8690
|
+
const apply = (current) => {
|
|
8691
|
+
if (input.projectId && current.projectId !== input.projectId) return void 0;
|
|
8692
|
+
if (input.expectedState && current.admissionState !== input.expectedState) return void 0;
|
|
8693
|
+
return {
|
|
8694
|
+
...current,
|
|
8695
|
+
admissionState: input.admissionState,
|
|
8696
|
+
admissionReason,
|
|
8697
|
+
...input.visibility !== void 0 ? { visibility: input.visibility } : {},
|
|
8698
|
+
updatedAt: now3
|
|
8699
|
+
};
|
|
8700
|
+
};
|
|
8701
|
+
let updated;
|
|
8702
|
+
if (projectDir) {
|
|
8703
|
+
const store = getObservationStore();
|
|
8704
|
+
await store.atomic(async (tx) => {
|
|
8705
|
+
const current = await tx.getById(input.observationId);
|
|
8706
|
+
if (!current) return;
|
|
8707
|
+
const next = apply(current);
|
|
8708
|
+
if (!next) return;
|
|
8709
|
+
await tx.update(next);
|
|
8710
|
+
updated = next;
|
|
8711
|
+
});
|
|
8712
|
+
} else {
|
|
8713
|
+
updated = apply(cached);
|
|
8714
|
+
}
|
|
8715
|
+
if (!updated) return void 0;
|
|
8716
|
+
observations = observations.map(
|
|
8717
|
+
(observation) => observation.id === updated.id && observation.projectId === updated.projectId ? updated : observation
|
|
8718
|
+
);
|
|
8719
|
+
try {
|
|
8720
|
+
await updateObservationMetadata(updated.projectId, updated.id, {
|
|
8721
|
+
admissionState: updated.admissionState,
|
|
8722
|
+
admissionReason: updated.admissionReason,
|
|
8723
|
+
...updated.visibility !== void 0 ? { visibility: updated.visibility } : {}
|
|
8724
|
+
});
|
|
8725
|
+
} catch {
|
|
8726
|
+
}
|
|
8727
|
+
if (updated.admissionState === "qualified" && resolveObservationVisibility(updated) === "project") {
|
|
8728
|
+
queueClaimDerivation(updated);
|
|
8729
|
+
}
|
|
8730
|
+
return updated;
|
|
8731
|
+
}
|
|
8505
8732
|
async function resolveObservations(ids, status = "resolved") {
|
|
8506
8733
|
const resolved = [];
|
|
8507
8734
|
const notFound = [];
|
|
@@ -8541,7 +8768,12 @@ async function resolveObservations(ids, status = "resolved") {
|
|
|
8541
8768
|
status,
|
|
8542
8769
|
source: obs.source ?? "agent",
|
|
8543
8770
|
sourceDetail: obs.sourceDetail ?? "",
|
|
8544
|
-
valueCategory: obs.valueCategory ?? ""
|
|
8771
|
+
valueCategory: obs.valueCategory ?? "",
|
|
8772
|
+
admissionState: obs.admissionState ?? "",
|
|
8773
|
+
admissionReason: obs.admissionReason ?? "",
|
|
8774
|
+
visibility: obs.visibility ?? "project",
|
|
8775
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
8776
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
8545
8777
|
};
|
|
8546
8778
|
await insertObservation(doc);
|
|
8547
8779
|
const obsId = obs.id;
|
|
@@ -8671,6 +8903,11 @@ async function reindexObservations() {
|
|
|
8671
8903
|
source: obs.source ?? "agent",
|
|
8672
8904
|
sourceDetail: obs.sourceDetail ?? "",
|
|
8673
8905
|
valueCategory: obs.valueCategory ?? "",
|
|
8906
|
+
admissionState: obs.admissionState ?? "",
|
|
8907
|
+
admissionReason: obs.admissionReason ?? "",
|
|
8908
|
+
visibility: obs.visibility ?? "project",
|
|
8909
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
8910
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
8674
8911
|
...compatibleEmbedding ? { embedding: compatibleEmbedding } : {}
|
|
8675
8912
|
};
|
|
8676
8913
|
await insertObservation(doc);
|
|
@@ -8743,6 +8980,7 @@ async function probeSearchIndex(projectId) {
|
|
|
8743
8980
|
await searchObservations({
|
|
8744
8981
|
query: "semantic memory retrieval status",
|
|
8745
8982
|
projectId,
|
|
8983
|
+
reader: { projectId },
|
|
8746
8984
|
limit: 1,
|
|
8747
8985
|
status: "all",
|
|
8748
8986
|
trackAccess: false
|
|
@@ -8808,6 +9046,11 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
8808
9046
|
source: obs.source ?? "agent",
|
|
8809
9047
|
sourceDetail: obs.sourceDetail ?? "",
|
|
8810
9048
|
valueCategory: obs.valueCategory ?? "",
|
|
9049
|
+
admissionState: obs.admissionState ?? "",
|
|
9050
|
+
admissionReason: obs.admissionReason ?? "",
|
|
9051
|
+
visibility: obs.visibility ?? "project",
|
|
9052
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
9053
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
8811
9054
|
embedding
|
|
8812
9055
|
};
|
|
8813
9056
|
await insertObservation(doc);
|
|
@@ -8849,6 +9092,7 @@ var init_observations = __esm({
|
|
|
8849
9092
|
init_provider();
|
|
8850
9093
|
init_secret_filter();
|
|
8851
9094
|
init_lifecycle();
|
|
9095
|
+
init_visibility();
|
|
8852
9096
|
observations = [];
|
|
8853
9097
|
nextId = 1;
|
|
8854
9098
|
projectDir = null;
|
|
@@ -9066,6 +9310,36 @@ var init_session_store = __esm({
|
|
|
9066
9310
|
}
|
|
9067
9311
|
});
|
|
9068
9312
|
|
|
9313
|
+
// src/memory/graph-scope.ts
|
|
9314
|
+
function projectGraphEntityNames(observations2) {
|
|
9315
|
+
const entityNames = /* @__PURE__ */ new Set();
|
|
9316
|
+
for (const observation of observations2) {
|
|
9317
|
+
if ((observation.status ?? "active") !== "active") continue;
|
|
9318
|
+
const entityName = observation.entityName?.trim();
|
|
9319
|
+
if (entityName) entityNames.add(entityName);
|
|
9320
|
+
for (const relatedEntity of observation.relatedEntities ?? []) {
|
|
9321
|
+
const name = relatedEntity.trim();
|
|
9322
|
+
if (name) entityNames.add(name);
|
|
9323
|
+
}
|
|
9324
|
+
}
|
|
9325
|
+
return entityNames;
|
|
9326
|
+
}
|
|
9327
|
+
function scopeKnowledgeGraphToProject(graph, observations2) {
|
|
9328
|
+
const entityNames = projectGraphEntityNames(observations2);
|
|
9329
|
+
const entities = graph.entities.filter((entity) => entityNames.has(entity.name));
|
|
9330
|
+
const visibleNames = new Set(entities.map((entity) => entity.name));
|
|
9331
|
+
const relations = graph.relations.filter(
|
|
9332
|
+
(relation) => visibleNames.has(relation.from) && visibleNames.has(relation.to)
|
|
9333
|
+
);
|
|
9334
|
+
return { entities, relations, entityNames };
|
|
9335
|
+
}
|
|
9336
|
+
var init_graph_scope = __esm({
|
|
9337
|
+
"src/memory/graph-scope.ts"() {
|
|
9338
|
+
"use strict";
|
|
9339
|
+
init_esm_shims();
|
|
9340
|
+
}
|
|
9341
|
+
});
|
|
9342
|
+
|
|
9069
9343
|
// src/memory/disclosure-policy.ts
|
|
9070
9344
|
function resolveEvidenceBasis(fields) {
|
|
9071
9345
|
if (fields.commitHash || fields.source === "git" || fields.sourceDetail === "git-ingest") {
|
|
@@ -9098,8 +9372,9 @@ function resolveSourceDetail(sourceDetail, source) {
|
|
|
9098
9372
|
return void 0;
|
|
9099
9373
|
}
|
|
9100
9374
|
function classifyLayer(fields) {
|
|
9101
|
-
const { valueCategory } = fields;
|
|
9375
|
+
const { valueCategory, admissionState } = fields;
|
|
9102
9376
|
const sd = resolveSourceDetail(fields.sourceDetail, fields.source);
|
|
9377
|
+
if (admissionState === "candidate" || admissionState === "ephemeral") return "L1";
|
|
9103
9378
|
if (valueCategory === "core") return "L2";
|
|
9104
9379
|
if (sd === "hook") return "L1";
|
|
9105
9380
|
if (sd === "git-ingest") return "L3";
|
|
@@ -9157,6 +9432,7 @@ function getEffectiveRetentionDays(doc) {
|
|
|
9157
9432
|
}
|
|
9158
9433
|
function isImmune(doc) {
|
|
9159
9434
|
if (doc.type === "probe") return false;
|
|
9435
|
+
if (doc.admissionState === "candidate" || doc.admissionState === "ephemeral") return false;
|
|
9160
9436
|
if (doc.valueCategory === "core") return true;
|
|
9161
9437
|
const importance = getImportanceLevel(doc);
|
|
9162
9438
|
if (importance === "critical") return true;
|
|
@@ -9166,6 +9442,7 @@ function isImmune(doc) {
|
|
|
9166
9442
|
}
|
|
9167
9443
|
function getImmunityReason(doc) {
|
|
9168
9444
|
if (doc.type === "probe") return null;
|
|
9445
|
+
if (doc.admissionState === "candidate" || doc.admissionState === "ephemeral") return null;
|
|
9169
9446
|
if (doc.valueCategory === "core") return "core valueCategory (formation-classified)";
|
|
9170
9447
|
const importance = getImportanceLevel(doc);
|
|
9171
9448
|
if (importance === "critical") return "critical importance";
|
|
@@ -9298,7 +9575,9 @@ function toRetentionDocument(obs, accessMap) {
|
|
|
9298
9575
|
status: obs.status ?? "active",
|
|
9299
9576
|
source: obs.source ?? "agent",
|
|
9300
9577
|
sourceDetail: obs.sourceDetail ?? "",
|
|
9301
|
-
valueCategory: obs.valueCategory ?? ""
|
|
9578
|
+
valueCategory: obs.valueCategory ?? "",
|
|
9579
|
+
admissionState: obs.admissionState ?? "",
|
|
9580
|
+
admissionReason: obs.admissionReason ?? ""
|
|
9302
9581
|
};
|
|
9303
9582
|
}
|
|
9304
9583
|
async function archiveExpiredBatch(_projectDir, options) {
|
|
@@ -9312,7 +9591,7 @@ async function archiveExpiredBatch(_projectDir, options) {
|
|
|
9312
9591
|
});
|
|
9313
9592
|
const hasMore = page.length > limit;
|
|
9314
9593
|
const scanned = hasMore ? page.slice(0, limit) : page;
|
|
9315
|
-
const candidateIds = scanned.filter((observation) => getRetentionZone(
|
|
9594
|
+
const candidateIds = scanned.filter((observation) => !options.reader || canManageObservation(observation, options.reader)).filter((observation) => getRetentionZone(
|
|
9316
9595
|
toRetentionDocument(observation, options.accessMap),
|
|
9317
9596
|
options.referenceTime
|
|
9318
9597
|
) === "archive-candidate").map((observation) => observation.id);
|
|
@@ -9325,7 +9604,7 @@ async function archiveExpiredBatch(_projectDir, options) {
|
|
|
9325
9604
|
const nextCursor = hasMore && scanned.length > 0 ? scanned[scanned.length - 1].id : void 0;
|
|
9326
9605
|
return nextCursor === void 0 ? { archived, scanned: scanned.length } : { archived, scanned: scanned.length, nextCursor };
|
|
9327
9606
|
}
|
|
9328
|
-
async function archiveExpired(projectDir2, referenceTime, accessMap, projectId) {
|
|
9607
|
+
async function archiveExpired(projectDir2, referenceTime, accessMap, projectId, reader) {
|
|
9329
9608
|
const store = getObservationStore();
|
|
9330
9609
|
if (projectId) {
|
|
9331
9610
|
let afterId;
|
|
@@ -9335,12 +9614,14 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId)
|
|
|
9335
9614
|
projectId,
|
|
9336
9615
|
afterId,
|
|
9337
9616
|
referenceTime,
|
|
9338
|
-
accessMap
|
|
9617
|
+
accessMap,
|
|
9618
|
+
reader
|
|
9339
9619
|
});
|
|
9340
9620
|
archived += batch.archived;
|
|
9341
9621
|
afterId = batch.nextCursor;
|
|
9342
9622
|
} while (afterId !== void 0);
|
|
9343
|
-
const
|
|
9623
|
+
const remainingObservations = await store.loadByProject(projectId, { status: "active" });
|
|
9624
|
+
const remaining = reader ? remainingObservations.filter((observation) => canManageObservation(observation, reader)).length : remainingObservations.length;
|
|
9344
9625
|
return { archived, remaining };
|
|
9345
9626
|
}
|
|
9346
9627
|
return await store.atomic(async (tx) => {
|
|
@@ -9370,6 +9651,7 @@ var init_retention = __esm({
|
|
|
9370
9651
|
"use strict";
|
|
9371
9652
|
init_esm_shims();
|
|
9372
9653
|
init_obs_store();
|
|
9654
|
+
init_visibility();
|
|
9373
9655
|
RETENTION_DAYS = {
|
|
9374
9656
|
critical: 365,
|
|
9375
9657
|
high: 180,
|
|
@@ -9405,6 +9687,49 @@ var init_retention = __esm({
|
|
|
9405
9687
|
}
|
|
9406
9688
|
});
|
|
9407
9689
|
|
|
9690
|
+
// src/memory/admission.ts
|
|
9691
|
+
var admission_exports = {};
|
|
9692
|
+
__export(admission_exports, {
|
|
9693
|
+
isCandidateObservation: () => isCandidateObservation,
|
|
9694
|
+
isEligibleForAutomaticDelivery: () => isEligibleForAutomaticDelivery,
|
|
9695
|
+
isEligibleForKnowledgePromotion: () => isEligibleForKnowledgePromotion,
|
|
9696
|
+
qualifyCandidateFromCurrentCode: () => qualifyCandidateFromCurrentCode
|
|
9697
|
+
});
|
|
9698
|
+
function isEligibleForAutomaticDelivery(observation) {
|
|
9699
|
+
return observation.admissionState !== "ephemeral" && observation.admissionState !== "candidate";
|
|
9700
|
+
}
|
|
9701
|
+
function isCandidateObservation(observation) {
|
|
9702
|
+
return observation.admissionState === "candidate";
|
|
9703
|
+
}
|
|
9704
|
+
function isEligibleForKnowledgePromotion(observation) {
|
|
9705
|
+
return isEligibleForAutomaticDelivery(observation) && observation.valueCategory !== "ephemeral" && resolveObservationVisibility(observation) === "project";
|
|
9706
|
+
}
|
|
9707
|
+
function qualifyCandidateFromCurrentCode(input) {
|
|
9708
|
+
if (!isCandidateObservation(input.observation)) return void 0;
|
|
9709
|
+
if (input.observation.valueCategory === "ephemeral") return void 0;
|
|
9710
|
+
if (input.currentCodeReferenceCount <= 0) return void 0;
|
|
9711
|
+
const typeLabel = DURABLE_AUTOMATIC_TYPES.has(input.observation.type) ? "high-value automatic record" : "automatic record";
|
|
9712
|
+
return {
|
|
9713
|
+
admissionState: "qualified",
|
|
9714
|
+
admissionReason: `${typeLabel} qualified against ${input.currentCodeReferenceCount} current Code Memory reference(s)`
|
|
9715
|
+
};
|
|
9716
|
+
}
|
|
9717
|
+
var DURABLE_AUTOMATIC_TYPES;
|
|
9718
|
+
var init_admission = __esm({
|
|
9719
|
+
"src/memory/admission.ts"() {
|
|
9720
|
+
"use strict";
|
|
9721
|
+
init_esm_shims();
|
|
9722
|
+
init_visibility();
|
|
9723
|
+
DURABLE_AUTOMATIC_TYPES = /* @__PURE__ */ new Set([
|
|
9724
|
+
"decision",
|
|
9725
|
+
"gotcha",
|
|
9726
|
+
"problem-solution",
|
|
9727
|
+
"trade-off",
|
|
9728
|
+
"why-it-exists"
|
|
9729
|
+
]);
|
|
9730
|
+
}
|
|
9731
|
+
});
|
|
9732
|
+
|
|
9408
9733
|
// src/workspace/workflow-sync.ts
|
|
9409
9734
|
import matter9 from "gray-matter";
|
|
9410
9735
|
var WorkflowSyncer;
|
|
@@ -10805,6 +11130,7 @@ var init_isolated_maintenance = __esm({
|
|
|
10805
11130
|
"retention-archive",
|
|
10806
11131
|
"consolidation",
|
|
10807
11132
|
"codegraph-refresh",
|
|
11133
|
+
"observation-qualify",
|
|
10808
11134
|
"claim-derive",
|
|
10809
11135
|
"claim-requalification",
|
|
10810
11136
|
"knowledge-compile",
|
|
@@ -11240,9 +11566,10 @@ async function loadConsolidationPage(projectId, options) {
|
|
|
11240
11566
|
return hasMore && observations2.length > 0 ? { observations: observations2, nextCursor: observations2[observations2.length - 1].id } : { observations: observations2 };
|
|
11241
11567
|
}
|
|
11242
11568
|
function findClusters(observations2, threshold) {
|
|
11243
|
-
|
|
11569
|
+
const eligible = observations2.filter(isEligibleForAutomaticDelivery).filter((observation) => resolveObservationVisibility(observation) === "project");
|
|
11570
|
+
if (eligible.length < MIN_CLUSTER_SIZE) return [];
|
|
11244
11571
|
const groups = /* @__PURE__ */ new Map();
|
|
11245
|
-
for (const obs of
|
|
11572
|
+
for (const obs of eligible) {
|
|
11246
11573
|
const key = `${obs.entityName}::${obs.type}`;
|
|
11247
11574
|
const group = groups.get(key) ?? [];
|
|
11248
11575
|
group.push(obs);
|
|
@@ -11379,6 +11706,8 @@ var init_consolidation = __esm({
|
|
|
11379
11706
|
"use strict";
|
|
11380
11707
|
init_esm_shims();
|
|
11381
11708
|
init_obs_store();
|
|
11709
|
+
init_admission();
|
|
11710
|
+
init_visibility();
|
|
11382
11711
|
DEFAULT_SIMILARITY_THRESHOLD = 0.45;
|
|
11383
11712
|
HIGH_VALUE_SIMILARITY_THRESHOLD = 0.85;
|
|
11384
11713
|
HIGH_VALUE_TYPES = /* @__PURE__ */ new Set(["gotcha", "decision", "trade-off", "reasoning", "problem-solution"]);
|
|
@@ -12178,6 +12507,7 @@ __export(claims_exports, {
|
|
|
12178
12507
|
buildClaimIdentity: () => buildClaimIdentity,
|
|
12179
12508
|
deriveLowRiskClaimsFromObservation: () => deriveLowRiskClaimsFromObservation,
|
|
12180
12509
|
requalifyClaimsForCodeState: () => requalifyClaimsForCodeState,
|
|
12510
|
+
reviewClaim: () => reviewClaim,
|
|
12181
12511
|
selectClaimsForTask: () => selectClaimsForTask,
|
|
12182
12512
|
supersedeClaim: () => supersedeClaim,
|
|
12183
12513
|
writeClaim: () => writeClaim
|
|
@@ -12364,6 +12694,39 @@ function writeClaim(store, input) {
|
|
|
12364
12694
|
};
|
|
12365
12695
|
});
|
|
12366
12696
|
}
|
|
12697
|
+
function reviewClaim(store, input) {
|
|
12698
|
+
if (input.reviewState !== "approved" && input.reviewState !== "rejected") {
|
|
12699
|
+
throw new Error("A claim review must approve or reject the claim");
|
|
12700
|
+
}
|
|
12701
|
+
const detail = compactText(input.detail, "review detail");
|
|
12702
|
+
return store.transaction(() => {
|
|
12703
|
+
const claim = store.getClaim(input.claimId);
|
|
12704
|
+
if (!claim) throw new Error("Claim was not found");
|
|
12705
|
+
if (input.reviewState === "approved" && claim.status === "superseded") {
|
|
12706
|
+
throw new Error("A superseded claim cannot be approved");
|
|
12707
|
+
}
|
|
12708
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
12709
|
+
const nextStatus = input.reviewState === "rejected" ? "unknown" : claim.status === "unknown" && claim.reviewState === "rejected" ? "active" : claim.status;
|
|
12710
|
+
const updated = {
|
|
12711
|
+
...claim,
|
|
12712
|
+
status: nextStatus,
|
|
12713
|
+
reviewState: input.reviewState,
|
|
12714
|
+
updatedAt: now3
|
|
12715
|
+
};
|
|
12716
|
+
store.updateClaim(updated);
|
|
12717
|
+
store.recordEvent({
|
|
12718
|
+
projectId: claim.projectId,
|
|
12719
|
+
claimId: claim.id,
|
|
12720
|
+
kind: "reviewed",
|
|
12721
|
+
fromStatus: claim.status,
|
|
12722
|
+
toStatus: updated.status,
|
|
12723
|
+
detail: "Review: " + claim.reviewState + " -> " + input.reviewState + ". " + detail,
|
|
12724
|
+
createdAt: now3
|
|
12725
|
+
});
|
|
12726
|
+
reconcileConflicts(store, claim.projectId, claim.conflictKey, now3);
|
|
12727
|
+
return store.getClaim(claim.id) ?? updated;
|
|
12728
|
+
});
|
|
12729
|
+
}
|
|
12367
12730
|
function supersedeClaim(store, input) {
|
|
12368
12731
|
const evidence = validateEvidence(input.evidence);
|
|
12369
12732
|
return store.transaction(() => {
|
|
@@ -12452,7 +12815,7 @@ function deriveLowRiskClaimsFromObservation(store, observation, codeStore) {
|
|
|
12452
12815
|
scope: "project",
|
|
12453
12816
|
confidence,
|
|
12454
12817
|
observedAt: observation.updatedAt ?? observation.createdAt,
|
|
12455
|
-
reviewState: "approved",
|
|
12818
|
+
reviewState: isGit ? "approved" : "needs-review",
|
|
12456
12819
|
origin: isGit ? "git" : "derived",
|
|
12457
12820
|
evidence
|
|
12458
12821
|
});
|
|
@@ -13328,81 +13691,425 @@ var init_workflow_store = __esm({
|
|
|
13328
13691
|
}
|
|
13329
13692
|
});
|
|
13330
13693
|
|
|
13331
|
-
// src/
|
|
13332
|
-
var
|
|
13333
|
-
__export(
|
|
13334
|
-
|
|
13335
|
-
|
|
13336
|
-
|
|
13337
|
-
|
|
13338
|
-
|
|
13339
|
-
|
|
13340
|
-
|
|
13341
|
-
|
|
13342
|
-
selectWorkspaceWorkflows: () => selectWorkspaceWorkflows,
|
|
13343
|
-
syncCanonicalWorkflows: () => syncCanonicalWorkflows,
|
|
13344
|
-
writeCanonicalWorkflow: () => writeCanonicalWorkflow
|
|
13694
|
+
// src/codegraph/task-lens.ts
|
|
13695
|
+
var task_lens_exports = {};
|
|
13696
|
+
__export(task_lens_exports, {
|
|
13697
|
+
containsTaskKeyword: () => containsTaskKeyword,
|
|
13698
|
+
lensPathCandidates: () => lensPathCandidates,
|
|
13699
|
+
lensVerificationHints: () => lensVerificationHints,
|
|
13700
|
+
rankLensPaths: () => rankLensPaths,
|
|
13701
|
+
rankLensSources: () => rankLensSources,
|
|
13702
|
+
resolveTaskLens: () => resolveTaskLens,
|
|
13703
|
+
scoreLensSource: () => scoreLensSource,
|
|
13704
|
+
shouldShowLensSource: () => shouldShowLensSource
|
|
13345
13705
|
});
|
|
13346
|
-
|
|
13347
|
-
|
|
13348
|
-
import path17 from "path";
|
|
13349
|
-
import matter11 from "gray-matter";
|
|
13350
|
-
function hash3(value) {
|
|
13351
|
-
return createHash12("sha256").update(value).digest("hex");
|
|
13352
|
-
}
|
|
13353
|
-
function now2() {
|
|
13354
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
13355
|
-
}
|
|
13356
|
-
function slug(value) {
|
|
13357
|
-
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 72);
|
|
13358
|
-
return normalized || "workflow";
|
|
13706
|
+
function normalizePath2(path25) {
|
|
13707
|
+
return path25.replace(/\\/g, "/");
|
|
13359
13708
|
}
|
|
13360
|
-
function
|
|
13361
|
-
return
|
|
13709
|
+
function tokenize2(text) {
|
|
13710
|
+
return (text.toLowerCase().match(/[a-z0-9_./-]+|[\u4e00-\u9fff]+/g) ?? []).map((token) => token.trim()).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
|
|
13362
13711
|
}
|
|
13363
|
-
function
|
|
13364
|
-
const
|
|
13365
|
-
|
|
13366
|
-
|
|
13712
|
+
function containsTaskKeyword(text, keyword) {
|
|
13713
|
+
const matcher = /^[a-z0-9_-]+$/i.test(keyword) ? new RegExp(`(^|[^a-z0-9_-])${keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?=[^a-z0-9_-]|$)`, "gi") : new RegExp(keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi");
|
|
13714
|
+
let match;
|
|
13715
|
+
while ((match = matcher.exec(text)) !== null) {
|
|
13716
|
+
const keywordIndex = match.index + (match[1]?.length ?? 0);
|
|
13717
|
+
if (!isNegatedTaskKeyword(text, keywordIndex)) return true;
|
|
13367
13718
|
}
|
|
13368
|
-
return
|
|
13719
|
+
return false;
|
|
13369
13720
|
}
|
|
13370
|
-
function
|
|
13371
|
-
const
|
|
13372
|
-
|
|
13373
|
-
|
|
13374
|
-
|
|
13721
|
+
function isNegatedTaskKeyword(text, keywordIndex) {
|
|
13722
|
+
const boundary = Math.max(
|
|
13723
|
+
text.lastIndexOf(".", keywordIndex - 1),
|
|
13724
|
+
text.lastIndexOf("!", keywordIndex - 1),
|
|
13725
|
+
text.lastIndexOf("?", keywordIndex - 1),
|
|
13726
|
+
text.lastIndexOf(";", keywordIndex - 1),
|
|
13727
|
+
text.lastIndexOf("\n", keywordIndex - 1),
|
|
13728
|
+
text.lastIndexOf("\u3002", keywordIndex - 1),
|
|
13729
|
+
text.lastIndexOf("\uFF01", keywordIndex - 1),
|
|
13730
|
+
text.lastIndexOf("\uFF1F", keywordIndex - 1),
|
|
13731
|
+
text.lastIndexOf("\uFF1B", keywordIndex - 1)
|
|
13732
|
+
);
|
|
13733
|
+
let prefix = text.slice(boundary + 1, keywordIndex).toLowerCase();
|
|
13734
|
+
const contrast = /(?:,|\uff0c)\s*(?:but|however|instead|yet|\u4f46\u662f|\u4f46|\u800c\u662f)\s*/gi;
|
|
13735
|
+
let contrastMatch;
|
|
13736
|
+
let contrastEnd = -1;
|
|
13737
|
+
while ((contrastMatch = contrast.exec(prefix)) !== null) contrastEnd = contrast.lastIndex;
|
|
13738
|
+
if (contrastEnd >= 0) prefix = prefix.slice(contrastEnd);
|
|
13739
|
+
if (/\b(?:do|does|did|should|must|can|could|will|would|may|might)\s+not\b/.test(prefix)) return true;
|
|
13740
|
+
if (/\b(?:don't|dont|never|without|avoid|skip)\b/.test(prefix)) return true;
|
|
13741
|
+
if (/(?:^|[\s,])no\s+(?:[a-z0-9_-]+\s*){0,4}$/i.test(prefix)) return true;
|
|
13742
|
+
const chineseClauseStart = Math.max(prefix.lastIndexOf(","), prefix.lastIndexOf("\uFF0C"), prefix.lastIndexOf("\u3001"));
|
|
13743
|
+
const chineseClause = prefix.slice(chineseClauseStart + 1);
|
|
13744
|
+
return /(?:\u4e0d\u8981|\u4e0d\u5e94|\u4e0d\u53ef|\u4e0d\u80fd|\u4e0d\u4f1a|\u7981\u6b62|\u52ff|\u4e0d)\s*(?:\u7acb\u5373|\u76f4\u63a5|\u518d|\u73b0\u5728|\u64c5\u81ea|\u5148)?\s*$/.test(chineseClause);
|
|
13375
13745
|
}
|
|
13376
|
-
function
|
|
13377
|
-
const
|
|
13378
|
-
if (
|
|
13379
|
-
|
|
13380
|
-
|
|
13746
|
+
function resolveTaskLens(task) {
|
|
13747
|
+
const normalized = (task ?? "").toLowerCase();
|
|
13748
|
+
if (!normalized.trim()) return LENSES.general;
|
|
13749
|
+
let best = {
|
|
13750
|
+
id: "general",
|
|
13751
|
+
score: 0,
|
|
13752
|
+
priority: Number.MAX_SAFE_INTEGER
|
|
13753
|
+
};
|
|
13754
|
+
for (const id of LENS_PRIORITY) {
|
|
13755
|
+
const score = KEYWORDS[id].reduce((sum, keyword) => sum + (containsTaskKeyword(normalized, keyword) ? 1 : 0), 0);
|
|
13756
|
+
const priority = LENS_PRIORITY.indexOf(id);
|
|
13757
|
+
if (score > best.score || score === best.score && score > 0 && priority < best.priority) {
|
|
13758
|
+
best = { id, score, priority };
|
|
13759
|
+
}
|
|
13381
13760
|
}
|
|
13382
|
-
return [
|
|
13761
|
+
return best.score > 0 ? LENSES[best.id] : LENSES.general;
|
|
13383
13762
|
}
|
|
13384
|
-
function
|
|
13385
|
-
const
|
|
13386
|
-
|
|
13387
|
-
|
|
13388
|
-
|
|
13763
|
+
function pathKindScore(path25, lens) {
|
|
13764
|
+
const normalized = normalizePath2(path25).toLowerCase();
|
|
13765
|
+
const name = normalized.split("/").pop() ?? normalized;
|
|
13766
|
+
const inDocs = normalized.startsWith("docs/") || name === "readme.md" || name.includes("readme");
|
|
13767
|
+
const isTest = /(^|\/)(tests?|__tests__|spec|fixtures?)\//.test(normalized) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized) || /\.(test|spec)\.py$/.test(normalized);
|
|
13768
|
+
const isSource = normalized.startsWith("src/") || normalized.includes("/src/");
|
|
13769
|
+
const isPackage = name === "package.json" || name === "package-lock.json" || name === "pnpm-lock.yaml" || name === "yarn.lock";
|
|
13770
|
+
const isChangelog = name === "changelog.md" || name === "changes.md";
|
|
13771
|
+
const isWorkflow = normalized.startsWith(".github/workflows/");
|
|
13772
|
+
switch (lens.id) {
|
|
13773
|
+
case "bugfix":
|
|
13774
|
+
return (isTest ? 80 : 0) + (isSource ? 50 : 0) + (normalized.includes("debug") ? 20 : 0);
|
|
13775
|
+
case "test":
|
|
13776
|
+
return (isTest ? 90 : 0) + (isSource ? 45 : 0);
|
|
13777
|
+
case "release":
|
|
13778
|
+
return (isChangelog ? 100 : 0) + (isPackage ? 90 : 0) + (isWorkflow ? 70 : 0) + (inDocs ? 35 : 0);
|
|
13779
|
+
case "onboarding":
|
|
13780
|
+
return (name === "readme.md" ? 100 : 0) + (inDocs ? 80 : 0) + (isPackage ? 45 : 0) + (isSource ? 20 : 0);
|
|
13781
|
+
case "docs":
|
|
13782
|
+
return (inDocs ? 100 : 0) + (isChangelog ? 60 : 0) + (isSource ? 25 : 0);
|
|
13783
|
+
case "refactor":
|
|
13784
|
+
return (isSource ? 70 : 0) + (isTest ? 65 : 0);
|
|
13785
|
+
case "feature":
|
|
13786
|
+
return (isSource ? 80 : 0) + (isTest ? 45 : 0) + (inDocs ? 20 : 0);
|
|
13787
|
+
default:
|
|
13788
|
+
if (isSource || isTest) return 60;
|
|
13789
|
+
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 45;
|
|
13790
|
+
if (inDocs) return 20;
|
|
13791
|
+
return 0;
|
|
13389
13792
|
}
|
|
13390
|
-
return value;
|
|
13391
13793
|
}
|
|
13392
|
-
function
|
|
13393
|
-
|
|
13394
|
-
|
|
13395
|
-
|
|
13396
|
-
throw new Error("workflow frontmatter field status is invalid");
|
|
13397
|
-
}
|
|
13398
|
-
return value;
|
|
13794
|
+
function tokenScore(text, tokens) {
|
|
13795
|
+
if (tokens.length === 0) return 0;
|
|
13796
|
+
const normalized = text.toLowerCase();
|
|
13797
|
+
return tokens.reduce((sum, token) => sum + (normalized.includes(token) ? 12 : 0), 0);
|
|
13399
13798
|
}
|
|
13400
|
-
function
|
|
13401
|
-
const
|
|
13402
|
-
if (
|
|
13403
|
-
|
|
13404
|
-
|
|
13405
|
-
|
|
13799
|
+
function sourceTaskMatchScore(source, task) {
|
|
13800
|
+
const tokens = tokenize2(task ?? "");
|
|
13801
|
+
if (tokens.length === 0) return 0;
|
|
13802
|
+
const text = [
|
|
13803
|
+
source.title,
|
|
13804
|
+
source.path ?? "",
|
|
13805
|
+
source.symbol ?? ""
|
|
13806
|
+
].join("\n").toLowerCase();
|
|
13807
|
+
const matches = tokens.filter((token) => text.includes(token)).length;
|
|
13808
|
+
if (matches >= 2) return 40;
|
|
13809
|
+
if (matches === 1) return 24;
|
|
13810
|
+
return 0;
|
|
13811
|
+
}
|
|
13812
|
+
function fallbackPathRank(path25) {
|
|
13813
|
+
const normalized = normalizePath2(path25);
|
|
13814
|
+
if (normalized.startsWith("src/")) return 0;
|
|
13815
|
+
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 1;
|
|
13816
|
+
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
|
|
13817
|
+
if (normalized.startsWith("docs/") || normalized.toLowerCase() === "readme.md") return 3;
|
|
13818
|
+
return 4;
|
|
13819
|
+
}
|
|
13820
|
+
function rankLensPaths(paths, lens, task) {
|
|
13821
|
+
const tokens = tokenize2(task ?? "");
|
|
13822
|
+
return [...new Set(paths.map(normalizePath2))].map((path25, index) => ({
|
|
13823
|
+
path: path25,
|
|
13824
|
+
index,
|
|
13825
|
+
score: pathKindScore(path25, lens) + tokenScore(path25, tokens),
|
|
13826
|
+
fallback: fallbackPathRank(path25)
|
|
13827
|
+
})).sort((a, b) => b.score - a.score || a.fallback - b.fallback || a.index - b.index || a.path.localeCompare(b.path)).map((item) => item.path);
|
|
13828
|
+
}
|
|
13829
|
+
function lensPathCandidates(lens) {
|
|
13830
|
+
switch (lens.id) {
|
|
13831
|
+
case "release":
|
|
13832
|
+
return ["CHANGELOG.md", "package.json", "package-lock.json", ".github/workflows/ci.yml", ".github/workflows/test.yml", "README.md"];
|
|
13833
|
+
case "onboarding":
|
|
13834
|
+
return ["README.md", "docs/README.md", "docs/API_REFERENCE.md", "package.json"];
|
|
13835
|
+
case "docs":
|
|
13836
|
+
return ["README.md", "README.zh-CN.md", "docs/README.md", "docs/API_REFERENCE.md", "CHANGELOG.md"];
|
|
13837
|
+
case "test":
|
|
13838
|
+
case "bugfix":
|
|
13839
|
+
return ["tests", "test"];
|
|
13840
|
+
default:
|
|
13841
|
+
return [];
|
|
13842
|
+
}
|
|
13843
|
+
}
|
|
13844
|
+
function scoreLensSource(source, lens, task) {
|
|
13845
|
+
const tokens = tokenize2(task ?? "");
|
|
13846
|
+
const text = [source.title, source.type, source.path ?? "", source.symbol ?? ""].join("\n");
|
|
13847
|
+
return pathKindScore(source.path ?? "", lens) + tokenScore(text, tokens) + sourceTaskMatchScore(source, task);
|
|
13848
|
+
}
|
|
13849
|
+
function rankLensSources(sources, lens, task) {
|
|
13850
|
+
return [...sources].map((source, index) => ({
|
|
13851
|
+
source,
|
|
13852
|
+
index,
|
|
13853
|
+
score: scoreLensSource(source, lens, task)
|
|
13854
|
+
})).sort((a, b) => b.score - a.score || a.source.observationId - b.source.observationId || a.index - b.index).map((item) => item.source);
|
|
13855
|
+
}
|
|
13856
|
+
function lensVerificationHints(lens) {
|
|
13857
|
+
switch (lens.id) {
|
|
13858
|
+
case "bugfix":
|
|
13859
|
+
return [
|
|
13860
|
+
"run the smallest failing test or repro first",
|
|
13861
|
+
"inspect the changed code path before trusting old memory"
|
|
13862
|
+
];
|
|
13863
|
+
case "test":
|
|
13864
|
+
return [
|
|
13865
|
+
"run the exact focused test file or test name first",
|
|
13866
|
+
"inspect fixtures and harness setup before changing assertions"
|
|
13867
|
+
];
|
|
13868
|
+
case "release":
|
|
13869
|
+
return [
|
|
13870
|
+
"run build, tests, package smoke, and publish dry-run where available",
|
|
13871
|
+
"verify package metadata, changelog, and Git state before publishing"
|
|
13872
|
+
];
|
|
13873
|
+
case "onboarding":
|
|
13874
|
+
return [
|
|
13875
|
+
"read the docs/start files first, then inspect only the code paths needed for the task",
|
|
13876
|
+
"treat old implementation memories as leads until current code confirms them"
|
|
13877
|
+
];
|
|
13878
|
+
case "refactor":
|
|
13879
|
+
return [
|
|
13880
|
+
"inspect call sites and tests before editing shared code",
|
|
13881
|
+
"run the narrow affected test plus one regression smoke"
|
|
13882
|
+
];
|
|
13883
|
+
case "docs":
|
|
13884
|
+
return [
|
|
13885
|
+
"check headings, links, commands, and examples against current code",
|
|
13886
|
+
"run the smallest docs or package smoke available"
|
|
13887
|
+
];
|
|
13888
|
+
case "feature":
|
|
13889
|
+
return [
|
|
13890
|
+
"inspect the closest existing implementation pattern before adding new code",
|
|
13891
|
+
"run focused tests plus one user-flow smoke after changes"
|
|
13892
|
+
];
|
|
13893
|
+
default:
|
|
13894
|
+
return [
|
|
13895
|
+
"inspect the Start here files before editing",
|
|
13896
|
+
"run the smallest relevant test or smoke command after changes"
|
|
13897
|
+
];
|
|
13898
|
+
}
|
|
13899
|
+
}
|
|
13900
|
+
function shouldShowLensSource(source, lens, task) {
|
|
13901
|
+
if (lens.id === "general") return true;
|
|
13902
|
+
const score = scoreLensSource(source, lens, task);
|
|
13903
|
+
if (lens.id === "onboarding" || lens.id === "release" || lens.id === "docs") return score >= 40;
|
|
13904
|
+
return score > 0;
|
|
13905
|
+
}
|
|
13906
|
+
var STOP_WORDS, LENSES, KEYWORDS, LENS_PRIORITY;
|
|
13907
|
+
var init_task_lens = __esm({
|
|
13908
|
+
"src/codegraph/task-lens.ts"() {
|
|
13909
|
+
"use strict";
|
|
13910
|
+
init_esm_shims();
|
|
13911
|
+
STOP_WORDS = /* @__PURE__ */ new Set([
|
|
13912
|
+
"a",
|
|
13913
|
+
"an",
|
|
13914
|
+
"and",
|
|
13915
|
+
"as",
|
|
13916
|
+
"for",
|
|
13917
|
+
"in",
|
|
13918
|
+
"into",
|
|
13919
|
+
"of",
|
|
13920
|
+
"on",
|
|
13921
|
+
"onto",
|
|
13922
|
+
"the",
|
|
13923
|
+
"this",
|
|
13924
|
+
"that",
|
|
13925
|
+
"to",
|
|
13926
|
+
"with",
|
|
13927
|
+
"work",
|
|
13928
|
+
"project",
|
|
13929
|
+
"continue",
|
|
13930
|
+
"\u7EE7\u7EED",
|
|
13931
|
+
"\u9879\u76EE"
|
|
13932
|
+
]);
|
|
13933
|
+
LENSES = {
|
|
13934
|
+
bugfix: {
|
|
13935
|
+
id: "bugfix",
|
|
13936
|
+
description: "debug the failure with current code and the smallest repro first",
|
|
13937
|
+
sourceLimit: 6,
|
|
13938
|
+
cautionLimit: 4,
|
|
13939
|
+
hideUnrelatedCautionDetails: true,
|
|
13940
|
+
hideUnrelatedReliableDetails: false
|
|
13941
|
+
},
|
|
13942
|
+
feature: {
|
|
13943
|
+
id: "feature",
|
|
13944
|
+
description: "build a scoped feature from nearby source, types, and user flow",
|
|
13945
|
+
sourceLimit: 6,
|
|
13946
|
+
cautionLimit: 3,
|
|
13947
|
+
hideUnrelatedCautionDetails: true,
|
|
13948
|
+
hideUnrelatedReliableDetails: false
|
|
13949
|
+
},
|
|
13950
|
+
release: {
|
|
13951
|
+
id: "release",
|
|
13952
|
+
description: "prepare a release using current metadata, changelog, build, and package checks",
|
|
13953
|
+
sourceLimit: 4,
|
|
13954
|
+
cautionLimit: 2,
|
|
13955
|
+
hideUnrelatedCautionDetails: true,
|
|
13956
|
+
hideUnrelatedReliableDetails: true
|
|
13957
|
+
},
|
|
13958
|
+
onboarding: {
|
|
13959
|
+
id: "onboarding",
|
|
13960
|
+
description: "understand the project shape before trusting old implementation details",
|
|
13961
|
+
sourceLimit: 4,
|
|
13962
|
+
cautionLimit: 2,
|
|
13963
|
+
hideUnrelatedCautionDetails: true,
|
|
13964
|
+
hideUnrelatedReliableDetails: true
|
|
13965
|
+
},
|
|
13966
|
+
refactor: {
|
|
13967
|
+
id: "refactor",
|
|
13968
|
+
description: "change structure carefully by reading shared code, call sites, and tests",
|
|
13969
|
+
sourceLimit: 6,
|
|
13970
|
+
cautionLimit: 4,
|
|
13971
|
+
hideUnrelatedCautionDetails: true,
|
|
13972
|
+
hideUnrelatedReliableDetails: false
|
|
13973
|
+
},
|
|
13974
|
+
docs: {
|
|
13975
|
+
id: "docs",
|
|
13976
|
+
description: "update documentation against current code and public entry points",
|
|
13977
|
+
sourceLimit: 5,
|
|
13978
|
+
cautionLimit: 2,
|
|
13979
|
+
hideUnrelatedCautionDetails: true,
|
|
13980
|
+
hideUnrelatedReliableDetails: true
|
|
13981
|
+
},
|
|
13982
|
+
test: {
|
|
13983
|
+
id: "test",
|
|
13984
|
+
description: "work from tests, fixtures, harnesses, and the related source files",
|
|
13985
|
+
sourceLimit: 6,
|
|
13986
|
+
cautionLimit: 3,
|
|
13987
|
+
hideUnrelatedCautionDetails: true,
|
|
13988
|
+
hideUnrelatedReliableDetails: false
|
|
13989
|
+
},
|
|
13990
|
+
general: {
|
|
13991
|
+
id: "general",
|
|
13992
|
+
description: "balanced project handoff with current facts, code memory, and verification hints",
|
|
13993
|
+
sourceLimit: 8,
|
|
13994
|
+
cautionLimit: 5,
|
|
13995
|
+
hideUnrelatedCautionDetails: false,
|
|
13996
|
+
hideUnrelatedReliableDetails: false
|
|
13997
|
+
}
|
|
13998
|
+
};
|
|
13999
|
+
KEYWORDS = {
|
|
14000
|
+
bugfix: [
|
|
14001
|
+
"bug",
|
|
14002
|
+
"crash",
|
|
14003
|
+
"incident",
|
|
14004
|
+
"debug",
|
|
14005
|
+
"error",
|
|
14006
|
+
"fail",
|
|
14007
|
+
"failing",
|
|
14008
|
+
"fix",
|
|
14009
|
+
"issue",
|
|
14010
|
+
"regression",
|
|
14011
|
+
"repro",
|
|
14012
|
+
"\u62A5\u9519",
|
|
14013
|
+
"\u5D29\u6E83",
|
|
14014
|
+
"\u6545\u969C",
|
|
14015
|
+
"\u5931\u8D25",
|
|
14016
|
+
"\u4FEE\u590D",
|
|
14017
|
+
"\u95EE\u9898"
|
|
14018
|
+
],
|
|
14019
|
+
feature: ["add", "build", "feature", "implement", "new", "support", "\u65B0\u589E", "\u5B9E\u73B0", "\u652F\u6301", "\u529F\u80FD"],
|
|
14020
|
+
release: ["bump", "changelog", "npm", "pack", "publish", "release", "version", "\u53D1\u7248", "\u53D1\u5E03", "\u7248\u672C"],
|
|
14021
|
+
onboarding: ["architecture", "handoff", "onboard", "overview", "understand", "\u63A5\u624B", "\u4E86\u89E3", "\u7406\u89E3", "\u67B6\u6784"],
|
|
14022
|
+
refactor: ["cleanup", "migrate", "refactor", "rename", "restructure", "\u91CD\u6784", "\u8FC1\u79FB", "\u6539\u9020"],
|
|
14023
|
+
docs: ["doc", "docs", "readme", "\u6587\u6863", "\u8BF4\u660E"],
|
|
14024
|
+
test: ["coverage", "fixture", "smoke", "spec", "test", "tests", "testing", "vitest", "\u6D4B\u8BD5"]
|
|
14025
|
+
};
|
|
14026
|
+
LENS_PRIORITY = [
|
|
14027
|
+
"bugfix",
|
|
14028
|
+
"release",
|
|
14029
|
+
"test",
|
|
14030
|
+
"refactor",
|
|
14031
|
+
"feature",
|
|
14032
|
+
"docs",
|
|
14033
|
+
"onboarding"
|
|
14034
|
+
];
|
|
14035
|
+
}
|
|
14036
|
+
});
|
|
14037
|
+
|
|
14038
|
+
// src/knowledge/workflows.ts
|
|
14039
|
+
var workflows_exports = {};
|
|
14040
|
+
__export(workflows_exports, {
|
|
14041
|
+
WORKFLOW_ADAPTER_TARGETS: () => WORKFLOW_ADAPTER_TARGETS,
|
|
14042
|
+
applyWorkflowAdapter: () => applyWorkflowAdapter,
|
|
14043
|
+
importWindsurfWorkflows: () => importWindsurfWorkflows,
|
|
14044
|
+
parseWorkflowMarkdown: () => parseWorkflowMarkdown,
|
|
14045
|
+
previewWorkflowAdapter: () => previewWorkflowAdapter,
|
|
14046
|
+
recordWorkflowRun: () => recordWorkflowRun,
|
|
14047
|
+
renderWorkflowMarkdown: () => renderWorkflowMarkdown,
|
|
14048
|
+
selectWorkflows: () => selectWorkflows,
|
|
14049
|
+
selectWorkspaceWorkflows: () => selectWorkspaceWorkflows,
|
|
14050
|
+
syncCanonicalWorkflows: () => syncCanonicalWorkflows,
|
|
14051
|
+
writeCanonicalWorkflow: () => writeCanonicalWorkflow
|
|
14052
|
+
});
|
|
14053
|
+
import { createHash as createHash12 } from "crypto";
|
|
14054
|
+
import { promises as fs14 } from "fs";
|
|
14055
|
+
import path17 from "path";
|
|
14056
|
+
import matter11 from "gray-matter";
|
|
14057
|
+
function hash3(value) {
|
|
14058
|
+
return createHash12("sha256").update(value).digest("hex");
|
|
14059
|
+
}
|
|
14060
|
+
function now2() {
|
|
14061
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
14062
|
+
}
|
|
14063
|
+
function slug(value) {
|
|
14064
|
+
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 72);
|
|
14065
|
+
return normalized || "workflow";
|
|
14066
|
+
}
|
|
14067
|
+
function titleFromName(name) {
|
|
14068
|
+
return name.replace(/[-_]+/g, " ").split(" ").filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ") || "Imported Workflow";
|
|
14069
|
+
}
|
|
14070
|
+
function requiredText(data, key) {
|
|
14071
|
+
const value = data[key];
|
|
14072
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
14073
|
+
throw new Error("workflow frontmatter requires " + key);
|
|
14074
|
+
}
|
|
14075
|
+
return value.trim();
|
|
14076
|
+
}
|
|
14077
|
+
function optionalText4(data, key) {
|
|
14078
|
+
const value = data[key];
|
|
14079
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
14080
|
+
if (typeof value !== "string") throw new Error("workflow frontmatter field " + key + " must be text");
|
|
14081
|
+
return value.trim() || void 0;
|
|
14082
|
+
}
|
|
14083
|
+
function optionalStringArray(data, key) {
|
|
14084
|
+
const value = data[key];
|
|
14085
|
+
if (value === void 0 || value === null) return [];
|
|
14086
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
14087
|
+
throw new Error("workflow frontmatter field " + key + " must be a string array");
|
|
14088
|
+
}
|
|
14089
|
+
return [...new Set(value.map((item) => item.trim()).filter(Boolean))];
|
|
14090
|
+
}
|
|
14091
|
+
function optionalNumber(data, key, fallback) {
|
|
14092
|
+
const value = data[key];
|
|
14093
|
+
if (value === void 0 || value === null || value === "") return fallback;
|
|
14094
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
14095
|
+
throw new Error("workflow frontmatter field " + key + " must be a positive integer");
|
|
14096
|
+
}
|
|
14097
|
+
return value;
|
|
14098
|
+
}
|
|
14099
|
+
function optionalStatus(data) {
|
|
14100
|
+
const value = data.status;
|
|
14101
|
+
if (value === void 0 || value === null || value === "") return "draft";
|
|
14102
|
+
if (typeof value !== "string" || !STATUS_VALUES.includes(value)) {
|
|
14103
|
+
throw new Error("workflow frontmatter field status is invalid");
|
|
14104
|
+
}
|
|
14105
|
+
return value;
|
|
14106
|
+
}
|
|
14107
|
+
function normalizeAgents(values) {
|
|
14108
|
+
const unknown = values.filter((value) => !KNOWN_AGENTS.includes(value));
|
|
14109
|
+
if (unknown.length > 0) {
|
|
14110
|
+
throw new Error("workflow frontmatter has unsupported agent: " + unknown[0]);
|
|
14111
|
+
}
|
|
14112
|
+
return values;
|
|
13406
14113
|
}
|
|
13407
14114
|
function phaseFromValue(value, index) {
|
|
13408
14115
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -13650,20 +14357,27 @@ function taskScore(workflow, task) {
|
|
|
13650
14357
|
const text = task.toLowerCase();
|
|
13651
14358
|
let score = 0;
|
|
13652
14359
|
const reasons = [];
|
|
14360
|
+
const matchedLenses = /* @__PURE__ */ new Set();
|
|
14361
|
+
const matchedTriggers = /* @__PURE__ */ new Set();
|
|
13653
14362
|
for (const lens of workflow.taskLenses) {
|
|
13654
14363
|
const terms = LENS_TERMS[lens.toLowerCase()] ?? [lens.toLowerCase()];
|
|
13655
|
-
if (terms.some((term) =>
|
|
14364
|
+
if (terms.some((term) => containsTaskKeyword(task, term))) {
|
|
13656
14365
|
score += 20;
|
|
13657
14366
|
reasons.push("matches " + lens + " workflow");
|
|
14367
|
+
matchedLenses.add(lens.toLowerCase());
|
|
13658
14368
|
}
|
|
13659
14369
|
}
|
|
13660
14370
|
for (const trigger of workflow.triggers) {
|
|
13661
14371
|
const term = trigger.toLowerCase().trim();
|
|
13662
|
-
if (term.length >= 2 &&
|
|
14372
|
+
if (term.length >= 2 && containsTaskKeyword(task, term)) {
|
|
13663
14373
|
score += 8;
|
|
13664
14374
|
reasons.push('matches trigger "' + trigger + '"');
|
|
14375
|
+
matchedTriggers.add(term);
|
|
13665
14376
|
}
|
|
13666
14377
|
}
|
|
14378
|
+
const hasSpecificLens = workflow.taskLenses.some((lens) => !GENERIC_WORKFLOW_LENSES.has(lens.toLowerCase()));
|
|
14379
|
+
const hasSpecificMatch = [...matchedLenses].some((lens) => !GENERIC_WORKFLOW_LENSES.has(lens)) || [...matchedTriggers].some((trigger) => !GENERIC_WORKFLOW_TERMS.has(trigger));
|
|
14380
|
+
if (hasSpecificLens && !hasSpecificMatch) return { score: 0, reasons: [] };
|
|
13667
14381
|
return { score, reasons: [...new Set(reasons)] };
|
|
13668
14382
|
}
|
|
13669
14383
|
function selectWorkflows(input) {
|
|
@@ -13681,9 +14395,28 @@ function selectWorkflows(input) {
|
|
|
13681
14395
|
return selected.slice(0, Math.max(1, Math.min(input.limit ?? 2, 2)));
|
|
13682
14396
|
}
|
|
13683
14397
|
function importedWorkflowSpec(input) {
|
|
14398
|
+
let sourceMatter;
|
|
14399
|
+
try {
|
|
14400
|
+
sourceMatter = matter11(input.raw);
|
|
14401
|
+
} catch (error) {
|
|
14402
|
+
throw new Error("Malformed Windsurf workflow Markdown: " + (error instanceof Error ? error.message : String(error)));
|
|
14403
|
+
}
|
|
14404
|
+
if (sourceMatter.data.id !== void 0) {
|
|
14405
|
+
const id = requiredText(sourceMatter.data, "id");
|
|
14406
|
+
const sourcePath2 = "workflows/" + slug(id) + ".md";
|
|
14407
|
+
const canonical = parsedWorkflow(sourceMatter.data, sourceMatter.content, {
|
|
14408
|
+
workspaceId: input.workspace.id,
|
|
14409
|
+
sourcePath: sourcePath2,
|
|
14410
|
+
contentHash: hash3(input.raw)
|
|
14411
|
+
});
|
|
14412
|
+
return materializeWorkflow({
|
|
14413
|
+
...canonical,
|
|
14414
|
+
importedFrom: input.sourcePath.replace(/\\/g, "/")
|
|
14415
|
+
});
|
|
14416
|
+
}
|
|
13684
14417
|
const entry = new WorkflowSyncer().parseWindsurfWorkflow(input.sourceName, input.raw);
|
|
13685
14418
|
const content = entry.content.trim() || "## Execute\n\nFollow the imported workflow.";
|
|
13686
|
-
const lenses = inferTaskLenses(entry.name + "\n" + entry.description
|
|
14419
|
+
const lenses = inferTaskLenses(entry.name + "\n" + entry.description);
|
|
13687
14420
|
const createdAt = now2();
|
|
13688
14421
|
const sourcePath = "workflows/" + slug(entry.name) + ".md";
|
|
13689
14422
|
const spec = {
|
|
@@ -13904,7 +14637,7 @@ async function selectWorkspaceWorkflows(input) {
|
|
|
13904
14637
|
errors: synced.errors
|
|
13905
14638
|
};
|
|
13906
14639
|
}
|
|
13907
|
-
var KNOWN_AGENTS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS;
|
|
14640
|
+
var KNOWN_AGENTS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS, GENERIC_WORKFLOW_LENSES, GENERIC_WORKFLOW_TERMS;
|
|
13908
14641
|
var init_workflows = __esm({
|
|
13909
14642
|
"src/knowledge/workflows.ts"() {
|
|
13910
14643
|
"use strict";
|
|
@@ -13913,6 +14646,7 @@ var init_workflows = __esm({
|
|
|
13913
14646
|
init_workflow_sync();
|
|
13914
14647
|
init_workspace();
|
|
13915
14648
|
init_workflow_store();
|
|
14649
|
+
init_task_lens();
|
|
13916
14650
|
KNOWN_AGENTS = [
|
|
13917
14651
|
"windsurf",
|
|
13918
14652
|
"cursor",
|
|
@@ -13944,6 +14678,8 @@ var init_workflows = __esm({
|
|
|
13944
14678
|
refactor: ["refactor", "cleanup", "restructure", "\u91CD\u6784", "\u6574\u7406"],
|
|
13945
14679
|
test: ["test", "verify", "smoke", "\u6D4B\u8BD5", "\u9A8C\u8BC1"]
|
|
13946
14680
|
};
|
|
14681
|
+
GENERIC_WORKFLOW_LENSES = /* @__PURE__ */ new Set(["review", "test"]);
|
|
14682
|
+
GENERIC_WORKFLOW_TERMS = /* @__PURE__ */ new Set(["review", "audit", "test", "verify", "smoke"]);
|
|
13947
14683
|
}
|
|
13948
14684
|
});
|
|
13949
14685
|
|
|
@@ -13990,6 +14726,15 @@ function claimDerivationCursor(payload) {
|
|
|
13990
14726
|
const value = payload.cursor;
|
|
13991
14727
|
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
13992
14728
|
}
|
|
14729
|
+
function observationQualificationBatchSize(payload) {
|
|
14730
|
+
const value = payload.limit;
|
|
14731
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE;
|
|
14732
|
+
return Math.min(500, Math.max(1, Math.floor(value)));
|
|
14733
|
+
}
|
|
14734
|
+
function observationQualificationCursor(payload) {
|
|
14735
|
+
const value = payload.cursor;
|
|
14736
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
14737
|
+
}
|
|
13993
14738
|
function observationId(payload) {
|
|
13994
14739
|
const value = payload.observationId;
|
|
13995
14740
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : void 0;
|
|
@@ -14168,6 +14913,71 @@ function createProjectMaintenanceHandler(projectId, projectDir2, projectRoot, op
|
|
|
14168
14913
|
...snapshot?.id ? { snapshotId: snapshot.id } : {},
|
|
14169
14914
|
...options.maintenanceQueue ? { queue: options.maintenanceQueue } : {}
|
|
14170
14915
|
});
|
|
14916
|
+
enqueueObservationQualification({
|
|
14917
|
+
projectId,
|
|
14918
|
+
dataDir: projectDir2,
|
|
14919
|
+
source: "codegraph-refresh",
|
|
14920
|
+
...options.maintenanceQueue ? { queue: options.maintenanceQueue } : {}
|
|
14921
|
+
});
|
|
14922
|
+
return { action: "complete" };
|
|
14923
|
+
}
|
|
14924
|
+
if (job.kind === "observation-qualify") {
|
|
14925
|
+
const [
|
|
14926
|
+
{ CodeGraphStore: CodeGraphStore2 },
|
|
14927
|
+
{ bindObservationToCode: bindObservationToCode2 },
|
|
14928
|
+
{ getObservationStore: getObservationStore2 },
|
|
14929
|
+
{ qualifyCandidateFromCurrentCode: qualifyCandidateFromCurrentCode2 },
|
|
14930
|
+
{ updateObservationAdmission: updateObservationAdmission2 }
|
|
14931
|
+
] = await Promise.all([
|
|
14932
|
+
Promise.resolve().then(() => (init_store(), store_exports)),
|
|
14933
|
+
Promise.resolve().then(() => (init_binder(), binder_exports)),
|
|
14934
|
+
Promise.resolve().then(() => (init_obs_store(), obs_store_exports)),
|
|
14935
|
+
Promise.resolve().then(() => (init_admission(), admission_exports)),
|
|
14936
|
+
Promise.resolve().then(() => (init_observations(), observations_exports))
|
|
14937
|
+
]);
|
|
14938
|
+
const codeStore = new CodeGraphStore2();
|
|
14939
|
+
await codeStore.init(projectDir2);
|
|
14940
|
+
const limit = observationQualificationBatchSize(job.payload);
|
|
14941
|
+
const page = await getObservationStore2().loadByProject(projectId, {
|
|
14942
|
+
status: "active",
|
|
14943
|
+
afterId: observationQualificationCursor(job.payload),
|
|
14944
|
+
limit: limit + 1
|
|
14945
|
+
});
|
|
14946
|
+
const hasMore = page.length > limit;
|
|
14947
|
+
const observations2 = hasMore ? page.slice(0, limit) : page;
|
|
14948
|
+
const indexedAtMs = Date.parse(codeStore.status(projectId).indexedAt ?? "");
|
|
14949
|
+
for (const observation of observations2) {
|
|
14950
|
+
if (observation.admissionState !== "candidate") continue;
|
|
14951
|
+
if (!Number.isFinite(indexedAtMs) || indexedAtMs < Date.parse(observation.createdAt)) continue;
|
|
14952
|
+
await bindObservationToCode2(codeStore, observation);
|
|
14953
|
+
const currentCodeReferenceCount = codeStore.listObservationRefs(projectId, observation.id).filter((reference) => reference.status === "current").length;
|
|
14954
|
+
const qualification = qualifyCandidateFromCurrentCode2({
|
|
14955
|
+
observation,
|
|
14956
|
+
currentCodeReferenceCount
|
|
14957
|
+
});
|
|
14958
|
+
if (!qualification) continue;
|
|
14959
|
+
await updateObservationAdmission2({
|
|
14960
|
+
observationId: observation.id,
|
|
14961
|
+
projectId,
|
|
14962
|
+
expectedState: "candidate",
|
|
14963
|
+
// Automatic capture earns project visibility only after a current
|
|
14964
|
+
// Code Memory link exists. Explicit/private records retain scope.
|
|
14965
|
+
...observation.sourceDetail === "hook" ? { visibility: "project" } : {},
|
|
14966
|
+
...qualification
|
|
14967
|
+
});
|
|
14968
|
+
}
|
|
14969
|
+
if (hasMore && observations2.length > 0) {
|
|
14970
|
+
return {
|
|
14971
|
+
action: "reschedule",
|
|
14972
|
+
delayMs: 0,
|
|
14973
|
+
resetAttempts: true,
|
|
14974
|
+
payload: {
|
|
14975
|
+
...job.payload,
|
|
14976
|
+
cursor: observations2[observations2.length - 1].id,
|
|
14977
|
+
limit
|
|
14978
|
+
}
|
|
14979
|
+
};
|
|
14980
|
+
}
|
|
14171
14981
|
return { action: "complete" };
|
|
14172
14982
|
}
|
|
14173
14983
|
if (job.kind === "claim-derive") {
|
|
@@ -14333,7 +15143,7 @@ function createProjectMaintenanceDispatcher(projectId, projectDir2, projectRoot,
|
|
|
14333
15143
|
return isolatedRunner({ job, projectRoot, dataDir: projectDir2 });
|
|
14334
15144
|
};
|
|
14335
15145
|
}
|
|
14336
|
-
var DEFAULT_VECTOR_BATCH_SIZE, DEFAULT_RETENTION_BATCH_SIZE, DEFAULT_CONSOLIDATION_BATCH_SIZE, DEFAULT_CODEGRAPH_MAX_FILES, DEFAULT_CLAIM_DERIVATION_BATCH_SIZE, VECTOR_RETRY_DELAY_MS, DEDUP_PER_PAIR_TIMEOUT_MS;
|
|
15146
|
+
var DEFAULT_VECTOR_BATCH_SIZE, DEFAULT_RETENTION_BATCH_SIZE, DEFAULT_CONSOLIDATION_BATCH_SIZE, DEFAULT_CODEGRAPH_MAX_FILES, DEFAULT_CLAIM_DERIVATION_BATCH_SIZE, DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE, VECTOR_RETRY_DELAY_MS, DEDUP_PER_PAIR_TIMEOUT_MS;
|
|
14337
15147
|
var init_project_maintenance = __esm({
|
|
14338
15148
|
"src/runtime/project-maintenance.ts"() {
|
|
14339
15149
|
"use strict";
|
|
@@ -14345,6 +15155,7 @@ var init_project_maintenance = __esm({
|
|
|
14345
15155
|
DEFAULT_CONSOLIDATION_BATCH_SIZE = 200;
|
|
14346
15156
|
DEFAULT_CODEGRAPH_MAX_FILES = 5e3;
|
|
14347
15157
|
DEFAULT_CLAIM_DERIVATION_BATCH_SIZE = 100;
|
|
15158
|
+
DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE = 100;
|
|
14348
15159
|
VECTOR_RETRY_DELAY_MS = 5e3;
|
|
14349
15160
|
DEDUP_PER_PAIR_TIMEOUT_MS = 5e3;
|
|
14350
15161
|
}
|
|
@@ -14586,9 +15397,9 @@ async function getSessionContext(projectDir2, projectId, limit = 3) {
|
|
|
14586
15397
|
return true;
|
|
14587
15398
|
});
|
|
14588
15399
|
})() : l2Scored).slice(0, 5).map(({ obs }) => obs);
|
|
14589
|
-
const l1HookObs = projectObs.filter((obs) => classifyLayer(obs) === "L1").sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, 3);
|
|
15400
|
+
const l1HookObs = projectObs.filter((obs) => isEligibleForAutomaticDelivery(obs) && classifyLayer(obs) === "L1").sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, 3);
|
|
14590
15401
|
const l3GitCount = projectObs.filter((obs) => classifyLayer(obs) === "L3").length;
|
|
14591
|
-
const totalHookCount = projectObs.filter((obs) => classifyLayer(obs) === "L1").length;
|
|
15402
|
+
const totalHookCount = projectObs.filter((obs) => isEligibleForAutomaticDelivery(obs) && classifyLayer(obs) === "L1").length;
|
|
14592
15403
|
const activeEntities = [
|
|
14593
15404
|
...new Set(l2Obs.map((o) => o.entityName).filter((n) => !!n && n.trim().length > 0))
|
|
14594
15405
|
].slice(0, 5);
|
|
@@ -14714,6 +15525,7 @@ var init_session = __esm({
|
|
|
14714
15525
|
"src/memory/session.ts"() {
|
|
14715
15526
|
"use strict";
|
|
14716
15527
|
init_esm_shims();
|
|
15528
|
+
init_admission();
|
|
14717
15529
|
init_disclosure_policy();
|
|
14718
15530
|
init_aliases();
|
|
14719
15531
|
init_obs_store();
|
|
@@ -14866,27 +15678,80 @@ function workflowOutput(selection) {
|
|
|
14866
15678
|
cautions: selection.cautions.map((caution) => short(caution, 22))
|
|
14867
15679
|
};
|
|
14868
15680
|
}
|
|
14869
|
-
function appendLine(lines, candidate, maxTokens, omitted, omittedKind) {
|
|
15681
|
+
function appendLine(lines, candidate, maxTokens, omitted, omittedKind, selected, receiptSelection) {
|
|
14870
15682
|
const next = lines.length ? lines.join("\n") + "\n" + candidate : candidate;
|
|
14871
15683
|
if (countTextTokens(next) <= maxTokens) {
|
|
14872
15684
|
lines.push(candidate);
|
|
15685
|
+
if (receiptSelection) selected?.push(receiptSelection);
|
|
14873
15686
|
return true;
|
|
14874
15687
|
}
|
|
14875
|
-
omitted.push(omittedKind);
|
|
14876
|
-
return false;
|
|
15688
|
+
omitted.push(omittedKind);
|
|
15689
|
+
return false;
|
|
15690
|
+
}
|
|
15691
|
+
function freshnessForMemory(status) {
|
|
15692
|
+
if (status === "current" || status === "suspect" || status === "stale") return status;
|
|
15693
|
+
return "unknown";
|
|
15694
|
+
}
|
|
15695
|
+
function receiptOmissionKind(raw) {
|
|
15696
|
+
if (raw.includes("task")) return "task";
|
|
15697
|
+
if (raw.includes("fact")) return "current-fact";
|
|
15698
|
+
if (raw.includes("state")) return "code-state";
|
|
15699
|
+
if (raw.includes("semantic")) return "semantic-code";
|
|
15700
|
+
if (raw.includes("start")) return "start-here";
|
|
15701
|
+
if (raw.includes("memory")) return "memory";
|
|
15702
|
+
if (raw.includes("claim")) return "claim";
|
|
15703
|
+
if (raw.includes("knowledge-page")) return "knowledge-page";
|
|
15704
|
+
if (raw.includes("workflow")) return "workflow";
|
|
15705
|
+
if (raw.includes("verification")) return "verification";
|
|
15706
|
+
if (raw.includes("caution")) return "caution";
|
|
15707
|
+
return void 0;
|
|
15708
|
+
}
|
|
15709
|
+
function receiptOmissions(omitted, hiddenCautionMemoryCount) {
|
|
15710
|
+
const counts = /* @__PURE__ */ new Map();
|
|
15711
|
+
for (const raw of omitted) {
|
|
15712
|
+
const kind = receiptOmissionKind(raw);
|
|
15713
|
+
if (!kind || raw.endsWith("-heading")) continue;
|
|
15714
|
+
counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
15715
|
+
}
|
|
15716
|
+
const receipt = [...counts.entries()].map(([kind, count2]) => ({
|
|
15717
|
+
kind,
|
|
15718
|
+
reason: "token-budget",
|
|
15719
|
+
count: count2
|
|
15720
|
+
}));
|
|
15721
|
+
if (hiddenCautionMemoryCount > 0) {
|
|
15722
|
+
receipt.push({
|
|
15723
|
+
kind: "caution",
|
|
15724
|
+
reason: "hidden-by-task-lens",
|
|
15725
|
+
count: hiddenCautionMemoryCount
|
|
15726
|
+
});
|
|
15727
|
+
}
|
|
15728
|
+
return receipt;
|
|
15729
|
+
}
|
|
15730
|
+
function scheduledActions(cautions) {
|
|
15731
|
+
return cautions.filter((caution) => caution.kind === "codegraph-refresh-queued").map((caution) => short(caution.message, 28));
|
|
14877
15732
|
}
|
|
14878
15733
|
function renderTaskWorksetPrompt(input) {
|
|
14879
15734
|
const maxTokens = input.budget?.maxTokens ?? 180;
|
|
14880
15735
|
const omitted = [];
|
|
15736
|
+
const selected = [];
|
|
14881
15737
|
const lines = ["Memorix Autopilot Brief"];
|
|
14882
15738
|
const task = short(input.task || "Continue the current task.", 34);
|
|
14883
|
-
appendLine(lines, "Task: " + task, maxTokens, omitted, "task-detail"
|
|
15739
|
+
appendLine(lines, "Task: " + task, maxTokens, omitted, "task-detail", selected, {
|
|
15740
|
+
kind: "task",
|
|
15741
|
+
reason: "current task supplied by the caller",
|
|
15742
|
+
trust: "source-backed"
|
|
15743
|
+
});
|
|
14884
15744
|
appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
|
|
14885
15745
|
if (input.cautions.length > 0 || input.cautionMemory.length > 0) {
|
|
14886
15746
|
appendLine(lines, "", maxTokens, omitted, "caution-heading");
|
|
14887
15747
|
appendLine(lines, "Cautions", maxTokens, omitted, "caution-heading");
|
|
14888
15748
|
for (const caution of input.cautions.slice(0, 6)) {
|
|
14889
|
-
appendLine(lines, "- " + short(caution.message, 22), maxTokens, omitted, "caution"
|
|
15749
|
+
appendLine(lines, "- " + short(caution.message, 22), maxTokens, omitted, "caution", selected, {
|
|
15750
|
+
kind: "caution",
|
|
15751
|
+
id: "caution:" + caution.kind,
|
|
15752
|
+
reason: "current project caution",
|
|
15753
|
+
trust: "source-backed"
|
|
15754
|
+
});
|
|
14890
15755
|
}
|
|
14891
15756
|
for (const memory of input.cautionMemory.slice(0, 3)) {
|
|
14892
15757
|
const location = memory.path ? memory.path + (memory.symbol ? "#" + memory.symbol : "") : "no current code location";
|
|
@@ -14896,7 +15761,15 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14896
15761
|
"- #" + memory.id + " " + memory.status + ": " + short(memory.title, 18) + " (" + location + reason + ")",
|
|
14897
15762
|
maxTokens,
|
|
14898
15763
|
omitted,
|
|
14899
|
-
"caution-memory"
|
|
15764
|
+
"caution-memory",
|
|
15765
|
+
selected,
|
|
15766
|
+
{
|
|
15767
|
+
kind: "memory",
|
|
15768
|
+
id: "memory:" + memory.id,
|
|
15769
|
+
reason: "task-relevant memory requiring source verification",
|
|
15770
|
+
freshness: freshnessForMemory(memory.status),
|
|
15771
|
+
trust: "historical"
|
|
15772
|
+
}
|
|
14900
15773
|
);
|
|
14901
15774
|
}
|
|
14902
15775
|
if (input.hiddenCautionMemoryCount > 0) {
|
|
@@ -14907,13 +15780,22 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14907
15780
|
appendLine(lines, "", maxTokens, omitted, "facts-heading");
|
|
14908
15781
|
appendLine(lines, "Current project facts", maxTokens, omitted, "facts-heading");
|
|
14909
15782
|
for (const fact of input.currentFacts.slice(0, 4)) {
|
|
14910
|
-
appendLine(lines, "- " + short(fact, 40), maxTokens, omitted, "current-fact"
|
|
15783
|
+
appendLine(lines, "- " + short(fact, 40), maxTokens, omitted, "current-fact", selected, {
|
|
15784
|
+
kind: "current-fact",
|
|
15785
|
+
reason: "current project state",
|
|
15786
|
+
trust: "source-backed"
|
|
15787
|
+
});
|
|
14911
15788
|
}
|
|
14912
15789
|
}
|
|
14913
15790
|
if (input.codeState) {
|
|
14914
15791
|
appendLine(lines, "", maxTokens, omitted, "state-heading");
|
|
14915
15792
|
appendLine(lines, "Project state", maxTokens, omitted, "state-heading");
|
|
14916
|
-
appendLine(lines, input.codeState, maxTokens, omitted, "code-state"
|
|
15793
|
+
appendLine(lines, input.codeState, maxTokens, omitted, "code-state", selected, {
|
|
15794
|
+
kind: "code-state",
|
|
15795
|
+
...input.provenance.snapshotId ? { id: "snapshot:" + input.provenance.snapshotId } : {},
|
|
15796
|
+
reason: "latest available Code State snapshot",
|
|
15797
|
+
trust: "source-backed"
|
|
15798
|
+
});
|
|
14917
15799
|
}
|
|
14918
15800
|
if (input.semanticCode && (input.semanticCode.entryPoints.length > 0 || input.semanticCode.relations.length > 0)) {
|
|
14919
15801
|
appendLine(lines, "", maxTokens, omitted, "semantic-code-heading");
|
|
@@ -14925,7 +15807,14 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14925
15807
|
"- " + location + ": " + short(relation.from.name, 12) + " " + short(relation.kind, 8) + " " + short(relation.to.name, 12),
|
|
14926
15808
|
maxTokens,
|
|
14927
15809
|
omitted,
|
|
14928
|
-
"semantic-relation"
|
|
15810
|
+
"semantic-relation",
|
|
15811
|
+
selected,
|
|
15812
|
+
{
|
|
15813
|
+
kind: "semantic-code",
|
|
15814
|
+
id: "code:" + location,
|
|
15815
|
+
reason: "validated optional semantic code relation",
|
|
15816
|
+
trust: "derived"
|
|
15817
|
+
}
|
|
14929
15818
|
);
|
|
14930
15819
|
}
|
|
14931
15820
|
if (input.semanticCode.relations.length === 0) {
|
|
@@ -14936,7 +15825,14 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14936
15825
|
"- " + location + ": " + short(entry.name, 16) + " (" + short(entry.kind, 8) + ")",
|
|
14937
15826
|
maxTokens,
|
|
14938
15827
|
omitted,
|
|
14939
|
-
"semantic-entry"
|
|
15828
|
+
"semantic-entry",
|
|
15829
|
+
selected,
|
|
15830
|
+
{
|
|
15831
|
+
kind: "semantic-code",
|
|
15832
|
+
id: "code:" + location,
|
|
15833
|
+
reason: "validated optional semantic code entry point",
|
|
15834
|
+
trust: "derived"
|
|
15835
|
+
}
|
|
14940
15836
|
);
|
|
14941
15837
|
}
|
|
14942
15838
|
}
|
|
@@ -14945,7 +15841,12 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14945
15841
|
appendLine(lines, "", maxTokens, omitted, "start-heading");
|
|
14946
15842
|
appendLine(lines, "Start here", maxTokens, omitted, "start-heading");
|
|
14947
15843
|
for (const source of input.startHere.slice(0, 5)) {
|
|
14948
|
-
appendLine(lines, "- " + source, maxTokens, omitted, "start-here"
|
|
15844
|
+
appendLine(lines, "- " + source, maxTokens, omitted, "start-here", selected, {
|
|
15845
|
+
kind: "start-here",
|
|
15846
|
+
id: "path:" + source,
|
|
15847
|
+
reason: "task-lensed starting point",
|
|
15848
|
+
trust: "derived"
|
|
15849
|
+
});
|
|
14949
15850
|
}
|
|
14950
15851
|
}
|
|
14951
15852
|
if (input.reliableMemory.length > 0) {
|
|
@@ -14958,7 +15859,15 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14958
15859
|
"- #" + memory.id + " " + memory.type + ": " + short(memory.title, 18) + " (" + location + ")",
|
|
14959
15860
|
maxTokens,
|
|
14960
15861
|
omitted,
|
|
14961
|
-
"reliable-memory"
|
|
15862
|
+
"reliable-memory",
|
|
15863
|
+
selected,
|
|
15864
|
+
{
|
|
15865
|
+
kind: "memory",
|
|
15866
|
+
id: "memory:" + memory.id,
|
|
15867
|
+
reason: "current code-bound memory",
|
|
15868
|
+
freshness: freshnessForMemory(memory.status),
|
|
15869
|
+
trust: "historical"
|
|
15870
|
+
}
|
|
14962
15871
|
);
|
|
14963
15872
|
}
|
|
14964
15873
|
}
|
|
@@ -14966,10 +15875,25 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14966
15875
|
appendLine(lines, "", maxTokens, omitted, "knowledge-heading");
|
|
14967
15876
|
appendLine(lines, "Project knowledge", maxTokens, omitted, "knowledge-heading");
|
|
14968
15877
|
for (const claim of input.claims.slice(0, 3)) {
|
|
14969
|
-
appendLine(lines, "- " + claim.assertion + " [" + claim.id + "]", maxTokens, omitted, "claim"
|
|
15878
|
+
appendLine(lines, "- " + claim.assertion + " [" + claim.id + "]", maxTokens, omitted, "claim", selected, {
|
|
15879
|
+
kind: "claim",
|
|
15880
|
+
id: "claim:" + claim.id,
|
|
15881
|
+
reason: "source-qualified task match",
|
|
15882
|
+
trust: "source-backed"
|
|
15883
|
+
});
|
|
14970
15884
|
}
|
|
14971
15885
|
for (const page of input.pages.slice(0, 2)) {
|
|
14972
|
-
|
|
15886
|
+
const supportsDeliveredClaim = page.claimIds.some((claimId) => selected.some((item) => item.kind === "claim" && item.id === "claim:" + claimId));
|
|
15887
|
+
if (!supportsDeliveredClaim) {
|
|
15888
|
+
omitted.push("knowledge-page-dependency");
|
|
15889
|
+
continue;
|
|
15890
|
+
}
|
|
15891
|
+
appendLine(lines, "- page: " + page.relativePath, maxTokens, omitted, "knowledge-page", selected, {
|
|
15892
|
+
kind: "knowledge-page",
|
|
15893
|
+
id: "page:" + page.id,
|
|
15894
|
+
reason: "approved page linked to a selected claim",
|
|
15895
|
+
trust: "source-backed"
|
|
15896
|
+
});
|
|
14973
15897
|
}
|
|
14974
15898
|
}
|
|
14975
15899
|
if (input.workflows.length > 0) {
|
|
@@ -14981,7 +15905,14 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14981
15905
|
"- " + workflow.title + ": " + workflow.firstPhase.title + " - " + workflow.firstPhase.instructions,
|
|
14982
15906
|
maxTokens,
|
|
14983
15907
|
omitted,
|
|
14984
|
-
"workflow"
|
|
15908
|
+
"workflow",
|
|
15909
|
+
selected,
|
|
15910
|
+
{
|
|
15911
|
+
kind: "workflow",
|
|
15912
|
+
id: "workflow:" + workflow.id,
|
|
15913
|
+
reason: "task-matching project workflow",
|
|
15914
|
+
trust: "source-backed"
|
|
15915
|
+
}
|
|
14985
15916
|
);
|
|
14986
15917
|
}
|
|
14987
15918
|
}
|
|
@@ -14989,16 +15920,23 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14989
15920
|
appendLine(lines, "", maxTokens, omitted, "verification-heading");
|
|
14990
15921
|
appendLine(lines, "Verify", maxTokens, omitted, "verification-heading");
|
|
14991
15922
|
for (const check of input.verification.slice(0, 4)) {
|
|
14992
|
-
appendLine(lines, "- " + short(check, 20), maxTokens, omitted, "verification"
|
|
15923
|
+
appendLine(lines, "- " + short(check, 20), maxTokens, omitted, "verification", selected, {
|
|
15924
|
+
kind: "verification",
|
|
15925
|
+
reason: "task-lensed verification guidance",
|
|
15926
|
+
trust: "derived"
|
|
15927
|
+
});
|
|
14993
15928
|
}
|
|
14994
15929
|
}
|
|
14995
15930
|
return {
|
|
14996
15931
|
prompt: lines.join("\n"),
|
|
14997
15932
|
tokenCount: countTextTokens(lines.join("\n")),
|
|
14998
|
-
omitted: unique(omitted)
|
|
15933
|
+
omitted: unique(omitted),
|
|
15934
|
+
omittedItems: omitted,
|
|
15935
|
+
selected
|
|
14999
15936
|
};
|
|
15000
15937
|
}
|
|
15001
15938
|
async function buildTaskWorkset(input) {
|
|
15939
|
+
const startedAt = Date.now();
|
|
15002
15940
|
const task = input.task?.trim() ?? "";
|
|
15003
15941
|
const maxTokens = Math.max(96, Math.min(Math.floor(input.maxTokens ?? 180), 320));
|
|
15004
15942
|
const cautions = [...input.runtimeCautions ?? [], ...snapshotCautions(input)];
|
|
@@ -15100,6 +16038,18 @@ async function buildTaskWorkset(input) {
|
|
|
15100
16038
|
...base,
|
|
15101
16039
|
budget: { maxTokens }
|
|
15102
16040
|
});
|
|
16041
|
+
const receipt = {
|
|
16042
|
+
version: "1.2.2",
|
|
16043
|
+
target: input.deliveryTarget ?? "project-context",
|
|
16044
|
+
elapsedMs: Math.max(0, Date.now() - startedAt),
|
|
16045
|
+
budget: {
|
|
16046
|
+
maxTokens,
|
|
16047
|
+
tokenCount: rendered.tokenCount
|
|
16048
|
+
},
|
|
16049
|
+
selected: rendered.selected,
|
|
16050
|
+
omitted: receiptOmissions(rendered.omittedItems, base.hiddenCautionMemoryCount),
|
|
16051
|
+
scheduledActions: scheduledActions(normalizedCautions)
|
|
16052
|
+
};
|
|
15103
16053
|
return {
|
|
15104
16054
|
...base,
|
|
15105
16055
|
budget: {
|
|
@@ -15107,6 +16057,7 @@ async function buildTaskWorkset(input) {
|
|
|
15107
16057
|
tokenCount: rendered.tokenCount,
|
|
15108
16058
|
omitted: rendered.omitted
|
|
15109
16059
|
},
|
|
16060
|
+
receipt,
|
|
15110
16061
|
prompt: rendered.prompt
|
|
15111
16062
|
};
|
|
15112
16063
|
}
|
|
@@ -15128,7 +16079,8 @@ var init_workset = __esm({
|
|
|
15128
16079
|
// src/codegraph/current-facts.ts
|
|
15129
16080
|
var current_facts_exports = {};
|
|
15130
16081
|
__export(current_facts_exports, {
|
|
15131
|
-
collectCurrentProjectFacts: () => collectCurrentProjectFacts
|
|
16082
|
+
collectCurrentProjectFacts: () => collectCurrentProjectFacts,
|
|
16083
|
+
formatGitFact: () => formatGitFact
|
|
15132
16084
|
});
|
|
15133
16085
|
import { execFileSync } from "child_process";
|
|
15134
16086
|
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
@@ -15173,11 +16125,16 @@ function runGit2(rootPath, args) {
|
|
|
15173
16125
|
}
|
|
15174
16126
|
}
|
|
15175
16127
|
function readGitFacts(rootPath) {
|
|
15176
|
-
const
|
|
16128
|
+
const available = runGit2(rootPath, ["rev-parse", "--is-inside-work-tree"]) === "true";
|
|
16129
|
+
if (!available) {
|
|
16130
|
+
return { available: false, dirty: false, detached: false };
|
|
16131
|
+
}
|
|
16132
|
+
const branch = runGit2(rootPath, ["symbolic-ref", "--quiet", "--short", "HEAD"]) ?? runGit2(rootPath, ["branch", "--show-current"]);
|
|
15177
16133
|
const commit = runGit2(rootPath, ["rev-parse", "--short", "HEAD"]);
|
|
15178
16134
|
const latestCommit = runGit2(rootPath, ["log", "-1", "--pretty=%s"]);
|
|
15179
16135
|
const dirty = Boolean(runGit2(rootPath, ["status", "--porcelain"]));
|
|
15180
16136
|
return {
|
|
16137
|
+
available: true,
|
|
15181
16138
|
...branch ? { branch } : {},
|
|
15182
16139
|
...commit ? { commit } : {},
|
|
15183
16140
|
...latestCommit ? { latestCommit } : {},
|
|
@@ -15185,6 +16142,15 @@ function readGitFacts(rootPath) {
|
|
|
15185
16142
|
detached: !branch
|
|
15186
16143
|
};
|
|
15187
16144
|
}
|
|
16145
|
+
function formatGitFact(git) {
|
|
16146
|
+
if (!git.available) return "Git: unavailable";
|
|
16147
|
+
const parts = [];
|
|
16148
|
+
if (git.detached) parts.push("detached HEAD");
|
|
16149
|
+
else if (git.branch) parts.push("branch " + git.branch);
|
|
16150
|
+
if (git.commit) parts.push("commit " + git.commit);
|
|
16151
|
+
parts.push(git.dirty ? "dirty worktree" : "clean worktree");
|
|
16152
|
+
return "Git: " + parts.join(", ");
|
|
16153
|
+
}
|
|
15188
16154
|
function parseProgressNote(content) {
|
|
15189
16155
|
const lastUpdated = content.match(/Last updated\*\*:\s*(\d{4}-\d{2}-\d{2})/i) ?? content.match(/Last updated:\s*(\d{4}-\d{2}-\d{2})/i);
|
|
15190
16156
|
const branchHint = content.match(/Branch\*\*:\s*([^\r\n]+)/i) ?? content.match(/Branch:\s*([^\r\n]+)/i);
|
|
@@ -15801,370 +16767,57 @@ function collectGraph(store, projectId, observations2, exclude) {
|
|
|
15801
16767
|
}
|
|
15802
16768
|
return {
|
|
15803
16769
|
files,
|
|
15804
|
-
symbols,
|
|
15805
|
-
refs,
|
|
15806
|
-
freshness,
|
|
15807
|
-
sources,
|
|
15808
|
-
suggestedReads: compactSuggestedReads(suggestedReads, 8, exclude)
|
|
15809
|
-
};
|
|
15810
|
-
}
|
|
15811
|
-
function overviewFromGraph(input) {
|
|
15812
|
-
const status = input.store.status(input.project.id);
|
|
15813
|
-
return {
|
|
15814
|
-
project: input.project,
|
|
15815
|
-
code: {
|
|
15816
|
-
provider: status.provider,
|
|
15817
|
-
files: status.files,
|
|
15818
|
-
symbols: status.symbols,
|
|
15819
|
-
edges: status.edges,
|
|
15820
|
-
refs: status.refs,
|
|
15821
|
-
...status.indexedAt ? { indexedAt: status.indexedAt } : {},
|
|
15822
|
-
languages: countLanguages(input.graph.files),
|
|
15823
|
-
...status.latestSnapshot ? { latestSnapshot: status.latestSnapshot } : {}
|
|
15824
|
-
},
|
|
15825
|
-
memory: {
|
|
15826
|
-
total: input.observations.filter((obs) => obs.projectId === input.project.id).length,
|
|
15827
|
-
active: input.active.length
|
|
15828
|
-
},
|
|
15829
|
-
freshness: input.graph.freshness,
|
|
15830
|
-
suggestedReads: input.graph.suggestedReads
|
|
15831
|
-
};
|
|
15832
|
-
}
|
|
15833
|
-
function buildProjectContextExplain(input) {
|
|
15834
|
-
const active = activeObservations(input.observations, input.project.id);
|
|
15835
|
-
const graph = collectGraph(input.store, input.project.id, active, input.exclude);
|
|
15836
|
-
const overview = overviewFromGraph({
|
|
15837
|
-
project: input.project,
|
|
15838
|
-
store: input.store,
|
|
15839
|
-
observations: input.observations,
|
|
15840
|
-
active,
|
|
15841
|
-
graph
|
|
15842
|
-
});
|
|
15843
|
-
return {
|
|
15844
|
-
project: input.project,
|
|
15845
|
-
sources: graph.sources.sort((a, b) => a.observationId - b.observationId || (a.path ?? "").localeCompare(b.path ?? "")),
|
|
15846
|
-
overview
|
|
15847
|
-
};
|
|
15848
|
-
}
|
|
15849
|
-
var init_project_context = __esm({
|
|
15850
|
-
"src/codegraph/project-context.ts"() {
|
|
15851
|
-
"use strict";
|
|
15852
|
-
init_esm_shims();
|
|
15853
|
-
init_freshness2();
|
|
15854
|
-
init_exclude();
|
|
15855
|
-
}
|
|
15856
|
-
});
|
|
15857
|
-
|
|
15858
|
-
// src/codegraph/task-lens.ts
|
|
15859
|
-
var task_lens_exports = {};
|
|
15860
|
-
__export(task_lens_exports, {
|
|
15861
|
-
lensPathCandidates: () => lensPathCandidates,
|
|
15862
|
-
lensVerificationHints: () => lensVerificationHints,
|
|
15863
|
-
rankLensPaths: () => rankLensPaths,
|
|
15864
|
-
rankLensSources: () => rankLensSources,
|
|
15865
|
-
resolveTaskLens: () => resolveTaskLens,
|
|
15866
|
-
scoreLensSource: () => scoreLensSource,
|
|
15867
|
-
shouldShowLensSource: () => shouldShowLensSource
|
|
15868
|
-
});
|
|
15869
|
-
function normalizePath2(path25) {
|
|
15870
|
-
return path25.replace(/\\/g, "/");
|
|
15871
|
-
}
|
|
15872
|
-
function tokenize2(text) {
|
|
15873
|
-
return (text.toLowerCase().match(/[a-z0-9_./-]+|[\u4e00-\u9fff]+/g) ?? []).map((token) => token.trim()).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
|
|
15874
|
-
}
|
|
15875
|
-
function containsKeyword(text, keyword) {
|
|
15876
|
-
if (/^[a-z0-9_-]+$/i.test(keyword)) {
|
|
15877
|
-
return new RegExp(`(^|[^a-z0-9_-])${keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}([^a-z0-9_-]|$)`, "i").test(text);
|
|
15878
|
-
}
|
|
15879
|
-
return text.includes(keyword);
|
|
15880
|
-
}
|
|
15881
|
-
function resolveTaskLens(task) {
|
|
15882
|
-
const normalized = (task ?? "").toLowerCase();
|
|
15883
|
-
if (!normalized.trim()) return LENSES.general;
|
|
15884
|
-
let best = {
|
|
15885
|
-
id: "general",
|
|
15886
|
-
score: 0,
|
|
15887
|
-
priority: Number.MAX_SAFE_INTEGER
|
|
15888
|
-
};
|
|
15889
|
-
for (const id of LENS_PRIORITY) {
|
|
15890
|
-
const score = KEYWORDS[id].reduce((sum, keyword) => sum + (containsKeyword(normalized, keyword) ? 1 : 0), 0);
|
|
15891
|
-
const priority = LENS_PRIORITY.indexOf(id);
|
|
15892
|
-
if (score > best.score || score === best.score && score > 0 && priority < best.priority) {
|
|
15893
|
-
best = { id, score, priority };
|
|
15894
|
-
}
|
|
15895
|
-
}
|
|
15896
|
-
return best.score > 0 ? LENSES[best.id] : LENSES.general;
|
|
15897
|
-
}
|
|
15898
|
-
function pathKindScore(path25, lens) {
|
|
15899
|
-
const normalized = normalizePath2(path25).toLowerCase();
|
|
15900
|
-
const name = normalized.split("/").pop() ?? normalized;
|
|
15901
|
-
const inDocs = normalized.startsWith("docs/") || name === "readme.md" || name.includes("readme");
|
|
15902
|
-
const isTest = /(^|\/)(tests?|__tests__|spec|fixtures?)\//.test(normalized) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized) || /\.(test|spec)\.py$/.test(normalized);
|
|
15903
|
-
const isSource = normalized.startsWith("src/") || normalized.includes("/src/");
|
|
15904
|
-
const isPackage = name === "package.json" || name === "package-lock.json" || name === "pnpm-lock.yaml" || name === "yarn.lock";
|
|
15905
|
-
const isChangelog = name === "changelog.md" || name === "changes.md";
|
|
15906
|
-
const isWorkflow = normalized.startsWith(".github/workflows/");
|
|
15907
|
-
switch (lens.id) {
|
|
15908
|
-
case "bugfix":
|
|
15909
|
-
return (isTest ? 80 : 0) + (isSource ? 50 : 0) + (normalized.includes("debug") ? 20 : 0);
|
|
15910
|
-
case "test":
|
|
15911
|
-
return (isTest ? 90 : 0) + (isSource ? 45 : 0);
|
|
15912
|
-
case "release":
|
|
15913
|
-
return (isChangelog ? 100 : 0) + (isPackage ? 90 : 0) + (isWorkflow ? 70 : 0) + (inDocs ? 35 : 0);
|
|
15914
|
-
case "onboarding":
|
|
15915
|
-
return (name === "readme.md" ? 100 : 0) + (inDocs ? 80 : 0) + (isPackage ? 45 : 0) + (isSource ? 20 : 0);
|
|
15916
|
-
case "docs":
|
|
15917
|
-
return (inDocs ? 100 : 0) + (isChangelog ? 60 : 0) + (isSource ? 25 : 0);
|
|
15918
|
-
case "refactor":
|
|
15919
|
-
return (isSource ? 70 : 0) + (isTest ? 65 : 0);
|
|
15920
|
-
case "feature":
|
|
15921
|
-
return (isSource ? 80 : 0) + (isTest ? 45 : 0) + (inDocs ? 20 : 0);
|
|
15922
|
-
default:
|
|
15923
|
-
if (isSource || isTest) return 60;
|
|
15924
|
-
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 45;
|
|
15925
|
-
if (inDocs) return 20;
|
|
15926
|
-
return 0;
|
|
15927
|
-
}
|
|
15928
|
-
}
|
|
15929
|
-
function tokenScore(text, tokens) {
|
|
15930
|
-
if (tokens.length === 0) return 0;
|
|
15931
|
-
const normalized = text.toLowerCase();
|
|
15932
|
-
return tokens.reduce((sum, token) => sum + (normalized.includes(token) ? 12 : 0), 0);
|
|
15933
|
-
}
|
|
15934
|
-
function sourceTaskMatchScore(source, task) {
|
|
15935
|
-
const tokens = tokenize2(task ?? "");
|
|
15936
|
-
if (tokens.length === 0) return 0;
|
|
15937
|
-
const text = [
|
|
15938
|
-
source.title,
|
|
15939
|
-
source.path ?? "",
|
|
15940
|
-
source.symbol ?? ""
|
|
15941
|
-
].join("\n").toLowerCase();
|
|
15942
|
-
const matches = tokens.filter((token) => text.includes(token)).length;
|
|
15943
|
-
if (matches >= 2) return 40;
|
|
15944
|
-
if (matches === 1) return 24;
|
|
15945
|
-
return 0;
|
|
15946
|
-
}
|
|
15947
|
-
function fallbackPathRank(path25) {
|
|
15948
|
-
const normalized = normalizePath2(path25);
|
|
15949
|
-
if (normalized.startsWith("src/")) return 0;
|
|
15950
|
-
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 1;
|
|
15951
|
-
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
|
|
15952
|
-
if (normalized.startsWith("docs/") || normalized.toLowerCase() === "readme.md") return 3;
|
|
15953
|
-
return 4;
|
|
15954
|
-
}
|
|
15955
|
-
function rankLensPaths(paths, lens, task) {
|
|
15956
|
-
const tokens = tokenize2(task ?? "");
|
|
15957
|
-
return [...new Set(paths.map(normalizePath2))].map((path25, index) => ({
|
|
15958
|
-
path: path25,
|
|
15959
|
-
index,
|
|
15960
|
-
score: pathKindScore(path25, lens) + tokenScore(path25, tokens),
|
|
15961
|
-
fallback: fallbackPathRank(path25)
|
|
15962
|
-
})).sort((a, b) => b.score - a.score || a.fallback - b.fallback || a.index - b.index || a.path.localeCompare(b.path)).map((item) => item.path);
|
|
15963
|
-
}
|
|
15964
|
-
function lensPathCandidates(lens) {
|
|
15965
|
-
switch (lens.id) {
|
|
15966
|
-
case "release":
|
|
15967
|
-
return ["CHANGELOG.md", "package.json", "package-lock.json", ".github/workflows/ci.yml", ".github/workflows/test.yml", "README.md"];
|
|
15968
|
-
case "onboarding":
|
|
15969
|
-
return ["README.md", "docs/README.md", "docs/API_REFERENCE.md", "package.json"];
|
|
15970
|
-
case "docs":
|
|
15971
|
-
return ["README.md", "README.zh-CN.md", "docs/README.md", "docs/API_REFERENCE.md", "CHANGELOG.md"];
|
|
15972
|
-
case "test":
|
|
15973
|
-
case "bugfix":
|
|
15974
|
-
return ["tests", "test"];
|
|
15975
|
-
default:
|
|
15976
|
-
return [];
|
|
15977
|
-
}
|
|
15978
|
-
}
|
|
15979
|
-
function scoreLensSource(source, lens, task) {
|
|
15980
|
-
const tokens = tokenize2(task ?? "");
|
|
15981
|
-
const text = [source.title, source.type, source.path ?? "", source.symbol ?? ""].join("\n");
|
|
15982
|
-
return pathKindScore(source.path ?? "", lens) + tokenScore(text, tokens) + sourceTaskMatchScore(source, task);
|
|
15983
|
-
}
|
|
15984
|
-
function rankLensSources(sources, lens, task) {
|
|
15985
|
-
return [...sources].map((source, index) => ({
|
|
15986
|
-
source,
|
|
15987
|
-
index,
|
|
15988
|
-
score: scoreLensSource(source, lens, task)
|
|
15989
|
-
})).sort((a, b) => b.score - a.score || a.source.observationId - b.source.observationId || a.index - b.index).map((item) => item.source);
|
|
15990
|
-
}
|
|
15991
|
-
function lensVerificationHints(lens) {
|
|
15992
|
-
switch (lens.id) {
|
|
15993
|
-
case "bugfix":
|
|
15994
|
-
return [
|
|
15995
|
-
"run the smallest failing test or repro first",
|
|
15996
|
-
"inspect the changed code path before trusting old memory"
|
|
15997
|
-
];
|
|
15998
|
-
case "test":
|
|
15999
|
-
return [
|
|
16000
|
-
"run the exact focused test file or test name first",
|
|
16001
|
-
"inspect fixtures and harness setup before changing assertions"
|
|
16002
|
-
];
|
|
16003
|
-
case "release":
|
|
16004
|
-
return [
|
|
16005
|
-
"run build, tests, package smoke, and publish dry-run where available",
|
|
16006
|
-
"verify package metadata, changelog, and Git state before publishing"
|
|
16007
|
-
];
|
|
16008
|
-
case "onboarding":
|
|
16009
|
-
return [
|
|
16010
|
-
"read the docs/start files first, then inspect only the code paths needed for the task",
|
|
16011
|
-
"treat old implementation memories as leads until current code confirms them"
|
|
16012
|
-
];
|
|
16013
|
-
case "refactor":
|
|
16014
|
-
return [
|
|
16015
|
-
"inspect call sites and tests before editing shared code",
|
|
16016
|
-
"run the narrow affected test plus one regression smoke"
|
|
16017
|
-
];
|
|
16018
|
-
case "docs":
|
|
16019
|
-
return [
|
|
16020
|
-
"check headings, links, commands, and examples against current code",
|
|
16021
|
-
"run the smallest docs or package smoke available"
|
|
16022
|
-
];
|
|
16023
|
-
case "feature":
|
|
16024
|
-
return [
|
|
16025
|
-
"inspect the closest existing implementation pattern before adding new code",
|
|
16026
|
-
"run focused tests plus one user-flow smoke after changes"
|
|
16027
|
-
];
|
|
16028
|
-
default:
|
|
16029
|
-
return [
|
|
16030
|
-
"inspect the Start here files before editing",
|
|
16031
|
-
"run the smallest relevant test or smoke command after changes"
|
|
16032
|
-
];
|
|
16033
|
-
}
|
|
16770
|
+
symbols,
|
|
16771
|
+
refs,
|
|
16772
|
+
freshness,
|
|
16773
|
+
sources,
|
|
16774
|
+
suggestedReads: compactSuggestedReads(suggestedReads, 8, exclude)
|
|
16775
|
+
};
|
|
16034
16776
|
}
|
|
16035
|
-
function
|
|
16036
|
-
|
|
16037
|
-
|
|
16038
|
-
|
|
16039
|
-
|
|
16777
|
+
function overviewFromGraph(input) {
|
|
16778
|
+
const status = input.store.status(input.project.id);
|
|
16779
|
+
return {
|
|
16780
|
+
project: input.project,
|
|
16781
|
+
code: {
|
|
16782
|
+
provider: status.provider,
|
|
16783
|
+
files: status.files,
|
|
16784
|
+
symbols: status.symbols,
|
|
16785
|
+
edges: status.edges,
|
|
16786
|
+
refs: status.refs,
|
|
16787
|
+
...status.indexedAt ? { indexedAt: status.indexedAt } : {},
|
|
16788
|
+
languages: countLanguages(input.graph.files),
|
|
16789
|
+
...status.latestSnapshot ? { latestSnapshot: status.latestSnapshot } : {}
|
|
16790
|
+
},
|
|
16791
|
+
memory: {
|
|
16792
|
+
total: input.observations.filter((obs) => obs.projectId === input.project.id).length,
|
|
16793
|
+
active: input.active.length
|
|
16794
|
+
},
|
|
16795
|
+
freshness: input.graph.freshness,
|
|
16796
|
+
suggestedReads: input.graph.suggestedReads
|
|
16797
|
+
};
|
|
16040
16798
|
}
|
|
16041
|
-
|
|
16042
|
-
|
|
16043
|
-
|
|
16799
|
+
function buildProjectContextExplain(input) {
|
|
16800
|
+
const active = activeObservations(input.observations, input.project.id);
|
|
16801
|
+
const graph = collectGraph(input.store, input.project.id, active, input.exclude);
|
|
16802
|
+
const overview = overviewFromGraph({
|
|
16803
|
+
project: input.project,
|
|
16804
|
+
store: input.store,
|
|
16805
|
+
observations: input.observations,
|
|
16806
|
+
active,
|
|
16807
|
+
graph
|
|
16808
|
+
});
|
|
16809
|
+
return {
|
|
16810
|
+
project: input.project,
|
|
16811
|
+
sources: graph.sources.sort((a, b) => a.observationId - b.observationId || (a.path ?? "").localeCompare(b.path ?? "")),
|
|
16812
|
+
overview
|
|
16813
|
+
};
|
|
16814
|
+
}
|
|
16815
|
+
var init_project_context = __esm({
|
|
16816
|
+
"src/codegraph/project-context.ts"() {
|
|
16044
16817
|
"use strict";
|
|
16045
16818
|
init_esm_shims();
|
|
16046
|
-
|
|
16047
|
-
|
|
16048
|
-
"an",
|
|
16049
|
-
"and",
|
|
16050
|
-
"as",
|
|
16051
|
-
"for",
|
|
16052
|
-
"in",
|
|
16053
|
-
"into",
|
|
16054
|
-
"of",
|
|
16055
|
-
"on",
|
|
16056
|
-
"onto",
|
|
16057
|
-
"the",
|
|
16058
|
-
"this",
|
|
16059
|
-
"that",
|
|
16060
|
-
"to",
|
|
16061
|
-
"with",
|
|
16062
|
-
"work",
|
|
16063
|
-
"project",
|
|
16064
|
-
"continue",
|
|
16065
|
-
"\u7EE7\u7EED",
|
|
16066
|
-
"\u9879\u76EE"
|
|
16067
|
-
]);
|
|
16068
|
-
LENSES = {
|
|
16069
|
-
bugfix: {
|
|
16070
|
-
id: "bugfix",
|
|
16071
|
-
description: "debug the failure with current code and the smallest repro first",
|
|
16072
|
-
sourceLimit: 6,
|
|
16073
|
-
cautionLimit: 4,
|
|
16074
|
-
hideUnrelatedCautionDetails: true,
|
|
16075
|
-
hideUnrelatedReliableDetails: false
|
|
16076
|
-
},
|
|
16077
|
-
feature: {
|
|
16078
|
-
id: "feature",
|
|
16079
|
-
description: "build a scoped feature from nearby source, types, and user flow",
|
|
16080
|
-
sourceLimit: 6,
|
|
16081
|
-
cautionLimit: 3,
|
|
16082
|
-
hideUnrelatedCautionDetails: true,
|
|
16083
|
-
hideUnrelatedReliableDetails: false
|
|
16084
|
-
},
|
|
16085
|
-
release: {
|
|
16086
|
-
id: "release",
|
|
16087
|
-
description: "prepare a release using current metadata, changelog, build, and package checks",
|
|
16088
|
-
sourceLimit: 4,
|
|
16089
|
-
cautionLimit: 2,
|
|
16090
|
-
hideUnrelatedCautionDetails: true,
|
|
16091
|
-
hideUnrelatedReliableDetails: true
|
|
16092
|
-
},
|
|
16093
|
-
onboarding: {
|
|
16094
|
-
id: "onboarding",
|
|
16095
|
-
description: "understand the project shape before trusting old implementation details",
|
|
16096
|
-
sourceLimit: 4,
|
|
16097
|
-
cautionLimit: 2,
|
|
16098
|
-
hideUnrelatedCautionDetails: true,
|
|
16099
|
-
hideUnrelatedReliableDetails: true
|
|
16100
|
-
},
|
|
16101
|
-
refactor: {
|
|
16102
|
-
id: "refactor",
|
|
16103
|
-
description: "change structure carefully by reading shared code, call sites, and tests",
|
|
16104
|
-
sourceLimit: 6,
|
|
16105
|
-
cautionLimit: 4,
|
|
16106
|
-
hideUnrelatedCautionDetails: true,
|
|
16107
|
-
hideUnrelatedReliableDetails: false
|
|
16108
|
-
},
|
|
16109
|
-
docs: {
|
|
16110
|
-
id: "docs",
|
|
16111
|
-
description: "update documentation against current code and public entry points",
|
|
16112
|
-
sourceLimit: 5,
|
|
16113
|
-
cautionLimit: 2,
|
|
16114
|
-
hideUnrelatedCautionDetails: true,
|
|
16115
|
-
hideUnrelatedReliableDetails: true
|
|
16116
|
-
},
|
|
16117
|
-
test: {
|
|
16118
|
-
id: "test",
|
|
16119
|
-
description: "work from tests, fixtures, harnesses, and the related source files",
|
|
16120
|
-
sourceLimit: 6,
|
|
16121
|
-
cautionLimit: 3,
|
|
16122
|
-
hideUnrelatedCautionDetails: true,
|
|
16123
|
-
hideUnrelatedReliableDetails: false
|
|
16124
|
-
},
|
|
16125
|
-
general: {
|
|
16126
|
-
id: "general",
|
|
16127
|
-
description: "balanced project handoff with current facts, code memory, and verification hints",
|
|
16128
|
-
sourceLimit: 8,
|
|
16129
|
-
cautionLimit: 5,
|
|
16130
|
-
hideUnrelatedCautionDetails: false,
|
|
16131
|
-
hideUnrelatedReliableDetails: false
|
|
16132
|
-
}
|
|
16133
|
-
};
|
|
16134
|
-
KEYWORDS = {
|
|
16135
|
-
bugfix: [
|
|
16136
|
-
"bug",
|
|
16137
|
-
"crash",
|
|
16138
|
-
"debug",
|
|
16139
|
-
"error",
|
|
16140
|
-
"fail",
|
|
16141
|
-
"failing",
|
|
16142
|
-
"fix",
|
|
16143
|
-
"issue",
|
|
16144
|
-
"regression",
|
|
16145
|
-
"repro",
|
|
16146
|
-
"\u62A5\u9519",
|
|
16147
|
-
"\u5D29\u6E83",
|
|
16148
|
-
"\u5931\u8D25",
|
|
16149
|
-
"\u4FEE\u590D",
|
|
16150
|
-
"\u95EE\u9898"
|
|
16151
|
-
],
|
|
16152
|
-
feature: ["add", "build", "feature", "implement", "new", "support", "\u65B0\u589E", "\u5B9E\u73B0", "\u652F\u6301", "\u529F\u80FD"],
|
|
16153
|
-
release: ["bump", "changelog", "npm", "pack", "publish", "release", "version", "\u53D1\u7248", "\u53D1\u5E03", "\u7248\u672C"],
|
|
16154
|
-
onboarding: ["architecture", "handoff", "onboard", "overview", "understand", "\u63A5\u624B", "\u4E86\u89E3", "\u7406\u89E3", "\u67B6\u6784"],
|
|
16155
|
-
refactor: ["cleanup", "migrate", "refactor", "rename", "restructure", "\u91CD\u6784", "\u8FC1\u79FB", "\u6539\u9020"],
|
|
16156
|
-
docs: ["doc", "docs", "readme", "\u6587\u6863", "\u8BF4\u660E"],
|
|
16157
|
-
test: ["coverage", "fixture", "smoke", "spec", "test", "tests", "testing", "vitest", "\u6D4B\u8BD5"]
|
|
16158
|
-
};
|
|
16159
|
-
LENS_PRIORITY = [
|
|
16160
|
-
"bugfix",
|
|
16161
|
-
"release",
|
|
16162
|
-
"test",
|
|
16163
|
-
"refactor",
|
|
16164
|
-
"feature",
|
|
16165
|
-
"docs",
|
|
16166
|
-
"onboarding"
|
|
16167
|
-
];
|
|
16819
|
+
init_freshness2();
|
|
16820
|
+
init_exclude();
|
|
16168
16821
|
}
|
|
16169
16822
|
});
|
|
16170
16823
|
|
|
@@ -16182,6 +16835,9 @@ import path19 from "path";
|
|
|
16182
16835
|
function activeProjectObservations(observations2, projectId) {
|
|
16183
16836
|
return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
|
|
16184
16837
|
}
|
|
16838
|
+
function deliveryEligibleProjectObservations(observations2, projectId) {
|
|
16839
|
+
return activeProjectObservations(observations2, projectId).filter((observation) => isEligibleForAutomaticDelivery(observation));
|
|
16840
|
+
}
|
|
16185
16841
|
function decideRefresh(input) {
|
|
16186
16842
|
if (input.mode === "never") {
|
|
16187
16843
|
return { performed: false, reason: "disabled", message: "Automatic project scan disabled." };
|
|
@@ -16256,13 +16912,27 @@ async function buildAutoProjectContext(input) {
|
|
|
16256
16912
|
activeProjectObservations(input.observations, input.project.id)
|
|
16257
16913
|
);
|
|
16258
16914
|
try {
|
|
16259
|
-
const {
|
|
16915
|
+
const { MaintenanceTargetStore: MaintenanceTargetStore2 } = await Promise.resolve().then(() => (init_maintenance_targets(), maintenance_targets_exports));
|
|
16916
|
+
new MaintenanceTargetStore2(input.dataDir).register({
|
|
16917
|
+
projectId: input.project.id,
|
|
16918
|
+
projectRoot: input.project.rootPath,
|
|
16919
|
+
dataDir: input.dataDir
|
|
16920
|
+
});
|
|
16921
|
+
const {
|
|
16922
|
+
enqueueClaimRequalification: enqueueClaimRequalification2,
|
|
16923
|
+
enqueueObservationQualification: enqueueObservationQualification2
|
|
16924
|
+
} = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
|
|
16260
16925
|
enqueueClaimRequalification2({
|
|
16261
16926
|
dataDir: input.dataDir,
|
|
16262
16927
|
projectId: input.project.id,
|
|
16263
16928
|
source: "foreground-refresh",
|
|
16264
16929
|
snapshotId: store.latestSnapshot(input.project.id)?.id
|
|
16265
16930
|
});
|
|
16931
|
+
enqueueObservationQualification2({
|
|
16932
|
+
dataDir: input.dataDir,
|
|
16933
|
+
projectId: input.project.id,
|
|
16934
|
+
source: "foreground-refresh"
|
|
16935
|
+
});
|
|
16266
16936
|
} catch {
|
|
16267
16937
|
}
|
|
16268
16938
|
refresh = { ...refresh, backfill };
|
|
@@ -16279,7 +16949,9 @@ async function buildAutoProjectContext(input) {
|
|
|
16279
16949
|
const explain = buildProjectContextExplain({
|
|
16280
16950
|
project: input.project,
|
|
16281
16951
|
store,
|
|
16282
|
-
|
|
16952
|
+
// Binding sees all active evidence, but automatic delivery sees only
|
|
16953
|
+
// evidence that has passed the control-plane admission gate.
|
|
16954
|
+
observations: deliveryEligibleProjectObservations(input.observations, input.project.id),
|
|
16283
16955
|
exclude
|
|
16284
16956
|
});
|
|
16285
16957
|
const overview = explain.overview;
|
|
@@ -16370,7 +17042,8 @@ async function buildAutoProjectContext(input) {
|
|
|
16370
17042
|
suspect: overview.freshness.suspect,
|
|
16371
17043
|
stale: overview.freshness.stale
|
|
16372
17044
|
},
|
|
16373
|
-
runtimeCautions
|
|
17045
|
+
runtimeCautions,
|
|
17046
|
+
...input.deliveryTarget ? { deliveryTarget: input.deliveryTarget } : {}
|
|
16374
17047
|
});
|
|
16375
17048
|
return {
|
|
16376
17049
|
project: input.project,
|
|
@@ -16463,15 +17136,7 @@ function formatCurrentFactsLines(facts) {
|
|
|
16463
17136
|
if (facts.latestChangelog) {
|
|
16464
17137
|
lines.push(`- Latest changelog: ${facts.latestChangelog.version}${facts.latestChangelog.date ? ` (${facts.latestChangelog.date})` : ""}`);
|
|
16465
17138
|
}
|
|
16466
|
-
|
|
16467
|
-
if (facts.git.detached) {
|
|
16468
|
-
gitParts.push("detached HEAD");
|
|
16469
|
-
} else if (facts.git.branch) {
|
|
16470
|
-
gitParts.push(`branch ${facts.git.branch}`);
|
|
16471
|
-
}
|
|
16472
|
-
if (facts.git.commit) gitParts.push(`commit ${facts.git.commit}`);
|
|
16473
|
-
gitParts.push(facts.git.dirty ? "dirty worktree" : "clean worktree");
|
|
16474
|
-
lines.push(`- Git: ${gitParts.join(", ")}`);
|
|
17139
|
+
lines.push("- " + formatGitFact(facts.git));
|
|
16475
17140
|
if (facts.git.latestCommit) lines.push(`- Latest commit: ${facts.git.latestCommit}`);
|
|
16476
17141
|
lines.push("- Current facts above outrank progress/dev-log files when they conflict.");
|
|
16477
17142
|
if (facts.staleNotes.length > 0) {
|
|
@@ -16493,11 +17158,7 @@ function worksetFactLines(facts) {
|
|
|
16493
17158
|
if (facts.latestChangelog) {
|
|
16494
17159
|
lines.push("Latest changelog: " + facts.latestChangelog.version + (facts.latestChangelog.date ? " (" + facts.latestChangelog.date + ")" : ""));
|
|
16495
17160
|
}
|
|
16496
|
-
|
|
16497
|
-
if (facts.git.branch) gitParts.push("branch " + facts.git.branch);
|
|
16498
|
-
if (facts.git.commit) gitParts.push("commit " + facts.git.commit);
|
|
16499
|
-
gitParts.push(facts.git.dirty ? "dirty worktree" : "clean worktree");
|
|
16500
|
-
lines.push("Git: " + gitParts.join(", "));
|
|
17161
|
+
lines.push(formatGitFact(facts.git));
|
|
16501
17162
|
for (const note of facts.staleNotes.slice(0, 1)) {
|
|
16502
17163
|
lines.push(
|
|
16503
17164
|
"Historical note: " + note.path + (note.branchHint ? " (branch hint " + note.branchHint + "; " + note.reason + ")" : " (" + note.reason + ")")
|
|
@@ -16616,6 +17277,7 @@ var init_auto_context = __esm({
|
|
|
16616
17277
|
init_external_provider();
|
|
16617
17278
|
init_project_context();
|
|
16618
17279
|
init_store();
|
|
17280
|
+
init_admission();
|
|
16619
17281
|
init_task_lens();
|
|
16620
17282
|
DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
16621
17283
|
}
|
|
@@ -16808,7 +17470,8 @@ async function attachTaskWorkset(input) {
|
|
|
16808
17470
|
suspect: input.pack.warnings.filter((warning) => warning.status === "suspect").length,
|
|
16809
17471
|
stale: input.pack.warnings.filter((warning) => warning.status === "stale").length
|
|
16810
17472
|
},
|
|
16811
|
-
runtimeCautions: input.runtimeCautions
|
|
17473
|
+
runtimeCautions: input.runtimeCautions,
|
|
17474
|
+
deliveryTarget: "context-pack"
|
|
16812
17475
|
});
|
|
16813
17476
|
return { ...input.pack, workset };
|
|
16814
17477
|
}
|
|
@@ -18220,11 +18883,14 @@ __export(export_import_exports, {
|
|
|
18220
18883
|
exportAsMarkdown: () => exportAsMarkdown,
|
|
18221
18884
|
importFromJson: () => importFromJson
|
|
18222
18885
|
});
|
|
18223
|
-
async function exportAsJson(projectDir2, projectId) {
|
|
18886
|
+
async function exportAsJson(projectDir2, projectId, reader) {
|
|
18224
18887
|
const store = getObservationStore();
|
|
18225
18888
|
const allObs = await store.loadAll();
|
|
18226
18889
|
const allSessions = await getSessionStore().loadAll();
|
|
18227
|
-
const projectObs =
|
|
18890
|
+
const projectObs = filterReadableObservations(
|
|
18891
|
+
allObs.filter((o) => o.projectId === projectId),
|
|
18892
|
+
reader
|
|
18893
|
+
);
|
|
18228
18894
|
const projectSessions = allSessions.filter((s) => s.projectId === projectId);
|
|
18229
18895
|
const typeBreakdown = {};
|
|
18230
18896
|
for (const obs of projectObs) {
|
|
@@ -18243,8 +18909,8 @@ async function exportAsJson(projectDir2, projectId) {
|
|
|
18243
18909
|
}
|
|
18244
18910
|
};
|
|
18245
18911
|
}
|
|
18246
|
-
async function exportAsMarkdown(projectDir2, projectId) {
|
|
18247
|
-
const data = await exportAsJson(projectDir2, projectId);
|
|
18912
|
+
async function exportAsMarkdown(projectDir2, projectId, reader) {
|
|
18913
|
+
const data = await exportAsJson(projectDir2, projectId, reader);
|
|
18248
18914
|
const lines = [];
|
|
18249
18915
|
lines.push(`# Memorix Export: ${projectId}`);
|
|
18250
18916
|
lines.push(`Exported: ${data.exportedAt}`);
|
|
@@ -18346,6 +19012,7 @@ var init_export_import = __esm({
|
|
|
18346
19012
|
"src/memory/export-import.ts"() {
|
|
18347
19013
|
"use strict";
|
|
18348
19014
|
init_esm_shims();
|
|
19015
|
+
init_visibility();
|
|
18349
19016
|
init_obs_store();
|
|
18350
19017
|
init_session_store();
|
|
18351
19018
|
OBSERVATION_ICONS2 = {
|
|
@@ -18522,6 +19189,7 @@ function isEligible(o, projectId) {
|
|
|
18522
19189
|
if (isCommandLog2(o)) return false;
|
|
18523
19190
|
if (isInactive(o)) return false;
|
|
18524
19191
|
if (isOtherProject(o, projectId)) return false;
|
|
19192
|
+
if (!isEligibleForKnowledgePromotion(o)) return false;
|
|
18525
19193
|
if (o.valueCategory === "ephemeral") return false;
|
|
18526
19194
|
if (!contextualHasSubstance(o)) return false;
|
|
18527
19195
|
return true;
|
|
@@ -18670,6 +19338,7 @@ var init_generator = __esm({
|
|
|
18670
19338
|
"src/wiki/generator.ts"() {
|
|
18671
19339
|
"use strict";
|
|
18672
19340
|
init_esm_shims();
|
|
19341
|
+
init_admission();
|
|
18673
19342
|
COMMAND_LOG_TITLE4 = /^(Ran:|Command:|Executed:)\s/i;
|
|
18674
19343
|
SECTION_DEFS = [
|
|
18675
19344
|
{
|
|
@@ -18956,6 +19625,9 @@ function sendError(res, message, status = 500) {
|
|
|
18956
19625
|
function filterByProject(items, projectId) {
|
|
18957
19626
|
return items.filter((item) => item.projectId === projectId);
|
|
18958
19627
|
}
|
|
19628
|
+
function filterDashboardObservations(observations2, projectId) {
|
|
19629
|
+
return filterReadableObservations(observations2, { projectId });
|
|
19630
|
+
}
|
|
18959
19631
|
function isActiveStatus(status) {
|
|
18960
19632
|
return (status ?? "active") === "active";
|
|
18961
19633
|
}
|
|
@@ -18979,13 +19651,8 @@ function prepareDashboardConfig(projectRoot) {
|
|
|
18979
19651
|
}
|
|
18980
19652
|
}
|
|
18981
19653
|
function computeProjectGraphCounts(allEntities, allRelations, projectObs) {
|
|
18982
|
-
const
|
|
18983
|
-
|
|
18984
|
-
);
|
|
18985
|
-
const entities = allEntities.filter((e) => entityNames.has(e.name));
|
|
18986
|
-
const entityNameSet = new Set(entities.map((e) => e.name));
|
|
18987
|
-
const relations = allRelations.filter((r) => entityNameSet.has(r.from) && entityNameSet.has(r.to));
|
|
18988
|
-
return { entities: entities.length, relations: relations.length, entityNames };
|
|
19654
|
+
const scoped = scopeKnowledgeGraphToProject({ entities: allEntities, relations: allRelations }, projectObs);
|
|
19655
|
+
return { entities: scoped.entities.length, relations: scoped.relations.length, entityNames: scoped.entityNames };
|
|
18989
19656
|
}
|
|
18990
19657
|
async function handleApi(req, res, dataDir, projectId, projectName, baseDir, projectRoot, projectResolved, mode = "standalone", port = 3210) {
|
|
18991
19658
|
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
@@ -19008,7 +19675,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19008
19675
|
switch (apiPath) {
|
|
19009
19676
|
case "/projects": {
|
|
19010
19677
|
try {
|
|
19011
|
-
const allObs =
|
|
19678
|
+
const allObs = filterReadableObservations(
|
|
19679
|
+
await getObservationStore().loadAll(),
|
|
19680
|
+
{}
|
|
19681
|
+
);
|
|
19012
19682
|
const projectSet = /* @__PURE__ */ new Map();
|
|
19013
19683
|
for (const obs of allObs) {
|
|
19014
19684
|
if (!isActiveStatus(obs.status)) continue;
|
|
@@ -19057,18 +19727,19 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19057
19727
|
await initGraphStore(effectiveDataDir);
|
|
19058
19728
|
const gStore = getGraphStore();
|
|
19059
19729
|
const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
19060
|
-
const graphObs =
|
|
19061
|
-
|
|
19062
|
-
|
|
19730
|
+
const graphObs = filterDashboardObservations(
|
|
19731
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19732
|
+
effectiveProjectId
|
|
19063
19733
|
);
|
|
19064
|
-
const
|
|
19065
|
-
|
|
19066
|
-
const relations = graph.relations.filter((r) => entityNameSet.has(r.from) && entityNameSet.has(r.to));
|
|
19067
|
-
sendJson(res, { entities, relations });
|
|
19734
|
+
const scoped = scopeKnowledgeGraphToProject(graph, graphObs);
|
|
19735
|
+
sendJson(res, { entities: scoped.entities, relations: scoped.relations });
|
|
19068
19736
|
break;
|
|
19069
19737
|
}
|
|
19070
19738
|
case "/observations": {
|
|
19071
|
-
const observations2 =
|
|
19739
|
+
const observations2 = filterDashboardObservations(
|
|
19740
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19741
|
+
effectiveProjectId
|
|
19742
|
+
);
|
|
19072
19743
|
sendJson(res, observations2);
|
|
19073
19744
|
break;
|
|
19074
19745
|
}
|
|
@@ -19081,7 +19752,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19081
19752
|
case "/stats": {
|
|
19082
19753
|
await initGraphStore(effectiveDataDir);
|
|
19083
19754
|
const graph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
|
|
19084
|
-
const observations2 =
|
|
19755
|
+
const observations2 = filterDashboardObservations(
|
|
19756
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19757
|
+
effectiveProjectId
|
|
19758
|
+
);
|
|
19085
19759
|
const nextId2 = await getObservationStore().loadIdCounter();
|
|
19086
19760
|
const projectGraphCounts = computeProjectGraphCounts(graph.entities, graph.relations, observations2);
|
|
19087
19761
|
const typeCounts = {};
|
|
@@ -19199,7 +19873,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19199
19873
|
break;
|
|
19200
19874
|
}
|
|
19201
19875
|
case "/retention": {
|
|
19202
|
-
const observations2 =
|
|
19876
|
+
const observations2 = filterDashboardObservations(
|
|
19877
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19878
|
+
effectiveProjectId
|
|
19879
|
+
);
|
|
19203
19880
|
const now3 = Date.now();
|
|
19204
19881
|
const scored = observations2.filter((obs) => obs.type !== "probe").map((obs) => {
|
|
19205
19882
|
const age = now3 - new Date(obs.createdAt || now3).getTime();
|
|
@@ -19237,7 +19914,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19237
19914
|
const { generateKnowledgeBase: generateKnowledgeBase2 } = await Promise.resolve().then(() => (init_generator(), generator_exports));
|
|
19238
19915
|
const { initMiniSkillStore: initMiniSkillStore2, getMiniSkillStore: getMiniSkillStore2 } = await Promise.resolve().then(() => (init_mini_skill_store(), mini_skill_store_exports));
|
|
19239
19916
|
await initMiniSkillStore2(effectiveDataDir);
|
|
19240
|
-
const allObs =
|
|
19917
|
+
const allObs = filterDashboardObservations(
|
|
19918
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19919
|
+
effectiveProjectId
|
|
19920
|
+
);
|
|
19241
19921
|
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
19242
19922
|
const overview = generateKnowledgeBase2({
|
|
19243
19923
|
projectId: effectiveProjectId,
|
|
@@ -19253,22 +19933,19 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19253
19933
|
const { initGraphStore: initGraphStore2, getGraphStore: getGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
19254
19934
|
await initMiniSkillStore2(effectiveDataDir);
|
|
19255
19935
|
await initGraphStore2(effectiveDataDir);
|
|
19256
|
-
const allObs =
|
|
19936
|
+
const allObs = filterDashboardObservations(
|
|
19937
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19938
|
+
effectiveProjectId
|
|
19939
|
+
);
|
|
19257
19940
|
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
19258
19941
|
const fullGraph = { entities: getGraphStore2().loadEntities(), relations: getGraphStore2().loadRelations() };
|
|
19259
|
-
const
|
|
19260
|
-
const projectEntityNames = new Set(
|
|
19261
|
-
graphObs.filter((o) => o.entityName).map((o) => o.entityName)
|
|
19262
|
-
);
|
|
19263
|
-
const scopedEntities = fullGraph.entities.filter((e) => projectEntityNames.has(e.name));
|
|
19264
|
-
const scopedEntityNameSet = new Set(scopedEntities.map((e) => e.name));
|
|
19265
|
-
const scopedRelations = fullGraph.relations.filter((r) => scopedEntityNameSet.has(r.from) && scopedEntityNameSet.has(r.to));
|
|
19942
|
+
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
19266
19943
|
const graph = generateKnowledgeGraph2({
|
|
19267
19944
|
projectId: effectiveProjectId,
|
|
19268
19945
|
observations: allObs,
|
|
19269
19946
|
miniSkills: skills,
|
|
19270
|
-
graphEntities:
|
|
19271
|
-
graphRelations:
|
|
19947
|
+
graphEntities: scoped.entities,
|
|
19948
|
+
graphRelations: scoped.relations
|
|
19272
19949
|
});
|
|
19273
19950
|
sendJson(res, graph);
|
|
19274
19951
|
break;
|
|
@@ -19382,7 +20059,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19382
20059
|
break;
|
|
19383
20060
|
}
|
|
19384
20061
|
case "/identity": {
|
|
19385
|
-
const allObs = await getObservationStore().loadAll();
|
|
20062
|
+
const allObs = filterReadableObservations(await getObservationStore().loadAll(), {});
|
|
19386
20063
|
const allProjectIds = [...new Set(allObs.map((o) => o.projectId).filter(Boolean))];
|
|
19387
20064
|
let classifyProjectId2 = () => "real";
|
|
19388
20065
|
let isDirtyProjectId2 = () => false;
|
|
@@ -19467,6 +20144,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19467
20144
|
sendError(res, "Observation not found", 404);
|
|
19468
20145
|
} else if (matchObs.projectId !== effectiveProjectId) {
|
|
19469
20146
|
sendError(res, `Observation #${obsId} belongs to project "${matchObs.projectId}", not "${effectiveProjectId}"`, 403);
|
|
20147
|
+
} else if (!canManageObservation(matchObs, { projectId: effectiveProjectId })) {
|
|
20148
|
+
sendError(res, `Observation #${obsId} is not manageable from the unbound dashboard.`, 403);
|
|
19470
20149
|
} else {
|
|
19471
20150
|
await obsStore.remove(obsId);
|
|
19472
20151
|
try {
|
|
@@ -19488,18 +20167,16 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19488
20167
|
if (apiPath === "/export") {
|
|
19489
20168
|
await initGraphStore(effectiveDataDir);
|
|
19490
20169
|
const fullGraph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
|
|
19491
|
-
const observations2 =
|
|
19492
|
-
|
|
19493
|
-
|
|
19494
|
-
observations2.filter((o) => (o.status ?? "active") === "active" && o.entityName).map((o) => o.entityName)
|
|
20170
|
+
const observations2 = filterDashboardObservations(
|
|
20171
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
20172
|
+
effectiveProjectId
|
|
19495
20173
|
);
|
|
19496
|
-
const
|
|
19497
|
-
const
|
|
19498
|
-
const exportRelations = fullGraph.relations.filter((r) => exportEntitySet.has(r.from) && exportEntitySet.has(r.to));
|
|
20174
|
+
const nextId2 = await getObservationStore().loadIdCounter();
|
|
20175
|
+
const scoped = scopeKnowledgeGraphToProject(fullGraph, observations2);
|
|
19499
20176
|
const exportData = {
|
|
19500
20177
|
project: { id: effectiveProjectId, name: effectiveProjectName },
|
|
19501
20178
|
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19502
|
-
graph: { entities:
|
|
20179
|
+
graph: { entities: scoped.entities, relations: scoped.relations },
|
|
19503
20180
|
observations: observations2,
|
|
19504
20181
|
nextId: nextId2
|
|
19505
20182
|
};
|
|
@@ -19816,6 +20493,8 @@ var init_server = __esm({
|
|
|
19816
20493
|
init_dotenv_loader();
|
|
19817
20494
|
init_yaml_loader();
|
|
19818
20495
|
init_yaml_loader();
|
|
20496
|
+
init_graph_scope();
|
|
20497
|
+
init_visibility();
|
|
19819
20498
|
MIME_TYPES = {
|
|
19820
20499
|
".html": "text/html; charset=utf-8",
|
|
19821
20500
|
".css": "text/css; charset=utf-8",
|
|
@@ -20593,6 +21272,11 @@ async function createHandoffArtifact(input, storeObservation2, teamStore) {
|
|
|
20593
21272
|
sourceDetail: "explicit",
|
|
20594
21273
|
valueCategory: "core",
|
|
20595
21274
|
// immune to retention archival
|
|
21275
|
+
// A direct handoff is not a project-wide memo. The sender retains access
|
|
21276
|
+
// and the explicit recipient receives a read grant. Broadcast handoffs
|
|
21277
|
+
// are deliberately visible only to active team members.
|
|
21278
|
+
visibility: input.toAgentId ? "personal" : "team",
|
|
21279
|
+
...input.toAgentId ? { sharedWithAgentIds: [input.toAgentId] } : {},
|
|
20596
21280
|
createdByAgentId: input.fromAgentId
|
|
20597
21281
|
});
|
|
20598
21282
|
try {
|
|
@@ -22298,6 +22982,7 @@ var init_hooks_path = __esm({
|
|
|
22298
22982
|
|
|
22299
22983
|
// src/sdk.ts
|
|
22300
22984
|
init_esm_shims();
|
|
22985
|
+
init_visibility();
|
|
22301
22986
|
|
|
22302
22987
|
// src/server.ts
|
|
22303
22988
|
init_esm_shims();
|
|
@@ -22443,6 +23128,24 @@ async function createAutoRelations(obs, extracted, graphManager) {
|
|
|
22443
23128
|
const relationType = inferRelationType(obs);
|
|
22444
23129
|
const relations = [];
|
|
22445
23130
|
const selfName = obs.entityName.toLowerCase();
|
|
23131
|
+
const explicitRelated = [...new Set((obs.relatedEntities ?? []).map((name) => name.trim()).filter((name) => name && name.toLowerCase() !== selfName))];
|
|
23132
|
+
if (explicitRelated.length > 0) {
|
|
23133
|
+
await graphManager.createEntities(explicitRelated.map((name) => ({
|
|
23134
|
+
name,
|
|
23135
|
+
entityType: "related",
|
|
23136
|
+
observations: []
|
|
23137
|
+
})));
|
|
23138
|
+
for (const name of explicitRelated) {
|
|
23139
|
+
const matchedEntity = graphManager.findEntityByName(name);
|
|
23140
|
+
if (matchedEntity) {
|
|
23141
|
+
relations.push({
|
|
23142
|
+
from: obs.entityName,
|
|
23143
|
+
to: matchedEntity.name,
|
|
23144
|
+
relationType: "related_entity"
|
|
23145
|
+
});
|
|
23146
|
+
}
|
|
23147
|
+
}
|
|
23148
|
+
}
|
|
22446
23149
|
const candidates = [
|
|
22447
23150
|
...extracted.identifiers,
|
|
22448
23151
|
...extracted.files.map((f) => f.split("/").pop()?.replace(/\.\w+$/, "") ?? ""),
|
|
@@ -22484,6 +23187,8 @@ async function createAutoRelations(obs, extracted, graphManager) {
|
|
|
22484
23187
|
|
|
22485
23188
|
// src/server.ts
|
|
22486
23189
|
init_entity_extractor();
|
|
23190
|
+
init_graph_scope();
|
|
23191
|
+
init_visibility();
|
|
22487
23192
|
|
|
22488
23193
|
// src/compact/engine.ts
|
|
22489
23194
|
init_esm_shims();
|
|
@@ -22609,7 +23314,15 @@ function formatTimeline(timeline) {
|
|
|
22609
23314
|
function formatObservationDetail(doc) {
|
|
22610
23315
|
const icon = getTypeIcon(doc.type);
|
|
22611
23316
|
const lines = [];
|
|
22612
|
-
const header = buildProvenanceHeader(
|
|
23317
|
+
const header = buildProvenanceHeader(
|
|
23318
|
+
doc.sourceDetail,
|
|
23319
|
+
doc.valueCategory,
|
|
23320
|
+
doc.admissionState,
|
|
23321
|
+
doc.admissionReason,
|
|
23322
|
+
doc.source,
|
|
23323
|
+
doc.commitHash,
|
|
23324
|
+
doc.relatedCommits
|
|
23325
|
+
);
|
|
22613
23326
|
if (header) {
|
|
22614
23327
|
lines.push(header);
|
|
22615
23328
|
lines.push("");
|
|
@@ -22644,7 +23357,7 @@ function formatObservationDetail(doc) {
|
|
|
22644
23357
|
}
|
|
22645
23358
|
return lines.join("\n");
|
|
22646
23359
|
}
|
|
22647
|
-
function buildProvenanceHeader(sourceDetail, valueCategory, source, commitHash, relatedCommits) {
|
|
23360
|
+
function buildProvenanceHeader(sourceDetail, valueCategory, admissionState, admissionReason, source, commitHash, relatedCommits) {
|
|
22648
23361
|
const sd = resolveSourceDetail(sourceDetail, source);
|
|
22649
23362
|
if (!sd) return "";
|
|
22650
23363
|
const label = sourceKindLabel(sd);
|
|
@@ -22655,11 +23368,21 @@ function buildProvenanceHeader(sourceDetail, valueCategory, source, commitHash,
|
|
|
22655
23368
|
if (verificationLine) {
|
|
22656
23369
|
lines.push(verificationLine);
|
|
22657
23370
|
}
|
|
22658
|
-
if (valueCategory === "core") {
|
|
23371
|
+
if (valueCategory === "core" && admissionState !== "candidate" && admissionState !== "ephemeral") {
|
|
22659
23372
|
lines.push("[CORE] Core \u2014 immune to decay");
|
|
23373
|
+
} else if (valueCategory === "core") {
|
|
23374
|
+
lines.push("[CORE] Core candidate \u2014 not durable until qualification completes");
|
|
22660
23375
|
} else if (valueCategory === "ephemeral") {
|
|
22661
23376
|
lines.push("[WARN] Ephemeral \u2014 short-lived signal");
|
|
22662
23377
|
}
|
|
23378
|
+
if (admissionState === "candidate") {
|
|
23379
|
+
lines.push("[PENDING] Candidate \u2014 excluded from automatic context until current Code Memory qualifies it");
|
|
23380
|
+
} else if (admissionState === "ephemeral") {
|
|
23381
|
+
lines.push("[TRACE] Ephemeral capture \u2014 excluded from automatic context");
|
|
23382
|
+
} else if (admissionState === "qualified") {
|
|
23383
|
+
lines.push("[QUALIFIED] Automatic evidence passed current Code Memory qualification");
|
|
23384
|
+
}
|
|
23385
|
+
if (admissionReason) lines.push(`Admission: ${redactCredentials(admissionReason)}`);
|
|
22663
23386
|
return lines.join("\n");
|
|
22664
23387
|
}
|
|
22665
23388
|
function sourceKindLabel(sd) {
|
|
@@ -22740,6 +23463,7 @@ init_obs_store();
|
|
|
22740
23463
|
init_mini_skill_store();
|
|
22741
23464
|
init_mini_skills();
|
|
22742
23465
|
init_secret_filter();
|
|
23466
|
+
init_visibility();
|
|
22743
23467
|
function normalizeMemoryBrowseQuery(query) {
|
|
22744
23468
|
const trimmed = query.trim();
|
|
22745
23469
|
if (!trimmed) return "";
|
|
@@ -22782,11 +23506,13 @@ async function compactSearch(options) {
|
|
|
22782
23506
|
);
|
|
22783
23507
|
let formatted = formatIndexTable(entries, searchOptions.query, !options.projectId);
|
|
22784
23508
|
if (entries.length === 0 && options.projectId) {
|
|
22785
|
-
const allObservations = getAllObservations();
|
|
23509
|
+
const allObservations = filterReadableObservations(getAllObservations(), options.reader);
|
|
22786
23510
|
const projectAliases = new Set(await resolveAliases(options.projectId).catch(() => [options.projectId]));
|
|
22787
23511
|
const projectHasStoredMemory = allObservations.some((obs) => obs.projectId && projectAliases.has(obs.projectId));
|
|
22788
23512
|
if (!projectHasStoredMemory) {
|
|
22789
|
-
formatted = `
|
|
23513
|
+
formatted = options.reader ? `No Memorix memories are available to this session yet.
|
|
23514
|
+
|
|
23515
|
+
The tool call worked, but this session has no project-visible memory to retrieve.` : `This project does not have any Memorix memories yet.
|
|
22790
23516
|
|
|
22791
23517
|
It looks like a fresh project: the tool call worked, but there is nothing stored to retrieve yet.
|
|
22792
23518
|
|
|
@@ -22800,8 +23526,8 @@ This project does have stored Memorix memories, but none matched the current que
|
|
|
22800
23526
|
const totalTokens = countTextTokens(formatted);
|
|
22801
23527
|
return { entries, formatted, totalTokens };
|
|
22802
23528
|
}
|
|
22803
|
-
async function compactTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3) {
|
|
22804
|
-
const result = await getTimeline(anchorId, projectId, depthBefore, depthAfter);
|
|
23529
|
+
async function compactTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3, reader) {
|
|
23530
|
+
const result = await getTimeline(anchorId, projectId, depthBefore, depthAfter, reader);
|
|
22805
23531
|
const timeline = {
|
|
22806
23532
|
anchorId,
|
|
22807
23533
|
anchorEntry: result.anchor,
|
|
@@ -22812,7 +23538,7 @@ async function compactTimeline(anchorId, projectId, depthBefore = 3, depthAfter
|
|
|
22812
23538
|
const totalTokens = countTextTokens(formatted);
|
|
22813
23539
|
return { timeline, formatted, totalTokens };
|
|
22814
23540
|
}
|
|
22815
|
-
async function compactDetail(idsOrRefs) {
|
|
23541
|
+
async function compactDetail(idsOrRefs, options = {}) {
|
|
22816
23542
|
const invalidRefs = [];
|
|
22817
23543
|
const parsedRefs = [];
|
|
22818
23544
|
for (let i = 0; i < idsOrRefs.length; i++) {
|
|
@@ -22869,7 +23595,7 @@ async function compactDetail(idsOrRefs) {
|
|
|
22869
23595
|
const missingRefs = [];
|
|
22870
23596
|
for (const ref of refs) {
|
|
22871
23597
|
const obs = getObservation(ref.id, ref.projectId);
|
|
22872
|
-
if (obs && (ref.projectId ? obs.projectId === ref.projectId : true)) {
|
|
23598
|
+
if (obs && (ref.projectId ? obs.projectId === ref.projectId : true) && canReadObservation(obs, options.reader)) {
|
|
22873
23599
|
documentMap.set(toRefKey(ref), {
|
|
22874
23600
|
id: makeOramaObservationId(obs.projectId, obs.id),
|
|
22875
23601
|
observationId: obs.id,
|
|
@@ -22888,7 +23614,12 @@ async function compactDetail(idsOrRefs) {
|
|
|
22888
23614
|
status: obs.status ?? "active",
|
|
22889
23615
|
source: obs.source ?? "agent",
|
|
22890
23616
|
sourceDetail: obs.sourceDetail ?? "",
|
|
22891
|
-
valueCategory: obs.valueCategory ?? ""
|
|
23617
|
+
valueCategory: obs.valueCategory ?? "",
|
|
23618
|
+
admissionState: obs.admissionState ?? "",
|
|
23619
|
+
admissionReason: obs.admissionReason ?? "",
|
|
23620
|
+
visibility: obs.visibility ?? "project",
|
|
23621
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
23622
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
22892
23623
|
});
|
|
22893
23624
|
} else {
|
|
22894
23625
|
missingRefs.push(ref);
|
|
@@ -22898,17 +23629,17 @@ async function compactDetail(idsOrRefs) {
|
|
|
22898
23629
|
for (const ref of missingRefs) {
|
|
22899
23630
|
const fallbackDocs = await getObservationsByIds([ref.id], ref.projectId);
|
|
22900
23631
|
const doc = fallbackDocs[0];
|
|
22901
|
-
if (doc) {
|
|
23632
|
+
if (doc && canReadObservation(doc, options.reader)) {
|
|
22902
23633
|
documentMap.set(toRefKey(ref), doc);
|
|
22903
23634
|
continue;
|
|
22904
23635
|
}
|
|
22905
23636
|
const stored = await getPersistedObservation(ref);
|
|
22906
|
-
if (stored) {
|
|
23637
|
+
if (stored && canReadObservation(stored, options.reader)) {
|
|
22907
23638
|
documentMap.set(toRefKey(ref), observationToDocument(stored));
|
|
22908
23639
|
}
|
|
22909
23640
|
}
|
|
22910
23641
|
}
|
|
22911
|
-
const allObs = getAllObservations();
|
|
23642
|
+
const allObs = filterReadableObservations(getAllObservations(), options.reader);
|
|
22912
23643
|
const crossRefMap = /* @__PURE__ */ new Map();
|
|
22913
23644
|
for (const ref of refs) {
|
|
22914
23645
|
const obs = getObservation(ref.id, ref.projectId);
|
|
@@ -23022,7 +23753,12 @@ function observationToDocument(obs) {
|
|
|
23022
23753
|
status: obs.status ?? "active",
|
|
23023
23754
|
source: obs.source ?? "agent",
|
|
23024
23755
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23025
|
-
valueCategory: obs.valueCategory ?? ""
|
|
23756
|
+
valueCategory: obs.valueCategory ?? "",
|
|
23757
|
+
admissionState: obs.admissionState ?? "",
|
|
23758
|
+
admissionReason: obs.admissionReason ?? "",
|
|
23759
|
+
visibility: obs.visibility ?? "project",
|
|
23760
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
23761
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
23026
23762
|
};
|
|
23027
23763
|
}
|
|
23028
23764
|
function formatMiniSkillDetail(skill2, provenanceStatus) {
|
|
@@ -23141,7 +23877,9 @@ function auditMemoryQuality(observations2, options) {
|
|
|
23141
23877
|
status: obs.status ?? "active",
|
|
23142
23878
|
source: obs.source ?? "agent",
|
|
23143
23879
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23144
|
-
valueCategory: obs.valueCategory ?? ""
|
|
23880
|
+
valueCategory: obs.valueCategory ?? "",
|
|
23881
|
+
admissionState: obs.admissionState ?? "",
|
|
23882
|
+
admissionReason: obs.admissionReason ?? ""
|
|
23145
23883
|
}, options.referenceTime) !== "active").map((obs) => makeEntry(obs, "Outside active retention zone"));
|
|
23146
23884
|
const summary = {
|
|
23147
23885
|
total: scoped.length,
|
|
@@ -23183,6 +23921,7 @@ function auditMemoryQuality(observations2, options) {
|
|
|
23183
23921
|
|
|
23184
23922
|
// src/memory/graph-context.ts
|
|
23185
23923
|
init_retention();
|
|
23924
|
+
init_admission();
|
|
23186
23925
|
function formatGraphContextPrompt(packet) {
|
|
23187
23926
|
const lines = [
|
|
23188
23927
|
"## Memory Context Packet",
|
|
@@ -23340,7 +24079,9 @@ function scoreForPacket(obs, queryTokens, referenceTime, broadMemoryQuery) {
|
|
|
23340
24079
|
status: obs.status ?? "active",
|
|
23341
24080
|
source: obs.source ?? "agent",
|
|
23342
24081
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23343
|
-
valueCategory: obs.valueCategory ?? ""
|
|
24082
|
+
valueCategory: obs.valueCategory ?? "",
|
|
24083
|
+
admissionState: obs.admissionState ?? "",
|
|
24084
|
+
admissionReason: obs.admissionReason ?? ""
|
|
23344
24085
|
}, referenceTime) !== "active") {
|
|
23345
24086
|
score -= 2;
|
|
23346
24087
|
}
|
|
@@ -23434,8 +24175,11 @@ function buildGraphContextPacket(observations2, options) {
|
|
|
23434
24175
|
...audit.issues.orphans.map((entry) => entry.id),
|
|
23435
24176
|
...audit.issues.retentionCandidates.map((entry) => entry.id)
|
|
23436
24177
|
]);
|
|
23437
|
-
const
|
|
23438
|
-
|
|
24178
|
+
const deliveryEligible = observations2.filter(
|
|
24179
|
+
(obs) => obs.projectId === options.projectId && isEligibleForAutomaticDelivery(obs)
|
|
24180
|
+
);
|
|
24181
|
+
const filteredObservations = deliveryEligible.filter((obs) => !riskIds.has(obs.id));
|
|
24182
|
+
const baseObservations = filteredObservations.length > 0 ? filteredObservations : deliveryEligible;
|
|
23439
24183
|
const memories = pickMemories(baseObservations, options.projectId, options.query, options.limit ?? 5, referenceTime);
|
|
23440
24184
|
const entities = buildEntities(memories);
|
|
23441
24185
|
const entityNames = new Set(entities.map((entity) => entity.name));
|
|
@@ -25922,6 +26666,8 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
25922
26666
|
let projectResolutionError = null;
|
|
25923
26667
|
let explicitProjectBound = false;
|
|
25924
26668
|
let currentAgentId;
|
|
26669
|
+
let teamStore;
|
|
26670
|
+
let initTeamStoreForProject;
|
|
25925
26671
|
if (detectedProject) {
|
|
25926
26672
|
rawProject = detectedProject;
|
|
25927
26673
|
} else {
|
|
@@ -26169,7 +26915,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
26169
26915
|
};
|
|
26170
26916
|
const server = existingServer ?? new McpServer({
|
|
26171
26917
|
name: "memorix",
|
|
26172
|
-
version: true ? "1.2.
|
|
26918
|
+
version: true ? "1.2.2" : "1.0.1"
|
|
26173
26919
|
});
|
|
26174
26920
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
26175
26921
|
server.registerTool = ((name, ...args) => {
|
|
@@ -26189,11 +26935,26 @@ The path should point to a directory containing a .git folder.`
|
|
|
26189
26935
|
}
|
|
26190
26936
|
return originalRegisterTool(name, ...args);
|
|
26191
26937
|
});
|
|
26938
|
+
const getObservationReader = (scope = "project") => {
|
|
26939
|
+
let isTeamMember = false;
|
|
26940
|
+
if (teamFeaturesEnabled && currentAgentId) {
|
|
26941
|
+
try {
|
|
26942
|
+
const agent = teamStore.getAgent(currentAgentId);
|
|
26943
|
+
isTeamMember = agent?.project_id === project.id && agent.status === "active";
|
|
26944
|
+
} catch {
|
|
26945
|
+
}
|
|
26946
|
+
}
|
|
26947
|
+
return {
|
|
26948
|
+
...scope === "project" ? { projectId: project.id } : {},
|
|
26949
|
+
...currentAgentId ? { agentId: currentAgentId } : {},
|
|
26950
|
+
isTeamMember
|
|
26951
|
+
};
|
|
26952
|
+
};
|
|
26192
26953
|
server.registerTool(
|
|
26193
26954
|
"memorix_store",
|
|
26194
26955
|
{
|
|
26195
26956
|
title: "Store Memory",
|
|
26196
|
-
description: "Store a new observation/memory. Automatically indexed for search. Use type to classify: gotcha ([GOTCHA] critical pitfall), decision ([DECISION] architecture choice), problem-solution ([FIX] bug fix), how-it-works ([INFO] explanation), what-changed ([CHANGE] change), discovery ([DISCOVERY] insight), why-it-exists ([WHY] rationale), trade-off ([TRADEOFF] compromise), session-request ([SESSION] original goal).
|
|
26957
|
+
description: "Store a new observation/memory. Automatically indexed for search. Use type to classify: gotcha ([GOTCHA] critical pitfall), decision ([DECISION] architecture choice), problem-solution ([FIX] bug fix), how-it-works ([INFO] explanation), what-changed ([CHANGE] change), discovery ([DISCOVERY] insight), why-it-exists ([WHY] rationale), trade-off ([TRADEOFF] compromise), session-request ([SESSION] original goal). Project visibility is the default. Personal or team visibility requires an explicitly joined coordination identity.",
|
|
26197
26958
|
inputSchema: {
|
|
26198
26959
|
entityName: z2.string().describe('The entity this observation belongs to (e.g., "auth-module", "port-config")'),
|
|
26199
26960
|
type: z2.enum(OBSERVATION_TYPES).describe("Observation type for classification"),
|
|
@@ -26211,361 +26972,214 @@ The path should point to a directory containing a .git folder.`
|
|
|
26211
26972
|
completion: z2.number().optional().describe("Completion percentage 0-100")
|
|
26212
26973
|
}).optional().describe("Progress tracking for task/feature observations"),
|
|
26213
26974
|
relatedCommits: z2.array(z2.string()).optional().describe("Git commit hashes this memory relates to (links ground truth \u2194 reasoning)"),
|
|
26214
|
-
relatedEntities: z2.array(z2.string()).optional().describe("Other entity names this memory cross-references")
|
|
26975
|
+
relatedEntities: z2.array(z2.string()).optional().describe("Other entity names this memory cross-references"),
|
|
26976
|
+
visibility: z2.enum(["personal", "project", "team"]).optional().describe(
|
|
26977
|
+
"Retrieval scope. Project is the normal shared default; personal/team require memorix_session_start with joinTeam=true."
|
|
26978
|
+
)
|
|
26215
26979
|
}
|
|
26216
26980
|
},
|
|
26217
|
-
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities }) => {
|
|
26981
|
+
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility }) => {
|
|
26218
26982
|
const unresolved = requireResolvedProject("store memory in the current project");
|
|
26219
26983
|
if (unresolved) return unresolved;
|
|
26220
|
-
|
|
26221
|
-
|
|
26222
|
-
|
|
26223
|
-
|
|
26224
|
-
|
|
26225
|
-
|
|
26226
|
-
|
|
26227
|
-
|
|
26228
|
-
|
|
26229
|
-
|
|
26230
|
-
|
|
26231
|
-
|
|
26232
|
-
|
|
26233
|
-
|
|
26234
|
-
|
|
26235
|
-
|
|
26236
|
-
|
|
26237
|
-
|
|
26238
|
-
|
|
26239
|
-
|
|
26240
|
-
|
|
26241
|
-
|
|
26242
|
-
|
|
26243
|
-
|
|
26244
|
-
|
|
26245
|
-
|
|
26246
|
-
|
|
26247
|
-
|
|
26248
|
-
|
|
26249
|
-
|
|
26250
|
-
if (event.stageDurationMs !== void 0) {
|
|
26251
|
-
completedFormationStages[event.stage] = event.stageDurationMs;
|
|
26252
|
-
}
|
|
26253
|
-
};
|
|
26254
|
-
try {
|
|
26255
|
-
const formationConfig = {
|
|
26256
|
-
mode: "active",
|
|
26257
|
-
useLLM: isLLMEnabled(),
|
|
26258
|
-
minValueScore: 0.3,
|
|
26259
|
-
searchMemories: async (q, limit, pid) => {
|
|
26260
|
-
const result = await compactSearch({ query: q, limit, projectId: pid, status: "active" });
|
|
26261
|
-
if (result.entries.length === 0) return [];
|
|
26262
|
-
const details = await compactDetail(result.entries.map((e) => e.id));
|
|
26263
|
-
return details.documents.map((d, i) => ({
|
|
26264
|
-
id: Number(d.id.replace("obs-", "")),
|
|
26265
|
-
observationId: d.observationId,
|
|
26266
|
-
title: d.title,
|
|
26267
|
-
narrative: d.narrative,
|
|
26268
|
-
facts: d.facts,
|
|
26269
|
-
entityName: d.entityName,
|
|
26270
|
-
type: d.type,
|
|
26271
|
-
score: result.entries[i]?.score ?? 0
|
|
26272
|
-
}));
|
|
26273
|
-
},
|
|
26274
|
-
getObservation: (id) => {
|
|
26275
|
-
const o = getObservation(id);
|
|
26276
|
-
if (!o) return null;
|
|
26277
|
-
return {
|
|
26278
|
-
id: o.id,
|
|
26279
|
-
entityName: o.entityName,
|
|
26280
|
-
type: o.type,
|
|
26281
|
-
title: o.title,
|
|
26282
|
-
narrative: o.narrative,
|
|
26283
|
-
facts: o.facts,
|
|
26284
|
-
topicKey: o.topicKey
|
|
26285
|
-
};
|
|
26286
|
-
},
|
|
26287
|
-
getEntityNames: () => graphManager.getEntityNames(),
|
|
26288
|
-
onStageEvent: onFormationStageEvent
|
|
26289
|
-
};
|
|
26290
|
-
formationResult = await withTimeout2(
|
|
26291
|
-
runFormation({
|
|
26292
|
-
entityName,
|
|
26293
|
-
type,
|
|
26294
|
-
title,
|
|
26295
|
-
narrative,
|
|
26296
|
-
facts: safeFacts,
|
|
26297
|
-
projectId: project.id,
|
|
26298
|
-
source: "explicit"
|
|
26299
|
-
}, formationConfig),
|
|
26300
|
-
FORMATION_TIMEOUT_MS,
|
|
26301
|
-
"Formation pipeline"
|
|
26302
|
-
);
|
|
26303
|
-
const modeIcon = "[FAST]";
|
|
26304
|
-
formationNote = `
|
|
26305
|
-
${modeIcon} Formation[active]: ${formationResult.evaluation.category} (${formationResult.evaluation.score.toFixed(2)}) | ${formationResult.resolution.action} | ${formationResult.pipeline.durationMs}ms`;
|
|
26306
|
-
if (formationResult.extraction.extractedFacts.length > 0) {
|
|
26307
|
-
formationNote += ` | +${formationResult.extraction.extractedFacts.length} facts`;
|
|
26308
|
-
}
|
|
26309
|
-
if (formationResult.extraction.titleImproved) formationNote += " | title\u2191";
|
|
26310
|
-
if (formationResult.extraction.entityResolved) formationNote += ` | entity\u2192${formationResult.entityName}`;
|
|
26311
|
-
if (formationResult.extraction.typeCorrected) formationNote += ` | type\u2192${formationResult.type}`;
|
|
26312
|
-
} catch (formationErr) {
|
|
26313
|
-
const isTimeout = formationErr instanceof Error && formationErr.message.includes("timed out");
|
|
26314
|
-
const elapsedMs = Date.now() - formationStartTime;
|
|
26315
|
-
const stageSummary = formatFormationStageDurations(completedFormationStages);
|
|
26316
|
-
console.error(
|
|
26317
|
-
`[memorix] Formation ${isTimeout ? "timed out" : "failed"} in memorix_store after ${elapsedMs}ms/${FORMATION_TIMEOUT_MS}ms at stage ${currentFormationStage}${stageSummary ? ` | completed: ${stageSummary}` : ""}`
|
|
26318
|
-
);
|
|
26319
|
-
if (!isTimeout && formationErr instanceof Error) {
|
|
26320
|
-
console.error(`[memorix] Formation error: ${formationErr.message}`);
|
|
26984
|
+
const requestedVisibility = visibility ?? "project";
|
|
26985
|
+
const reader = getObservationReader();
|
|
26986
|
+
if (requestedVisibility !== "project" && !currentAgentId) {
|
|
26987
|
+
return {
|
|
26988
|
+
content: [{ type: "text", text: "Personal or team memory requires memorix_session_start with joinTeam=true so Memorix can bind an owner." }],
|
|
26989
|
+
isError: true
|
|
26990
|
+
};
|
|
26991
|
+
}
|
|
26992
|
+
if (requestedVisibility === "team" && !reader.isTeamMember) {
|
|
26993
|
+
return {
|
|
26994
|
+
content: [{ type: "text", text: "Team memory requires an active coordination membership in the current project." }],
|
|
26995
|
+
isError: true
|
|
26996
|
+
};
|
|
26997
|
+
}
|
|
26998
|
+
try {
|
|
26999
|
+
return await withFreshIndex(async () => {
|
|
27000
|
+
let entityName = rawEntityName;
|
|
27001
|
+
let type = rawType;
|
|
27002
|
+
let title = rawTitle;
|
|
27003
|
+
let safeFacts = facts ? coerceStringArray(facts) : void 0;
|
|
27004
|
+
const safeFiles = filesModified ? coerceStringArray(filesModified) : void 0;
|
|
27005
|
+
const safeConcepts = concepts ? coerceStringArray(concepts) : void 0;
|
|
27006
|
+
let formationMode = "active";
|
|
27007
|
+
if (process.env.MEMORIX_FORMATION_MODE) {
|
|
27008
|
+
formationMode = process.env.MEMORIX_FORMATION_MODE;
|
|
27009
|
+
} else {
|
|
27010
|
+
try {
|
|
27011
|
+
const { getBehaviorConfig: getBehaviorConfig2 } = await Promise.resolve().then(() => (init_behavior(), behavior_exports));
|
|
27012
|
+
formationMode = getBehaviorConfig2({ projectRoot: project.rootPath }).formationMode;
|
|
27013
|
+
} catch {
|
|
26321
27014
|
}
|
|
26322
|
-
formationNote = `
|
|
26323
|
-
[WARN] Formation ${isTimeout ? "timed out" : "failed"} \u2014 storing base observation without enrichment`;
|
|
26324
27015
|
}
|
|
26325
|
-
|
|
26326
|
-
|
|
26327
|
-
|
|
26328
|
-
if (
|
|
26329
|
-
|
|
26330
|
-
|
|
26331
|
-
|
|
26332
|
-
|
|
26333
|
-
|
|
26334
|
-
|
|
26335
|
-
|
|
26336
|
-
|
|
26337
|
-
|
|
26338
|
-
|
|
26339
|
-
|
|
26340
|
-
|
|
26341
|
-
progress,
|
|
26342
|
-
sourceDetail: "explicit",
|
|
26343
|
-
createdByAgentId: currentAgentId
|
|
26344
|
-
});
|
|
26345
|
-
return {
|
|
26346
|
-
content: [{
|
|
26347
|
-
type: "text",
|
|
26348
|
-
text: `[UPDATED] Formation MERGE: merged into #${targetId} (${reason})${formationNote}`
|
|
26349
|
-
}]
|
|
26350
|
-
};
|
|
26351
|
-
}
|
|
26352
|
-
} else if (action2 === "evolve" && targetId) {
|
|
26353
|
-
const targetObs = getObservation(targetId);
|
|
26354
|
-
if (targetObs) {
|
|
26355
|
-
await storeObservation({
|
|
26356
|
-
entityName: targetObs.entityName,
|
|
26357
|
-
type: targetObs.type,
|
|
26358
|
-
title: formationResult.title,
|
|
26359
|
-
narrative: formationResult.narrative,
|
|
26360
|
-
facts: formationResult.facts,
|
|
26361
|
-
filesModified: safeFiles,
|
|
26362
|
-
concepts: safeConcepts,
|
|
26363
|
-
projectId: project.id,
|
|
26364
|
-
topicKey: targetObs.topicKey,
|
|
26365
|
-
progress,
|
|
26366
|
-
sourceDetail: "explicit",
|
|
26367
|
-
createdByAgentId: currentAgentId
|
|
26368
|
-
});
|
|
26369
|
-
return {
|
|
26370
|
-
content: [{
|
|
26371
|
-
type: "text",
|
|
26372
|
-
text: `[UPDATED] Formation EVOLVE: evolved #${targetId} (${reason})${formationNote}`
|
|
26373
|
-
}]
|
|
26374
|
-
};
|
|
26375
|
-
}
|
|
26376
|
-
} else if (action2 === "discard") {
|
|
26377
|
-
return {
|
|
26378
|
-
content: [{
|
|
26379
|
-
type: "text",
|
|
26380
|
-
text: `[SKIP] Formation DISCARD: ${reason}${formationNote}`
|
|
26381
|
-
}]
|
|
27016
|
+
const useFormation = formationMode === "active";
|
|
27017
|
+
let formationResult = null;
|
|
27018
|
+
let formationNote = "";
|
|
27019
|
+
if (useFormation && !topicKey && !progress) {
|
|
27020
|
+
let currentFormationStage = "setup";
|
|
27021
|
+
const completedFormationStages = {};
|
|
27022
|
+
const formationStartTime = Date.now();
|
|
27023
|
+
const onFormationStageEvent = (event) => {
|
|
27024
|
+
if (event.status === "start") {
|
|
27025
|
+
currentFormationStage = event.stage;
|
|
27026
|
+
return;
|
|
27027
|
+
}
|
|
27028
|
+
currentFormationStage = event.stage;
|
|
27029
|
+
if (event.stageDurationMs !== void 0) {
|
|
27030
|
+
completedFormationStages[event.stage] = event.stageDurationMs;
|
|
27031
|
+
}
|
|
26382
27032
|
};
|
|
26383
|
-
|
|
26384
|
-
|
|
26385
|
-
|
|
26386
|
-
|
|
26387
|
-
|
|
26388
|
-
|
|
26389
|
-
|
|
26390
|
-
|
|
26391
|
-
|
|
26392
|
-
|
|
26393
|
-
|
|
26394
|
-
|
|
26395
|
-
|
|
26396
|
-
|
|
26397
|
-
|
|
26398
|
-
|
|
26399
|
-
|
|
26400
|
-
|
|
26401
|
-
|
|
26402
|
-
|
|
26403
|
-
|
|
26404
|
-
|
|
26405
|
-
|
|
26406
|
-
const decision = await withTimeout2(
|
|
26407
|
-
compactOnWrite(
|
|
26408
|
-
{ title, narrative, facts: safeFacts ?? [] },
|
|
26409
|
-
existingMemories
|
|
26410
|
-
),
|
|
26411
|
-
COMPACT_ON_WRITE_TIMEOUT_MS,
|
|
26412
|
-
"Compact-on-write"
|
|
26413
|
-
);
|
|
26414
|
-
if (decision.action === "UPDATE" && decision.targetId) {
|
|
26415
|
-
const targetObs = getObservation(decision.targetId);
|
|
26416
|
-
if (targetObs) {
|
|
26417
|
-
await storeObservation({
|
|
26418
|
-
entityName: targetObs.entityName,
|
|
26419
|
-
type: targetObs.type,
|
|
26420
|
-
title: decision.mergedNarrative ? title : targetObs.title,
|
|
26421
|
-
narrative: decision.mergedNarrative ?? narrative,
|
|
26422
|
-
facts: decision.mergedFacts ?? safeFacts,
|
|
26423
|
-
filesModified: safeFiles,
|
|
26424
|
-
concepts: safeConcepts,
|
|
26425
|
-
projectId: project.id,
|
|
26426
|
-
topicKey: targetObs.topicKey,
|
|
26427
|
-
progress,
|
|
26428
|
-
sourceDetail: "explicit",
|
|
26429
|
-
createdByAgentId: currentAgentId
|
|
26430
|
-
});
|
|
26431
|
-
compactAction = `[UPDATED] Compact UPDATE: merged into #${decision.targetId} (${decision.reason})`;
|
|
26432
|
-
compactMerged = true;
|
|
27033
|
+
try {
|
|
27034
|
+
const formationConfig = {
|
|
27035
|
+
mode: "active",
|
|
27036
|
+
useLLM: isLLMEnabled(),
|
|
27037
|
+
minValueScore: 0.3,
|
|
27038
|
+
searchMemories: async (q, limit, pid) => {
|
|
27039
|
+
const result = await compactSearch({ query: q, limit, projectId: pid, status: "active", reader });
|
|
27040
|
+
if (result.entries.length === 0) return [];
|
|
27041
|
+
const details = await compactDetail(result.entries.map((e) => e.id), { reader });
|
|
27042
|
+
return details.documents.map((d, i) => ({
|
|
27043
|
+
id: Number(d.id.replace("obs-", "")),
|
|
27044
|
+
observationId: d.observationId,
|
|
27045
|
+
title: d.title,
|
|
27046
|
+
narrative: d.narrative,
|
|
27047
|
+
facts: d.facts,
|
|
27048
|
+
entityName: d.entityName,
|
|
27049
|
+
type: d.type,
|
|
27050
|
+
score: result.entries[i]?.score ?? 0
|
|
27051
|
+
}));
|
|
27052
|
+
},
|
|
27053
|
+
getObservation: (id) => {
|
|
27054
|
+
const o = getObservation(id);
|
|
27055
|
+
if (!o || o.projectId !== project.id || !canReadObservation(o, reader)) return null;
|
|
26433
27056
|
return {
|
|
26434
|
-
|
|
26435
|
-
|
|
26436
|
-
|
|
26437
|
-
|
|
26438
|
-
|
|
27057
|
+
id: o.id,
|
|
27058
|
+
entityName: o.entityName,
|
|
27059
|
+
type: o.type,
|
|
27060
|
+
title: o.title,
|
|
27061
|
+
narrative: o.narrative,
|
|
27062
|
+
facts: o.facts,
|
|
27063
|
+
topicKey: o.topicKey
|
|
26439
27064
|
};
|
|
26440
|
-
}
|
|
26441
|
-
|
|
27065
|
+
},
|
|
27066
|
+
getEntityNames: () => graphManager.getEntityNames(),
|
|
27067
|
+
onStageEvent: onFormationStageEvent
|
|
27068
|
+
};
|
|
27069
|
+
formationResult = await withTimeout2(
|
|
27070
|
+
runFormation({
|
|
27071
|
+
entityName,
|
|
27072
|
+
type,
|
|
27073
|
+
title,
|
|
27074
|
+
narrative,
|
|
27075
|
+
facts: safeFacts,
|
|
27076
|
+
projectId: project.id,
|
|
27077
|
+
source: "explicit"
|
|
27078
|
+
}, formationConfig),
|
|
27079
|
+
FORMATION_TIMEOUT_MS,
|
|
27080
|
+
"Formation pipeline"
|
|
27081
|
+
);
|
|
27082
|
+
const modeIcon = "[FAST]";
|
|
27083
|
+
formationNote = `
|
|
27084
|
+
${modeIcon} Formation[active]: ${formationResult.evaluation.category} (${formationResult.evaluation.score.toFixed(2)}) | ${formationResult.resolution.action} | ${formationResult.pipeline.durationMs}ms`;
|
|
27085
|
+
if (formationResult.extraction.extractedFacts.length > 0) {
|
|
27086
|
+
formationNote += ` | +${formationResult.extraction.extractedFacts.length} facts`;
|
|
27087
|
+
}
|
|
27088
|
+
if (formationResult.extraction.titleImproved) formationNote += " | title\u2191";
|
|
27089
|
+
if (formationResult.extraction.entityResolved) formationNote += ` | entity\u2192${formationResult.entityName}`;
|
|
27090
|
+
if (formationResult.extraction.typeCorrected) formationNote += ` | type\u2192${formationResult.type}`;
|
|
27091
|
+
} catch (formationErr) {
|
|
27092
|
+
const isTimeout = formationErr instanceof Error && formationErr.message.includes("timed out");
|
|
27093
|
+
const elapsedMs = Date.now() - formationStartTime;
|
|
27094
|
+
const stageSummary = formatFormationStageDurations(completedFormationStages);
|
|
27095
|
+
console.error(
|
|
27096
|
+
`[memorix] Formation ${isTimeout ? "timed out" : "failed"} in memorix_store after ${elapsedMs}ms/${FORMATION_TIMEOUT_MS}ms at stage ${currentFormationStage}${stageSummary ? ` | completed: ${stageSummary}` : ""}`
|
|
27097
|
+
);
|
|
27098
|
+
if (!isTimeout && formationErr instanceof Error) {
|
|
27099
|
+
console.error(`[memorix] Formation error: ${formationErr.message}`);
|
|
27100
|
+
}
|
|
27101
|
+
formationNote = `
|
|
27102
|
+
[WARN] Formation ${isTimeout ? "timed out" : "failed"} \u2014 storing base observation without enrichment`;
|
|
27103
|
+
}
|
|
27104
|
+
}
|
|
27105
|
+
if (useFormation && formationResult && formationResult.resolution.action !== "new") {
|
|
27106
|
+
const { action: action2, targetId, reason } = formationResult.resolution;
|
|
27107
|
+
if (action2 === "merge" && targetId) {
|
|
27108
|
+
const targetObs = getObservation(targetId);
|
|
27109
|
+
if (targetObs && targetObs.projectId === project.id && canReadObservation(targetObs, reader)) {
|
|
27110
|
+
await storeObservation({
|
|
27111
|
+
entityName: targetObs.entityName,
|
|
27112
|
+
type: targetObs.type,
|
|
27113
|
+
title: formationResult.title,
|
|
27114
|
+
narrative: formationResult.narrative,
|
|
27115
|
+
facts: formationResult.facts,
|
|
27116
|
+
filesModified: safeFiles,
|
|
27117
|
+
concepts: safeConcepts,
|
|
27118
|
+
projectId: project.id,
|
|
27119
|
+
topicKey: targetObs.topicKey,
|
|
27120
|
+
progress,
|
|
27121
|
+
sourceDetail: "explicit",
|
|
27122
|
+
createdByAgentId: currentAgentId,
|
|
27123
|
+
visibility,
|
|
27124
|
+
visibilityReader: reader
|
|
27125
|
+
});
|
|
26442
27126
|
return {
|
|
26443
27127
|
content: [{
|
|
26444
27128
|
type: "text",
|
|
26445
|
-
text: `[
|
|
26446
|
-
Existing memory #${decision.targetId} already covers this.
|
|
26447
|
-
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
27129
|
+
text: `[UPDATED] Formation MERGE: merged into #${targetId} (${reason})${formationNote}`
|
|
26448
27130
|
}]
|
|
26449
27131
|
};
|
|
26450
|
-
} else if (decision.action === "DELETE" && decision.targetId) {
|
|
26451
|
-
const { resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
26452
|
-
await resolveObservations2([decision.targetId], "resolved");
|
|
26453
|
-
compactAction = ` | Compact: resolved outdated #${decision.targetId}`;
|
|
26454
27132
|
}
|
|
26455
|
-
|
|
26456
|
-
|
|
26457
|
-
|
|
26458
|
-
|
|
26459
|
-
|
|
26460
|
-
|
|
27133
|
+
} else if (action2 === "evolve" && targetId) {
|
|
27134
|
+
const targetObs = getObservation(targetId);
|
|
27135
|
+
if (targetObs && targetObs.projectId === project.id && canReadObservation(targetObs, reader)) {
|
|
27136
|
+
await storeObservation({
|
|
27137
|
+
entityName: targetObs.entityName,
|
|
27138
|
+
type: targetObs.type,
|
|
27139
|
+
title: formationResult.title,
|
|
27140
|
+
narrative: formationResult.narrative,
|
|
27141
|
+
facts: formationResult.facts,
|
|
27142
|
+
filesModified: safeFiles,
|
|
27143
|
+
concepts: safeConcepts,
|
|
27144
|
+
projectId: project.id,
|
|
27145
|
+
topicKey: targetObs.topicKey,
|
|
27146
|
+
progress,
|
|
27147
|
+
sourceDetail: "explicit",
|
|
27148
|
+
createdByAgentId: currentAgentId,
|
|
27149
|
+
visibility,
|
|
27150
|
+
visibilityReader: reader
|
|
27151
|
+
});
|
|
27152
|
+
return {
|
|
27153
|
+
content: [{
|
|
27154
|
+
type: "text",
|
|
27155
|
+
text: `[UPDATED] Formation EVOLVE: evolved #${targetId} (${reason})${formationNote}`
|
|
27156
|
+
}]
|
|
27157
|
+
};
|
|
26461
27158
|
}
|
|
26462
|
-
}
|
|
26463
|
-
|
|
26464
|
-
|
|
26465
|
-
|
|
26466
|
-
|
|
26467
|
-
|
|
26468
|
-
|
|
26469
|
-
const currentFacts = safeFacts ?? [];
|
|
26470
|
-
const currentLower = new Set(currentFacts.map((f) => f.toLowerCase().trim()));
|
|
26471
|
-
const newFacts = llmFacts.filter((f) => !currentLower.has(f.toLowerCase().trim()));
|
|
26472
|
-
if (newFacts.length > 0) {
|
|
26473
|
-
safeFacts = [...currentFacts, ...newFacts];
|
|
27159
|
+
} else if (action2 === "discard") {
|
|
27160
|
+
return {
|
|
27161
|
+
content: [{
|
|
27162
|
+
type: "text",
|
|
27163
|
+
text: `[SKIP] Formation DISCARD: ${reason}${formationNote}`
|
|
27164
|
+
}]
|
|
27165
|
+
};
|
|
26474
27166
|
}
|
|
26475
27167
|
}
|
|
26476
|
-
|
|
26477
|
-
|
|
26478
|
-
|
|
26479
|
-
if (formationResult.extraction.typeCorrected && formationResult.type) {
|
|
26480
|
-
type = formationResult.type;
|
|
26481
|
-
}
|
|
26482
|
-
if (formationResult.extraction.entityResolved && formationResult.entityName) {
|
|
26483
|
-
entityName = formationResult.entityName;
|
|
26484
|
-
}
|
|
26485
|
-
}
|
|
26486
|
-
await graphManager.createEntities([
|
|
26487
|
-
{ name: entityName, entityType: "auto", observations: [] }
|
|
26488
|
-
]);
|
|
26489
|
-
let sessionId;
|
|
26490
|
-
try {
|
|
26491
|
-
const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
26492
|
-
const active = await getActiveSession2(projectDir2, project.id);
|
|
26493
|
-
if (active) sessionId = active.id;
|
|
26494
|
-
} catch {
|
|
26495
|
-
}
|
|
26496
|
-
let finalNarrative = narrative;
|
|
26497
|
-
let compressionNote = "";
|
|
26498
|
-
try {
|
|
26499
|
-
const { compressNarrative: compressNarrative2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
|
|
26500
|
-
const { compressed, saved, usedLLM } = await withTimeout2(
|
|
26501
|
-
compressNarrative2(narrative, safeFacts, type),
|
|
26502
|
-
COMPRESSION_TIMEOUT_MS,
|
|
26503
|
-
"Narrative compression"
|
|
26504
|
-
);
|
|
26505
|
-
if (usedLLM && saved > 0) {
|
|
26506
|
-
finalNarrative = compressed;
|
|
26507
|
-
compressionNote = ` | compressed -${saved} tokens`;
|
|
26508
|
-
}
|
|
26509
|
-
} catch {
|
|
26510
|
-
}
|
|
26511
|
-
let attributionWarning = "";
|
|
26512
|
-
try {
|
|
26513
|
-
const attrCheck = await checkProjectAttribution(entityName, project.id, getAllObservations());
|
|
26514
|
-
if (attrCheck.suspicious) {
|
|
26515
|
-
attributionWarning = `
|
|
26516
|
-
[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.`;
|
|
26517
|
-
}
|
|
26518
|
-
} catch {
|
|
26519
|
-
}
|
|
26520
|
-
const { observation: obs, upserted } = await storeObservation({
|
|
26521
|
-
entityName,
|
|
26522
|
-
type,
|
|
26523
|
-
title,
|
|
26524
|
-
narrative: finalNarrative,
|
|
26525
|
-
facts: safeFacts,
|
|
26526
|
-
filesModified: safeFiles,
|
|
26527
|
-
concepts: safeConcepts,
|
|
26528
|
-
projectId: project.id,
|
|
26529
|
-
topicKey,
|
|
26530
|
-
sessionId,
|
|
26531
|
-
progress,
|
|
26532
|
-
relatedCommits,
|
|
26533
|
-
relatedEntities,
|
|
26534
|
-
sourceDetail: "explicit",
|
|
26535
|
-
valueCategory: formationResult?.evaluation.category,
|
|
26536
|
-
createdByAgentId: currentAgentId
|
|
26537
|
-
});
|
|
26538
|
-
await graphManager.addObservations([
|
|
26539
|
-
{ entityName, contents: [`[#${obs.id}] ${title}`] }
|
|
26540
|
-
]);
|
|
26541
|
-
const extracted = extractEntities([title, narrative, ...safeFacts ?? []].join(" "));
|
|
26542
|
-
const autoRelCount = await createAutoRelations(obs, extracted, graphManager);
|
|
26543
|
-
const enrichmentParts = [];
|
|
26544
|
-
const autoFiles = obs.filesModified.filter((f) => !(safeFiles ?? []).includes(f));
|
|
26545
|
-
const autoConcepts = obs.concepts.filter((c) => !(safeConcepts ?? []).includes(c));
|
|
26546
|
-
if (autoFiles.length > 0) enrichmentParts.push(`+${autoFiles.length} files extracted`);
|
|
26547
|
-
if (autoConcepts.length > 0) enrichmentParts.push(`+${autoConcepts.length} concepts enriched`);
|
|
26548
|
-
if (autoRelCount > 0) enrichmentParts.push(`+${autoRelCount} relations auto-created`);
|
|
26549
|
-
if (obs.hasCausalLanguage) enrichmentParts.push("causal language detected");
|
|
26550
|
-
if (upserted) enrichmentParts.push(`topic upserted (rev ${obs.revisionCount ?? 1})`);
|
|
26551
|
-
const enrichment = enrichmentParts.length > 0 ? `
|
|
26552
|
-
Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
26553
|
-
const action = upserted ? "[UPDATED] Updated" : "[OK] Stored";
|
|
26554
|
-
if (!useFormation && !topicKey && !progress) {
|
|
26555
|
-
const shadowFormation = async () => {
|
|
26556
|
-
let oldCompactDecision = null;
|
|
27168
|
+
let compactAction = "";
|
|
27169
|
+
let compactMerged = false;
|
|
27170
|
+
if (!useFormation && !topicKey && !progress) {
|
|
26557
27171
|
try {
|
|
26558
|
-
const compactStart = Date.now();
|
|
26559
27172
|
const searchResult = await compactSearch({
|
|
26560
27173
|
query: `${title} ${narrative.substring(0, 200)}`,
|
|
26561
27174
|
limit: 5,
|
|
26562
27175
|
projectId: project.id,
|
|
26563
|
-
status: "active"
|
|
27176
|
+
status: "active",
|
|
27177
|
+
reader
|
|
26564
27178
|
});
|
|
26565
27179
|
const similarEntries = searchResult.entries.map((e) => e);
|
|
26566
27180
|
if (similarEntries.length > 0) {
|
|
26567
27181
|
const similarIds = similarEntries.map((e) => e.id);
|
|
26568
|
-
const details = await compactDetail(similarIds);
|
|
27182
|
+
const details = await compactDetail(similarIds, { reader });
|
|
26569
27183
|
const existingMemories = details.documents.map((d, i) => ({
|
|
26570
27184
|
id: d.observationId,
|
|
26571
27185
|
title: d.title,
|
|
@@ -26573,78 +27187,266 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
|
26573
27187
|
facts: d.facts,
|
|
26574
27188
|
score: similarEntries[i]?.score ?? 0
|
|
26575
27189
|
}));
|
|
26576
|
-
const decision = await
|
|
26577
|
-
|
|
26578
|
-
|
|
27190
|
+
const decision = await withTimeout2(
|
|
27191
|
+
compactOnWrite(
|
|
27192
|
+
{ title, narrative, facts: safeFacts ?? [] },
|
|
27193
|
+
existingMemories
|
|
27194
|
+
),
|
|
27195
|
+
COMPACT_ON_WRITE_TIMEOUT_MS,
|
|
27196
|
+
"Compact-on-write"
|
|
26579
27197
|
);
|
|
26580
|
-
|
|
26581
|
-
|
|
26582
|
-
|
|
26583
|
-
|
|
26584
|
-
|
|
26585
|
-
|
|
27198
|
+
if (decision.action === "UPDATE" && decision.targetId) {
|
|
27199
|
+
const targetObs = getObservation(decision.targetId);
|
|
27200
|
+
if (targetObs && targetObs.projectId === project.id && canReadObservation(targetObs, reader)) {
|
|
27201
|
+
await storeObservation({
|
|
27202
|
+
entityName: targetObs.entityName,
|
|
27203
|
+
type: targetObs.type,
|
|
27204
|
+
title: decision.mergedNarrative ? title : targetObs.title,
|
|
27205
|
+
narrative: decision.mergedNarrative ?? narrative,
|
|
27206
|
+
facts: decision.mergedFacts ?? safeFacts,
|
|
27207
|
+
filesModified: safeFiles,
|
|
27208
|
+
concepts: safeConcepts,
|
|
27209
|
+
projectId: project.id,
|
|
27210
|
+
topicKey: targetObs.topicKey,
|
|
27211
|
+
progress,
|
|
27212
|
+
sourceDetail: "explicit",
|
|
27213
|
+
createdByAgentId: currentAgentId,
|
|
27214
|
+
visibility,
|
|
27215
|
+
visibilityReader: reader
|
|
27216
|
+
});
|
|
27217
|
+
compactAction = `[UPDATED] Compact UPDATE: merged into #${decision.targetId} (${decision.reason})`;
|
|
27218
|
+
compactMerged = true;
|
|
27219
|
+
return {
|
|
27220
|
+
content: [{
|
|
27221
|
+
type: "text",
|
|
27222
|
+
text: `${compactAction}
|
|
27223
|
+
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
27224
|
+
}]
|
|
27225
|
+
};
|
|
27226
|
+
}
|
|
27227
|
+
} else if (decision.action === "NONE") {
|
|
27228
|
+
return {
|
|
27229
|
+
content: [{
|
|
27230
|
+
type: "text",
|
|
27231
|
+
text: `[SKIP] Compact SKIP: ${decision.reason}
|
|
27232
|
+
Existing memory #${decision.targetId} already covers this.
|
|
27233
|
+
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
27234
|
+
}]
|
|
27235
|
+
};
|
|
27236
|
+
} else if (decision.action === "DELETE" && decision.targetId) {
|
|
27237
|
+
const { resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27238
|
+
await resolveObservations2([decision.targetId], "resolved");
|
|
27239
|
+
compactAction = ` | Compact: resolved outdated #${decision.targetId}`;
|
|
27240
|
+
}
|
|
27241
|
+
if (decision.enrichedFacts && decision.enrichedFacts.length > 0) {
|
|
27242
|
+
const currentFacts = safeFacts ?? [];
|
|
27243
|
+
const newFacts = decision.enrichedFacts.filter((f) => !currentFacts.includes(f));
|
|
27244
|
+
if (newFacts.length > 0) {
|
|
27245
|
+
compactAction += ` | +${newFacts.length} LLM-extracted facts`;
|
|
27246
|
+
}
|
|
27247
|
+
}
|
|
26586
27248
|
}
|
|
26587
27249
|
} catch {
|
|
26588
27250
|
}
|
|
26589
|
-
|
|
26590
|
-
|
|
26591
|
-
|
|
26592
|
-
|
|
26593
|
-
|
|
26594
|
-
|
|
26595
|
-
|
|
26596
|
-
|
|
26597
|
-
|
|
26598
|
-
|
|
26599
|
-
|
|
26600
|
-
|
|
26601
|
-
|
|
26602
|
-
|
|
26603
|
-
|
|
26604
|
-
|
|
26605
|
-
|
|
26606
|
-
|
|
26607
|
-
|
|
26608
|
-
|
|
26609
|
-
|
|
26610
|
-
|
|
26611
|
-
|
|
26612
|
-
|
|
26613
|
-
|
|
26614
|
-
|
|
26615
|
-
const
|
|
26616
|
-
|
|
26617
|
-
|
|
26618
|
-
|
|
27251
|
+
}
|
|
27252
|
+
if (formationResult && formationResult.resolution.action === "new") {
|
|
27253
|
+
const llmFacts = formationResult.extraction.extractedFacts;
|
|
27254
|
+
if (llmFacts.length > 0) {
|
|
27255
|
+
const currentFacts = safeFacts ?? [];
|
|
27256
|
+
const currentLower = new Set(currentFacts.map((f) => f.toLowerCase().trim()));
|
|
27257
|
+
const newFacts = llmFacts.filter((f) => !currentLower.has(f.toLowerCase().trim()));
|
|
27258
|
+
if (newFacts.length > 0) {
|
|
27259
|
+
safeFacts = [...currentFacts, ...newFacts];
|
|
27260
|
+
}
|
|
27261
|
+
}
|
|
27262
|
+
if (formationResult.extraction.titleImproved && formationResult.title) {
|
|
27263
|
+
title = formationResult.title;
|
|
27264
|
+
}
|
|
27265
|
+
if (formationResult.extraction.typeCorrected && formationResult.type) {
|
|
27266
|
+
type = formationResult.type;
|
|
27267
|
+
}
|
|
27268
|
+
if (formationResult.extraction.entityResolved && formationResult.entityName) {
|
|
27269
|
+
entityName = formationResult.entityName;
|
|
27270
|
+
}
|
|
27271
|
+
}
|
|
27272
|
+
await graphManager.createEntities([
|
|
27273
|
+
{ name: entityName, entityType: "auto", observations: [] }
|
|
27274
|
+
]);
|
|
27275
|
+
let sessionId;
|
|
27276
|
+
try {
|
|
27277
|
+
const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
27278
|
+
const active = await getActiveSession2(projectDir2, project.id);
|
|
27279
|
+
if (active) sessionId = active.id;
|
|
27280
|
+
} catch {
|
|
27281
|
+
}
|
|
27282
|
+
let finalNarrative = narrative;
|
|
27283
|
+
let compressionNote = "";
|
|
27284
|
+
try {
|
|
27285
|
+
const { compressNarrative: compressNarrative2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
|
|
27286
|
+
const { compressed, saved, usedLLM } = await withTimeout2(
|
|
27287
|
+
compressNarrative2(narrative, safeFacts, type),
|
|
27288
|
+
COMPRESSION_TIMEOUT_MS,
|
|
27289
|
+
"Narrative compression"
|
|
26619
27290
|
);
|
|
26620
|
-
|
|
26621
|
-
|
|
26622
|
-
|
|
26623
|
-
formationAction: formed.resolution.action,
|
|
26624
|
-
formationTargetId: formed.resolution.targetId,
|
|
26625
|
-
oldCompactAction: oldCompactDecision.action,
|
|
26626
|
-
oldCompactTargetId: oldCompactDecision.targetId,
|
|
26627
|
-
oldCompactReason: oldCompactDecision.reason,
|
|
26628
|
-
formationValueScore: formed.evaluation.score,
|
|
26629
|
-
formationValueCategory: formed.evaluation.category,
|
|
26630
|
-
formationDurationMs: formed.pipeline.durationMs,
|
|
26631
|
-
compactDurationMs: oldCompactDecision.durationMs
|
|
26632
|
-
});
|
|
27291
|
+
if (usedLLM && saved > 0) {
|
|
27292
|
+
finalNarrative = compressed;
|
|
27293
|
+
compressionNote = ` | compressed -${saved} tokens`;
|
|
26633
27294
|
}
|
|
26634
|
-
}
|
|
26635
|
-
|
|
27295
|
+
} catch {
|
|
27296
|
+
}
|
|
27297
|
+
let attributionWarning = "";
|
|
27298
|
+
try {
|
|
27299
|
+
const attrCheck = await checkProjectAttribution(
|
|
27300
|
+
entityName,
|
|
27301
|
+
project.id,
|
|
27302
|
+
filterReadableObservations(getAllObservations(), reader)
|
|
27303
|
+
);
|
|
27304
|
+
if (attrCheck.suspicious) {
|
|
27305
|
+
attributionWarning = `
|
|
27306
|
+
[WARN] Attribution notice: entity "${entityName}" has 0 observations in "${project.id}" but ${attrCheck.count} in "${attrCheck.knownIn}" (confidence: ${attrCheck.confidence}). Verify the correct project is bound before storing.`;
|
|
27307
|
+
}
|
|
27308
|
+
} catch {
|
|
27309
|
+
}
|
|
27310
|
+
const { observation: obs, upserted } = await storeObservation({
|
|
27311
|
+
entityName,
|
|
27312
|
+
type,
|
|
27313
|
+
title,
|
|
27314
|
+
narrative: finalNarrative,
|
|
27315
|
+
facts: safeFacts,
|
|
27316
|
+
filesModified: safeFiles,
|
|
27317
|
+
concepts: safeConcepts,
|
|
27318
|
+
projectId: project.id,
|
|
27319
|
+
topicKey,
|
|
27320
|
+
sessionId,
|
|
27321
|
+
progress,
|
|
27322
|
+
relatedCommits,
|
|
27323
|
+
relatedEntities,
|
|
27324
|
+
sourceDetail: "explicit",
|
|
27325
|
+
valueCategory: formationResult?.evaluation.category,
|
|
27326
|
+
createdByAgentId: currentAgentId,
|
|
27327
|
+
visibility,
|
|
27328
|
+
visibilityReader: reader
|
|
26636
27329
|
});
|
|
26637
|
-
|
|
26638
|
-
|
|
26639
|
-
|
|
26640
|
-
|
|
26641
|
-
|
|
26642
|
-
|
|
27330
|
+
await graphManager.addObservations([
|
|
27331
|
+
{ entityName, contents: [`[#${obs.id}] ${title}`] }
|
|
27332
|
+
]);
|
|
27333
|
+
const extracted = extractEntities([title, narrative, ...safeFacts ?? []].join(" "));
|
|
27334
|
+
const autoRelCount = await createAutoRelations(obs, extracted, graphManager);
|
|
27335
|
+
const enrichmentParts = [];
|
|
27336
|
+
const autoFiles = obs.filesModified.filter((f) => !(safeFiles ?? []).includes(f));
|
|
27337
|
+
const autoConcepts = obs.concepts.filter((c) => !(safeConcepts ?? []).includes(c));
|
|
27338
|
+
if (autoFiles.length > 0) enrichmentParts.push(`+${autoFiles.length} files extracted`);
|
|
27339
|
+
if (autoConcepts.length > 0) enrichmentParts.push(`+${autoConcepts.length} concepts enriched`);
|
|
27340
|
+
if (autoRelCount > 0) enrichmentParts.push(`+${autoRelCount} relations auto-created`);
|
|
27341
|
+
if (obs.hasCausalLanguage) enrichmentParts.push("causal language detected");
|
|
27342
|
+
if (upserted) enrichmentParts.push(`topic upserted (rev ${obs.revisionCount ?? 1})`);
|
|
27343
|
+
const enrichment = enrichmentParts.length > 0 ? `
|
|
27344
|
+
Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
27345
|
+
const action = upserted ? "[UPDATED] Updated" : "[OK] Stored";
|
|
27346
|
+
if (!useFormation && !topicKey && !progress) {
|
|
27347
|
+
const shadowFormation = async () => {
|
|
27348
|
+
let oldCompactDecision = null;
|
|
27349
|
+
try {
|
|
27350
|
+
const compactStart = Date.now();
|
|
27351
|
+
const searchResult = await compactSearch({
|
|
27352
|
+
query: `${title} ${narrative.substring(0, 200)}`,
|
|
27353
|
+
limit: 5,
|
|
27354
|
+
projectId: project.id,
|
|
27355
|
+
status: "active",
|
|
27356
|
+
reader
|
|
27357
|
+
});
|
|
27358
|
+
const similarEntries = searchResult.entries.map((e) => e);
|
|
27359
|
+
if (similarEntries.length > 0) {
|
|
27360
|
+
const similarIds = similarEntries.map((e) => e.id);
|
|
27361
|
+
const details = await compactDetail(similarIds, { reader });
|
|
27362
|
+
const existingMemories = details.documents.map((d, i) => ({
|
|
27363
|
+
id: d.observationId,
|
|
27364
|
+
title: d.title,
|
|
27365
|
+
narrative: d.narrative,
|
|
27366
|
+
facts: d.facts,
|
|
27367
|
+
score: similarEntries[i]?.score ?? 0
|
|
27368
|
+
}));
|
|
27369
|
+
const decision = await compactOnWrite(
|
|
27370
|
+
{ title, narrative, facts: safeFacts ?? [] },
|
|
27371
|
+
existingMemories
|
|
27372
|
+
);
|
|
27373
|
+
oldCompactDecision = {
|
|
27374
|
+
action: decision.action,
|
|
27375
|
+
targetId: decision.targetId,
|
|
27376
|
+
reason: decision.reason,
|
|
27377
|
+
durationMs: Date.now() - compactStart
|
|
27378
|
+
};
|
|
27379
|
+
}
|
|
27380
|
+
} catch {
|
|
27381
|
+
}
|
|
27382
|
+
const formationConfig = {
|
|
27383
|
+
mode: formationMode,
|
|
27384
|
+
useLLM: isLLMEnabled(),
|
|
27385
|
+
minValueScore: 0.3,
|
|
27386
|
+
searchMemories: async (q, limit, pid) => {
|
|
27387
|
+
const result = await compactSearch({ query: q, limit, projectId: pid, status: "active", reader });
|
|
27388
|
+
if (result.entries.length === 0) return [];
|
|
27389
|
+
const details = await compactDetail(result.entries.map((e) => e.id), { reader });
|
|
27390
|
+
return details.documents.map((d, i) => ({
|
|
27391
|
+
id: Number(d.id.replace("obs-", "")),
|
|
27392
|
+
observationId: d.observationId,
|
|
27393
|
+
title: d.title,
|
|
27394
|
+
narrative: d.narrative,
|
|
27395
|
+
facts: d.facts,
|
|
27396
|
+
entityName: d.entityName,
|
|
27397
|
+
type: d.type,
|
|
27398
|
+
score: result.entries[i]?.score ?? 0
|
|
27399
|
+
}));
|
|
27400
|
+
},
|
|
27401
|
+
getObservation: (id) => {
|
|
27402
|
+
const o = getObservation(id);
|
|
27403
|
+
if (!o || o.projectId !== project.id || !canReadObservation(o, reader)) return null;
|
|
27404
|
+
return { id: o.id, entityName: o.entityName, type: o.type, title: o.title, narrative: o.narrative, facts: o.facts, topicKey: o.topicKey };
|
|
27405
|
+
},
|
|
27406
|
+
getEntityNames: () => graphManager.getEntityNames()
|
|
27407
|
+
};
|
|
27408
|
+
const formed = await withTimeout2(
|
|
27409
|
+
runFormation({ entityName, type, title, narrative, facts: safeFacts, projectId: project.id, source: "explicit", topicKey }, formationConfig),
|
|
27410
|
+
FORMATION_TIMEOUT_MS,
|
|
27411
|
+
"Shadow formation"
|
|
27412
|
+
);
|
|
27413
|
+
const { recordBeforeAfterMetrics: recordBeforeAfterMetrics2 } = await Promise.resolve().then(() => (init_formation(), formation_exports));
|
|
27414
|
+
if (oldCompactDecision) {
|
|
27415
|
+
recordBeforeAfterMetrics2({
|
|
27416
|
+
formationAction: formed.resolution.action,
|
|
27417
|
+
formationTargetId: formed.resolution.targetId,
|
|
27418
|
+
oldCompactAction: oldCompactDecision.action,
|
|
27419
|
+
oldCompactTargetId: oldCompactDecision.targetId,
|
|
27420
|
+
oldCompactReason: oldCompactDecision.reason,
|
|
27421
|
+
formationValueScore: formed.evaluation.score,
|
|
27422
|
+
formationValueCategory: formed.evaluation.category,
|
|
27423
|
+
formationDurationMs: formed.pipeline.durationMs,
|
|
27424
|
+
compactDurationMs: oldCompactDecision.durationMs
|
|
27425
|
+
});
|
|
27426
|
+
}
|
|
27427
|
+
};
|
|
27428
|
+
shadowFormation().catch(() => {
|
|
27429
|
+
});
|
|
27430
|
+
}
|
|
27431
|
+
return {
|
|
27432
|
+
content: [
|
|
27433
|
+
{
|
|
27434
|
+
type: "text",
|
|
27435
|
+
text: `${action} observation #${obs.id} "${title}" (~${obs.tokens} tokens)
|
|
26643
27436
|
Entity: ${entityName} | Type: ${type} | Project: ${project.id}${obs.topicKey ? ` | Topic: ${obs.topicKey}` : ""}${compactAction}${compressionNote}${enrichment}${formationNote}${attributionWarning}`
|
|
26644
|
-
|
|
26645
|
-
|
|
27437
|
+
}
|
|
27438
|
+
]
|
|
27439
|
+
};
|
|
27440
|
+
});
|
|
27441
|
+
} catch (error) {
|
|
27442
|
+
return {
|
|
27443
|
+
content: [{
|
|
27444
|
+
type: "text",
|
|
27445
|
+
text: error instanceof Error ? error.message : "Failed to store memory."
|
|
27446
|
+
}],
|
|
27447
|
+
isError: true
|
|
26646
27448
|
};
|
|
26647
|
-
}
|
|
27449
|
+
}
|
|
26648
27450
|
}
|
|
26649
27451
|
);
|
|
26650
27452
|
server.registerTool(
|
|
@@ -26716,7 +27518,8 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
26716
27518
|
// Use scope: 'global' to explicitly search all projects.
|
|
26717
27519
|
projectId: scope === "global" ? void 0 : project.id,
|
|
26718
27520
|
status: status ?? "active",
|
|
26719
|
-
source
|
|
27521
|
+
source,
|
|
27522
|
+
reader: getObservationReader(scope === "global" ? "global" : "project")
|
|
26720
27523
|
});
|
|
26721
27524
|
const timeoutPromise = new Promise(
|
|
26722
27525
|
(_, reject) => setTimeout(() => reject(new Error(`Search timeout after ${TIMEOUT_MS}ms`)), TIMEOUT_MS)
|
|
@@ -26774,7 +27577,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26774
27577
|
const unresolved = requireResolvedProject("build graph context for the current project");
|
|
26775
27578
|
if (unresolved) return unresolved;
|
|
26776
27579
|
const { getObservationStore: getObservationStore2 } = await Promise.resolve().then(() => (init_obs_store(), obs_store_exports));
|
|
26777
|
-
const observations2 =
|
|
27580
|
+
const observations2 = filterReadableObservations(
|
|
27581
|
+
await getObservationStore2().loadByProject(project.id),
|
|
27582
|
+
getObservationReader()
|
|
27583
|
+
);
|
|
26778
27584
|
const packet = buildGraphContextPacket(observations2, {
|
|
26779
27585
|
projectId: project.id,
|
|
26780
27586
|
query,
|
|
@@ -26826,7 +27632,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26826
27632
|
Promise.resolve().then(() => (init_maintenance_jobs(), maintenance_jobs_exports)),
|
|
26827
27633
|
Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports))
|
|
26828
27634
|
]);
|
|
26829
|
-
const observations2 =
|
|
27635
|
+
const observations2 = filterReadableObservations(
|
|
27636
|
+
await getObservationStore2().loadByProject(project.id, { status: "active" }),
|
|
27637
|
+
getObservationReader()
|
|
27638
|
+
);
|
|
26830
27639
|
const context = await buildAutoProjectContext2({
|
|
26831
27640
|
project,
|
|
26832
27641
|
dataDir: projectDir2,
|
|
@@ -26901,7 +27710,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26901
27710
|
{ getResolvedConfig: getResolvedConfig2 },
|
|
26902
27711
|
{ getExternalCodeGraphContext: getExternalCodeGraphContext2 },
|
|
26903
27712
|
{ getObservationStore: getObservationStore2 },
|
|
26904
|
-
{ collectCurrentProjectFacts: collectCurrentProjectFacts2 },
|
|
27713
|
+
{ collectCurrentProjectFacts: collectCurrentProjectFacts2, formatGitFact: formatGitFact2 },
|
|
26905
27714
|
{ resolveTaskLens: resolveTaskLens2 }
|
|
26906
27715
|
] = await Promise.all([
|
|
26907
27716
|
Promise.resolve().then(() => (init_store(), store_exports)),
|
|
@@ -26916,7 +27725,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26916
27725
|
await store.init(projectDir2);
|
|
26917
27726
|
const codegraphConfig = getResolvedConfig2({ projectRoot: project.rootPath }).codegraph;
|
|
26918
27727
|
const exclude = codegraphConfig.excludePatterns;
|
|
26919
|
-
const observations2 =
|
|
27728
|
+
const observations2 = filterReadableObservations(
|
|
27729
|
+
await getObservationStore2().loadByProject(project.id, { status: "active" }),
|
|
27730
|
+
getObservationReader()
|
|
27731
|
+
);
|
|
26920
27732
|
observations2.reverse();
|
|
26921
27733
|
const basePack = assembleContextPackForTask2({
|
|
26922
27734
|
store,
|
|
@@ -26942,7 +27754,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26942
27754
|
if (currentFacts.latestChangelog) {
|
|
26943
27755
|
worksetFacts.push("Latest changelog: " + currentFacts.latestChangelog.version + (currentFacts.latestChangelog.date ? " (" + currentFacts.latestChangelog.date + ")" : ""));
|
|
26944
27756
|
}
|
|
26945
|
-
worksetFacts.push(
|
|
27757
|
+
worksetFacts.push(formatGitFact2(currentFacts.git));
|
|
26946
27758
|
const codeState = snapshot ? "- Code state: " + (snapshot.baseRevision ? snapshot.baseRevision.slice(0, 12) : "Git unavailable") + ", " + snapshot.worktreeState + " worktree, epoch " + snapshot.sourceEpoch : "- Code state: no completed snapshot yet";
|
|
26947
27759
|
const pack = await attachTaskWorkset2({
|
|
26948
27760
|
pack: basePack,
|
|
@@ -26974,11 +27786,13 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26974
27786
|
"memorix_knowledge",
|
|
26975
27787
|
{
|
|
26976
27788
|
title: "Knowledge Workspace",
|
|
26977
|
-
description: "Manage the reviewable project Knowledge Workspace. Use it only for deliberate knowledge operations: initialize a local or versioned workspace,
|
|
27789
|
+
description: "Manage the reviewable project Knowledge Workspace. Use it only for deliberate knowledge operations: initialize a local or versioned workspace, review source-backed claims, compile proposals, lint, apply a reviewed proposal, or manage canonical workflows. It is intentionally absent from the default micro/lite profiles.",
|
|
26978
27790
|
inputSchema: {
|
|
26979
27791
|
action: z2.enum([
|
|
26980
27792
|
"workspace_init",
|
|
26981
27793
|
"status",
|
|
27794
|
+
"claim_list",
|
|
27795
|
+
"claim_review",
|
|
26982
27796
|
"compile",
|
|
26983
27797
|
"lint",
|
|
26984
27798
|
"proposal_apply",
|
|
@@ -26993,6 +27807,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26993
27807
|
path: z2.string().optional().describe("Explicit versioned workspace path, used only by workspace_init"),
|
|
26994
27808
|
proposalId: z2.string().optional().describe("Pending proposal id for proposal_apply"),
|
|
26995
27809
|
allowManualOverwrite: z2.boolean().optional().default(false).describe("Explicitly allow proposal_apply to replace a manually edited page"),
|
|
27810
|
+
claimId: z2.string().optional().describe("Source-backed claim id for claim_review"),
|
|
27811
|
+
claimReviewState: z2.enum(["approved", "rejected"]).optional().describe("Deliberate review verdict for claim_review"),
|
|
27812
|
+
reviewDetail: z2.string().max(2e3).optional().describe("Evidence check performed before approving or rejecting a claim"),
|
|
26996
27813
|
workflowId: z2.string().optional().describe("Canonical workflow id for workflow preview, apply, or run"),
|
|
26997
27814
|
agent: z2.string().optional().describe("Target agent for a workflow adapter"),
|
|
26998
27815
|
task: z2.string().optional().describe("Task text for workflow selection or a workflow run"),
|
|
@@ -27009,6 +27826,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27009
27826
|
path: workspacePath,
|
|
27010
27827
|
proposalId,
|
|
27011
27828
|
allowManualOverwrite,
|
|
27829
|
+
claimId,
|
|
27830
|
+
claimReviewState,
|
|
27831
|
+
reviewDetail,
|
|
27012
27832
|
workflowId,
|
|
27013
27833
|
agent,
|
|
27014
27834
|
task,
|
|
@@ -27031,6 +27851,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27031
27851
|
};
|
|
27032
27852
|
const [
|
|
27033
27853
|
{ ClaimStore: ClaimStore2 },
|
|
27854
|
+
{ reviewClaim: reviewClaim2 },
|
|
27034
27855
|
{ CodeGraphStore: CodeGraphStore2 },
|
|
27035
27856
|
{ initializeKnowledgeWorkspace: initializeKnowledgeWorkspace2, loadKnowledgeWorkspace: loadKnowledgeWorkspace2 },
|
|
27036
27857
|
{ KnowledgeWorkspaceStore: KnowledgeWorkspaceStore2 },
|
|
@@ -27046,6 +27867,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27046
27867
|
}
|
|
27047
27868
|
] = await Promise.all([
|
|
27048
27869
|
Promise.resolve().then(() => (init_claim_store(), claim_store_exports)),
|
|
27870
|
+
Promise.resolve().then(() => (init_claims(), claims_exports)),
|
|
27049
27871
|
Promise.resolve().then(() => (init_store(), store_exports)),
|
|
27050
27872
|
Promise.resolve().then(() => (init_workspace(), workspace_exports)),
|
|
27051
27873
|
Promise.resolve().then(() => (init_workspace_store(), workspace_store_exports)),
|
|
@@ -27095,10 +27917,52 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27095
27917
|
targetPath: proposal.targetPath,
|
|
27096
27918
|
reason: proposal.reason,
|
|
27097
27919
|
createdAt: proposal.createdAt
|
|
27098
|
-
}))
|
|
27920
|
+
})),
|
|
27921
|
+
reviewableClaims: claims.listClaims(project.id, { limit: 100 }).filter((claim) => claim.reviewState === "needs-review").map((claim) => ({ id: claim.id, subject: claim.subject, predicate: claim.predicate, objectValue: claim.objectValue }))
|
|
27099
27922
|
}
|
|
27100
27923
|
});
|
|
27101
27924
|
}
|
|
27925
|
+
if (action === "claim_list") {
|
|
27926
|
+
return text({
|
|
27927
|
+
claims: claims.listClaims(project.id, { limit: 100 }).map((claim) => ({
|
|
27928
|
+
id: claim.id,
|
|
27929
|
+
subject: claim.subject,
|
|
27930
|
+
predicate: claim.predicate,
|
|
27931
|
+
objectValue: claim.objectValue,
|
|
27932
|
+
status: claim.status,
|
|
27933
|
+
reviewState: claim.reviewState,
|
|
27934
|
+
origin: claim.origin,
|
|
27935
|
+
confidence: claim.confidence,
|
|
27936
|
+
evidenceCount: claims.listEvidence(claim.id).length
|
|
27937
|
+
})),
|
|
27938
|
+
next: "Approve only after checking the linked evidence. Rejected claims stay out of retrieval and publication."
|
|
27939
|
+
});
|
|
27940
|
+
}
|
|
27941
|
+
if (action === "claim_review") {
|
|
27942
|
+
const requestedClaimId = requireText(claimId, "claimId");
|
|
27943
|
+
const detail = requireText(reviewDetail, "reviewDetail");
|
|
27944
|
+
if (!requestedClaimId || !claimReviewState || !detail) {
|
|
27945
|
+
return text({ error: "claimId, claimReviewState, and reviewDetail are required for claim_review." }, true);
|
|
27946
|
+
}
|
|
27947
|
+
const existing = claims.getClaim(requestedClaimId);
|
|
27948
|
+
if (!existing || existing.projectId !== project.id) {
|
|
27949
|
+
return text({ error: "Claim was not found for the current project." }, true);
|
|
27950
|
+
}
|
|
27951
|
+
const claim = reviewClaim2(claims, {
|
|
27952
|
+
claimId: requestedClaimId,
|
|
27953
|
+
reviewState: claimReviewState,
|
|
27954
|
+
detail
|
|
27955
|
+
});
|
|
27956
|
+
return text({
|
|
27957
|
+
claim: {
|
|
27958
|
+
id: claim.id,
|
|
27959
|
+
status: claim.status,
|
|
27960
|
+
reviewState: claim.reviewState,
|
|
27961
|
+
updatedAt: claim.updatedAt
|
|
27962
|
+
},
|
|
27963
|
+
next: claim.reviewState === "approved" ? "This approved source-backed claim can now be considered by knowledge compilation." : "This rejected claim is excluded from retrieval and knowledge compilation."
|
|
27964
|
+
});
|
|
27965
|
+
}
|
|
27102
27966
|
if (action === "compile") {
|
|
27103
27967
|
const result = await compileKnowledgeWorkspace2({ workspace, claims });
|
|
27104
27968
|
return text({
|
|
@@ -27137,7 +28001,13 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27137
28001
|
if (action === "workflow_import") {
|
|
27138
28002
|
const result = await importWindsurfWorkflows2({ workspace, projectRoot });
|
|
27139
28003
|
return text({
|
|
27140
|
-
imported: result.imported.map((workflow2) => ({
|
|
28004
|
+
imported: result.imported.map((workflow2) => ({
|
|
28005
|
+
id: workflow2.id,
|
|
28006
|
+
title: workflow2.title,
|
|
28007
|
+
sourcePath: workflow2.sourcePath,
|
|
28008
|
+
importedFrom: workflow2.importedFrom,
|
|
28009
|
+
verificationGates: workflow2.verificationGates
|
|
28010
|
+
})),
|
|
27141
28011
|
skipped: result.skipped
|
|
27142
28012
|
});
|
|
27143
28013
|
}
|
|
@@ -27149,7 +28019,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27149
28019
|
title: workflow2.title,
|
|
27150
28020
|
status: workflow2.status,
|
|
27151
28021
|
taskLenses: workflow2.taskLenses,
|
|
27152
|
-
sourcePath: workflow2.sourcePath
|
|
28022
|
+
sourcePath: workflow2.sourcePath,
|
|
28023
|
+
importedFrom: workflow2.importedFrom,
|
|
28024
|
+
verificationGates: workflow2.verificationGates
|
|
27153
28025
|
})),
|
|
27154
28026
|
parseErrors: synced.errors
|
|
27155
28027
|
});
|
|
@@ -27228,9 +28100,15 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27228
28100
|
}
|
|
27229
28101
|
},
|
|
27230
28102
|
async ({ ids, status }) => {
|
|
27231
|
-
const { resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28103
|
+
const { resolveObservations: resolveObservations2, getObservation: getObservation2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27232
28104
|
const safeIds = (Array.isArray(ids) ? ids : [ids]).map((id) => coerceNumber(id, 0)).filter((id) => id > 0);
|
|
27233
|
-
const
|
|
28105
|
+
const reader = getObservationReader();
|
|
28106
|
+
const authorizedIds = safeIds.filter((id) => {
|
|
28107
|
+
const observation = getObservation2(id, project.id);
|
|
28108
|
+
return observation ? canManageObservation(observation, reader) : false;
|
|
28109
|
+
});
|
|
28110
|
+
const result = await resolveObservations2(authorizedIds, status ?? "resolved");
|
|
28111
|
+
const deniedCount = safeIds.length - authorizedIds.length;
|
|
27234
28112
|
const parts = [];
|
|
27235
28113
|
if (result.resolved.length > 0) {
|
|
27236
28114
|
parts.push(`[OK] Resolved ${result.resolved.length} observation(s): #${result.resolved.join(", #")}`);
|
|
@@ -27238,6 +28116,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27238
28116
|
if (result.notFound.length > 0) {
|
|
27239
28117
|
parts.push(`[WARN] Not found: #${result.notFound.join(", #")}`);
|
|
27240
28118
|
}
|
|
28119
|
+
if (deniedCount > 0) {
|
|
28120
|
+
parts.push(`[WARN] Skipped ${deniedCount} observation(s) outside this session's write scope.`);
|
|
28121
|
+
}
|
|
27241
28122
|
parts.push('\nResolved memories are hidden from default search. Use status="all" to include them.');
|
|
27242
28123
|
parts.push('[STATS] Run `memorix_retention` with `action: "report"` to check remaining cleanup status.');
|
|
27243
28124
|
return {
|
|
@@ -27267,6 +28148,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27267
28148
|
async ({ entityName, decision, alternatives, rationale, constraints, expectedOutcome, risks, concepts, filesModified, relatedCommits, relatedEntities }) => {
|
|
27268
28149
|
const unresolved = requireResolvedProject("store reasoning in the current project");
|
|
27269
28150
|
if (unresolved) return unresolved;
|
|
28151
|
+
const reader = getObservationReader();
|
|
27270
28152
|
return withFreshIndex(async () => {
|
|
27271
28153
|
const narrativeParts = [rationale];
|
|
27272
28154
|
if (alternatives && alternatives.length > 0) {
|
|
@@ -27289,7 +28171,11 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27289
28171
|
]);
|
|
27290
28172
|
let reasoningAttributionWarning = "";
|
|
27291
28173
|
try {
|
|
27292
|
-
const attrCheck = await checkProjectAttribution(
|
|
28174
|
+
const attrCheck = await checkProjectAttribution(
|
|
28175
|
+
entityName,
|
|
28176
|
+
project.id,
|
|
28177
|
+
filterReadableObservations(getAllObservations(), reader)
|
|
28178
|
+
);
|
|
27293
28179
|
if (attrCheck.suspicious) {
|
|
27294
28180
|
reasoningAttributionWarning = `
|
|
27295
28181
|
[WARN] Attribution notice: entity "${entityName}" has 0 observations in "${project.id}" but ${attrCheck.count} in "${attrCheck.knownIn}" (confidence: ${attrCheck.confidence}). Verify the correct project is bound before storing.`;
|
|
@@ -27309,7 +28195,8 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27309
28195
|
relatedCommits,
|
|
27310
28196
|
relatedEntities,
|
|
27311
28197
|
sourceDetail: "explicit",
|
|
27312
|
-
createdByAgentId: currentAgentId
|
|
28198
|
+
createdByAgentId: currentAgentId,
|
|
28199
|
+
visibility: "project"
|
|
27313
28200
|
});
|
|
27314
28201
|
await graphManager.addObservations([
|
|
27315
28202
|
{ entityName, contents: [`[#${obs.id}] [REASONING] ${decision}`] }
|
|
@@ -27341,7 +28228,11 @@ Entity: ${entityName} | ${facts.length} facts | ${obs.tokens} tokens${reasoningA
|
|
|
27341
28228
|
const minCount = threshold ?? 2;
|
|
27342
28229
|
let entries;
|
|
27343
28230
|
try {
|
|
27344
|
-
entries = await auditProjectObservations(
|
|
28231
|
+
entries = await auditProjectObservations(
|
|
28232
|
+
project.id,
|
|
28233
|
+
await withFreshIndex(() => filterReadableObservations(getAllObservations(), getObservationReader())),
|
|
28234
|
+
minCount
|
|
28235
|
+
);
|
|
27345
28236
|
} catch (err) {
|
|
27346
28237
|
return {
|
|
27347
28238
|
content: [{
|
|
@@ -27406,7 +28297,8 @@ Entity: ${entityName} | ${facts.length} facts | ${obs.tokens} tokens${reasoningA
|
|
|
27406
28297
|
limit: safeLimit,
|
|
27407
28298
|
type: "reasoning",
|
|
27408
28299
|
projectId: scope === "global" ? void 0 : project.id,
|
|
27409
|
-
status: "active"
|
|
28300
|
+
status: "active",
|
|
28301
|
+
reader: getObservationReader(scope === "global" ? "global" : "project")
|
|
27410
28302
|
}));
|
|
27411
28303
|
if (result.entries.length === 0) {
|
|
27412
28304
|
return {
|
|
@@ -27431,7 +28323,11 @@ ${result.formatted}` }]
|
|
|
27431
28323
|
},
|
|
27432
28324
|
async ({ query, dryRun }) => {
|
|
27433
28325
|
const { getAllObservations: getAllObservations2, resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27434
|
-
const
|
|
28326
|
+
const reader = getObservationReader();
|
|
28327
|
+
const allObs = await withFreshIndex(() => filterReadableObservations(
|
|
28328
|
+
getAllObservations2().filter((o) => (o.status ?? "active") === "active" && o.projectId === project.id),
|
|
28329
|
+
reader
|
|
28330
|
+
).filter((observation) => canManageObservation(observation, reader)));
|
|
27435
28331
|
if (allObs.length < 2) {
|
|
27436
28332
|
return { content: [{ type: "text", text: "Not enough active memories to deduplicate." }] };
|
|
27437
28333
|
}
|
|
@@ -27445,7 +28341,7 @@ ${result.formatted}` }]
|
|
|
27445
28341
|
}
|
|
27446
28342
|
let candidates;
|
|
27447
28343
|
if (query) {
|
|
27448
|
-
const searchResult = await compactSearch({ query, limit: 20, projectId: project.id, status: "active" });
|
|
28344
|
+
const searchResult = await compactSearch({ query, limit: 20, projectId: project.id, status: "active", reader });
|
|
27449
28345
|
const idSet = new Set(searchResult.entries.map((e) => e.id));
|
|
27450
28346
|
candidates = allObs.filter((o) => idSet.has(o.id));
|
|
27451
28347
|
} else {
|
|
@@ -27535,7 +28431,8 @@ ${actions.join("\n")}`
|
|
|
27535
28431
|
safeAnchor,
|
|
27536
28432
|
project.id,
|
|
27537
28433
|
safeBefore,
|
|
27538
|
-
safeAfter
|
|
28434
|
+
safeAfter,
|
|
28435
|
+
getObservationReader()
|
|
27539
28436
|
);
|
|
27540
28437
|
return {
|
|
27541
28438
|
content: [
|
|
@@ -27569,12 +28466,14 @@ ${actions.join("\n")}`
|
|
|
27569
28466
|
const safeTypedRefs = coerceStringArray(typedRefs);
|
|
27570
28467
|
let result;
|
|
27571
28468
|
try {
|
|
28469
|
+
const hasCrossProjectRef = safeRefs.some((ref) => ref.projectId && ref.projectId !== project.id) || safeTypedRefs.some((ref) => ref.includes("@") && !ref.endsWith(`@${project.id}`));
|
|
28470
|
+
const reader = getObservationReader(hasCrossProjectRef ? "global" : "project");
|
|
27572
28471
|
if (safeTypedRefs.length > 0) {
|
|
27573
|
-
result = await compactDetail(safeTypedRefs);
|
|
28472
|
+
result = await compactDetail(safeTypedRefs, { reader });
|
|
27574
28473
|
} else if (safeRefs.length > 0) {
|
|
27575
|
-
result = await compactDetail(safeRefs);
|
|
28474
|
+
result = await compactDetail(safeRefs, { reader });
|
|
27576
28475
|
} else {
|
|
27577
|
-
result = await compactDetail(safeIds.map((id) => ({ id, projectId: project.id })));
|
|
28476
|
+
result = await compactDetail(safeIds.map((id) => ({ id, projectId: project.id })), { reader });
|
|
27578
28477
|
}
|
|
27579
28478
|
} catch (err) {
|
|
27580
28479
|
return { content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }], isError: true };
|
|
@@ -27600,11 +28499,15 @@ ${actions.join("\n")}`
|
|
|
27600
28499
|
},
|
|
27601
28500
|
async (args) => {
|
|
27602
28501
|
const action = args.action ?? "report";
|
|
27603
|
-
const { getRetentionSummary: getRetentionSummary2, getArchiveCandidates: getArchiveCandidates2, rankByRelevance: rankByRelevance2,
|
|
28502
|
+
const { getRetentionSummary: getRetentionSummary2, getArchiveCandidates: getArchiveCandidates2, rankByRelevance: rankByRelevance2, getRetentionZone: getRetentionZone2, explainRetention: explainRetention2 } = await Promise.resolve().then(() => (init_retention(), retention_exports));
|
|
27604
28503
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_orama_store(), orama_store_exports));
|
|
27605
28504
|
const { search: search2 } = await import("@orama/orama");
|
|
27606
|
-
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27607
|
-
const
|
|
28505
|
+
const { getAllObservations: getAllObservations2, resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28506
|
+
const reader = getObservationReader();
|
|
28507
|
+
const allObs = await withFreshIndex(() => filterReadableObservations(
|
|
28508
|
+
getAllObservations2().filter((observation) => observation.projectId === project.id),
|
|
28509
|
+
reader
|
|
28510
|
+
));
|
|
27608
28511
|
const accessMap = /* @__PURE__ */ new Map();
|
|
27609
28512
|
try {
|
|
27610
28513
|
const database = await getDb2();
|
|
@@ -27621,20 +28524,6 @@ ${actions.join("\n")}`
|
|
|
27621
28524
|
}
|
|
27622
28525
|
} catch {
|
|
27623
28526
|
}
|
|
27624
|
-
if (action === "archive") {
|
|
27625
|
-
const result = await archiveExpired2(projectDir2, void 0, accessMap, project.id);
|
|
27626
|
-
if (result.archived === 0) {
|
|
27627
|
-
return {
|
|
27628
|
-
content: [{ type: "text", text: "[OK] No expired observations to archive. All memories are within their retention period." }]
|
|
27629
|
-
};
|
|
27630
|
-
}
|
|
27631
|
-
return {
|
|
27632
|
-
content: [{ type: "text", text: `[ARCHIVED] Archived ${result.archived} expired observations (status set to 'archived' in-place)
|
|
27633
|
-
${result.remaining} active observations remaining.
|
|
27634
|
-
|
|
27635
|
-
Archived memories are hidden from default search but can be found with status: "all".` }]
|
|
27636
|
-
};
|
|
27637
|
-
}
|
|
27638
28527
|
const docs = allObs.map((obs) => ({
|
|
27639
28528
|
id: `obs-${obs.id}`,
|
|
27640
28529
|
observationId: obs.id,
|
|
@@ -27653,13 +28542,34 @@ Archived memories are hidden from default search but can be found with status: "
|
|
|
27653
28542
|
status: obs.status ?? "active",
|
|
27654
28543
|
source: obs.source ?? "agent",
|
|
27655
28544
|
sourceDetail: obs.sourceDetail ?? "",
|
|
27656
|
-
valueCategory: obs.valueCategory ?? ""
|
|
28545
|
+
valueCategory: obs.valueCategory ?? "",
|
|
28546
|
+
admissionState: obs.admissionState ?? "",
|
|
28547
|
+
admissionReason: obs.admissionReason ?? "",
|
|
28548
|
+
visibility: obs.visibility ?? "project",
|
|
28549
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
28550
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
27657
28551
|
}));
|
|
27658
28552
|
if (docs.length === 0) {
|
|
27659
28553
|
return {
|
|
27660
28554
|
content: [{ type: "text", text: "No observations found for this project." }]
|
|
27661
28555
|
};
|
|
27662
28556
|
}
|
|
28557
|
+
if (action === "archive") {
|
|
28558
|
+
const managedIds = new Set(
|
|
28559
|
+
allObs.filter((observation) => canManageObservation(observation, reader)).map((observation) => observation.id)
|
|
28560
|
+
);
|
|
28561
|
+
const candidates2 = getArchiveCandidates2(docs).filter((document) => managedIds.has(document.observationId));
|
|
28562
|
+
if (candidates2.length === 0) {
|
|
28563
|
+
return {
|
|
28564
|
+
content: [{ type: "text", text: "[OK] No expired memories in this session's write scope to archive." }]
|
|
28565
|
+
};
|
|
28566
|
+
}
|
|
28567
|
+
const result = await resolveObservations2(candidates2.map((document) => document.observationId), "archived");
|
|
28568
|
+
return {
|
|
28569
|
+
content: [{ type: "text", text: `[ARCHIVED] Archived ${result.resolved.length} expired observation(s) in this session's write scope.
|
|
28570
|
+
${Math.max(0, managedIds.size - result.resolved.length)} visible writable observations remaining.` }]
|
|
28571
|
+
};
|
|
28572
|
+
}
|
|
27663
28573
|
if (action === "stale") {
|
|
27664
28574
|
const staleDocs = docs.filter((d) => getRetentionZone2(d) === "stale");
|
|
27665
28575
|
if (staleDocs.length === 0) {
|
|
@@ -27978,13 +28888,14 @@ Archived memories are hidden from default search but can be found with status: "
|
|
|
27978
28888
|
async function scopeGraphToProject(graph) {
|
|
27979
28889
|
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27980
28890
|
const allObs = await withFreshIndex(() => getAllObservations2());
|
|
27981
|
-
const
|
|
27982
|
-
|
|
28891
|
+
const scoped = scopeKnowledgeGraphToProject(
|
|
28892
|
+
graph,
|
|
28893
|
+
filterReadableObservations(
|
|
28894
|
+
allObs.filter((observation) => observation.projectId === project.id),
|
|
28895
|
+
getObservationReader()
|
|
28896
|
+
)
|
|
27983
28897
|
);
|
|
27984
|
-
|
|
27985
|
-
const entityNameSet = new Set(entities.map((e) => e.name));
|
|
27986
|
-
const relations = graph.relations.filter((r) => entityNameSet.has(r.from) && entityNameSet.has(r.to));
|
|
27987
|
-
return { entities, relations };
|
|
28898
|
+
return { entities: scoped.entities, relations: scoped.relations };
|
|
27988
28899
|
}
|
|
27989
28900
|
server.registerTool(
|
|
27990
28901
|
"read_graph",
|
|
@@ -28257,7 +29168,10 @@ ${skill2.content}` }]
|
|
|
28257
29168
|
};
|
|
28258
29169
|
}
|
|
28259
29170
|
const { getObservationStore: getStore } = await Promise.resolve().then(() => (init_obs_store(), obs_store_exports));
|
|
28260
|
-
const allObs =
|
|
29171
|
+
const allObs = filterReadableObservations(
|
|
29172
|
+
(await getStore().loadAll()).filter((observation) => observation.projectId === project.id),
|
|
29173
|
+
getObservationReader()
|
|
29174
|
+
);
|
|
28261
29175
|
const obsData = allObs.map((o) => ({
|
|
28262
29176
|
id: o.id || 0,
|
|
28263
29177
|
entityName: o.entityName || "unknown",
|
|
@@ -28356,7 +29270,11 @@ ${skill2.content}` }]
|
|
|
28356
29270
|
}
|
|
28357
29271
|
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28358
29272
|
const allObs = await withFreshIndex(() => getAllObservations2());
|
|
28359
|
-
const
|
|
29273
|
+
const reader = getObservationReader();
|
|
29274
|
+
const matched = filterReadableObservations(
|
|
29275
|
+
allObs.filter((observation) => observation.projectId === project.id && observationIds.includes(observation.id)),
|
|
29276
|
+
reader
|
|
29277
|
+
);
|
|
28360
29278
|
if (matched.length === 0) {
|
|
28361
29279
|
return { content: [{ type: "text", text: `No observations found for IDs: [${observationIds.join(", ")}]. Use \`memorix_search\` to find valid IDs.` }], isError: true };
|
|
28362
29280
|
}
|
|
@@ -28364,6 +29282,13 @@ ${skill2.content}` }]
|
|
|
28364
29282
|
if (nonActive.length > 0) {
|
|
28365
29283
|
return { content: [{ type: "text", text: `Cannot promote: ${nonActive.length} observation(s) are not active: ${nonActive.map((o) => `#${o.id} (${o.status})`).join(", ")}. Only active observations can be promoted to permanent knowledge.` }], isError: true };
|
|
28366
29284
|
}
|
|
29285
|
+
const nonProjectShared = matched.filter((observation) => resolveObservationVisibility(observation) !== "project");
|
|
29286
|
+
if (nonProjectShared.length > 0) {
|
|
29287
|
+
return {
|
|
29288
|
+
content: [{ type: "text", text: `Cannot promote private or team-scoped observations: ${nonProjectShared.map((observation) => `#${observation.id}`).join(", ")}. Promote only deliberate project-shared knowledge.` }],
|
|
29289
|
+
isError: true
|
|
29290
|
+
};
|
|
29291
|
+
}
|
|
28367
29292
|
const skill2 = await promoteToMiniSkill2(projectDir2, project.id, matched, { trigger, instruction, tags });
|
|
28368
29293
|
const lines = [
|
|
28369
29294
|
`[OK] Created mini-skill #${skill2.id}`,
|
|
@@ -28522,8 +29447,11 @@ Ensure the path points to a directory containing a .git folder (or a subdirector
|
|
|
28522
29447
|
const lastSeen = registeredAgent.last_seen_obs_generation;
|
|
28523
29448
|
const store = getObservationStore();
|
|
28524
29449
|
const currentGen = store.getGeneration();
|
|
28525
|
-
const projectObs = await withFreshIndex(() =>
|
|
28526
|
-
(
|
|
29450
|
+
const projectObs = await withFreshIndex(() => filterReadableObservations(
|
|
29451
|
+
getAllObservations().filter(
|
|
29452
|
+
(o) => o.projectId === project.id && (o.writeGeneration ?? 0) > lastSeen
|
|
29453
|
+
),
|
|
29454
|
+
getObservationReader()
|
|
28527
29455
|
));
|
|
28528
29456
|
const wm = computeWatermark2(lastSeen, currentGen, projectObs.length);
|
|
28529
29457
|
if (wm.newObservationCount > 0) {
|
|
@@ -28692,7 +29620,7 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
28692
29620
|
"memorix_transfer",
|
|
28693
29621
|
{
|
|
28694
29622
|
title: "Transfer Memories",
|
|
28695
|
-
description: 'Export or import project memories. Action "export": export observations and sessions (JSON or Markdown). Action "import": import from a JSON export (re-assigns IDs, skips duplicate topicKeys).',
|
|
29623
|
+
description: 'Export or import project memories. Action "export": export observations visible to the current agent and project sessions (JSON or Markdown). Action "import": import from a JSON export (re-assigns IDs, skips duplicate topicKeys).',
|
|
28696
29624
|
inputSchema: {
|
|
28697
29625
|
action: z2.enum(["export", "import"]).describe("Operation: export or import"),
|
|
28698
29626
|
format: z2.enum(["json", "markdown"]).optional().describe("Export format (for export, default: json)"),
|
|
@@ -28703,10 +29631,10 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
28703
29631
|
if (action === "export") {
|
|
28704
29632
|
const { exportAsJson: exportAsJson2, exportAsMarkdown: exportAsMarkdown2 } = await Promise.resolve().then(() => (init_export_import(), export_import_exports));
|
|
28705
29633
|
if (format === "markdown") {
|
|
28706
|
-
const md = await exportAsMarkdown2(projectDir2, project.id);
|
|
29634
|
+
const md = await exportAsMarkdown2(projectDir2, project.id, getObservationReader());
|
|
28707
29635
|
return { content: [{ type: "text", text: md }] };
|
|
28708
29636
|
}
|
|
28709
|
-
const data = await exportAsJson2(projectDir2, project.id);
|
|
29637
|
+
const data = await exportAsJson2(projectDir2, project.id, getObservationReader());
|
|
28710
29638
|
const json = JSON.stringify(data, null, 2);
|
|
28711
29639
|
return {
|
|
28712
29640
|
content: [{
|
|
@@ -28903,8 +29831,6 @@ ${json}
|
|
|
28903
29831
|
}
|
|
28904
29832
|
}
|
|
28905
29833
|
);
|
|
28906
|
-
let teamStore;
|
|
28907
|
-
let initTeamStoreForProject;
|
|
28908
29834
|
if (teamFeaturesEnabled) {
|
|
28909
29835
|
const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
|
|
28910
29836
|
initTeamStoreForProject = initTeamStore2;
|
|
@@ -29177,7 +30103,7 @@ ${lines.join("\n")}` }] };
|
|
|
29177
30103
|
description: `Send, broadcast, or read messages between agents. Durable: messages survive restarts and reach inactive recipients. Action "send": direct message to one agent. Action "broadcast": message all agents. Action "inbox": read this agent's inbox.`,
|
|
29178
30104
|
inputSchema: {
|
|
29179
30105
|
action: z2.enum(["send", "broadcast", "inbox"]).describe("Operation to perform"),
|
|
29180
|
-
from: z2.string().optional().describe("
|
|
30106
|
+
from: z2.string().optional().describe("Your sender agent ID. Omit it to use this session identity."),
|
|
29181
30107
|
to: z2.string().optional().describe("Receiver agent ID (for send)"),
|
|
29182
30108
|
type: z2.enum(["request", "response", "info", "announcement", "contract", "error", "handoff"]).optional().describe("Message type (for send/broadcast)"),
|
|
29183
30109
|
content: z2.string().optional().describe("Message content (for send/broadcast)"),
|
|
@@ -29188,13 +30114,20 @@ ${lines.join("\n")}` }] };
|
|
|
29188
30114
|
}
|
|
29189
30115
|
},
|
|
29190
30116
|
async ({ action, from, to, type: msgType, content, agentId, markRead, toRole, handoffStatus }) => {
|
|
30117
|
+
const requireCurrentTeamAgent = () => {
|
|
30118
|
+
if (!currentAgentId) return null;
|
|
30119
|
+
const agent = teamStore.getAgent(currentAgentId);
|
|
30120
|
+
return agent?.project_id === project.id && agent.status === "active" ? agent : null;
|
|
30121
|
+
};
|
|
29191
30122
|
if (action === "send") {
|
|
29192
|
-
|
|
30123
|
+
const sender = requireCurrentTeamAgent();
|
|
30124
|
+
if (!sender || !msgType || !content) return { content: [{ type: "text", text: "[ERROR] active session identity, type, and content required for send" }], isError: true };
|
|
30125
|
+
if (from && from !== currentAgentId) return { content: [{ type: "text", text: "[ERROR] from must match the current session identity" }], isError: true };
|
|
29193
30126
|
if (!to && !toRole) return { content: [{ type: "text", text: "[ERROR] either to (agent ID) or toRole is required for send" }], isError: true };
|
|
29194
30127
|
if (content.length > 1e4) return { content: [{ type: "text", text: "[ERROR] Message too large (max 10KB)" }], isError: true };
|
|
29195
30128
|
const msg = teamStore.sendMessage({
|
|
29196
30129
|
projectId: project.id,
|
|
29197
|
-
senderAgentId:
|
|
30130
|
+
senderAgentId: currentAgentId,
|
|
29198
30131
|
recipientAgentId: to ?? null,
|
|
29199
30132
|
type: msgType,
|
|
29200
30133
|
content,
|
|
@@ -29206,11 +30139,13 @@ ${lines.join("\n")}` }] };
|
|
|
29206
30139
|
return { content: [{ type: "text", text: `Message sent (${msgType}) to ${target} | ID: ${msg.id.slice(0, 8)}\u2026${toRole ? ` [role: ${toRole}]` : ""}` }] };
|
|
29207
30140
|
}
|
|
29208
30141
|
if (action === "broadcast") {
|
|
29209
|
-
|
|
30142
|
+
const sender = requireCurrentTeamAgent();
|
|
30143
|
+
if (!sender || !msgType || !content) return { content: [{ type: "text", text: "[ERROR] active session identity, type, and content required for broadcast" }], isError: true };
|
|
30144
|
+
if (from && from !== currentAgentId) return { content: [{ type: "text", text: "[ERROR] from must match the current session identity" }], isError: true };
|
|
29210
30145
|
if (content.length > 1e4) return { content: [{ type: "text", text: "[ERROR] Message too large (max 10KB)" }], isError: true };
|
|
29211
30146
|
const msg = teamStore.sendMessage({
|
|
29212
30147
|
projectId: project.id,
|
|
29213
|
-
senderAgentId:
|
|
30148
|
+
senderAgentId: currentAgentId,
|
|
29214
30149
|
recipientAgentId: null,
|
|
29215
30150
|
type: msgType,
|
|
29216
30151
|
content
|
|
@@ -29218,8 +30153,12 @@ ${lines.join("\n")}` }] };
|
|
|
29218
30153
|
if ("error" in msg) return { content: [{ type: "text", text: `[ERROR] ${msg.error}` }], isError: true };
|
|
29219
30154
|
return { content: [{ type: "text", text: `Broadcast (${msgType}) | ID: ${msg.id.slice(0, 8)}\u2026` }] };
|
|
29220
30155
|
}
|
|
29221
|
-
const
|
|
29222
|
-
if (!
|
|
30156
|
+
const inboxAgent = requireCurrentTeamAgent();
|
|
30157
|
+
if (!inboxAgent) return { content: [{ type: "text", text: "[ERROR] active session identity required for inbox" }], isError: true };
|
|
30158
|
+
if (agentId && agentId !== currentAgentId || from && from !== currentAgentId) {
|
|
30159
|
+
return { content: [{ type: "text", text: "[ERROR] inbox access is limited to the current session identity" }], isError: true };
|
|
30160
|
+
}
|
|
30161
|
+
const inboxId = currentAgentId;
|
|
29223
30162
|
const inbox = teamStore.getInbox(project.id, inboxId);
|
|
29224
30163
|
const unread = teamStore.getUnreadCount(project.id, inboxId);
|
|
29225
30164
|
if (inbox.length === 0) return { content: [{ type: "text", text: "Inbox empty" }] };
|
|
@@ -29247,24 +30186,34 @@ ${lines.join("\n")}` }] };
|
|
|
29247
30186
|
},
|
|
29248
30187
|
async ({ agentId, markInboxRead }) => {
|
|
29249
30188
|
const { computeWatermark: computeWatermark2, computePoll: computePoll2 } = await Promise.resolve().then(() => (init_poll(), poll_exports));
|
|
30189
|
+
if (agentId && agentId !== currentAgentId) {
|
|
30190
|
+
return {
|
|
30191
|
+
content: [{ type: "text", text: "[ERROR] agentId must match the current session identity." }],
|
|
30192
|
+
isError: true
|
|
30193
|
+
};
|
|
30194
|
+
}
|
|
30195
|
+
const effectiveAgentId = currentAgentId;
|
|
29250
30196
|
let watermark = computeWatermark2(0, 0, 0);
|
|
29251
|
-
if (
|
|
29252
|
-
const agent = teamStore.getAgent(
|
|
30197
|
+
if (effectiveAgentId) {
|
|
30198
|
+
const agent = teamStore.getAgent(effectiveAgentId);
|
|
29253
30199
|
if (agent) {
|
|
29254
30200
|
const lastSeen = agent.last_seen_obs_generation;
|
|
29255
30201
|
const store = getObservationStore();
|
|
29256
30202
|
const currentGen = store.getGeneration();
|
|
29257
|
-
const projectObs = await withFreshIndex(() =>
|
|
29258
|
-
(
|
|
30203
|
+
const projectObs = await withFreshIndex(() => filterReadableObservations(
|
|
30204
|
+
getAllObservations().filter(
|
|
30205
|
+
(o) => o.projectId === project.id && (o.writeGeneration ?? 0) > lastSeen
|
|
30206
|
+
),
|
|
30207
|
+
getObservationReader()
|
|
29259
30208
|
));
|
|
29260
30209
|
watermark = computeWatermark2(lastSeen, currentGen, projectObs.length);
|
|
29261
|
-
teamStore.updateWatermark(
|
|
29262
|
-
teamStore.heartbeat(
|
|
30210
|
+
teamStore.updateWatermark(effectiveAgentId, currentGen);
|
|
30211
|
+
teamStore.heartbeat(effectiveAgentId);
|
|
29263
30212
|
}
|
|
29264
30213
|
}
|
|
29265
|
-
const poll = computePoll2(teamStore, project.id,
|
|
29266
|
-
if (markInboxRead &&
|
|
29267
|
-
teamStore.markAllRead(project.id,
|
|
30214
|
+
const poll = computePoll2(teamStore, project.id, effectiveAgentId ?? null, watermark);
|
|
30215
|
+
if (markInboxRead && effectiveAgentId) {
|
|
30216
|
+
teamStore.markAllRead(project.id, effectiveAgentId);
|
|
29268
30217
|
}
|
|
29269
30218
|
const lines = [];
|
|
29270
30219
|
if (poll.agent) {
|
|
@@ -29324,7 +30273,7 @@ ${lines.join("\n")}` }] };
|
|
|
29324
30273
|
"memorix_handoff",
|
|
29325
30274
|
{
|
|
29326
30275
|
title: "Team Handoff \u2014 Agent Context Transfer",
|
|
29327
|
-
description: "Create a structured handoff artifact when passing work to another agent. The handoff is stored as a durable observation (
|
|
30276
|
+
description: "Create a structured handoff artifact when passing work to another agent. A targeted handoff is visible only to its sender and recipient; a broadcast handoff is team-visible. The handoff is stored as a durable observation (immune to archival) and a notification message is sent to the recipient. Use this when completing a task and another agent should continue, or when you want to leave context for whoever works on this next.",
|
|
29328
30277
|
inputSchema: {
|
|
29329
30278
|
fromAgentId: z2.string().describe("Your agent ID (from team_manage join or session_start with joinTeam=true)"),
|
|
29330
30279
|
summary: z2.string().describe("Human-readable summary of what you did and what needs to happen next"),
|
|
@@ -29337,6 +30286,34 @@ ${lines.join("\n")}` }] };
|
|
|
29337
30286
|
},
|
|
29338
30287
|
async ({ fromAgentId, summary, context, toAgentId, taskId, filesModified, concepts }) => {
|
|
29339
30288
|
const { createHandoffArtifact: createHandoffArtifact2 } = await Promise.resolve().then(() => (init_handoff(), handoff_exports));
|
|
30289
|
+
if (!currentAgentId) {
|
|
30290
|
+
return {
|
|
30291
|
+
content: [{ type: "text", text: "Create a coordination identity first: call memorix_session_start with joinTeam=true." }],
|
|
30292
|
+
isError: true
|
|
30293
|
+
};
|
|
30294
|
+
}
|
|
30295
|
+
if (fromAgentId !== currentAgentId) {
|
|
30296
|
+
return {
|
|
30297
|
+
content: [{ type: "text", text: "fromAgentId must match the identity returned for this session. Memorix will not create a handoff on behalf of another agent." }],
|
|
30298
|
+
isError: true
|
|
30299
|
+
};
|
|
30300
|
+
}
|
|
30301
|
+
const sender = teamStore.getAgent(currentAgentId);
|
|
30302
|
+
if (!sender || sender.project_id !== project.id || sender.status !== "active") {
|
|
30303
|
+
return {
|
|
30304
|
+
content: [{ type: "text", text: "The current coordination identity is not an active member of this project." }],
|
|
30305
|
+
isError: true
|
|
30306
|
+
};
|
|
30307
|
+
}
|
|
30308
|
+
if (toAgentId) {
|
|
30309
|
+
const recipient = teamStore.getAgent(toAgentId);
|
|
30310
|
+
if (!recipient || recipient.project_id !== project.id) {
|
|
30311
|
+
return {
|
|
30312
|
+
content: [{ type: "text", text: "The handoff recipient must be an agent registered in the current project." }],
|
|
30313
|
+
isError: true
|
|
30314
|
+
};
|
|
30315
|
+
}
|
|
30316
|
+
}
|
|
29340
30317
|
const result = await createHandoffArtifact2(
|
|
29341
30318
|
{
|
|
29342
30319
|
projectId: project.id,
|
|
@@ -29576,6 +30553,8 @@ var MemoryClient = class {
|
|
|
29576
30553
|
_projectId;
|
|
29577
30554
|
_projectRoot;
|
|
29578
30555
|
_dataDir;
|
|
30556
|
+
/** SDK calls are unbound by default, so they see only project-shared records. */
|
|
30557
|
+
_reader;
|
|
29579
30558
|
_closed = false;
|
|
29580
30559
|
// Internal module references — loaded lazily to avoid top-level side effects
|
|
29581
30560
|
_observations;
|
|
@@ -29587,6 +30566,7 @@ var MemoryClient = class {
|
|
|
29587
30566
|
this._projectId = projectId;
|
|
29588
30567
|
this._projectRoot = projectRoot;
|
|
29589
30568
|
this._dataDir = dataDir;
|
|
30569
|
+
this._reader = { projectId };
|
|
29590
30570
|
}
|
|
29591
30571
|
/** The canonical project ID (derived from Git remote or local path) */
|
|
29592
30572
|
get projectId() {
|
|
@@ -29645,7 +30625,8 @@ var MemoryClient = class {
|
|
|
29645
30625
|
this._ensureOpen();
|
|
29646
30626
|
return this._observations.storeObservation({
|
|
29647
30627
|
...input,
|
|
29648
|
-
projectId: this._projectId
|
|
30628
|
+
projectId: this._projectId,
|
|
30629
|
+
visibilityReader: this._reader
|
|
29649
30630
|
});
|
|
29650
30631
|
}
|
|
29651
30632
|
/**
|
|
@@ -29669,7 +30650,8 @@ var MemoryClient = class {
|
|
|
29669
30650
|
limit: options.limit ?? 20,
|
|
29670
30651
|
type: options.type,
|
|
29671
30652
|
source: options.source,
|
|
29672
|
-
status: options.status === "all" ? void 0 : options.status ?? "active"
|
|
30653
|
+
status: options.status === "all" ? void 0 : options.status ?? "active",
|
|
30654
|
+
reader: this._reader
|
|
29673
30655
|
};
|
|
29674
30656
|
return this._oramaStore.searchObservations(searchOpts);
|
|
29675
30657
|
}
|
|
@@ -29680,7 +30662,8 @@ var MemoryClient = class {
|
|
|
29680
30662
|
this._ensureOpen();
|
|
29681
30663
|
await this._freshness.withFreshIndex(() => {
|
|
29682
30664
|
});
|
|
29683
|
-
|
|
30665
|
+
const observation = this._observations.getObservation(id, this._projectId);
|
|
30666
|
+
return observation && canReadObservation(observation, this._reader) ? observation : void 0;
|
|
29684
30667
|
}
|
|
29685
30668
|
/**
|
|
29686
30669
|
* Get all observations for this project.
|
|
@@ -29689,7 +30672,10 @@ var MemoryClient = class {
|
|
|
29689
30672
|
this._ensureOpen();
|
|
29690
30673
|
await this._freshness.withFreshIndex(() => {
|
|
29691
30674
|
});
|
|
29692
|
-
return
|
|
30675
|
+
return filterReadableObservations(
|
|
30676
|
+
this._observations.getProjectObservations(this._projectId),
|
|
30677
|
+
this._reader
|
|
30678
|
+
);
|
|
29693
30679
|
}
|
|
29694
30680
|
/**
|
|
29695
30681
|
* Get the total observation count for this project.
|
|
@@ -29698,7 +30684,10 @@ var MemoryClient = class {
|
|
|
29698
30684
|
this._ensureOpen();
|
|
29699
30685
|
await this._freshness.withFreshIndex(() => {
|
|
29700
30686
|
});
|
|
29701
|
-
return
|
|
30687
|
+
return filterReadableObservations(
|
|
30688
|
+
this._observations.getProjectObservations(this._projectId),
|
|
30689
|
+
this._reader
|
|
30690
|
+
).length;
|
|
29702
30691
|
}
|
|
29703
30692
|
/**
|
|
29704
30693
|
* Mark observations as resolved or archived.
|
|
@@ -29714,7 +30703,16 @@ var MemoryClient = class {
|
|
|
29714
30703
|
*/
|
|
29715
30704
|
async resolve(ids, status = "resolved") {
|
|
29716
30705
|
this._ensureOpen();
|
|
29717
|
-
|
|
30706
|
+
const manageableIds = ids.filter((id) => {
|
|
30707
|
+
const observation = this._observations.getObservation(id, this._projectId);
|
|
30708
|
+
return observation && canManageObservation(observation, this._reader);
|
|
30709
|
+
});
|
|
30710
|
+
const deniedIds = ids.filter((id) => !manageableIds.includes(id));
|
|
30711
|
+
const result = await this._observations.resolveObservations(manageableIds, status);
|
|
30712
|
+
return {
|
|
30713
|
+
resolved: result.resolved,
|
|
30714
|
+
notFound: [.../* @__PURE__ */ new Set([...result.notFound, ...deniedIds])]
|
|
30715
|
+
};
|
|
29718
30716
|
}
|
|
29719
30717
|
/**
|
|
29720
30718
|
* Release resources (close SQLite handle, reset index).
|