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/index.js
CHANGED
|
@@ -209,7 +209,11 @@ CREATE TABLE IF NOT EXISTS observations (
|
|
|
209
209
|
relatedCommits TEXT,
|
|
210
210
|
relatedEntities TEXT,
|
|
211
211
|
sourceDetail TEXT,
|
|
212
|
-
valueCategory TEXT
|
|
212
|
+
valueCategory TEXT,
|
|
213
|
+
admissionState TEXT,
|
|
214
|
+
admissionReason TEXT,
|
|
215
|
+
visibility TEXT,
|
|
216
|
+
sharedWithAgentIds TEXT
|
|
213
217
|
);
|
|
214
218
|
`;
|
|
215
219
|
CREATE_MINI_SKILLS_TABLE = `
|
|
@@ -632,6 +636,8 @@ CREATE INDEX IF NOT EXISTS idx_observations_projectId ON observations(projectId)
|
|
|
632
636
|
CREATE INDEX IF NOT EXISTS idx_observations_topicKey ON observations(projectId, topicKey);
|
|
633
637
|
CREATE INDEX IF NOT EXISTS idx_observations_status ON observations(status);
|
|
634
638
|
CREATE INDEX IF NOT EXISTS idx_observations_project_status_id ON observations(projectId, status, id);
|
|
639
|
+
CREATE INDEX IF NOT EXISTS idx_observations_project_admission ON observations(projectId, status, admissionState, id);
|
|
640
|
+
CREATE INDEX IF NOT EXISTS idx_observations_project_visibility ON observations(projectId, status, visibility, id);
|
|
635
641
|
CREATE INDEX IF NOT EXISTS idx_mini_skills_projectId ON mini_skills(projectId);
|
|
636
642
|
CREATE INDEX IF NOT EXISTS idx_sessions_projectId ON sessions(projectId);
|
|
637
643
|
CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(projectId, status);
|
|
@@ -676,6 +682,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
|
676
682
|
WHERE status IN ('pending', 'running', 'retry');
|
|
677
683
|
`;
|
|
678
684
|
SCHEMA_MIGRATIONS = [
|
|
685
|
+
{
|
|
686
|
+
id: "1.2.2-observation-admission",
|
|
687
|
+
apply: (db2) => {
|
|
688
|
+
addColumnIfMissing(db2, "observations", "admissionState", "admissionState TEXT");
|
|
689
|
+
addColumnIfMissing(db2, "observations", "admissionReason", "admissionReason TEXT");
|
|
690
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_observations_project_admission ON observations(projectId, status, admissionState, id)");
|
|
691
|
+
}
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
id: "1.2.2-observation-visibility",
|
|
695
|
+
apply: (db2) => {
|
|
696
|
+
addColumnIfMissing(db2, "observations", "visibility", "visibility TEXT");
|
|
697
|
+
addColumnIfMissing(db2, "observations", "sharedWithAgentIds", "sharedWithAgentIds TEXT");
|
|
698
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_observations_project_visibility ON observations(projectId, status, visibility, id)");
|
|
699
|
+
}
|
|
700
|
+
},
|
|
679
701
|
{
|
|
680
702
|
id: "1.2-code-state-snapshots",
|
|
681
703
|
apply: (db2) => {
|
|
@@ -2118,6 +2140,58 @@ var init_mini_skills = __esm({
|
|
|
2118
2140
|
}
|
|
2119
2141
|
});
|
|
2120
2142
|
|
|
2143
|
+
// src/memory/visibility.ts
|
|
2144
|
+
function resolveObservationVisibility(record) {
|
|
2145
|
+
switch (record.visibility) {
|
|
2146
|
+
case "personal":
|
|
2147
|
+
case "team":
|
|
2148
|
+
case "project":
|
|
2149
|
+
return record.visibility;
|
|
2150
|
+
default:
|
|
2151
|
+
return "project";
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
function sharedAgentIds(record) {
|
|
2155
|
+
if (Array.isArray(record.sharedWithAgentIds)) return record.sharedWithAgentIds;
|
|
2156
|
+
if (typeof record.sharedWithAgentIds !== "string" || !record.sharedWithAgentIds) return [];
|
|
2157
|
+
try {
|
|
2158
|
+
const parsed = JSON.parse(record.sharedWithAgentIds);
|
|
2159
|
+
return Array.isArray(parsed) ? parsed.filter((id) => typeof id === "string") : [];
|
|
2160
|
+
} catch {
|
|
2161
|
+
return [];
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
function canReadObservation(record, reader) {
|
|
2165
|
+
if (!reader) return true;
|
|
2166
|
+
const sameProject = reader.projectId === record.projectId;
|
|
2167
|
+
const visibility = resolveObservationVisibility(record);
|
|
2168
|
+
if (visibility === "project") return !reader.projectId || sameProject;
|
|
2169
|
+
if (!sameProject || !reader.agentId) return false;
|
|
2170
|
+
if (visibility === "team") return reader.isTeamMember === true;
|
|
2171
|
+
return record.createdByAgentId === reader.agentId || sharedAgentIds(record).includes(reader.agentId);
|
|
2172
|
+
}
|
|
2173
|
+
function canManageObservation(record, reader) {
|
|
2174
|
+
if (!reader) return true;
|
|
2175
|
+
if (!reader.projectId || reader.projectId !== record.projectId) return false;
|
|
2176
|
+
switch (resolveObservationVisibility(record)) {
|
|
2177
|
+
case "project":
|
|
2178
|
+
return true;
|
|
2179
|
+
case "team":
|
|
2180
|
+
return reader.isTeamMember === true;
|
|
2181
|
+
case "personal":
|
|
2182
|
+
return Boolean(reader.agentId && record.createdByAgentId === reader.agentId);
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
function filterReadableObservations(observations2, reader) {
|
|
2186
|
+
return reader ? observations2.filter((observation) => canReadObservation(observation, reader)) : [...observations2];
|
|
2187
|
+
}
|
|
2188
|
+
var init_visibility = __esm({
|
|
2189
|
+
"src/memory/visibility.ts"() {
|
|
2190
|
+
"use strict";
|
|
2191
|
+
init_esm_shims();
|
|
2192
|
+
}
|
|
2193
|
+
});
|
|
2194
|
+
|
|
2121
2195
|
// src/config/config-paths.ts
|
|
2122
2196
|
import { join } from "path";
|
|
2123
2197
|
function getGlobalConfigTomlPath(homeDir) {
|
|
@@ -5345,7 +5419,8 @@ __export(orama_store_exports, {
|
|
|
5345
5419
|
makeOramaObservationId: () => makeOramaObservationId,
|
|
5346
5420
|
removeObservation: () => removeObservation,
|
|
5347
5421
|
resetDb: () => resetDb,
|
|
5348
|
-
searchObservations: () => searchObservations
|
|
5422
|
+
searchObservations: () => searchObservations,
|
|
5423
|
+
updateObservationMetadata: () => updateObservationMetadata
|
|
5349
5424
|
});
|
|
5350
5425
|
import { create, insert as insert2, search, remove as remove2, update, count, getByID } from "@orama/orama";
|
|
5351
5426
|
function getLastSearchMode(projectId) {
|
|
@@ -5423,6 +5498,11 @@ async function initializeDb(options, generation) {
|
|
|
5423
5498
|
source: "string",
|
|
5424
5499
|
sourceDetail: "string",
|
|
5425
5500
|
valueCategory: "string",
|
|
5501
|
+
admissionState: "string",
|
|
5502
|
+
admissionReason: "string",
|
|
5503
|
+
visibility: "string",
|
|
5504
|
+
createdByAgentId: "string",
|
|
5505
|
+
sharedWithAgentIds: "string",
|
|
5426
5506
|
documentType: "string",
|
|
5427
5507
|
knowledgeLayer: "string"
|
|
5428
5508
|
};
|
|
@@ -5561,6 +5641,13 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
5561
5641
|
lastAccessedAt: obs.lastAccessedAt || "",
|
|
5562
5642
|
status: obs.status ?? "active",
|
|
5563
5643
|
source: obs.source || "agent",
|
|
5644
|
+
sourceDetail: obs.sourceDetail ?? "",
|
|
5645
|
+
valueCategory: obs.valueCategory ?? "",
|
|
5646
|
+
admissionState: obs.admissionState ?? "",
|
|
5647
|
+
admissionReason: obs.admissionReason ?? "",
|
|
5648
|
+
visibility: obs.visibility ?? "project",
|
|
5649
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
5650
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
5564
5651
|
documentType: "observation",
|
|
5565
5652
|
knowledgeLayer: resolveKnowledgeLayer("observation", obs.sourceDetail, obs.source),
|
|
5566
5653
|
...compatibleVector ? { embedding: compatibleVector } : {}
|
|
@@ -5594,6 +5681,16 @@ async function insertObservation(doc) {
|
|
|
5594
5681
|
await insert2(database, doc);
|
|
5595
5682
|
rememberObservationDoc(doc);
|
|
5596
5683
|
}
|
|
5684
|
+
async function updateObservationMetadata(projectId, observationId2, patch) {
|
|
5685
|
+
const database = await getDb();
|
|
5686
|
+
const id = makeOramaObservationId(projectId, observationId2);
|
|
5687
|
+
const existing = getByID(database, id);
|
|
5688
|
+
if (!existing) return false;
|
|
5689
|
+
const next = { ...existing, ...patch };
|
|
5690
|
+
await update(database, id, next);
|
|
5691
|
+
rememberObservationDoc(next);
|
|
5692
|
+
return true;
|
|
5693
|
+
}
|
|
5597
5694
|
async function removeObservation(oramaId) {
|
|
5598
5695
|
const database = await getDb();
|
|
5599
5696
|
await remove2(database, oramaId);
|
|
@@ -5747,7 +5844,7 @@ async function searchObservations(options) {
|
|
|
5747
5844
|
if (!projectIds) return true;
|
|
5748
5845
|
const doc = hit.document;
|
|
5749
5846
|
return projectIds.includes(doc.projectId);
|
|
5750
|
-
}).filter((hit) => {
|
|
5847
|
+
}).filter((hit) => canReadObservation(hit.document, options.reader)).filter((hit) => {
|
|
5751
5848
|
if (statusFilter === "all") return true;
|
|
5752
5849
|
const doc = hit.document;
|
|
5753
5850
|
return (doc.status || "active") === statusFilter;
|
|
@@ -5778,6 +5875,8 @@ async function searchObservations(options) {
|
|
|
5778
5875
|
source: doc.source || "agent",
|
|
5779
5876
|
sourceDetail: doc.sourceDetail || void 0,
|
|
5780
5877
|
valueCategory: doc.valueCategory || void 0,
|
|
5878
|
+
admissionState: doc.admissionState || void 0,
|
|
5879
|
+
visibility: doc.visibility || void 0,
|
|
5781
5880
|
entityName: doc.entityName || void 0,
|
|
5782
5881
|
documentType: doc.documentType || "observation",
|
|
5783
5882
|
knowledgeLayer: doc.knowledgeLayer || "project-truth",
|
|
@@ -5821,6 +5920,12 @@ async function searchObservations(options) {
|
|
|
5821
5920
|
score: isCommandStyleEntry(entry.title) ? entry.score * 0.3 : entry.score
|
|
5822
5921
|
}));
|
|
5823
5922
|
}
|
|
5923
|
+
if (hasQuery) {
|
|
5924
|
+
const qualifiedEntries = intermediate.filter(
|
|
5925
|
+
(entry) => entry.admissionState !== "candidate" && entry.admissionState !== "ephemeral"
|
|
5926
|
+
);
|
|
5927
|
+
if (qualifiedEntries.length > 0) intermediate = qualifiedEntries;
|
|
5928
|
+
}
|
|
5824
5929
|
if (intentResult?.preferChronological) {
|
|
5825
5930
|
intermediate.sort((a, b) => new Date(b.rawTime).getTime() - new Date(a.rawTime).getTime());
|
|
5826
5931
|
} else {
|
|
@@ -5964,7 +6069,8 @@ async function searchObservations(options) {
|
|
|
5964
6069
|
}
|
|
5965
6070
|
let entries = intermediate.map(({ rawTime: _, _isCommandLog: _c, ...rest }) => rest);
|
|
5966
6071
|
for (const hit of results.hits) {
|
|
5967
|
-
|
|
6072
|
+
const doc = hit.document;
|
|
6073
|
+
if (canReadObservation(doc, options.reader)) rememberObservationDoc(doc);
|
|
5968
6074
|
}
|
|
5969
6075
|
if (hasQuery && originalQuery) {
|
|
5970
6076
|
const queryLower = originalQuery.toLowerCase();
|
|
@@ -6008,7 +6114,8 @@ async function searchObservations(options) {
|
|
|
6008
6114
|
entries = applyTokenBudget(entries, options.maxTokens);
|
|
6009
6115
|
}
|
|
6010
6116
|
if (options.trackAccess !== false) {
|
|
6011
|
-
const
|
|
6117
|
+
const returnedKeys = new Set(entries.map((entry) => makeEntryKey(entry.projectId, entry.id)));
|
|
6118
|
+
const hitDocs = results.hits.map((hit) => ({ id: hit.id, doc: hit.document })).filter(({ doc }) => returnedKeys.has(makeEntryKey(doc.projectId, doc.observationId)));
|
|
6012
6119
|
recordAccessBatch(hitDocs).catch(() => {
|
|
6013
6120
|
});
|
|
6014
6121
|
}
|
|
@@ -6040,11 +6147,11 @@ async function getObservationsByIds(ids, projectId) {
|
|
|
6040
6147
|
}
|
|
6041
6148
|
return results;
|
|
6042
6149
|
}
|
|
6043
|
-
async function getTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3) {
|
|
6150
|
+
async function getTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3, reader) {
|
|
6044
6151
|
const { withFreshIndex: withFreshIndex2 } = await Promise.resolve().then(() => (init_freshness(), freshness_exports));
|
|
6045
6152
|
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
6046
6153
|
const rawObs = await withFreshIndex2(() => getAllObservations2());
|
|
6047
|
-
const allObs = projectId ? rawObs.filter((o) => o.projectId === projectId) : rawObs;
|
|
6154
|
+
const allObs = (projectId ? rawObs.filter((o) => o.projectId === projectId) : rawObs).filter((observation) => canReadObservation(observation, reader));
|
|
6048
6155
|
const sorted = allObs.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
6049
6156
|
const anchorIndex = sorted.findIndex((o) => o.id === anchorId);
|
|
6050
6157
|
if (anchorIndex === -1) {
|
|
@@ -6061,7 +6168,9 @@ async function getTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3)
|
|
|
6061
6168
|
tokens: obs.tokens,
|
|
6062
6169
|
source: obs.source || void 0,
|
|
6063
6170
|
sourceDetail: obs.sourceDetail || void 0,
|
|
6064
|
-
valueCategory: obs.valueCategory || void 0
|
|
6171
|
+
valueCategory: obs.valueCategory || void 0,
|
|
6172
|
+
admissionState: obs.admissionState || void 0,
|
|
6173
|
+
visibility: obs.visibility || void 0
|
|
6065
6174
|
};
|
|
6066
6175
|
};
|
|
6067
6176
|
const before = sorted.slice(Math.max(0, anchorIndex - depthBefore), anchorIndex).map(toIndexEntry);
|
|
@@ -6129,6 +6238,7 @@ var init_orama_store = __esm({
|
|
|
6129
6238
|
init_esm_shims();
|
|
6130
6239
|
init_types();
|
|
6131
6240
|
init_mini_skills();
|
|
6241
|
+
init_visibility();
|
|
6132
6242
|
init_provider();
|
|
6133
6243
|
init_project_affinity();
|
|
6134
6244
|
init_intent_detector();
|
|
@@ -6185,6 +6295,10 @@ function obsToRow(obs) {
|
|
|
6185
6295
|
relatedEntities: obs.relatedEntities ? JSON.stringify(obs.relatedEntities) : null,
|
|
6186
6296
|
sourceDetail: obs.sourceDetail ?? null,
|
|
6187
6297
|
valueCategory: obs.valueCategory ?? null,
|
|
6298
|
+
admissionState: obs.admissionState ?? null,
|
|
6299
|
+
admissionReason: obs.admissionReason ?? null,
|
|
6300
|
+
visibility: obs.visibility ?? null,
|
|
6301
|
+
sharedWithAgentIds: obs.sharedWithAgentIds ? JSON.stringify(obs.sharedWithAgentIds) : null,
|
|
6188
6302
|
createdByAgentId: obs.createdByAgentId ?? null,
|
|
6189
6303
|
writeGeneration: obs.writeGeneration ?? 0
|
|
6190
6304
|
};
|
|
@@ -6215,6 +6329,10 @@ function rowToObs(row) {
|
|
|
6215
6329
|
...row.relatedEntities ? { relatedEntities: safeJsonParse3(row.relatedEntities, []) } : {},
|
|
6216
6330
|
...row.sourceDetail ? { sourceDetail: row.sourceDetail } : {},
|
|
6217
6331
|
...row.valueCategory ? { valueCategory: row.valueCategory } : {},
|
|
6332
|
+
...row.admissionState ? { admissionState: row.admissionState } : {},
|
|
6333
|
+
...row.admissionReason ? { admissionReason: row.admissionReason } : {},
|
|
6334
|
+
...row.visibility ? { visibility: row.visibility } : {},
|
|
6335
|
+
...row.sharedWithAgentIds ? { sharedWithAgentIds: safeJsonParse3(row.sharedWithAgentIds, []) } : {},
|
|
6218
6336
|
...row.createdByAgentId ? { createdByAgentId: row.createdByAgentId } : {},
|
|
6219
6337
|
...row.writeGeneration ? { writeGeneration: row.writeGeneration } : {}
|
|
6220
6338
|
};
|
|
@@ -6266,12 +6384,14 @@ var init_sqlite_store = __esm({
|
|
|
6266
6384
|
(id, entityName, type, title, narrative, facts, filesModified, concepts, tokens,
|
|
6267
6385
|
createdAt, updatedAt, projectId, hasCausalLanguage, topicKey, revisionCount,
|
|
6268
6386
|
sessionId, status, progress, source, commitHash, relatedCommits, relatedEntities,
|
|
6269
|
-
sourceDetail, valueCategory,
|
|
6387
|
+
sourceDetail, valueCategory, admissionState, admissionReason, visibility, sharedWithAgentIds,
|
|
6388
|
+
createdByAgentId, writeGeneration)
|
|
6270
6389
|
VALUES
|
|
6271
6390
|
(@id, @entityName, @type, @title, @narrative, @facts, @filesModified, @concepts, @tokens,
|
|
6272
6391
|
@createdAt, @updatedAt, @projectId, @hasCausalLanguage, @topicKey, @revisionCount,
|
|
6273
6392
|
@sessionId, @status, @progress, @source, @commitHash, @relatedCommits, @relatedEntities,
|
|
6274
|
-
@sourceDetail, @valueCategory, @
|
|
6393
|
+
@sourceDetail, @valueCategory, @admissionState, @admissionReason, @visibility, @sharedWithAgentIds,
|
|
6394
|
+
@createdByAgentId, @writeGeneration)
|
|
6275
6395
|
`);
|
|
6276
6396
|
this.stmtUpdate = this.stmtInsert;
|
|
6277
6397
|
this.stmtSetStatus = this.db.prepare(`UPDATE observations SET status = ? WHERE id = ?`);
|
|
@@ -6823,6 +6943,7 @@ var init_maintenance_jobs = __esm({
|
|
|
6823
6943
|
"retention-archive",
|
|
6824
6944
|
"consolidation",
|
|
6825
6945
|
"codegraph-refresh",
|
|
6946
|
+
"observation-qualify",
|
|
6826
6947
|
"claim-derive",
|
|
6827
6948
|
"claim-requalification",
|
|
6828
6949
|
"knowledge-compile",
|
|
@@ -7181,7 +7302,8 @@ __export(lifecycle_exports, {
|
|
|
7181
7302
|
enqueueClaimDerivation: () => enqueueClaimDerivation,
|
|
7182
7303
|
enqueueClaimRequalification: () => enqueueClaimRequalification,
|
|
7183
7304
|
enqueueCodegraphRefresh: () => enqueueCodegraphRefresh,
|
|
7184
|
-
enqueueKnowledgeFollowups: () => enqueueKnowledgeFollowups
|
|
7305
|
+
enqueueKnowledgeFollowups: () => enqueueKnowledgeFollowups,
|
|
7306
|
+
enqueueObservationQualification: () => enqueueObservationQualification
|
|
7185
7307
|
});
|
|
7186
7308
|
function queueFor(input) {
|
|
7187
7309
|
return input.queue ?? new MaintenanceJobStore(input.dataDir);
|
|
@@ -7216,6 +7338,14 @@ function enqueueClaimDerivation(input) {
|
|
|
7216
7338
|
payload: { observationId: input.observationId }
|
|
7217
7339
|
});
|
|
7218
7340
|
}
|
|
7341
|
+
function enqueueObservationQualification(input) {
|
|
7342
|
+
queueFor(input).enqueue({
|
|
7343
|
+
projectId: input.projectId,
|
|
7344
|
+
kind: "observation-qualify",
|
|
7345
|
+
dedupeKey: "observation-qualify",
|
|
7346
|
+
payload: { source: input.source, limit: 100 }
|
|
7347
|
+
});
|
|
7348
|
+
}
|
|
7219
7349
|
function enqueueKnowledgeFollowups(input) {
|
|
7220
7350
|
const queue = queueFor(input);
|
|
7221
7351
|
const payload = {
|
|
@@ -8080,6 +8210,7 @@ __export(observations_exports, {
|
|
|
8080
8210
|
resolveObservations: () => resolveObservations,
|
|
8081
8211
|
storeObservation: () => storeObservation,
|
|
8082
8212
|
suggestTopicKey: () => suggestTopicKey,
|
|
8213
|
+
updateObservationAdmission: () => updateObservationAdmission,
|
|
8083
8214
|
withFreshObservations: () => withFreshObservations
|
|
8084
8215
|
});
|
|
8085
8216
|
function logEmbeddingFailureOnce(key, message) {
|
|
@@ -8146,6 +8277,18 @@ function queueClaimDerivation(observation) {
|
|
|
8146
8277
|
} catch {
|
|
8147
8278
|
}
|
|
8148
8279
|
}
|
|
8280
|
+
function queueObservationQualification(observation) {
|
|
8281
|
+
const dataDir = projectDir;
|
|
8282
|
+
if (!dataDir || observation.admissionState !== "candidate") return;
|
|
8283
|
+
try {
|
|
8284
|
+
enqueueObservationQualification({
|
|
8285
|
+
dataDir,
|
|
8286
|
+
projectId: observation.projectId,
|
|
8287
|
+
source: "automatic-capture:" + observation.id
|
|
8288
|
+
});
|
|
8289
|
+
} catch {
|
|
8290
|
+
}
|
|
8291
|
+
}
|
|
8149
8292
|
function isVectorCompatibleWithCurrentIndex(embedding) {
|
|
8150
8293
|
if (!embedding) return false;
|
|
8151
8294
|
const vectorDimensions = getVectorDimensions();
|
|
@@ -8201,6 +8344,9 @@ async function storeObservation(input) {
|
|
|
8201
8344
|
(o) => o.topicKey === input.topicKey && o.projectId === input.projectId
|
|
8202
8345
|
);
|
|
8203
8346
|
if (existing) {
|
|
8347
|
+
if (input.visibilityReader && !canManageObservation(existing, input.visibilityReader)) {
|
|
8348
|
+
throw new Error("Cannot update a memory outside this session's write scope.");
|
|
8349
|
+
}
|
|
8204
8350
|
return { observation: await upsertObservation(existing, input, now3), upserted: true };
|
|
8205
8351
|
}
|
|
8206
8352
|
}
|
|
@@ -8237,6 +8383,9 @@ async function storeObservation(input) {
|
|
|
8237
8383
|
if (input.topicKey) {
|
|
8238
8384
|
const diskExisting = await tx.findByTopicKey(input.projectId, input.topicKey);
|
|
8239
8385
|
if (diskExisting) {
|
|
8386
|
+
if (input.visibilityReader && !canManageObservation(diskExisting, input.visibilityReader)) {
|
|
8387
|
+
throw new Error("Cannot update a memory outside this session's write scope.");
|
|
8388
|
+
}
|
|
8240
8389
|
upsertedInsideLock = true;
|
|
8241
8390
|
observation = diskExisting;
|
|
8242
8391
|
return;
|
|
@@ -8267,6 +8416,10 @@ async function storeObservation(input) {
|
|
|
8267
8416
|
relatedEntities: input.relatedEntities,
|
|
8268
8417
|
sourceDetail: input.sourceDetail,
|
|
8269
8418
|
valueCategory: input.valueCategory,
|
|
8419
|
+
admissionState: input.admissionState,
|
|
8420
|
+
admissionReason: input.admissionReason ? sanitizeCredentials(input.admissionReason) : void 0,
|
|
8421
|
+
visibility: input.visibility ?? "project",
|
|
8422
|
+
sharedWithAgentIds: input.sharedWithAgentIds,
|
|
8270
8423
|
createdByAgentId: input.createdByAgentId,
|
|
8271
8424
|
// Predict the generation that atomic() will commit after this callback.
|
|
8272
8425
|
// bumpGeneration() runs after fn(tx) returns, incrementing by 1.
|
|
@@ -8313,6 +8466,10 @@ async function storeObservation(input) {
|
|
|
8313
8466
|
relatedEntities: input.relatedEntities,
|
|
8314
8467
|
sourceDetail: input.sourceDetail,
|
|
8315
8468
|
valueCategory: input.valueCategory,
|
|
8469
|
+
admissionState: input.admissionState,
|
|
8470
|
+
admissionReason: input.admissionReason ? sanitizeCredentials(input.admissionReason) : void 0,
|
|
8471
|
+
visibility: input.visibility ?? "project",
|
|
8472
|
+
sharedWithAgentIds: input.sharedWithAgentIds,
|
|
8316
8473
|
createdByAgentId: input.createdByAgentId,
|
|
8317
8474
|
writeGeneration: 0
|
|
8318
8475
|
};
|
|
@@ -8336,7 +8493,12 @@ async function storeObservation(input) {
|
|
|
8336
8493
|
status: "active",
|
|
8337
8494
|
source: input.source ?? "agent",
|
|
8338
8495
|
sourceDetail: input.sourceDetail ?? "",
|
|
8339
|
-
valueCategory: input.valueCategory ?? ""
|
|
8496
|
+
valueCategory: input.valueCategory ?? "",
|
|
8497
|
+
admissionState: input.admissionState ?? "",
|
|
8498
|
+
admissionReason: input.admissionReason ? sanitizeCredentials(input.admissionReason) : "",
|
|
8499
|
+
visibility: input.visibility ?? "project",
|
|
8500
|
+
createdByAgentId: input.createdByAgentId ?? "",
|
|
8501
|
+
sharedWithAgentIds: JSON.stringify(input.sharedWithAgentIds ?? [])
|
|
8340
8502
|
};
|
|
8341
8503
|
await insertObservation(doc);
|
|
8342
8504
|
};
|
|
@@ -8345,7 +8507,10 @@ async function storeObservation(input) {
|
|
|
8345
8507
|
return { observation: await upsertObservation(observation, input, now3), upserted: true };
|
|
8346
8508
|
}
|
|
8347
8509
|
await bindObservationCodeRefsBestEffort(observation);
|
|
8348
|
-
|
|
8510
|
+
queueObservationQualification(observation);
|
|
8511
|
+
if (resolveObservationVisibility(observation) === "project") {
|
|
8512
|
+
queueClaimDerivation(observation);
|
|
8513
|
+
}
|
|
8349
8514
|
const obsId = observation.id;
|
|
8350
8515
|
vectorMissingIds.add(obsId);
|
|
8351
8516
|
const searchableText = [input.title, input.narrative, ...input.facts ?? []].join(" ");
|
|
@@ -8418,6 +8583,10 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8418
8583
|
if (input.progress) existing.progress = input.progress;
|
|
8419
8584
|
if (input.sourceDetail !== void 0) existing.sourceDetail = input.sourceDetail;
|
|
8420
8585
|
if (input.valueCategory !== void 0) existing.valueCategory = input.valueCategory;
|
|
8586
|
+
if (input.admissionState !== void 0) existing.admissionState = input.admissionState;
|
|
8587
|
+
if (input.admissionReason !== void 0) existing.admissionReason = sanitizeCredentials(input.admissionReason);
|
|
8588
|
+
if (input.visibility !== void 0) existing.visibility = input.visibility;
|
|
8589
|
+
if (input.sharedWithAgentIds !== void 0) existing.sharedWithAgentIds = input.sharedWithAgentIds;
|
|
8421
8590
|
const doc = {
|
|
8422
8591
|
id: makeOramaObservationId(existing.projectId, existing.id),
|
|
8423
8592
|
observationId: existing.id,
|
|
@@ -8436,7 +8605,12 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8436
8605
|
status: "active",
|
|
8437
8606
|
source: existing.source ?? "agent",
|
|
8438
8607
|
sourceDetail: existing.sourceDetail ?? "",
|
|
8439
|
-
valueCategory: existing.valueCategory ?? ""
|
|
8608
|
+
valueCategory: existing.valueCategory ?? "",
|
|
8609
|
+
admissionState: existing.admissionState ?? "",
|
|
8610
|
+
admissionReason: existing.admissionReason ?? "",
|
|
8611
|
+
visibility: existing.visibility ?? "project",
|
|
8612
|
+
createdByAgentId: existing.createdByAgentId ?? "",
|
|
8613
|
+
sharedWithAgentIds: JSON.stringify(existing.sharedWithAgentIds ?? [])
|
|
8440
8614
|
};
|
|
8441
8615
|
const oramaId = makeOramaObservationId(existing.projectId, existing.id);
|
|
8442
8616
|
try {
|
|
@@ -8459,7 +8633,10 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8459
8633
|
await store.update(existing);
|
|
8460
8634
|
}
|
|
8461
8635
|
await bindObservationCodeRefsBestEffort(existing);
|
|
8462
|
-
|
|
8636
|
+
queueObservationQualification(existing);
|
|
8637
|
+
if (resolveObservationVisibility(existing) === "project") {
|
|
8638
|
+
queueClaimDerivation(existing);
|
|
8639
|
+
}
|
|
8463
8640
|
const searchableText = [input.title, input.narrative, ...input.facts ?? []].join(" ");
|
|
8464
8641
|
const obsId = existing.id;
|
|
8465
8642
|
vectorMissingIds.add(obsId);
|
|
@@ -8498,6 +8675,56 @@ async function upsertObservation(existing, input, now3) {
|
|
|
8498
8675
|
function getObservation(id, projectId) {
|
|
8499
8676
|
return observations.find((o) => o.id === id && (projectId ? o.projectId === projectId : true));
|
|
8500
8677
|
}
|
|
8678
|
+
async function updateObservationAdmission(input) {
|
|
8679
|
+
await ensureFreshObservations();
|
|
8680
|
+
const cached = observations.find(
|
|
8681
|
+
(observation) => observation.id === input.observationId && (!input.projectId || observation.projectId === input.projectId)
|
|
8682
|
+
);
|
|
8683
|
+
if (!cached) return void 0;
|
|
8684
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
8685
|
+
const admissionReason = sanitizeCredentials(input.admissionReason).slice(0, 240);
|
|
8686
|
+
const apply = (current) => {
|
|
8687
|
+
if (input.projectId && current.projectId !== input.projectId) return void 0;
|
|
8688
|
+
if (input.expectedState && current.admissionState !== input.expectedState) return void 0;
|
|
8689
|
+
return {
|
|
8690
|
+
...current,
|
|
8691
|
+
admissionState: input.admissionState,
|
|
8692
|
+
admissionReason,
|
|
8693
|
+
...input.visibility !== void 0 ? { visibility: input.visibility } : {},
|
|
8694
|
+
updatedAt: now3
|
|
8695
|
+
};
|
|
8696
|
+
};
|
|
8697
|
+
let updated;
|
|
8698
|
+
if (projectDir) {
|
|
8699
|
+
const store = getObservationStore();
|
|
8700
|
+
await store.atomic(async (tx) => {
|
|
8701
|
+
const current = await tx.getById(input.observationId);
|
|
8702
|
+
if (!current) return;
|
|
8703
|
+
const next = apply(current);
|
|
8704
|
+
if (!next) return;
|
|
8705
|
+
await tx.update(next);
|
|
8706
|
+
updated = next;
|
|
8707
|
+
});
|
|
8708
|
+
} else {
|
|
8709
|
+
updated = apply(cached);
|
|
8710
|
+
}
|
|
8711
|
+
if (!updated) return void 0;
|
|
8712
|
+
observations = observations.map(
|
|
8713
|
+
(observation) => observation.id === updated.id && observation.projectId === updated.projectId ? updated : observation
|
|
8714
|
+
);
|
|
8715
|
+
try {
|
|
8716
|
+
await updateObservationMetadata(updated.projectId, updated.id, {
|
|
8717
|
+
admissionState: updated.admissionState,
|
|
8718
|
+
admissionReason: updated.admissionReason,
|
|
8719
|
+
...updated.visibility !== void 0 ? { visibility: updated.visibility } : {}
|
|
8720
|
+
});
|
|
8721
|
+
} catch {
|
|
8722
|
+
}
|
|
8723
|
+
if (updated.admissionState === "qualified" && resolveObservationVisibility(updated) === "project") {
|
|
8724
|
+
queueClaimDerivation(updated);
|
|
8725
|
+
}
|
|
8726
|
+
return updated;
|
|
8727
|
+
}
|
|
8501
8728
|
async function resolveObservations(ids, status = "resolved") {
|
|
8502
8729
|
const resolved = [];
|
|
8503
8730
|
const notFound = [];
|
|
@@ -8537,7 +8764,12 @@ async function resolveObservations(ids, status = "resolved") {
|
|
|
8537
8764
|
status,
|
|
8538
8765
|
source: obs.source ?? "agent",
|
|
8539
8766
|
sourceDetail: obs.sourceDetail ?? "",
|
|
8540
|
-
valueCategory: obs.valueCategory ?? ""
|
|
8767
|
+
valueCategory: obs.valueCategory ?? "",
|
|
8768
|
+
admissionState: obs.admissionState ?? "",
|
|
8769
|
+
admissionReason: obs.admissionReason ?? "",
|
|
8770
|
+
visibility: obs.visibility ?? "project",
|
|
8771
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
8772
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
8541
8773
|
};
|
|
8542
8774
|
await insertObservation(doc);
|
|
8543
8775
|
const obsId = obs.id;
|
|
@@ -8667,6 +8899,11 @@ async function reindexObservations() {
|
|
|
8667
8899
|
source: obs.source ?? "agent",
|
|
8668
8900
|
sourceDetail: obs.sourceDetail ?? "",
|
|
8669
8901
|
valueCategory: obs.valueCategory ?? "",
|
|
8902
|
+
admissionState: obs.admissionState ?? "",
|
|
8903
|
+
admissionReason: obs.admissionReason ?? "",
|
|
8904
|
+
visibility: obs.visibility ?? "project",
|
|
8905
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
8906
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
8670
8907
|
...compatibleEmbedding ? { embedding: compatibleEmbedding } : {}
|
|
8671
8908
|
};
|
|
8672
8909
|
await insertObservation(doc);
|
|
@@ -8739,6 +8976,7 @@ async function probeSearchIndex(projectId) {
|
|
|
8739
8976
|
await searchObservations({
|
|
8740
8977
|
query: "semantic memory retrieval status",
|
|
8741
8978
|
projectId,
|
|
8979
|
+
reader: { projectId },
|
|
8742
8980
|
limit: 1,
|
|
8743
8981
|
status: "all",
|
|
8744
8982
|
trackAccess: false
|
|
@@ -8804,6 +9042,11 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
8804
9042
|
source: obs.source ?? "agent",
|
|
8805
9043
|
sourceDetail: obs.sourceDetail ?? "",
|
|
8806
9044
|
valueCategory: obs.valueCategory ?? "",
|
|
9045
|
+
admissionState: obs.admissionState ?? "",
|
|
9046
|
+
admissionReason: obs.admissionReason ?? "",
|
|
9047
|
+
visibility: obs.visibility ?? "project",
|
|
9048
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
9049
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? []),
|
|
8807
9050
|
embedding
|
|
8808
9051
|
};
|
|
8809
9052
|
await insertObservation(doc);
|
|
@@ -8845,6 +9088,7 @@ var init_observations = __esm({
|
|
|
8845
9088
|
init_provider();
|
|
8846
9089
|
init_secret_filter();
|
|
8847
9090
|
init_lifecycle();
|
|
9091
|
+
init_visibility();
|
|
8848
9092
|
observations = [];
|
|
8849
9093
|
nextId = 1;
|
|
8850
9094
|
projectDir = null;
|
|
@@ -9062,6 +9306,36 @@ var init_session_store = __esm({
|
|
|
9062
9306
|
}
|
|
9063
9307
|
});
|
|
9064
9308
|
|
|
9309
|
+
// src/memory/graph-scope.ts
|
|
9310
|
+
function projectGraphEntityNames(observations2) {
|
|
9311
|
+
const entityNames = /* @__PURE__ */ new Set();
|
|
9312
|
+
for (const observation of observations2) {
|
|
9313
|
+
if ((observation.status ?? "active") !== "active") continue;
|
|
9314
|
+
const entityName = observation.entityName?.trim();
|
|
9315
|
+
if (entityName) entityNames.add(entityName);
|
|
9316
|
+
for (const relatedEntity of observation.relatedEntities ?? []) {
|
|
9317
|
+
const name = relatedEntity.trim();
|
|
9318
|
+
if (name) entityNames.add(name);
|
|
9319
|
+
}
|
|
9320
|
+
}
|
|
9321
|
+
return entityNames;
|
|
9322
|
+
}
|
|
9323
|
+
function scopeKnowledgeGraphToProject(graph, observations2) {
|
|
9324
|
+
const entityNames = projectGraphEntityNames(observations2);
|
|
9325
|
+
const entities = graph.entities.filter((entity) => entityNames.has(entity.name));
|
|
9326
|
+
const visibleNames = new Set(entities.map((entity) => entity.name));
|
|
9327
|
+
const relations = graph.relations.filter(
|
|
9328
|
+
(relation) => visibleNames.has(relation.from) && visibleNames.has(relation.to)
|
|
9329
|
+
);
|
|
9330
|
+
return { entities, relations, entityNames };
|
|
9331
|
+
}
|
|
9332
|
+
var init_graph_scope = __esm({
|
|
9333
|
+
"src/memory/graph-scope.ts"() {
|
|
9334
|
+
"use strict";
|
|
9335
|
+
init_esm_shims();
|
|
9336
|
+
}
|
|
9337
|
+
});
|
|
9338
|
+
|
|
9065
9339
|
// src/memory/disclosure-policy.ts
|
|
9066
9340
|
function resolveEvidenceBasis(fields) {
|
|
9067
9341
|
if (fields.commitHash || fields.source === "git" || fields.sourceDetail === "git-ingest") {
|
|
@@ -9094,8 +9368,9 @@ function resolveSourceDetail(sourceDetail, source) {
|
|
|
9094
9368
|
return void 0;
|
|
9095
9369
|
}
|
|
9096
9370
|
function classifyLayer(fields) {
|
|
9097
|
-
const { valueCategory } = fields;
|
|
9371
|
+
const { valueCategory, admissionState } = fields;
|
|
9098
9372
|
const sd = resolveSourceDetail(fields.sourceDetail, fields.source);
|
|
9373
|
+
if (admissionState === "candidate" || admissionState === "ephemeral") return "L1";
|
|
9099
9374
|
if (valueCategory === "core") return "L2";
|
|
9100
9375
|
if (sd === "hook") return "L1";
|
|
9101
9376
|
if (sd === "git-ingest") return "L3";
|
|
@@ -9153,6 +9428,7 @@ function getEffectiveRetentionDays(doc) {
|
|
|
9153
9428
|
}
|
|
9154
9429
|
function isImmune(doc) {
|
|
9155
9430
|
if (doc.type === "probe") return false;
|
|
9431
|
+
if (doc.admissionState === "candidate" || doc.admissionState === "ephemeral") return false;
|
|
9156
9432
|
if (doc.valueCategory === "core") return true;
|
|
9157
9433
|
const importance = getImportanceLevel(doc);
|
|
9158
9434
|
if (importance === "critical") return true;
|
|
@@ -9162,6 +9438,7 @@ function isImmune(doc) {
|
|
|
9162
9438
|
}
|
|
9163
9439
|
function getImmunityReason(doc) {
|
|
9164
9440
|
if (doc.type === "probe") return null;
|
|
9441
|
+
if (doc.admissionState === "candidate" || doc.admissionState === "ephemeral") return null;
|
|
9165
9442
|
if (doc.valueCategory === "core") return "core valueCategory (formation-classified)";
|
|
9166
9443
|
const importance = getImportanceLevel(doc);
|
|
9167
9444
|
if (importance === "critical") return "critical importance";
|
|
@@ -9294,7 +9571,9 @@ function toRetentionDocument(obs, accessMap) {
|
|
|
9294
9571
|
status: obs.status ?? "active",
|
|
9295
9572
|
source: obs.source ?? "agent",
|
|
9296
9573
|
sourceDetail: obs.sourceDetail ?? "",
|
|
9297
|
-
valueCategory: obs.valueCategory ?? ""
|
|
9574
|
+
valueCategory: obs.valueCategory ?? "",
|
|
9575
|
+
admissionState: obs.admissionState ?? "",
|
|
9576
|
+
admissionReason: obs.admissionReason ?? ""
|
|
9298
9577
|
};
|
|
9299
9578
|
}
|
|
9300
9579
|
async function archiveExpiredBatch(_projectDir, options) {
|
|
@@ -9308,7 +9587,7 @@ async function archiveExpiredBatch(_projectDir, options) {
|
|
|
9308
9587
|
});
|
|
9309
9588
|
const hasMore = page.length > limit;
|
|
9310
9589
|
const scanned = hasMore ? page.slice(0, limit) : page;
|
|
9311
|
-
const candidateIds = scanned.filter((observation) => getRetentionZone(
|
|
9590
|
+
const candidateIds = scanned.filter((observation) => !options.reader || canManageObservation(observation, options.reader)).filter((observation) => getRetentionZone(
|
|
9312
9591
|
toRetentionDocument(observation, options.accessMap),
|
|
9313
9592
|
options.referenceTime
|
|
9314
9593
|
) === "archive-candidate").map((observation) => observation.id);
|
|
@@ -9321,7 +9600,7 @@ async function archiveExpiredBatch(_projectDir, options) {
|
|
|
9321
9600
|
const nextCursor = hasMore && scanned.length > 0 ? scanned[scanned.length - 1].id : void 0;
|
|
9322
9601
|
return nextCursor === void 0 ? { archived, scanned: scanned.length } : { archived, scanned: scanned.length, nextCursor };
|
|
9323
9602
|
}
|
|
9324
|
-
async function archiveExpired(projectDir2, referenceTime, accessMap, projectId) {
|
|
9603
|
+
async function archiveExpired(projectDir2, referenceTime, accessMap, projectId, reader) {
|
|
9325
9604
|
const store = getObservationStore();
|
|
9326
9605
|
if (projectId) {
|
|
9327
9606
|
let afterId;
|
|
@@ -9331,12 +9610,14 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId)
|
|
|
9331
9610
|
projectId,
|
|
9332
9611
|
afterId,
|
|
9333
9612
|
referenceTime,
|
|
9334
|
-
accessMap
|
|
9613
|
+
accessMap,
|
|
9614
|
+
reader
|
|
9335
9615
|
});
|
|
9336
9616
|
archived += batch.archived;
|
|
9337
9617
|
afterId = batch.nextCursor;
|
|
9338
9618
|
} while (afterId !== void 0);
|
|
9339
|
-
const
|
|
9619
|
+
const remainingObservations = await store.loadByProject(projectId, { status: "active" });
|
|
9620
|
+
const remaining = reader ? remainingObservations.filter((observation) => canManageObservation(observation, reader)).length : remainingObservations.length;
|
|
9340
9621
|
return { archived, remaining };
|
|
9341
9622
|
}
|
|
9342
9623
|
return await store.atomic(async (tx) => {
|
|
@@ -9366,6 +9647,7 @@ var init_retention = __esm({
|
|
|
9366
9647
|
"use strict";
|
|
9367
9648
|
init_esm_shims();
|
|
9368
9649
|
init_obs_store();
|
|
9650
|
+
init_visibility();
|
|
9369
9651
|
RETENTION_DAYS = {
|
|
9370
9652
|
critical: 365,
|
|
9371
9653
|
high: 180,
|
|
@@ -9401,6 +9683,49 @@ var init_retention = __esm({
|
|
|
9401
9683
|
}
|
|
9402
9684
|
});
|
|
9403
9685
|
|
|
9686
|
+
// src/memory/admission.ts
|
|
9687
|
+
var admission_exports = {};
|
|
9688
|
+
__export(admission_exports, {
|
|
9689
|
+
isCandidateObservation: () => isCandidateObservation,
|
|
9690
|
+
isEligibleForAutomaticDelivery: () => isEligibleForAutomaticDelivery,
|
|
9691
|
+
isEligibleForKnowledgePromotion: () => isEligibleForKnowledgePromotion,
|
|
9692
|
+
qualifyCandidateFromCurrentCode: () => qualifyCandidateFromCurrentCode
|
|
9693
|
+
});
|
|
9694
|
+
function isEligibleForAutomaticDelivery(observation) {
|
|
9695
|
+
return observation.admissionState !== "ephemeral" && observation.admissionState !== "candidate";
|
|
9696
|
+
}
|
|
9697
|
+
function isCandidateObservation(observation) {
|
|
9698
|
+
return observation.admissionState === "candidate";
|
|
9699
|
+
}
|
|
9700
|
+
function isEligibleForKnowledgePromotion(observation) {
|
|
9701
|
+
return isEligibleForAutomaticDelivery(observation) && observation.valueCategory !== "ephemeral" && resolveObservationVisibility(observation) === "project";
|
|
9702
|
+
}
|
|
9703
|
+
function qualifyCandidateFromCurrentCode(input) {
|
|
9704
|
+
if (!isCandidateObservation(input.observation)) return void 0;
|
|
9705
|
+
if (input.observation.valueCategory === "ephemeral") return void 0;
|
|
9706
|
+
if (input.currentCodeReferenceCount <= 0) return void 0;
|
|
9707
|
+
const typeLabel = DURABLE_AUTOMATIC_TYPES.has(input.observation.type) ? "high-value automatic record" : "automatic record";
|
|
9708
|
+
return {
|
|
9709
|
+
admissionState: "qualified",
|
|
9710
|
+
admissionReason: `${typeLabel} qualified against ${input.currentCodeReferenceCount} current Code Memory reference(s)`
|
|
9711
|
+
};
|
|
9712
|
+
}
|
|
9713
|
+
var DURABLE_AUTOMATIC_TYPES;
|
|
9714
|
+
var init_admission = __esm({
|
|
9715
|
+
"src/memory/admission.ts"() {
|
|
9716
|
+
"use strict";
|
|
9717
|
+
init_esm_shims();
|
|
9718
|
+
init_visibility();
|
|
9719
|
+
DURABLE_AUTOMATIC_TYPES = /* @__PURE__ */ new Set([
|
|
9720
|
+
"decision",
|
|
9721
|
+
"gotcha",
|
|
9722
|
+
"problem-solution",
|
|
9723
|
+
"trade-off",
|
|
9724
|
+
"why-it-exists"
|
|
9725
|
+
]);
|
|
9726
|
+
}
|
|
9727
|
+
});
|
|
9728
|
+
|
|
9404
9729
|
// src/workspace/workflow-sync.ts
|
|
9405
9730
|
import matter9 from "gray-matter";
|
|
9406
9731
|
var WorkflowSyncer;
|
|
@@ -10801,6 +11126,7 @@ var init_isolated_maintenance = __esm({
|
|
|
10801
11126
|
"retention-archive",
|
|
10802
11127
|
"consolidation",
|
|
10803
11128
|
"codegraph-refresh",
|
|
11129
|
+
"observation-qualify",
|
|
10804
11130
|
"claim-derive",
|
|
10805
11131
|
"claim-requalification",
|
|
10806
11132
|
"knowledge-compile",
|
|
@@ -11236,9 +11562,10 @@ async function loadConsolidationPage(projectId, options) {
|
|
|
11236
11562
|
return hasMore && observations2.length > 0 ? { observations: observations2, nextCursor: observations2[observations2.length - 1].id } : { observations: observations2 };
|
|
11237
11563
|
}
|
|
11238
11564
|
function findClusters(observations2, threshold) {
|
|
11239
|
-
|
|
11565
|
+
const eligible = observations2.filter(isEligibleForAutomaticDelivery).filter((observation) => resolveObservationVisibility(observation) === "project");
|
|
11566
|
+
if (eligible.length < MIN_CLUSTER_SIZE) return [];
|
|
11240
11567
|
const groups = /* @__PURE__ */ new Map();
|
|
11241
|
-
for (const obs of
|
|
11568
|
+
for (const obs of eligible) {
|
|
11242
11569
|
const key = `${obs.entityName}::${obs.type}`;
|
|
11243
11570
|
const group = groups.get(key) ?? [];
|
|
11244
11571
|
group.push(obs);
|
|
@@ -11375,6 +11702,8 @@ var init_consolidation = __esm({
|
|
|
11375
11702
|
"use strict";
|
|
11376
11703
|
init_esm_shims();
|
|
11377
11704
|
init_obs_store();
|
|
11705
|
+
init_admission();
|
|
11706
|
+
init_visibility();
|
|
11378
11707
|
DEFAULT_SIMILARITY_THRESHOLD = 0.45;
|
|
11379
11708
|
HIGH_VALUE_SIMILARITY_THRESHOLD = 0.85;
|
|
11380
11709
|
HIGH_VALUE_TYPES = /* @__PURE__ */ new Set(["gotcha", "decision", "trade-off", "reasoning", "problem-solution"]);
|
|
@@ -12174,6 +12503,7 @@ __export(claims_exports, {
|
|
|
12174
12503
|
buildClaimIdentity: () => buildClaimIdentity,
|
|
12175
12504
|
deriveLowRiskClaimsFromObservation: () => deriveLowRiskClaimsFromObservation,
|
|
12176
12505
|
requalifyClaimsForCodeState: () => requalifyClaimsForCodeState,
|
|
12506
|
+
reviewClaim: () => reviewClaim,
|
|
12177
12507
|
selectClaimsForTask: () => selectClaimsForTask,
|
|
12178
12508
|
supersedeClaim: () => supersedeClaim,
|
|
12179
12509
|
writeClaim: () => writeClaim
|
|
@@ -12360,6 +12690,39 @@ function writeClaim(store, input) {
|
|
|
12360
12690
|
};
|
|
12361
12691
|
});
|
|
12362
12692
|
}
|
|
12693
|
+
function reviewClaim(store, input) {
|
|
12694
|
+
if (input.reviewState !== "approved" && input.reviewState !== "rejected") {
|
|
12695
|
+
throw new Error("A claim review must approve or reject the claim");
|
|
12696
|
+
}
|
|
12697
|
+
const detail = compactText(input.detail, "review detail");
|
|
12698
|
+
return store.transaction(() => {
|
|
12699
|
+
const claim = store.getClaim(input.claimId);
|
|
12700
|
+
if (!claim) throw new Error("Claim was not found");
|
|
12701
|
+
if (input.reviewState === "approved" && claim.status === "superseded") {
|
|
12702
|
+
throw new Error("A superseded claim cannot be approved");
|
|
12703
|
+
}
|
|
12704
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
12705
|
+
const nextStatus = input.reviewState === "rejected" ? "unknown" : claim.status === "unknown" && claim.reviewState === "rejected" ? "active" : claim.status;
|
|
12706
|
+
const updated = {
|
|
12707
|
+
...claim,
|
|
12708
|
+
status: nextStatus,
|
|
12709
|
+
reviewState: input.reviewState,
|
|
12710
|
+
updatedAt: now3
|
|
12711
|
+
};
|
|
12712
|
+
store.updateClaim(updated);
|
|
12713
|
+
store.recordEvent({
|
|
12714
|
+
projectId: claim.projectId,
|
|
12715
|
+
claimId: claim.id,
|
|
12716
|
+
kind: "reviewed",
|
|
12717
|
+
fromStatus: claim.status,
|
|
12718
|
+
toStatus: updated.status,
|
|
12719
|
+
detail: "Review: " + claim.reviewState + " -> " + input.reviewState + ". " + detail,
|
|
12720
|
+
createdAt: now3
|
|
12721
|
+
});
|
|
12722
|
+
reconcileConflicts(store, claim.projectId, claim.conflictKey, now3);
|
|
12723
|
+
return store.getClaim(claim.id) ?? updated;
|
|
12724
|
+
});
|
|
12725
|
+
}
|
|
12363
12726
|
function supersedeClaim(store, input) {
|
|
12364
12727
|
const evidence = validateEvidence(input.evidence);
|
|
12365
12728
|
return store.transaction(() => {
|
|
@@ -12448,7 +12811,7 @@ function deriveLowRiskClaimsFromObservation(store, observation, codeStore) {
|
|
|
12448
12811
|
scope: "project",
|
|
12449
12812
|
confidence,
|
|
12450
12813
|
observedAt: observation.updatedAt ?? observation.createdAt,
|
|
12451
|
-
reviewState: "approved",
|
|
12814
|
+
reviewState: isGit ? "approved" : "needs-review",
|
|
12452
12815
|
origin: isGit ? "git" : "derived",
|
|
12453
12816
|
evidence
|
|
12454
12817
|
});
|
|
@@ -13324,81 +13687,425 @@ var init_workflow_store = __esm({
|
|
|
13324
13687
|
}
|
|
13325
13688
|
});
|
|
13326
13689
|
|
|
13327
|
-
// src/
|
|
13328
|
-
var
|
|
13329
|
-
__export(
|
|
13330
|
-
|
|
13331
|
-
|
|
13332
|
-
|
|
13333
|
-
|
|
13334
|
-
|
|
13335
|
-
|
|
13336
|
-
|
|
13337
|
-
|
|
13338
|
-
selectWorkspaceWorkflows: () => selectWorkspaceWorkflows,
|
|
13339
|
-
syncCanonicalWorkflows: () => syncCanonicalWorkflows,
|
|
13340
|
-
writeCanonicalWorkflow: () => writeCanonicalWorkflow
|
|
13690
|
+
// src/codegraph/task-lens.ts
|
|
13691
|
+
var task_lens_exports = {};
|
|
13692
|
+
__export(task_lens_exports, {
|
|
13693
|
+
containsTaskKeyword: () => containsTaskKeyword,
|
|
13694
|
+
lensPathCandidates: () => lensPathCandidates,
|
|
13695
|
+
lensVerificationHints: () => lensVerificationHints,
|
|
13696
|
+
rankLensPaths: () => rankLensPaths,
|
|
13697
|
+
rankLensSources: () => rankLensSources,
|
|
13698
|
+
resolveTaskLens: () => resolveTaskLens,
|
|
13699
|
+
scoreLensSource: () => scoreLensSource,
|
|
13700
|
+
shouldShowLensSource: () => shouldShowLensSource
|
|
13341
13701
|
});
|
|
13342
|
-
|
|
13343
|
-
|
|
13344
|
-
import path17 from "path";
|
|
13345
|
-
import matter11 from "gray-matter";
|
|
13346
|
-
function hash3(value) {
|
|
13347
|
-
return createHash12("sha256").update(value).digest("hex");
|
|
13348
|
-
}
|
|
13349
|
-
function now2() {
|
|
13350
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
13351
|
-
}
|
|
13352
|
-
function slug(value) {
|
|
13353
|
-
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 72);
|
|
13354
|
-
return normalized || "workflow";
|
|
13702
|
+
function normalizePath2(path25) {
|
|
13703
|
+
return path25.replace(/\\/g, "/");
|
|
13355
13704
|
}
|
|
13356
|
-
function
|
|
13357
|
-
return
|
|
13705
|
+
function tokenize2(text) {
|
|
13706
|
+
return (text.toLowerCase().match(/[a-z0-9_./-]+|[\u4e00-\u9fff]+/g) ?? []).map((token) => token.trim()).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
|
|
13358
13707
|
}
|
|
13359
|
-
function
|
|
13360
|
-
const
|
|
13361
|
-
|
|
13362
|
-
|
|
13708
|
+
function containsTaskKeyword(text, keyword) {
|
|
13709
|
+
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");
|
|
13710
|
+
let match;
|
|
13711
|
+
while ((match = matcher.exec(text)) !== null) {
|
|
13712
|
+
const keywordIndex = match.index + (match[1]?.length ?? 0);
|
|
13713
|
+
if (!isNegatedTaskKeyword(text, keywordIndex)) return true;
|
|
13363
13714
|
}
|
|
13364
|
-
return
|
|
13715
|
+
return false;
|
|
13365
13716
|
}
|
|
13366
|
-
function
|
|
13367
|
-
const
|
|
13368
|
-
|
|
13369
|
-
|
|
13370
|
-
|
|
13717
|
+
function isNegatedTaskKeyword(text, keywordIndex) {
|
|
13718
|
+
const boundary = Math.max(
|
|
13719
|
+
text.lastIndexOf(".", keywordIndex - 1),
|
|
13720
|
+
text.lastIndexOf("!", keywordIndex - 1),
|
|
13721
|
+
text.lastIndexOf("?", keywordIndex - 1),
|
|
13722
|
+
text.lastIndexOf(";", keywordIndex - 1),
|
|
13723
|
+
text.lastIndexOf("\n", keywordIndex - 1),
|
|
13724
|
+
text.lastIndexOf("\u3002", keywordIndex - 1),
|
|
13725
|
+
text.lastIndexOf("\uFF01", keywordIndex - 1),
|
|
13726
|
+
text.lastIndexOf("\uFF1F", keywordIndex - 1),
|
|
13727
|
+
text.lastIndexOf("\uFF1B", keywordIndex - 1)
|
|
13728
|
+
);
|
|
13729
|
+
let prefix = text.slice(boundary + 1, keywordIndex).toLowerCase();
|
|
13730
|
+
const contrast = /(?:,|\uff0c)\s*(?:but|however|instead|yet|\u4f46\u662f|\u4f46|\u800c\u662f)\s*/gi;
|
|
13731
|
+
let contrastMatch;
|
|
13732
|
+
let contrastEnd = -1;
|
|
13733
|
+
while ((contrastMatch = contrast.exec(prefix)) !== null) contrastEnd = contrast.lastIndex;
|
|
13734
|
+
if (contrastEnd >= 0) prefix = prefix.slice(contrastEnd);
|
|
13735
|
+
if (/\b(?:do|does|did|should|must|can|could|will|would|may|might)\s+not\b/.test(prefix)) return true;
|
|
13736
|
+
if (/\b(?:don't|dont|never|without|avoid|skip)\b/.test(prefix)) return true;
|
|
13737
|
+
if (/(?:^|[\s,])no\s+(?:[a-z0-9_-]+\s*){0,4}$/i.test(prefix)) return true;
|
|
13738
|
+
const chineseClauseStart = Math.max(prefix.lastIndexOf(","), prefix.lastIndexOf("\uFF0C"), prefix.lastIndexOf("\u3001"));
|
|
13739
|
+
const chineseClause = prefix.slice(chineseClauseStart + 1);
|
|
13740
|
+
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);
|
|
13371
13741
|
}
|
|
13372
|
-
function
|
|
13373
|
-
const
|
|
13374
|
-
if (
|
|
13375
|
-
|
|
13376
|
-
|
|
13742
|
+
function resolveTaskLens(task) {
|
|
13743
|
+
const normalized = (task ?? "").toLowerCase();
|
|
13744
|
+
if (!normalized.trim()) return LENSES.general;
|
|
13745
|
+
let best = {
|
|
13746
|
+
id: "general",
|
|
13747
|
+
score: 0,
|
|
13748
|
+
priority: Number.MAX_SAFE_INTEGER
|
|
13749
|
+
};
|
|
13750
|
+
for (const id of LENS_PRIORITY) {
|
|
13751
|
+
const score = KEYWORDS[id].reduce((sum, keyword) => sum + (containsTaskKeyword(normalized, keyword) ? 1 : 0), 0);
|
|
13752
|
+
const priority = LENS_PRIORITY.indexOf(id);
|
|
13753
|
+
if (score > best.score || score === best.score && score > 0 && priority < best.priority) {
|
|
13754
|
+
best = { id, score, priority };
|
|
13755
|
+
}
|
|
13377
13756
|
}
|
|
13378
|
-
return [
|
|
13757
|
+
return best.score > 0 ? LENSES[best.id] : LENSES.general;
|
|
13379
13758
|
}
|
|
13380
|
-
function
|
|
13381
|
-
const
|
|
13382
|
-
|
|
13383
|
-
|
|
13384
|
-
|
|
13759
|
+
function pathKindScore(path25, lens) {
|
|
13760
|
+
const normalized = normalizePath2(path25).toLowerCase();
|
|
13761
|
+
const name = normalized.split("/").pop() ?? normalized;
|
|
13762
|
+
const inDocs = normalized.startsWith("docs/") || name === "readme.md" || name.includes("readme");
|
|
13763
|
+
const isTest = /(^|\/)(tests?|__tests__|spec|fixtures?)\//.test(normalized) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized) || /\.(test|spec)\.py$/.test(normalized);
|
|
13764
|
+
const isSource = normalized.startsWith("src/") || normalized.includes("/src/");
|
|
13765
|
+
const isPackage = name === "package.json" || name === "package-lock.json" || name === "pnpm-lock.yaml" || name === "yarn.lock";
|
|
13766
|
+
const isChangelog = name === "changelog.md" || name === "changes.md";
|
|
13767
|
+
const isWorkflow = normalized.startsWith(".github/workflows/");
|
|
13768
|
+
switch (lens.id) {
|
|
13769
|
+
case "bugfix":
|
|
13770
|
+
return (isTest ? 80 : 0) + (isSource ? 50 : 0) + (normalized.includes("debug") ? 20 : 0);
|
|
13771
|
+
case "test":
|
|
13772
|
+
return (isTest ? 90 : 0) + (isSource ? 45 : 0);
|
|
13773
|
+
case "release":
|
|
13774
|
+
return (isChangelog ? 100 : 0) + (isPackage ? 90 : 0) + (isWorkflow ? 70 : 0) + (inDocs ? 35 : 0);
|
|
13775
|
+
case "onboarding":
|
|
13776
|
+
return (name === "readme.md" ? 100 : 0) + (inDocs ? 80 : 0) + (isPackage ? 45 : 0) + (isSource ? 20 : 0);
|
|
13777
|
+
case "docs":
|
|
13778
|
+
return (inDocs ? 100 : 0) + (isChangelog ? 60 : 0) + (isSource ? 25 : 0);
|
|
13779
|
+
case "refactor":
|
|
13780
|
+
return (isSource ? 70 : 0) + (isTest ? 65 : 0);
|
|
13781
|
+
case "feature":
|
|
13782
|
+
return (isSource ? 80 : 0) + (isTest ? 45 : 0) + (inDocs ? 20 : 0);
|
|
13783
|
+
default:
|
|
13784
|
+
if (isSource || isTest) return 60;
|
|
13785
|
+
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 45;
|
|
13786
|
+
if (inDocs) return 20;
|
|
13787
|
+
return 0;
|
|
13385
13788
|
}
|
|
13386
|
-
return value;
|
|
13387
13789
|
}
|
|
13388
|
-
function
|
|
13389
|
-
|
|
13390
|
-
|
|
13391
|
-
|
|
13392
|
-
throw new Error("workflow frontmatter field status is invalid");
|
|
13393
|
-
}
|
|
13394
|
-
return value;
|
|
13790
|
+
function tokenScore(text, tokens) {
|
|
13791
|
+
if (tokens.length === 0) return 0;
|
|
13792
|
+
const normalized = text.toLowerCase();
|
|
13793
|
+
return tokens.reduce((sum, token) => sum + (normalized.includes(token) ? 12 : 0), 0);
|
|
13395
13794
|
}
|
|
13396
|
-
function
|
|
13397
|
-
const
|
|
13398
|
-
if (
|
|
13399
|
-
|
|
13400
|
-
|
|
13401
|
-
|
|
13795
|
+
function sourceTaskMatchScore(source, task) {
|
|
13796
|
+
const tokens = tokenize2(task ?? "");
|
|
13797
|
+
if (tokens.length === 0) return 0;
|
|
13798
|
+
const text = [
|
|
13799
|
+
source.title,
|
|
13800
|
+
source.path ?? "",
|
|
13801
|
+
source.symbol ?? ""
|
|
13802
|
+
].join("\n").toLowerCase();
|
|
13803
|
+
const matches = tokens.filter((token) => text.includes(token)).length;
|
|
13804
|
+
if (matches >= 2) return 40;
|
|
13805
|
+
if (matches === 1) return 24;
|
|
13806
|
+
return 0;
|
|
13807
|
+
}
|
|
13808
|
+
function fallbackPathRank(path25) {
|
|
13809
|
+
const normalized = normalizePath2(path25);
|
|
13810
|
+
if (normalized.startsWith("src/")) return 0;
|
|
13811
|
+
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 1;
|
|
13812
|
+
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
|
|
13813
|
+
if (normalized.startsWith("docs/") || normalized.toLowerCase() === "readme.md") return 3;
|
|
13814
|
+
return 4;
|
|
13815
|
+
}
|
|
13816
|
+
function rankLensPaths(paths, lens, task) {
|
|
13817
|
+
const tokens = tokenize2(task ?? "");
|
|
13818
|
+
return [...new Set(paths.map(normalizePath2))].map((path25, index) => ({
|
|
13819
|
+
path: path25,
|
|
13820
|
+
index,
|
|
13821
|
+
score: pathKindScore(path25, lens) + tokenScore(path25, tokens),
|
|
13822
|
+
fallback: fallbackPathRank(path25)
|
|
13823
|
+
})).sort((a, b) => b.score - a.score || a.fallback - b.fallback || a.index - b.index || a.path.localeCompare(b.path)).map((item) => item.path);
|
|
13824
|
+
}
|
|
13825
|
+
function lensPathCandidates(lens) {
|
|
13826
|
+
switch (lens.id) {
|
|
13827
|
+
case "release":
|
|
13828
|
+
return ["CHANGELOG.md", "package.json", "package-lock.json", ".github/workflows/ci.yml", ".github/workflows/test.yml", "README.md"];
|
|
13829
|
+
case "onboarding":
|
|
13830
|
+
return ["README.md", "docs/README.md", "docs/API_REFERENCE.md", "package.json"];
|
|
13831
|
+
case "docs":
|
|
13832
|
+
return ["README.md", "README.zh-CN.md", "docs/README.md", "docs/API_REFERENCE.md", "CHANGELOG.md"];
|
|
13833
|
+
case "test":
|
|
13834
|
+
case "bugfix":
|
|
13835
|
+
return ["tests", "test"];
|
|
13836
|
+
default:
|
|
13837
|
+
return [];
|
|
13838
|
+
}
|
|
13839
|
+
}
|
|
13840
|
+
function scoreLensSource(source, lens, task) {
|
|
13841
|
+
const tokens = tokenize2(task ?? "");
|
|
13842
|
+
const text = [source.title, source.type, source.path ?? "", source.symbol ?? ""].join("\n");
|
|
13843
|
+
return pathKindScore(source.path ?? "", lens) + tokenScore(text, tokens) + sourceTaskMatchScore(source, task);
|
|
13844
|
+
}
|
|
13845
|
+
function rankLensSources(sources, lens, task) {
|
|
13846
|
+
return [...sources].map((source, index) => ({
|
|
13847
|
+
source,
|
|
13848
|
+
index,
|
|
13849
|
+
score: scoreLensSource(source, lens, task)
|
|
13850
|
+
})).sort((a, b) => b.score - a.score || a.source.observationId - b.source.observationId || a.index - b.index).map((item) => item.source);
|
|
13851
|
+
}
|
|
13852
|
+
function lensVerificationHints(lens) {
|
|
13853
|
+
switch (lens.id) {
|
|
13854
|
+
case "bugfix":
|
|
13855
|
+
return [
|
|
13856
|
+
"run the smallest failing test or repro first",
|
|
13857
|
+
"inspect the changed code path before trusting old memory"
|
|
13858
|
+
];
|
|
13859
|
+
case "test":
|
|
13860
|
+
return [
|
|
13861
|
+
"run the exact focused test file or test name first",
|
|
13862
|
+
"inspect fixtures and harness setup before changing assertions"
|
|
13863
|
+
];
|
|
13864
|
+
case "release":
|
|
13865
|
+
return [
|
|
13866
|
+
"run build, tests, package smoke, and publish dry-run where available",
|
|
13867
|
+
"verify package metadata, changelog, and Git state before publishing"
|
|
13868
|
+
];
|
|
13869
|
+
case "onboarding":
|
|
13870
|
+
return [
|
|
13871
|
+
"read the docs/start files first, then inspect only the code paths needed for the task",
|
|
13872
|
+
"treat old implementation memories as leads until current code confirms them"
|
|
13873
|
+
];
|
|
13874
|
+
case "refactor":
|
|
13875
|
+
return [
|
|
13876
|
+
"inspect call sites and tests before editing shared code",
|
|
13877
|
+
"run the narrow affected test plus one regression smoke"
|
|
13878
|
+
];
|
|
13879
|
+
case "docs":
|
|
13880
|
+
return [
|
|
13881
|
+
"check headings, links, commands, and examples against current code",
|
|
13882
|
+
"run the smallest docs or package smoke available"
|
|
13883
|
+
];
|
|
13884
|
+
case "feature":
|
|
13885
|
+
return [
|
|
13886
|
+
"inspect the closest existing implementation pattern before adding new code",
|
|
13887
|
+
"run focused tests plus one user-flow smoke after changes"
|
|
13888
|
+
];
|
|
13889
|
+
default:
|
|
13890
|
+
return [
|
|
13891
|
+
"inspect the Start here files before editing",
|
|
13892
|
+
"run the smallest relevant test or smoke command after changes"
|
|
13893
|
+
];
|
|
13894
|
+
}
|
|
13895
|
+
}
|
|
13896
|
+
function shouldShowLensSource(source, lens, task) {
|
|
13897
|
+
if (lens.id === "general") return true;
|
|
13898
|
+
const score = scoreLensSource(source, lens, task);
|
|
13899
|
+
if (lens.id === "onboarding" || lens.id === "release" || lens.id === "docs") return score >= 40;
|
|
13900
|
+
return score > 0;
|
|
13901
|
+
}
|
|
13902
|
+
var STOP_WORDS, LENSES, KEYWORDS, LENS_PRIORITY;
|
|
13903
|
+
var init_task_lens = __esm({
|
|
13904
|
+
"src/codegraph/task-lens.ts"() {
|
|
13905
|
+
"use strict";
|
|
13906
|
+
init_esm_shims();
|
|
13907
|
+
STOP_WORDS = /* @__PURE__ */ new Set([
|
|
13908
|
+
"a",
|
|
13909
|
+
"an",
|
|
13910
|
+
"and",
|
|
13911
|
+
"as",
|
|
13912
|
+
"for",
|
|
13913
|
+
"in",
|
|
13914
|
+
"into",
|
|
13915
|
+
"of",
|
|
13916
|
+
"on",
|
|
13917
|
+
"onto",
|
|
13918
|
+
"the",
|
|
13919
|
+
"this",
|
|
13920
|
+
"that",
|
|
13921
|
+
"to",
|
|
13922
|
+
"with",
|
|
13923
|
+
"work",
|
|
13924
|
+
"project",
|
|
13925
|
+
"continue",
|
|
13926
|
+
"\u7EE7\u7EED",
|
|
13927
|
+
"\u9879\u76EE"
|
|
13928
|
+
]);
|
|
13929
|
+
LENSES = {
|
|
13930
|
+
bugfix: {
|
|
13931
|
+
id: "bugfix",
|
|
13932
|
+
description: "debug the failure with current code and the smallest repro first",
|
|
13933
|
+
sourceLimit: 6,
|
|
13934
|
+
cautionLimit: 4,
|
|
13935
|
+
hideUnrelatedCautionDetails: true,
|
|
13936
|
+
hideUnrelatedReliableDetails: false
|
|
13937
|
+
},
|
|
13938
|
+
feature: {
|
|
13939
|
+
id: "feature",
|
|
13940
|
+
description: "build a scoped feature from nearby source, types, and user flow",
|
|
13941
|
+
sourceLimit: 6,
|
|
13942
|
+
cautionLimit: 3,
|
|
13943
|
+
hideUnrelatedCautionDetails: true,
|
|
13944
|
+
hideUnrelatedReliableDetails: false
|
|
13945
|
+
},
|
|
13946
|
+
release: {
|
|
13947
|
+
id: "release",
|
|
13948
|
+
description: "prepare a release using current metadata, changelog, build, and package checks",
|
|
13949
|
+
sourceLimit: 4,
|
|
13950
|
+
cautionLimit: 2,
|
|
13951
|
+
hideUnrelatedCautionDetails: true,
|
|
13952
|
+
hideUnrelatedReliableDetails: true
|
|
13953
|
+
},
|
|
13954
|
+
onboarding: {
|
|
13955
|
+
id: "onboarding",
|
|
13956
|
+
description: "understand the project shape before trusting old implementation details",
|
|
13957
|
+
sourceLimit: 4,
|
|
13958
|
+
cautionLimit: 2,
|
|
13959
|
+
hideUnrelatedCautionDetails: true,
|
|
13960
|
+
hideUnrelatedReliableDetails: true
|
|
13961
|
+
},
|
|
13962
|
+
refactor: {
|
|
13963
|
+
id: "refactor",
|
|
13964
|
+
description: "change structure carefully by reading shared code, call sites, and tests",
|
|
13965
|
+
sourceLimit: 6,
|
|
13966
|
+
cautionLimit: 4,
|
|
13967
|
+
hideUnrelatedCautionDetails: true,
|
|
13968
|
+
hideUnrelatedReliableDetails: false
|
|
13969
|
+
},
|
|
13970
|
+
docs: {
|
|
13971
|
+
id: "docs",
|
|
13972
|
+
description: "update documentation against current code and public entry points",
|
|
13973
|
+
sourceLimit: 5,
|
|
13974
|
+
cautionLimit: 2,
|
|
13975
|
+
hideUnrelatedCautionDetails: true,
|
|
13976
|
+
hideUnrelatedReliableDetails: true
|
|
13977
|
+
},
|
|
13978
|
+
test: {
|
|
13979
|
+
id: "test",
|
|
13980
|
+
description: "work from tests, fixtures, harnesses, and the related source files",
|
|
13981
|
+
sourceLimit: 6,
|
|
13982
|
+
cautionLimit: 3,
|
|
13983
|
+
hideUnrelatedCautionDetails: true,
|
|
13984
|
+
hideUnrelatedReliableDetails: false
|
|
13985
|
+
},
|
|
13986
|
+
general: {
|
|
13987
|
+
id: "general",
|
|
13988
|
+
description: "balanced project handoff with current facts, code memory, and verification hints",
|
|
13989
|
+
sourceLimit: 8,
|
|
13990
|
+
cautionLimit: 5,
|
|
13991
|
+
hideUnrelatedCautionDetails: false,
|
|
13992
|
+
hideUnrelatedReliableDetails: false
|
|
13993
|
+
}
|
|
13994
|
+
};
|
|
13995
|
+
KEYWORDS = {
|
|
13996
|
+
bugfix: [
|
|
13997
|
+
"bug",
|
|
13998
|
+
"crash",
|
|
13999
|
+
"incident",
|
|
14000
|
+
"debug",
|
|
14001
|
+
"error",
|
|
14002
|
+
"fail",
|
|
14003
|
+
"failing",
|
|
14004
|
+
"fix",
|
|
14005
|
+
"issue",
|
|
14006
|
+
"regression",
|
|
14007
|
+
"repro",
|
|
14008
|
+
"\u62A5\u9519",
|
|
14009
|
+
"\u5D29\u6E83",
|
|
14010
|
+
"\u6545\u969C",
|
|
14011
|
+
"\u5931\u8D25",
|
|
14012
|
+
"\u4FEE\u590D",
|
|
14013
|
+
"\u95EE\u9898"
|
|
14014
|
+
],
|
|
14015
|
+
feature: ["add", "build", "feature", "implement", "new", "support", "\u65B0\u589E", "\u5B9E\u73B0", "\u652F\u6301", "\u529F\u80FD"],
|
|
14016
|
+
release: ["bump", "changelog", "npm", "pack", "publish", "release", "version", "\u53D1\u7248", "\u53D1\u5E03", "\u7248\u672C"],
|
|
14017
|
+
onboarding: ["architecture", "handoff", "onboard", "overview", "understand", "\u63A5\u624B", "\u4E86\u89E3", "\u7406\u89E3", "\u67B6\u6784"],
|
|
14018
|
+
refactor: ["cleanup", "migrate", "refactor", "rename", "restructure", "\u91CD\u6784", "\u8FC1\u79FB", "\u6539\u9020"],
|
|
14019
|
+
docs: ["doc", "docs", "readme", "\u6587\u6863", "\u8BF4\u660E"],
|
|
14020
|
+
test: ["coverage", "fixture", "smoke", "spec", "test", "tests", "testing", "vitest", "\u6D4B\u8BD5"]
|
|
14021
|
+
};
|
|
14022
|
+
LENS_PRIORITY = [
|
|
14023
|
+
"bugfix",
|
|
14024
|
+
"release",
|
|
14025
|
+
"test",
|
|
14026
|
+
"refactor",
|
|
14027
|
+
"feature",
|
|
14028
|
+
"docs",
|
|
14029
|
+
"onboarding"
|
|
14030
|
+
];
|
|
14031
|
+
}
|
|
14032
|
+
});
|
|
14033
|
+
|
|
14034
|
+
// src/knowledge/workflows.ts
|
|
14035
|
+
var workflows_exports = {};
|
|
14036
|
+
__export(workflows_exports, {
|
|
14037
|
+
WORKFLOW_ADAPTER_TARGETS: () => WORKFLOW_ADAPTER_TARGETS,
|
|
14038
|
+
applyWorkflowAdapter: () => applyWorkflowAdapter,
|
|
14039
|
+
importWindsurfWorkflows: () => importWindsurfWorkflows,
|
|
14040
|
+
parseWorkflowMarkdown: () => parseWorkflowMarkdown,
|
|
14041
|
+
previewWorkflowAdapter: () => previewWorkflowAdapter,
|
|
14042
|
+
recordWorkflowRun: () => recordWorkflowRun,
|
|
14043
|
+
renderWorkflowMarkdown: () => renderWorkflowMarkdown,
|
|
14044
|
+
selectWorkflows: () => selectWorkflows,
|
|
14045
|
+
selectWorkspaceWorkflows: () => selectWorkspaceWorkflows,
|
|
14046
|
+
syncCanonicalWorkflows: () => syncCanonicalWorkflows,
|
|
14047
|
+
writeCanonicalWorkflow: () => writeCanonicalWorkflow
|
|
14048
|
+
});
|
|
14049
|
+
import { createHash as createHash12 } from "crypto";
|
|
14050
|
+
import { promises as fs14 } from "fs";
|
|
14051
|
+
import path17 from "path";
|
|
14052
|
+
import matter11 from "gray-matter";
|
|
14053
|
+
function hash3(value) {
|
|
14054
|
+
return createHash12("sha256").update(value).digest("hex");
|
|
14055
|
+
}
|
|
14056
|
+
function now2() {
|
|
14057
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
14058
|
+
}
|
|
14059
|
+
function slug(value) {
|
|
14060
|
+
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 72);
|
|
14061
|
+
return normalized || "workflow";
|
|
14062
|
+
}
|
|
14063
|
+
function titleFromName(name) {
|
|
14064
|
+
return name.replace(/[-_]+/g, " ").split(" ").filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ") || "Imported Workflow";
|
|
14065
|
+
}
|
|
14066
|
+
function requiredText(data, key) {
|
|
14067
|
+
const value = data[key];
|
|
14068
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
14069
|
+
throw new Error("workflow frontmatter requires " + key);
|
|
14070
|
+
}
|
|
14071
|
+
return value.trim();
|
|
14072
|
+
}
|
|
14073
|
+
function optionalText4(data, key) {
|
|
14074
|
+
const value = data[key];
|
|
14075
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
14076
|
+
if (typeof value !== "string") throw new Error("workflow frontmatter field " + key + " must be text");
|
|
14077
|
+
return value.trim() || void 0;
|
|
14078
|
+
}
|
|
14079
|
+
function optionalStringArray(data, key) {
|
|
14080
|
+
const value = data[key];
|
|
14081
|
+
if (value === void 0 || value === null) return [];
|
|
14082
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
14083
|
+
throw new Error("workflow frontmatter field " + key + " must be a string array");
|
|
14084
|
+
}
|
|
14085
|
+
return [...new Set(value.map((item) => item.trim()).filter(Boolean))];
|
|
14086
|
+
}
|
|
14087
|
+
function optionalNumber(data, key, fallback) {
|
|
14088
|
+
const value = data[key];
|
|
14089
|
+
if (value === void 0 || value === null || value === "") return fallback;
|
|
14090
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
14091
|
+
throw new Error("workflow frontmatter field " + key + " must be a positive integer");
|
|
14092
|
+
}
|
|
14093
|
+
return value;
|
|
14094
|
+
}
|
|
14095
|
+
function optionalStatus(data) {
|
|
14096
|
+
const value = data.status;
|
|
14097
|
+
if (value === void 0 || value === null || value === "") return "draft";
|
|
14098
|
+
if (typeof value !== "string" || !STATUS_VALUES.includes(value)) {
|
|
14099
|
+
throw new Error("workflow frontmatter field status is invalid");
|
|
14100
|
+
}
|
|
14101
|
+
return value;
|
|
14102
|
+
}
|
|
14103
|
+
function normalizeAgents(values) {
|
|
14104
|
+
const unknown = values.filter((value) => !KNOWN_AGENTS.includes(value));
|
|
14105
|
+
if (unknown.length > 0) {
|
|
14106
|
+
throw new Error("workflow frontmatter has unsupported agent: " + unknown[0]);
|
|
14107
|
+
}
|
|
14108
|
+
return values;
|
|
13402
14109
|
}
|
|
13403
14110
|
function phaseFromValue(value, index) {
|
|
13404
14111
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -13646,20 +14353,27 @@ function taskScore(workflow, task) {
|
|
|
13646
14353
|
const text = task.toLowerCase();
|
|
13647
14354
|
let score = 0;
|
|
13648
14355
|
const reasons = [];
|
|
14356
|
+
const matchedLenses = /* @__PURE__ */ new Set();
|
|
14357
|
+
const matchedTriggers = /* @__PURE__ */ new Set();
|
|
13649
14358
|
for (const lens of workflow.taskLenses) {
|
|
13650
14359
|
const terms = LENS_TERMS[lens.toLowerCase()] ?? [lens.toLowerCase()];
|
|
13651
|
-
if (terms.some((term) =>
|
|
14360
|
+
if (terms.some((term) => containsTaskKeyword(task, term))) {
|
|
13652
14361
|
score += 20;
|
|
13653
14362
|
reasons.push("matches " + lens + " workflow");
|
|
14363
|
+
matchedLenses.add(lens.toLowerCase());
|
|
13654
14364
|
}
|
|
13655
14365
|
}
|
|
13656
14366
|
for (const trigger of workflow.triggers) {
|
|
13657
14367
|
const term = trigger.toLowerCase().trim();
|
|
13658
|
-
if (term.length >= 2 &&
|
|
14368
|
+
if (term.length >= 2 && containsTaskKeyword(task, term)) {
|
|
13659
14369
|
score += 8;
|
|
13660
14370
|
reasons.push('matches trigger "' + trigger + '"');
|
|
14371
|
+
matchedTriggers.add(term);
|
|
13661
14372
|
}
|
|
13662
14373
|
}
|
|
14374
|
+
const hasSpecificLens = workflow.taskLenses.some((lens) => !GENERIC_WORKFLOW_LENSES.has(lens.toLowerCase()));
|
|
14375
|
+
const hasSpecificMatch = [...matchedLenses].some((lens) => !GENERIC_WORKFLOW_LENSES.has(lens)) || [...matchedTriggers].some((trigger) => !GENERIC_WORKFLOW_TERMS.has(trigger));
|
|
14376
|
+
if (hasSpecificLens && !hasSpecificMatch) return { score: 0, reasons: [] };
|
|
13663
14377
|
return { score, reasons: [...new Set(reasons)] };
|
|
13664
14378
|
}
|
|
13665
14379
|
function selectWorkflows(input) {
|
|
@@ -13677,9 +14391,28 @@ function selectWorkflows(input) {
|
|
|
13677
14391
|
return selected.slice(0, Math.max(1, Math.min(input.limit ?? 2, 2)));
|
|
13678
14392
|
}
|
|
13679
14393
|
function importedWorkflowSpec(input) {
|
|
14394
|
+
let sourceMatter;
|
|
14395
|
+
try {
|
|
14396
|
+
sourceMatter = matter11(input.raw);
|
|
14397
|
+
} catch (error) {
|
|
14398
|
+
throw new Error("Malformed Windsurf workflow Markdown: " + (error instanceof Error ? error.message : String(error)));
|
|
14399
|
+
}
|
|
14400
|
+
if (sourceMatter.data.id !== void 0) {
|
|
14401
|
+
const id = requiredText(sourceMatter.data, "id");
|
|
14402
|
+
const sourcePath2 = "workflows/" + slug(id) + ".md";
|
|
14403
|
+
const canonical = parsedWorkflow(sourceMatter.data, sourceMatter.content, {
|
|
14404
|
+
workspaceId: input.workspace.id,
|
|
14405
|
+
sourcePath: sourcePath2,
|
|
14406
|
+
contentHash: hash3(input.raw)
|
|
14407
|
+
});
|
|
14408
|
+
return materializeWorkflow({
|
|
14409
|
+
...canonical,
|
|
14410
|
+
importedFrom: input.sourcePath.replace(/\\/g, "/")
|
|
14411
|
+
});
|
|
14412
|
+
}
|
|
13680
14413
|
const entry = new WorkflowSyncer().parseWindsurfWorkflow(input.sourceName, input.raw);
|
|
13681
14414
|
const content = entry.content.trim() || "## Execute\n\nFollow the imported workflow.";
|
|
13682
|
-
const lenses = inferTaskLenses(entry.name + "\n" + entry.description
|
|
14415
|
+
const lenses = inferTaskLenses(entry.name + "\n" + entry.description);
|
|
13683
14416
|
const createdAt = now2();
|
|
13684
14417
|
const sourcePath = "workflows/" + slug(entry.name) + ".md";
|
|
13685
14418
|
const spec = {
|
|
@@ -13900,7 +14633,7 @@ async function selectWorkspaceWorkflows(input) {
|
|
|
13900
14633
|
errors: synced.errors
|
|
13901
14634
|
};
|
|
13902
14635
|
}
|
|
13903
|
-
var KNOWN_AGENTS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS;
|
|
14636
|
+
var KNOWN_AGENTS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS, GENERIC_WORKFLOW_LENSES, GENERIC_WORKFLOW_TERMS;
|
|
13904
14637
|
var init_workflows = __esm({
|
|
13905
14638
|
"src/knowledge/workflows.ts"() {
|
|
13906
14639
|
"use strict";
|
|
@@ -13909,6 +14642,7 @@ var init_workflows = __esm({
|
|
|
13909
14642
|
init_workflow_sync();
|
|
13910
14643
|
init_workspace();
|
|
13911
14644
|
init_workflow_store();
|
|
14645
|
+
init_task_lens();
|
|
13912
14646
|
KNOWN_AGENTS = [
|
|
13913
14647
|
"windsurf",
|
|
13914
14648
|
"cursor",
|
|
@@ -13940,6 +14674,8 @@ var init_workflows = __esm({
|
|
|
13940
14674
|
refactor: ["refactor", "cleanup", "restructure", "\u91CD\u6784", "\u6574\u7406"],
|
|
13941
14675
|
test: ["test", "verify", "smoke", "\u6D4B\u8BD5", "\u9A8C\u8BC1"]
|
|
13942
14676
|
};
|
|
14677
|
+
GENERIC_WORKFLOW_LENSES = /* @__PURE__ */ new Set(["review", "test"]);
|
|
14678
|
+
GENERIC_WORKFLOW_TERMS = /* @__PURE__ */ new Set(["review", "audit", "test", "verify", "smoke"]);
|
|
13943
14679
|
}
|
|
13944
14680
|
});
|
|
13945
14681
|
|
|
@@ -13986,6 +14722,15 @@ function claimDerivationCursor(payload) {
|
|
|
13986
14722
|
const value = payload.cursor;
|
|
13987
14723
|
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
13988
14724
|
}
|
|
14725
|
+
function observationQualificationBatchSize(payload) {
|
|
14726
|
+
const value = payload.limit;
|
|
14727
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE;
|
|
14728
|
+
return Math.min(500, Math.max(1, Math.floor(value)));
|
|
14729
|
+
}
|
|
14730
|
+
function observationQualificationCursor(payload) {
|
|
14731
|
+
const value = payload.cursor;
|
|
14732
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
14733
|
+
}
|
|
13989
14734
|
function observationId(payload) {
|
|
13990
14735
|
const value = payload.observationId;
|
|
13991
14736
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : void 0;
|
|
@@ -14164,6 +14909,71 @@ function createProjectMaintenanceHandler(projectId, projectDir2, projectRoot, op
|
|
|
14164
14909
|
...snapshot?.id ? { snapshotId: snapshot.id } : {},
|
|
14165
14910
|
...options.maintenanceQueue ? { queue: options.maintenanceQueue } : {}
|
|
14166
14911
|
});
|
|
14912
|
+
enqueueObservationQualification({
|
|
14913
|
+
projectId,
|
|
14914
|
+
dataDir: projectDir2,
|
|
14915
|
+
source: "codegraph-refresh",
|
|
14916
|
+
...options.maintenanceQueue ? { queue: options.maintenanceQueue } : {}
|
|
14917
|
+
});
|
|
14918
|
+
return { action: "complete" };
|
|
14919
|
+
}
|
|
14920
|
+
if (job.kind === "observation-qualify") {
|
|
14921
|
+
const [
|
|
14922
|
+
{ CodeGraphStore: CodeGraphStore2 },
|
|
14923
|
+
{ bindObservationToCode: bindObservationToCode2 },
|
|
14924
|
+
{ getObservationStore: getObservationStore2 },
|
|
14925
|
+
{ qualifyCandidateFromCurrentCode: qualifyCandidateFromCurrentCode2 },
|
|
14926
|
+
{ updateObservationAdmission: updateObservationAdmission2 }
|
|
14927
|
+
] = await Promise.all([
|
|
14928
|
+
Promise.resolve().then(() => (init_store(), store_exports)),
|
|
14929
|
+
Promise.resolve().then(() => (init_binder(), binder_exports)),
|
|
14930
|
+
Promise.resolve().then(() => (init_obs_store(), obs_store_exports)),
|
|
14931
|
+
Promise.resolve().then(() => (init_admission(), admission_exports)),
|
|
14932
|
+
Promise.resolve().then(() => (init_observations(), observations_exports))
|
|
14933
|
+
]);
|
|
14934
|
+
const codeStore = new CodeGraphStore2();
|
|
14935
|
+
await codeStore.init(projectDir2);
|
|
14936
|
+
const limit = observationQualificationBatchSize(job.payload);
|
|
14937
|
+
const page = await getObservationStore2().loadByProject(projectId, {
|
|
14938
|
+
status: "active",
|
|
14939
|
+
afterId: observationQualificationCursor(job.payload),
|
|
14940
|
+
limit: limit + 1
|
|
14941
|
+
});
|
|
14942
|
+
const hasMore = page.length > limit;
|
|
14943
|
+
const observations2 = hasMore ? page.slice(0, limit) : page;
|
|
14944
|
+
const indexedAtMs = Date.parse(codeStore.status(projectId).indexedAt ?? "");
|
|
14945
|
+
for (const observation of observations2) {
|
|
14946
|
+
if (observation.admissionState !== "candidate") continue;
|
|
14947
|
+
if (!Number.isFinite(indexedAtMs) || indexedAtMs < Date.parse(observation.createdAt)) continue;
|
|
14948
|
+
await bindObservationToCode2(codeStore, observation);
|
|
14949
|
+
const currentCodeReferenceCount = codeStore.listObservationRefs(projectId, observation.id).filter((reference) => reference.status === "current").length;
|
|
14950
|
+
const qualification = qualifyCandidateFromCurrentCode2({
|
|
14951
|
+
observation,
|
|
14952
|
+
currentCodeReferenceCount
|
|
14953
|
+
});
|
|
14954
|
+
if (!qualification) continue;
|
|
14955
|
+
await updateObservationAdmission2({
|
|
14956
|
+
observationId: observation.id,
|
|
14957
|
+
projectId,
|
|
14958
|
+
expectedState: "candidate",
|
|
14959
|
+
// Automatic capture earns project visibility only after a current
|
|
14960
|
+
// Code Memory link exists. Explicit/private records retain scope.
|
|
14961
|
+
...observation.sourceDetail === "hook" ? { visibility: "project" } : {},
|
|
14962
|
+
...qualification
|
|
14963
|
+
});
|
|
14964
|
+
}
|
|
14965
|
+
if (hasMore && observations2.length > 0) {
|
|
14966
|
+
return {
|
|
14967
|
+
action: "reschedule",
|
|
14968
|
+
delayMs: 0,
|
|
14969
|
+
resetAttempts: true,
|
|
14970
|
+
payload: {
|
|
14971
|
+
...job.payload,
|
|
14972
|
+
cursor: observations2[observations2.length - 1].id,
|
|
14973
|
+
limit
|
|
14974
|
+
}
|
|
14975
|
+
};
|
|
14976
|
+
}
|
|
14167
14977
|
return { action: "complete" };
|
|
14168
14978
|
}
|
|
14169
14979
|
if (job.kind === "claim-derive") {
|
|
@@ -14329,7 +15139,7 @@ function createProjectMaintenanceDispatcher(projectId, projectDir2, projectRoot,
|
|
|
14329
15139
|
return isolatedRunner({ job, projectRoot, dataDir: projectDir2 });
|
|
14330
15140
|
};
|
|
14331
15141
|
}
|
|
14332
|
-
var DEFAULT_VECTOR_BATCH_SIZE, DEFAULT_RETENTION_BATCH_SIZE, DEFAULT_CONSOLIDATION_BATCH_SIZE, DEFAULT_CODEGRAPH_MAX_FILES, DEFAULT_CLAIM_DERIVATION_BATCH_SIZE, VECTOR_RETRY_DELAY_MS, DEDUP_PER_PAIR_TIMEOUT_MS;
|
|
15142
|
+
var DEFAULT_VECTOR_BATCH_SIZE, DEFAULT_RETENTION_BATCH_SIZE, DEFAULT_CONSOLIDATION_BATCH_SIZE, DEFAULT_CODEGRAPH_MAX_FILES, DEFAULT_CLAIM_DERIVATION_BATCH_SIZE, DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE, VECTOR_RETRY_DELAY_MS, DEDUP_PER_PAIR_TIMEOUT_MS;
|
|
14333
15143
|
var init_project_maintenance = __esm({
|
|
14334
15144
|
"src/runtime/project-maintenance.ts"() {
|
|
14335
15145
|
"use strict";
|
|
@@ -14341,6 +15151,7 @@ var init_project_maintenance = __esm({
|
|
|
14341
15151
|
DEFAULT_CONSOLIDATION_BATCH_SIZE = 200;
|
|
14342
15152
|
DEFAULT_CODEGRAPH_MAX_FILES = 5e3;
|
|
14343
15153
|
DEFAULT_CLAIM_DERIVATION_BATCH_SIZE = 100;
|
|
15154
|
+
DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE = 100;
|
|
14344
15155
|
VECTOR_RETRY_DELAY_MS = 5e3;
|
|
14345
15156
|
DEDUP_PER_PAIR_TIMEOUT_MS = 5e3;
|
|
14346
15157
|
}
|
|
@@ -14582,9 +15393,9 @@ async function getSessionContext(projectDir2, projectId, limit = 3) {
|
|
|
14582
15393
|
return true;
|
|
14583
15394
|
});
|
|
14584
15395
|
})() : l2Scored).slice(0, 5).map(({ obs }) => obs);
|
|
14585
|
-
const l1HookObs = projectObs.filter((obs) => classifyLayer(obs) === "L1").sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, 3);
|
|
15396
|
+
const l1HookObs = projectObs.filter((obs) => isEligibleForAutomaticDelivery(obs) && classifyLayer(obs) === "L1").sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, 3);
|
|
14586
15397
|
const l3GitCount = projectObs.filter((obs) => classifyLayer(obs) === "L3").length;
|
|
14587
|
-
const totalHookCount = projectObs.filter((obs) => classifyLayer(obs) === "L1").length;
|
|
15398
|
+
const totalHookCount = projectObs.filter((obs) => isEligibleForAutomaticDelivery(obs) && classifyLayer(obs) === "L1").length;
|
|
14588
15399
|
const activeEntities = [
|
|
14589
15400
|
...new Set(l2Obs.map((o) => o.entityName).filter((n) => !!n && n.trim().length > 0))
|
|
14590
15401
|
].slice(0, 5);
|
|
@@ -14710,6 +15521,7 @@ var init_session = __esm({
|
|
|
14710
15521
|
"src/memory/session.ts"() {
|
|
14711
15522
|
"use strict";
|
|
14712
15523
|
init_esm_shims();
|
|
15524
|
+
init_admission();
|
|
14713
15525
|
init_disclosure_policy();
|
|
14714
15526
|
init_aliases();
|
|
14715
15527
|
init_obs_store();
|
|
@@ -14862,27 +15674,80 @@ function workflowOutput(selection) {
|
|
|
14862
15674
|
cautions: selection.cautions.map((caution) => short(caution, 22))
|
|
14863
15675
|
};
|
|
14864
15676
|
}
|
|
14865
|
-
function appendLine(lines, candidate, maxTokens, omitted, omittedKind) {
|
|
15677
|
+
function appendLine(lines, candidate, maxTokens, omitted, omittedKind, selected, receiptSelection) {
|
|
14866
15678
|
const next = lines.length ? lines.join("\n") + "\n" + candidate : candidate;
|
|
14867
15679
|
if (countTextTokens(next) <= maxTokens) {
|
|
14868
15680
|
lines.push(candidate);
|
|
15681
|
+
if (receiptSelection) selected?.push(receiptSelection);
|
|
14869
15682
|
return true;
|
|
14870
15683
|
}
|
|
14871
15684
|
omitted.push(omittedKind);
|
|
14872
15685
|
return false;
|
|
14873
15686
|
}
|
|
15687
|
+
function freshnessForMemory(status) {
|
|
15688
|
+
if (status === "current" || status === "suspect" || status === "stale") return status;
|
|
15689
|
+
return "unknown";
|
|
15690
|
+
}
|
|
15691
|
+
function receiptOmissionKind(raw) {
|
|
15692
|
+
if (raw.includes("task")) return "task";
|
|
15693
|
+
if (raw.includes("fact")) return "current-fact";
|
|
15694
|
+
if (raw.includes("state")) return "code-state";
|
|
15695
|
+
if (raw.includes("semantic")) return "semantic-code";
|
|
15696
|
+
if (raw.includes("start")) return "start-here";
|
|
15697
|
+
if (raw.includes("memory")) return "memory";
|
|
15698
|
+
if (raw.includes("claim")) return "claim";
|
|
15699
|
+
if (raw.includes("knowledge-page")) return "knowledge-page";
|
|
15700
|
+
if (raw.includes("workflow")) return "workflow";
|
|
15701
|
+
if (raw.includes("verification")) return "verification";
|
|
15702
|
+
if (raw.includes("caution")) return "caution";
|
|
15703
|
+
return void 0;
|
|
15704
|
+
}
|
|
15705
|
+
function receiptOmissions(omitted, hiddenCautionMemoryCount) {
|
|
15706
|
+
const counts = /* @__PURE__ */ new Map();
|
|
15707
|
+
for (const raw of omitted) {
|
|
15708
|
+
const kind = receiptOmissionKind(raw);
|
|
15709
|
+
if (!kind || raw.endsWith("-heading")) continue;
|
|
15710
|
+
counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
15711
|
+
}
|
|
15712
|
+
const receipt = [...counts.entries()].map(([kind, count2]) => ({
|
|
15713
|
+
kind,
|
|
15714
|
+
reason: "token-budget",
|
|
15715
|
+
count: count2
|
|
15716
|
+
}));
|
|
15717
|
+
if (hiddenCautionMemoryCount > 0) {
|
|
15718
|
+
receipt.push({
|
|
15719
|
+
kind: "caution",
|
|
15720
|
+
reason: "hidden-by-task-lens",
|
|
15721
|
+
count: hiddenCautionMemoryCount
|
|
15722
|
+
});
|
|
15723
|
+
}
|
|
15724
|
+
return receipt;
|
|
15725
|
+
}
|
|
15726
|
+
function scheduledActions(cautions) {
|
|
15727
|
+
return cautions.filter((caution) => caution.kind === "codegraph-refresh-queued").map((caution) => short(caution.message, 28));
|
|
15728
|
+
}
|
|
14874
15729
|
function renderTaskWorksetPrompt(input) {
|
|
14875
15730
|
const maxTokens = input.budget?.maxTokens ?? 180;
|
|
14876
15731
|
const omitted = [];
|
|
15732
|
+
const selected = [];
|
|
14877
15733
|
const lines = ["Memorix Autopilot Brief"];
|
|
14878
15734
|
const task = short(input.task || "Continue the current task.", 34);
|
|
14879
|
-
appendLine(lines, "Task: " + task, maxTokens, omitted, "task-detail"
|
|
15735
|
+
appendLine(lines, "Task: " + task, maxTokens, omitted, "task-detail", selected, {
|
|
15736
|
+
kind: "task",
|
|
15737
|
+
reason: "current task supplied by the caller",
|
|
15738
|
+
trust: "source-backed"
|
|
15739
|
+
});
|
|
14880
15740
|
appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
|
|
14881
15741
|
if (input.cautions.length > 0 || input.cautionMemory.length > 0) {
|
|
14882
15742
|
appendLine(lines, "", maxTokens, omitted, "caution-heading");
|
|
14883
15743
|
appendLine(lines, "Cautions", maxTokens, omitted, "caution-heading");
|
|
14884
15744
|
for (const caution of input.cautions.slice(0, 6)) {
|
|
14885
|
-
appendLine(lines, "- " + short(caution.message, 22), maxTokens, omitted, "caution"
|
|
15745
|
+
appendLine(lines, "- " + short(caution.message, 22), maxTokens, omitted, "caution", selected, {
|
|
15746
|
+
kind: "caution",
|
|
15747
|
+
id: "caution:" + caution.kind,
|
|
15748
|
+
reason: "current project caution",
|
|
15749
|
+
trust: "source-backed"
|
|
15750
|
+
});
|
|
14886
15751
|
}
|
|
14887
15752
|
for (const memory of input.cautionMemory.slice(0, 3)) {
|
|
14888
15753
|
const location = memory.path ? memory.path + (memory.symbol ? "#" + memory.symbol : "") : "no current code location";
|
|
@@ -14892,7 +15757,15 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14892
15757
|
"- #" + memory.id + " " + memory.status + ": " + short(memory.title, 18) + " (" + location + reason + ")",
|
|
14893
15758
|
maxTokens,
|
|
14894
15759
|
omitted,
|
|
14895
|
-
"caution-memory"
|
|
15760
|
+
"caution-memory",
|
|
15761
|
+
selected,
|
|
15762
|
+
{
|
|
15763
|
+
kind: "memory",
|
|
15764
|
+
id: "memory:" + memory.id,
|
|
15765
|
+
reason: "task-relevant memory requiring source verification",
|
|
15766
|
+
freshness: freshnessForMemory(memory.status),
|
|
15767
|
+
trust: "historical"
|
|
15768
|
+
}
|
|
14896
15769
|
);
|
|
14897
15770
|
}
|
|
14898
15771
|
if (input.hiddenCautionMemoryCount > 0) {
|
|
@@ -14903,13 +15776,22 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14903
15776
|
appendLine(lines, "", maxTokens, omitted, "facts-heading");
|
|
14904
15777
|
appendLine(lines, "Current project facts", maxTokens, omitted, "facts-heading");
|
|
14905
15778
|
for (const fact of input.currentFacts.slice(0, 4)) {
|
|
14906
|
-
appendLine(lines, "- " + short(fact, 40), maxTokens, omitted, "current-fact"
|
|
15779
|
+
appendLine(lines, "- " + short(fact, 40), maxTokens, omitted, "current-fact", selected, {
|
|
15780
|
+
kind: "current-fact",
|
|
15781
|
+
reason: "current project state",
|
|
15782
|
+
trust: "source-backed"
|
|
15783
|
+
});
|
|
14907
15784
|
}
|
|
14908
15785
|
}
|
|
14909
15786
|
if (input.codeState) {
|
|
14910
15787
|
appendLine(lines, "", maxTokens, omitted, "state-heading");
|
|
14911
15788
|
appendLine(lines, "Project state", maxTokens, omitted, "state-heading");
|
|
14912
|
-
appendLine(lines, input.codeState, maxTokens, omitted, "code-state"
|
|
15789
|
+
appendLine(lines, input.codeState, maxTokens, omitted, "code-state", selected, {
|
|
15790
|
+
kind: "code-state",
|
|
15791
|
+
...input.provenance.snapshotId ? { id: "snapshot:" + input.provenance.snapshotId } : {},
|
|
15792
|
+
reason: "latest available Code State snapshot",
|
|
15793
|
+
trust: "source-backed"
|
|
15794
|
+
});
|
|
14913
15795
|
}
|
|
14914
15796
|
if (input.semanticCode && (input.semanticCode.entryPoints.length > 0 || input.semanticCode.relations.length > 0)) {
|
|
14915
15797
|
appendLine(lines, "", maxTokens, omitted, "semantic-code-heading");
|
|
@@ -14921,7 +15803,14 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14921
15803
|
"- " + location + ": " + short(relation.from.name, 12) + " " + short(relation.kind, 8) + " " + short(relation.to.name, 12),
|
|
14922
15804
|
maxTokens,
|
|
14923
15805
|
omitted,
|
|
14924
|
-
"semantic-relation"
|
|
15806
|
+
"semantic-relation",
|
|
15807
|
+
selected,
|
|
15808
|
+
{
|
|
15809
|
+
kind: "semantic-code",
|
|
15810
|
+
id: "code:" + location,
|
|
15811
|
+
reason: "validated optional semantic code relation",
|
|
15812
|
+
trust: "derived"
|
|
15813
|
+
}
|
|
14925
15814
|
);
|
|
14926
15815
|
}
|
|
14927
15816
|
if (input.semanticCode.relations.length === 0) {
|
|
@@ -14932,7 +15821,14 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14932
15821
|
"- " + location + ": " + short(entry.name, 16) + " (" + short(entry.kind, 8) + ")",
|
|
14933
15822
|
maxTokens,
|
|
14934
15823
|
omitted,
|
|
14935
|
-
"semantic-entry"
|
|
15824
|
+
"semantic-entry",
|
|
15825
|
+
selected,
|
|
15826
|
+
{
|
|
15827
|
+
kind: "semantic-code",
|
|
15828
|
+
id: "code:" + location,
|
|
15829
|
+
reason: "validated optional semantic code entry point",
|
|
15830
|
+
trust: "derived"
|
|
15831
|
+
}
|
|
14936
15832
|
);
|
|
14937
15833
|
}
|
|
14938
15834
|
}
|
|
@@ -14941,7 +15837,12 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14941
15837
|
appendLine(lines, "", maxTokens, omitted, "start-heading");
|
|
14942
15838
|
appendLine(lines, "Start here", maxTokens, omitted, "start-heading");
|
|
14943
15839
|
for (const source of input.startHere.slice(0, 5)) {
|
|
14944
|
-
appendLine(lines, "- " + source, maxTokens, omitted, "start-here"
|
|
15840
|
+
appendLine(lines, "- " + source, maxTokens, omitted, "start-here", selected, {
|
|
15841
|
+
kind: "start-here",
|
|
15842
|
+
id: "path:" + source,
|
|
15843
|
+
reason: "task-lensed starting point",
|
|
15844
|
+
trust: "derived"
|
|
15845
|
+
});
|
|
14945
15846
|
}
|
|
14946
15847
|
}
|
|
14947
15848
|
if (input.reliableMemory.length > 0) {
|
|
@@ -14954,7 +15855,15 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14954
15855
|
"- #" + memory.id + " " + memory.type + ": " + short(memory.title, 18) + " (" + location + ")",
|
|
14955
15856
|
maxTokens,
|
|
14956
15857
|
omitted,
|
|
14957
|
-
"reliable-memory"
|
|
15858
|
+
"reliable-memory",
|
|
15859
|
+
selected,
|
|
15860
|
+
{
|
|
15861
|
+
kind: "memory",
|
|
15862
|
+
id: "memory:" + memory.id,
|
|
15863
|
+
reason: "current code-bound memory",
|
|
15864
|
+
freshness: freshnessForMemory(memory.status),
|
|
15865
|
+
trust: "historical"
|
|
15866
|
+
}
|
|
14958
15867
|
);
|
|
14959
15868
|
}
|
|
14960
15869
|
}
|
|
@@ -14962,10 +15871,25 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14962
15871
|
appendLine(lines, "", maxTokens, omitted, "knowledge-heading");
|
|
14963
15872
|
appendLine(lines, "Project knowledge", maxTokens, omitted, "knowledge-heading");
|
|
14964
15873
|
for (const claim of input.claims.slice(0, 3)) {
|
|
14965
|
-
appendLine(lines, "- " + claim.assertion + " [" + claim.id + "]", maxTokens, omitted, "claim"
|
|
15874
|
+
appendLine(lines, "- " + claim.assertion + " [" + claim.id + "]", maxTokens, omitted, "claim", selected, {
|
|
15875
|
+
kind: "claim",
|
|
15876
|
+
id: "claim:" + claim.id,
|
|
15877
|
+
reason: "source-qualified task match",
|
|
15878
|
+
trust: "source-backed"
|
|
15879
|
+
});
|
|
14966
15880
|
}
|
|
14967
15881
|
for (const page of input.pages.slice(0, 2)) {
|
|
14968
|
-
|
|
15882
|
+
const supportsDeliveredClaim = page.claimIds.some((claimId) => selected.some((item) => item.kind === "claim" && item.id === "claim:" + claimId));
|
|
15883
|
+
if (!supportsDeliveredClaim) {
|
|
15884
|
+
omitted.push("knowledge-page-dependency");
|
|
15885
|
+
continue;
|
|
15886
|
+
}
|
|
15887
|
+
appendLine(lines, "- page: " + page.relativePath, maxTokens, omitted, "knowledge-page", selected, {
|
|
15888
|
+
kind: "knowledge-page",
|
|
15889
|
+
id: "page:" + page.id,
|
|
15890
|
+
reason: "approved page linked to a selected claim",
|
|
15891
|
+
trust: "source-backed"
|
|
15892
|
+
});
|
|
14969
15893
|
}
|
|
14970
15894
|
}
|
|
14971
15895
|
if (input.workflows.length > 0) {
|
|
@@ -14977,7 +15901,14 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14977
15901
|
"- " + workflow.title + ": " + workflow.firstPhase.title + " - " + workflow.firstPhase.instructions,
|
|
14978
15902
|
maxTokens,
|
|
14979
15903
|
omitted,
|
|
14980
|
-
"workflow"
|
|
15904
|
+
"workflow",
|
|
15905
|
+
selected,
|
|
15906
|
+
{
|
|
15907
|
+
kind: "workflow",
|
|
15908
|
+
id: "workflow:" + workflow.id,
|
|
15909
|
+
reason: "task-matching project workflow",
|
|
15910
|
+
trust: "source-backed"
|
|
15911
|
+
}
|
|
14981
15912
|
);
|
|
14982
15913
|
}
|
|
14983
15914
|
}
|
|
@@ -14985,16 +15916,23 @@ function renderTaskWorksetPrompt(input) {
|
|
|
14985
15916
|
appendLine(lines, "", maxTokens, omitted, "verification-heading");
|
|
14986
15917
|
appendLine(lines, "Verify", maxTokens, omitted, "verification-heading");
|
|
14987
15918
|
for (const check of input.verification.slice(0, 4)) {
|
|
14988
|
-
appendLine(lines, "- " + short(check, 20), maxTokens, omitted, "verification"
|
|
15919
|
+
appendLine(lines, "- " + short(check, 20), maxTokens, omitted, "verification", selected, {
|
|
15920
|
+
kind: "verification",
|
|
15921
|
+
reason: "task-lensed verification guidance",
|
|
15922
|
+
trust: "derived"
|
|
15923
|
+
});
|
|
14989
15924
|
}
|
|
14990
15925
|
}
|
|
14991
15926
|
return {
|
|
14992
15927
|
prompt: lines.join("\n"),
|
|
14993
15928
|
tokenCount: countTextTokens(lines.join("\n")),
|
|
14994
|
-
omitted: unique(omitted)
|
|
15929
|
+
omitted: unique(omitted),
|
|
15930
|
+
omittedItems: omitted,
|
|
15931
|
+
selected
|
|
14995
15932
|
};
|
|
14996
15933
|
}
|
|
14997
15934
|
async function buildTaskWorkset(input) {
|
|
15935
|
+
const startedAt = Date.now();
|
|
14998
15936
|
const task = input.task?.trim() ?? "";
|
|
14999
15937
|
const maxTokens = Math.max(96, Math.min(Math.floor(input.maxTokens ?? 180), 320));
|
|
15000
15938
|
const cautions = [...input.runtimeCautions ?? [], ...snapshotCautions(input)];
|
|
@@ -15096,6 +16034,18 @@ async function buildTaskWorkset(input) {
|
|
|
15096
16034
|
...base,
|
|
15097
16035
|
budget: { maxTokens }
|
|
15098
16036
|
});
|
|
16037
|
+
const receipt = {
|
|
16038
|
+
version: "1.2.2",
|
|
16039
|
+
target: input.deliveryTarget ?? "project-context",
|
|
16040
|
+
elapsedMs: Math.max(0, Date.now() - startedAt),
|
|
16041
|
+
budget: {
|
|
16042
|
+
maxTokens,
|
|
16043
|
+
tokenCount: rendered.tokenCount
|
|
16044
|
+
},
|
|
16045
|
+
selected: rendered.selected,
|
|
16046
|
+
omitted: receiptOmissions(rendered.omittedItems, base.hiddenCautionMemoryCount),
|
|
16047
|
+
scheduledActions: scheduledActions(normalizedCautions)
|
|
16048
|
+
};
|
|
15099
16049
|
return {
|
|
15100
16050
|
...base,
|
|
15101
16051
|
budget: {
|
|
@@ -15103,6 +16053,7 @@ async function buildTaskWorkset(input) {
|
|
|
15103
16053
|
tokenCount: rendered.tokenCount,
|
|
15104
16054
|
omitted: rendered.omitted
|
|
15105
16055
|
},
|
|
16056
|
+
receipt,
|
|
15106
16057
|
prompt: rendered.prompt
|
|
15107
16058
|
};
|
|
15108
16059
|
}
|
|
@@ -15124,7 +16075,8 @@ var init_workset = __esm({
|
|
|
15124
16075
|
// src/codegraph/current-facts.ts
|
|
15125
16076
|
var current_facts_exports = {};
|
|
15126
16077
|
__export(current_facts_exports, {
|
|
15127
|
-
collectCurrentProjectFacts: () => collectCurrentProjectFacts
|
|
16078
|
+
collectCurrentProjectFacts: () => collectCurrentProjectFacts,
|
|
16079
|
+
formatGitFact: () => formatGitFact
|
|
15128
16080
|
});
|
|
15129
16081
|
import { execFileSync } from "child_process";
|
|
15130
16082
|
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
@@ -15169,11 +16121,16 @@ function runGit2(rootPath, args) {
|
|
|
15169
16121
|
}
|
|
15170
16122
|
}
|
|
15171
16123
|
function readGitFacts(rootPath) {
|
|
15172
|
-
const
|
|
16124
|
+
const available = runGit2(rootPath, ["rev-parse", "--is-inside-work-tree"]) === "true";
|
|
16125
|
+
if (!available) {
|
|
16126
|
+
return { available: false, dirty: false, detached: false };
|
|
16127
|
+
}
|
|
16128
|
+
const branch = runGit2(rootPath, ["symbolic-ref", "--quiet", "--short", "HEAD"]) ?? runGit2(rootPath, ["branch", "--show-current"]);
|
|
15173
16129
|
const commit = runGit2(rootPath, ["rev-parse", "--short", "HEAD"]);
|
|
15174
16130
|
const latestCommit = runGit2(rootPath, ["log", "-1", "--pretty=%s"]);
|
|
15175
16131
|
const dirty = Boolean(runGit2(rootPath, ["status", "--porcelain"]));
|
|
15176
16132
|
return {
|
|
16133
|
+
available: true,
|
|
15177
16134
|
...branch ? { branch } : {},
|
|
15178
16135
|
...commit ? { commit } : {},
|
|
15179
16136
|
...latestCommit ? { latestCommit } : {},
|
|
@@ -15181,6 +16138,15 @@ function readGitFacts(rootPath) {
|
|
|
15181
16138
|
detached: !branch
|
|
15182
16139
|
};
|
|
15183
16140
|
}
|
|
16141
|
+
function formatGitFact(git) {
|
|
16142
|
+
if (!git.available) return "Git: unavailable";
|
|
16143
|
+
const parts = [];
|
|
16144
|
+
if (git.detached) parts.push("detached HEAD");
|
|
16145
|
+
else if (git.branch) parts.push("branch " + git.branch);
|
|
16146
|
+
if (git.commit) parts.push("commit " + git.commit);
|
|
16147
|
+
parts.push(git.dirty ? "dirty worktree" : "clean worktree");
|
|
16148
|
+
return "Git: " + parts.join(", ");
|
|
16149
|
+
}
|
|
15184
16150
|
function parseProgressNote(content) {
|
|
15185
16151
|
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);
|
|
15186
16152
|
const branchHint = content.match(/Branch\*\*:\s*([^\r\n]+)/i) ?? content.match(/Branch:\s*([^\r\n]+)/i);
|
|
@@ -15791,376 +16757,63 @@ function collectGraph(store, projectId, observations2, exclude) {
|
|
|
15791
16757
|
status: "unbound",
|
|
15792
16758
|
reason: "Recorded file hint; Code Memory refresh is pending."
|
|
15793
16759
|
});
|
|
15794
|
-
freshness.unbound++;
|
|
15795
|
-
}
|
|
15796
|
-
}
|
|
15797
|
-
}
|
|
15798
|
-
return {
|
|
15799
|
-
files,
|
|
15800
|
-
symbols,
|
|
15801
|
-
refs,
|
|
15802
|
-
freshness,
|
|
15803
|
-
sources,
|
|
15804
|
-
suggestedReads: compactSuggestedReads(suggestedReads, 8, exclude)
|
|
15805
|
-
};
|
|
15806
|
-
}
|
|
15807
|
-
function overviewFromGraph(input) {
|
|
15808
|
-
const status = input.store.status(input.project.id);
|
|
15809
|
-
return {
|
|
15810
|
-
project: input.project,
|
|
15811
|
-
code: {
|
|
15812
|
-
provider: status.provider,
|
|
15813
|
-
files: status.files,
|
|
15814
|
-
symbols: status.symbols,
|
|
15815
|
-
edges: status.edges,
|
|
15816
|
-
refs: status.refs,
|
|
15817
|
-
...status.indexedAt ? { indexedAt: status.indexedAt } : {},
|
|
15818
|
-
languages: countLanguages(input.graph.files),
|
|
15819
|
-
...status.latestSnapshot ? { latestSnapshot: status.latestSnapshot } : {}
|
|
15820
|
-
},
|
|
15821
|
-
memory: {
|
|
15822
|
-
total: input.observations.filter((obs) => obs.projectId === input.project.id).length,
|
|
15823
|
-
active: input.active.length
|
|
15824
|
-
},
|
|
15825
|
-
freshness: input.graph.freshness,
|
|
15826
|
-
suggestedReads: input.graph.suggestedReads
|
|
15827
|
-
};
|
|
15828
|
-
}
|
|
15829
|
-
function buildProjectContextExplain(input) {
|
|
15830
|
-
const active = activeObservations(input.observations, input.project.id);
|
|
15831
|
-
const graph = collectGraph(input.store, input.project.id, active, input.exclude);
|
|
15832
|
-
const overview = overviewFromGraph({
|
|
15833
|
-
project: input.project,
|
|
15834
|
-
store: input.store,
|
|
15835
|
-
observations: input.observations,
|
|
15836
|
-
active,
|
|
15837
|
-
graph
|
|
15838
|
-
});
|
|
15839
|
-
return {
|
|
15840
|
-
project: input.project,
|
|
15841
|
-
sources: graph.sources.sort((a, b) => a.observationId - b.observationId || (a.path ?? "").localeCompare(b.path ?? "")),
|
|
15842
|
-
overview
|
|
15843
|
-
};
|
|
15844
|
-
}
|
|
15845
|
-
var init_project_context = __esm({
|
|
15846
|
-
"src/codegraph/project-context.ts"() {
|
|
15847
|
-
"use strict";
|
|
15848
|
-
init_esm_shims();
|
|
15849
|
-
init_freshness2();
|
|
15850
|
-
init_exclude();
|
|
15851
|
-
}
|
|
15852
|
-
});
|
|
15853
|
-
|
|
15854
|
-
// src/codegraph/task-lens.ts
|
|
15855
|
-
var task_lens_exports = {};
|
|
15856
|
-
__export(task_lens_exports, {
|
|
15857
|
-
lensPathCandidates: () => lensPathCandidates,
|
|
15858
|
-
lensVerificationHints: () => lensVerificationHints,
|
|
15859
|
-
rankLensPaths: () => rankLensPaths,
|
|
15860
|
-
rankLensSources: () => rankLensSources,
|
|
15861
|
-
resolveTaskLens: () => resolveTaskLens,
|
|
15862
|
-
scoreLensSource: () => scoreLensSource,
|
|
15863
|
-
shouldShowLensSource: () => shouldShowLensSource
|
|
15864
|
-
});
|
|
15865
|
-
function normalizePath2(path25) {
|
|
15866
|
-
return path25.replace(/\\/g, "/");
|
|
15867
|
-
}
|
|
15868
|
-
function tokenize2(text) {
|
|
15869
|
-
return (text.toLowerCase().match(/[a-z0-9_./-]+|[\u4e00-\u9fff]+/g) ?? []).map((token) => token.trim()).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
|
|
15870
|
-
}
|
|
15871
|
-
function containsKeyword(text, keyword) {
|
|
15872
|
-
if (/^[a-z0-9_-]+$/i.test(keyword)) {
|
|
15873
|
-
return new RegExp(`(^|[^a-z0-9_-])${keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}([^a-z0-9_-]|$)`, "i").test(text);
|
|
15874
|
-
}
|
|
15875
|
-
return text.includes(keyword);
|
|
15876
|
-
}
|
|
15877
|
-
function resolveTaskLens(task) {
|
|
15878
|
-
const normalized = (task ?? "").toLowerCase();
|
|
15879
|
-
if (!normalized.trim()) return LENSES.general;
|
|
15880
|
-
let best = {
|
|
15881
|
-
id: "general",
|
|
15882
|
-
score: 0,
|
|
15883
|
-
priority: Number.MAX_SAFE_INTEGER
|
|
15884
|
-
};
|
|
15885
|
-
for (const id of LENS_PRIORITY) {
|
|
15886
|
-
const score = KEYWORDS[id].reduce((sum, keyword) => sum + (containsKeyword(normalized, keyword) ? 1 : 0), 0);
|
|
15887
|
-
const priority = LENS_PRIORITY.indexOf(id);
|
|
15888
|
-
if (score > best.score || score === best.score && score > 0 && priority < best.priority) {
|
|
15889
|
-
best = { id, score, priority };
|
|
15890
|
-
}
|
|
15891
|
-
}
|
|
15892
|
-
return best.score > 0 ? LENSES[best.id] : LENSES.general;
|
|
15893
|
-
}
|
|
15894
|
-
function pathKindScore(path25, lens) {
|
|
15895
|
-
const normalized = normalizePath2(path25).toLowerCase();
|
|
15896
|
-
const name = normalized.split("/").pop() ?? normalized;
|
|
15897
|
-
const inDocs = normalized.startsWith("docs/") || name === "readme.md" || name.includes("readme");
|
|
15898
|
-
const isTest = /(^|\/)(tests?|__tests__|spec|fixtures?)\//.test(normalized) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized) || /\.(test|spec)\.py$/.test(normalized);
|
|
15899
|
-
const isSource = normalized.startsWith("src/") || normalized.includes("/src/");
|
|
15900
|
-
const isPackage = name === "package.json" || name === "package-lock.json" || name === "pnpm-lock.yaml" || name === "yarn.lock";
|
|
15901
|
-
const isChangelog = name === "changelog.md" || name === "changes.md";
|
|
15902
|
-
const isWorkflow = normalized.startsWith(".github/workflows/");
|
|
15903
|
-
switch (lens.id) {
|
|
15904
|
-
case "bugfix":
|
|
15905
|
-
return (isTest ? 80 : 0) + (isSource ? 50 : 0) + (normalized.includes("debug") ? 20 : 0);
|
|
15906
|
-
case "test":
|
|
15907
|
-
return (isTest ? 90 : 0) + (isSource ? 45 : 0);
|
|
15908
|
-
case "release":
|
|
15909
|
-
return (isChangelog ? 100 : 0) + (isPackage ? 90 : 0) + (isWorkflow ? 70 : 0) + (inDocs ? 35 : 0);
|
|
15910
|
-
case "onboarding":
|
|
15911
|
-
return (name === "readme.md" ? 100 : 0) + (inDocs ? 80 : 0) + (isPackage ? 45 : 0) + (isSource ? 20 : 0);
|
|
15912
|
-
case "docs":
|
|
15913
|
-
return (inDocs ? 100 : 0) + (isChangelog ? 60 : 0) + (isSource ? 25 : 0);
|
|
15914
|
-
case "refactor":
|
|
15915
|
-
return (isSource ? 70 : 0) + (isTest ? 65 : 0);
|
|
15916
|
-
case "feature":
|
|
15917
|
-
return (isSource ? 80 : 0) + (isTest ? 45 : 0) + (inDocs ? 20 : 0);
|
|
15918
|
-
default:
|
|
15919
|
-
if (isSource || isTest) return 60;
|
|
15920
|
-
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 45;
|
|
15921
|
-
if (inDocs) return 20;
|
|
15922
|
-
return 0;
|
|
15923
|
-
}
|
|
15924
|
-
}
|
|
15925
|
-
function tokenScore(text, tokens) {
|
|
15926
|
-
if (tokens.length === 0) return 0;
|
|
15927
|
-
const normalized = text.toLowerCase();
|
|
15928
|
-
return tokens.reduce((sum, token) => sum + (normalized.includes(token) ? 12 : 0), 0);
|
|
15929
|
-
}
|
|
15930
|
-
function sourceTaskMatchScore(source, task) {
|
|
15931
|
-
const tokens = tokenize2(task ?? "");
|
|
15932
|
-
if (tokens.length === 0) return 0;
|
|
15933
|
-
const text = [
|
|
15934
|
-
source.title,
|
|
15935
|
-
source.path ?? "",
|
|
15936
|
-
source.symbol ?? ""
|
|
15937
|
-
].join("\n").toLowerCase();
|
|
15938
|
-
const matches = tokens.filter((token) => text.includes(token)).length;
|
|
15939
|
-
if (matches >= 2) return 40;
|
|
15940
|
-
if (matches === 1) return 24;
|
|
15941
|
-
return 0;
|
|
15942
|
-
}
|
|
15943
|
-
function fallbackPathRank(path25) {
|
|
15944
|
-
const normalized = normalizePath2(path25);
|
|
15945
|
-
if (normalized.startsWith("src/")) return 0;
|
|
15946
|
-
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 1;
|
|
15947
|
-
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
|
|
15948
|
-
if (normalized.startsWith("docs/") || normalized.toLowerCase() === "readme.md") return 3;
|
|
15949
|
-
return 4;
|
|
15950
|
-
}
|
|
15951
|
-
function rankLensPaths(paths, lens, task) {
|
|
15952
|
-
const tokens = tokenize2(task ?? "");
|
|
15953
|
-
return [...new Set(paths.map(normalizePath2))].map((path25, index) => ({
|
|
15954
|
-
path: path25,
|
|
15955
|
-
index,
|
|
15956
|
-
score: pathKindScore(path25, lens) + tokenScore(path25, tokens),
|
|
15957
|
-
fallback: fallbackPathRank(path25)
|
|
15958
|
-
})).sort((a, b) => b.score - a.score || a.fallback - b.fallback || a.index - b.index || a.path.localeCompare(b.path)).map((item) => item.path);
|
|
15959
|
-
}
|
|
15960
|
-
function lensPathCandidates(lens) {
|
|
15961
|
-
switch (lens.id) {
|
|
15962
|
-
case "release":
|
|
15963
|
-
return ["CHANGELOG.md", "package.json", "package-lock.json", ".github/workflows/ci.yml", ".github/workflows/test.yml", "README.md"];
|
|
15964
|
-
case "onboarding":
|
|
15965
|
-
return ["README.md", "docs/README.md", "docs/API_REFERENCE.md", "package.json"];
|
|
15966
|
-
case "docs":
|
|
15967
|
-
return ["README.md", "README.zh-CN.md", "docs/README.md", "docs/API_REFERENCE.md", "CHANGELOG.md"];
|
|
15968
|
-
case "test":
|
|
15969
|
-
case "bugfix":
|
|
15970
|
-
return ["tests", "test"];
|
|
15971
|
-
default:
|
|
15972
|
-
return [];
|
|
15973
|
-
}
|
|
15974
|
-
}
|
|
15975
|
-
function scoreLensSource(source, lens, task) {
|
|
15976
|
-
const tokens = tokenize2(task ?? "");
|
|
15977
|
-
const text = [source.title, source.type, source.path ?? "", source.symbol ?? ""].join("\n");
|
|
15978
|
-
return pathKindScore(source.path ?? "", lens) + tokenScore(text, tokens) + sourceTaskMatchScore(source, task);
|
|
15979
|
-
}
|
|
15980
|
-
function rankLensSources(sources, lens, task) {
|
|
15981
|
-
return [...sources].map((source, index) => ({
|
|
15982
|
-
source,
|
|
15983
|
-
index,
|
|
15984
|
-
score: scoreLensSource(source, lens, task)
|
|
15985
|
-
})).sort((a, b) => b.score - a.score || a.source.observationId - b.source.observationId || a.index - b.index).map((item) => item.source);
|
|
15986
|
-
}
|
|
15987
|
-
function lensVerificationHints(lens) {
|
|
15988
|
-
switch (lens.id) {
|
|
15989
|
-
case "bugfix":
|
|
15990
|
-
return [
|
|
15991
|
-
"run the smallest failing test or repro first",
|
|
15992
|
-
"inspect the changed code path before trusting old memory"
|
|
15993
|
-
];
|
|
15994
|
-
case "test":
|
|
15995
|
-
return [
|
|
15996
|
-
"run the exact focused test file or test name first",
|
|
15997
|
-
"inspect fixtures and harness setup before changing assertions"
|
|
15998
|
-
];
|
|
15999
|
-
case "release":
|
|
16000
|
-
return [
|
|
16001
|
-
"run build, tests, package smoke, and publish dry-run where available",
|
|
16002
|
-
"verify package metadata, changelog, and Git state before publishing"
|
|
16003
|
-
];
|
|
16004
|
-
case "onboarding":
|
|
16005
|
-
return [
|
|
16006
|
-
"read the docs/start files first, then inspect only the code paths needed for the task",
|
|
16007
|
-
"treat old implementation memories as leads until current code confirms them"
|
|
16008
|
-
];
|
|
16009
|
-
case "refactor":
|
|
16010
|
-
return [
|
|
16011
|
-
"inspect call sites and tests before editing shared code",
|
|
16012
|
-
"run the narrow affected test plus one regression smoke"
|
|
16013
|
-
];
|
|
16014
|
-
case "docs":
|
|
16015
|
-
return [
|
|
16016
|
-
"check headings, links, commands, and examples against current code",
|
|
16017
|
-
"run the smallest docs or package smoke available"
|
|
16018
|
-
];
|
|
16019
|
-
case "feature":
|
|
16020
|
-
return [
|
|
16021
|
-
"inspect the closest existing implementation pattern before adding new code",
|
|
16022
|
-
"run focused tests plus one user-flow smoke after changes"
|
|
16023
|
-
];
|
|
16024
|
-
default:
|
|
16025
|
-
return [
|
|
16026
|
-
"inspect the Start here files before editing",
|
|
16027
|
-
"run the smallest relevant test or smoke command after changes"
|
|
16028
|
-
];
|
|
16760
|
+
freshness.unbound++;
|
|
16761
|
+
}
|
|
16762
|
+
}
|
|
16029
16763
|
}
|
|
16764
|
+
return {
|
|
16765
|
+
files,
|
|
16766
|
+
symbols,
|
|
16767
|
+
refs,
|
|
16768
|
+
freshness,
|
|
16769
|
+
sources,
|
|
16770
|
+
suggestedReads: compactSuggestedReads(suggestedReads, 8, exclude)
|
|
16771
|
+
};
|
|
16030
16772
|
}
|
|
16031
|
-
function
|
|
16032
|
-
|
|
16033
|
-
|
|
16034
|
-
|
|
16035
|
-
|
|
16773
|
+
function overviewFromGraph(input) {
|
|
16774
|
+
const status = input.store.status(input.project.id);
|
|
16775
|
+
return {
|
|
16776
|
+
project: input.project,
|
|
16777
|
+
code: {
|
|
16778
|
+
provider: status.provider,
|
|
16779
|
+
files: status.files,
|
|
16780
|
+
symbols: status.symbols,
|
|
16781
|
+
edges: status.edges,
|
|
16782
|
+
refs: status.refs,
|
|
16783
|
+
...status.indexedAt ? { indexedAt: status.indexedAt } : {},
|
|
16784
|
+
languages: countLanguages(input.graph.files),
|
|
16785
|
+
...status.latestSnapshot ? { latestSnapshot: status.latestSnapshot } : {}
|
|
16786
|
+
},
|
|
16787
|
+
memory: {
|
|
16788
|
+
total: input.observations.filter((obs) => obs.projectId === input.project.id).length,
|
|
16789
|
+
active: input.active.length
|
|
16790
|
+
},
|
|
16791
|
+
freshness: input.graph.freshness,
|
|
16792
|
+
suggestedReads: input.graph.suggestedReads
|
|
16793
|
+
};
|
|
16036
16794
|
}
|
|
16037
|
-
|
|
16038
|
-
|
|
16039
|
-
|
|
16795
|
+
function buildProjectContextExplain(input) {
|
|
16796
|
+
const active = activeObservations(input.observations, input.project.id);
|
|
16797
|
+
const graph = collectGraph(input.store, input.project.id, active, input.exclude);
|
|
16798
|
+
const overview = overviewFromGraph({
|
|
16799
|
+
project: input.project,
|
|
16800
|
+
store: input.store,
|
|
16801
|
+
observations: input.observations,
|
|
16802
|
+
active,
|
|
16803
|
+
graph
|
|
16804
|
+
});
|
|
16805
|
+
return {
|
|
16806
|
+
project: input.project,
|
|
16807
|
+
sources: graph.sources.sort((a, b) => a.observationId - b.observationId || (a.path ?? "").localeCompare(b.path ?? "")),
|
|
16808
|
+
overview
|
|
16809
|
+
};
|
|
16810
|
+
}
|
|
16811
|
+
var init_project_context = __esm({
|
|
16812
|
+
"src/codegraph/project-context.ts"() {
|
|
16040
16813
|
"use strict";
|
|
16041
16814
|
init_esm_shims();
|
|
16042
|
-
|
|
16043
|
-
|
|
16044
|
-
"an",
|
|
16045
|
-
"and",
|
|
16046
|
-
"as",
|
|
16047
|
-
"for",
|
|
16048
|
-
"in",
|
|
16049
|
-
"into",
|
|
16050
|
-
"of",
|
|
16051
|
-
"on",
|
|
16052
|
-
"onto",
|
|
16053
|
-
"the",
|
|
16054
|
-
"this",
|
|
16055
|
-
"that",
|
|
16056
|
-
"to",
|
|
16057
|
-
"with",
|
|
16058
|
-
"work",
|
|
16059
|
-
"project",
|
|
16060
|
-
"continue",
|
|
16061
|
-
"\u7EE7\u7EED",
|
|
16062
|
-
"\u9879\u76EE"
|
|
16063
|
-
]);
|
|
16064
|
-
LENSES = {
|
|
16065
|
-
bugfix: {
|
|
16066
|
-
id: "bugfix",
|
|
16067
|
-
description: "debug the failure with current code and the smallest repro first",
|
|
16068
|
-
sourceLimit: 6,
|
|
16069
|
-
cautionLimit: 4,
|
|
16070
|
-
hideUnrelatedCautionDetails: true,
|
|
16071
|
-
hideUnrelatedReliableDetails: false
|
|
16072
|
-
},
|
|
16073
|
-
feature: {
|
|
16074
|
-
id: "feature",
|
|
16075
|
-
description: "build a scoped feature from nearby source, types, and user flow",
|
|
16076
|
-
sourceLimit: 6,
|
|
16077
|
-
cautionLimit: 3,
|
|
16078
|
-
hideUnrelatedCautionDetails: true,
|
|
16079
|
-
hideUnrelatedReliableDetails: false
|
|
16080
|
-
},
|
|
16081
|
-
release: {
|
|
16082
|
-
id: "release",
|
|
16083
|
-
description: "prepare a release using current metadata, changelog, build, and package checks",
|
|
16084
|
-
sourceLimit: 4,
|
|
16085
|
-
cautionLimit: 2,
|
|
16086
|
-
hideUnrelatedCautionDetails: true,
|
|
16087
|
-
hideUnrelatedReliableDetails: true
|
|
16088
|
-
},
|
|
16089
|
-
onboarding: {
|
|
16090
|
-
id: "onboarding",
|
|
16091
|
-
description: "understand the project shape before trusting old implementation details",
|
|
16092
|
-
sourceLimit: 4,
|
|
16093
|
-
cautionLimit: 2,
|
|
16094
|
-
hideUnrelatedCautionDetails: true,
|
|
16095
|
-
hideUnrelatedReliableDetails: true
|
|
16096
|
-
},
|
|
16097
|
-
refactor: {
|
|
16098
|
-
id: "refactor",
|
|
16099
|
-
description: "change structure carefully by reading shared code, call sites, and tests",
|
|
16100
|
-
sourceLimit: 6,
|
|
16101
|
-
cautionLimit: 4,
|
|
16102
|
-
hideUnrelatedCautionDetails: true,
|
|
16103
|
-
hideUnrelatedReliableDetails: false
|
|
16104
|
-
},
|
|
16105
|
-
docs: {
|
|
16106
|
-
id: "docs",
|
|
16107
|
-
description: "update documentation against current code and public entry points",
|
|
16108
|
-
sourceLimit: 5,
|
|
16109
|
-
cautionLimit: 2,
|
|
16110
|
-
hideUnrelatedCautionDetails: true,
|
|
16111
|
-
hideUnrelatedReliableDetails: true
|
|
16112
|
-
},
|
|
16113
|
-
test: {
|
|
16114
|
-
id: "test",
|
|
16115
|
-
description: "work from tests, fixtures, harnesses, and the related source files",
|
|
16116
|
-
sourceLimit: 6,
|
|
16117
|
-
cautionLimit: 3,
|
|
16118
|
-
hideUnrelatedCautionDetails: true,
|
|
16119
|
-
hideUnrelatedReliableDetails: false
|
|
16120
|
-
},
|
|
16121
|
-
general: {
|
|
16122
|
-
id: "general",
|
|
16123
|
-
description: "balanced project handoff with current facts, code memory, and verification hints",
|
|
16124
|
-
sourceLimit: 8,
|
|
16125
|
-
cautionLimit: 5,
|
|
16126
|
-
hideUnrelatedCautionDetails: false,
|
|
16127
|
-
hideUnrelatedReliableDetails: false
|
|
16128
|
-
}
|
|
16129
|
-
};
|
|
16130
|
-
KEYWORDS = {
|
|
16131
|
-
bugfix: [
|
|
16132
|
-
"bug",
|
|
16133
|
-
"crash",
|
|
16134
|
-
"debug",
|
|
16135
|
-
"error",
|
|
16136
|
-
"fail",
|
|
16137
|
-
"failing",
|
|
16138
|
-
"fix",
|
|
16139
|
-
"issue",
|
|
16140
|
-
"regression",
|
|
16141
|
-
"repro",
|
|
16142
|
-
"\u62A5\u9519",
|
|
16143
|
-
"\u5D29\u6E83",
|
|
16144
|
-
"\u5931\u8D25",
|
|
16145
|
-
"\u4FEE\u590D",
|
|
16146
|
-
"\u95EE\u9898"
|
|
16147
|
-
],
|
|
16148
|
-
feature: ["add", "build", "feature", "implement", "new", "support", "\u65B0\u589E", "\u5B9E\u73B0", "\u652F\u6301", "\u529F\u80FD"],
|
|
16149
|
-
release: ["bump", "changelog", "npm", "pack", "publish", "release", "version", "\u53D1\u7248", "\u53D1\u5E03", "\u7248\u672C"],
|
|
16150
|
-
onboarding: ["architecture", "handoff", "onboard", "overview", "understand", "\u63A5\u624B", "\u4E86\u89E3", "\u7406\u89E3", "\u67B6\u6784"],
|
|
16151
|
-
refactor: ["cleanup", "migrate", "refactor", "rename", "restructure", "\u91CD\u6784", "\u8FC1\u79FB", "\u6539\u9020"],
|
|
16152
|
-
docs: ["doc", "docs", "readme", "\u6587\u6863", "\u8BF4\u660E"],
|
|
16153
|
-
test: ["coverage", "fixture", "smoke", "spec", "test", "tests", "testing", "vitest", "\u6D4B\u8BD5"]
|
|
16154
|
-
};
|
|
16155
|
-
LENS_PRIORITY = [
|
|
16156
|
-
"bugfix",
|
|
16157
|
-
"release",
|
|
16158
|
-
"test",
|
|
16159
|
-
"refactor",
|
|
16160
|
-
"feature",
|
|
16161
|
-
"docs",
|
|
16162
|
-
"onboarding"
|
|
16163
|
-
];
|
|
16815
|
+
init_freshness2();
|
|
16816
|
+
init_exclude();
|
|
16164
16817
|
}
|
|
16165
16818
|
});
|
|
16166
16819
|
|
|
@@ -16178,6 +16831,9 @@ import path19 from "path";
|
|
|
16178
16831
|
function activeProjectObservations(observations2, projectId) {
|
|
16179
16832
|
return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
|
|
16180
16833
|
}
|
|
16834
|
+
function deliveryEligibleProjectObservations(observations2, projectId) {
|
|
16835
|
+
return activeProjectObservations(observations2, projectId).filter((observation) => isEligibleForAutomaticDelivery(observation));
|
|
16836
|
+
}
|
|
16181
16837
|
function decideRefresh(input) {
|
|
16182
16838
|
if (input.mode === "never") {
|
|
16183
16839
|
return { performed: false, reason: "disabled", message: "Automatic project scan disabled." };
|
|
@@ -16252,13 +16908,27 @@ async function buildAutoProjectContext(input) {
|
|
|
16252
16908
|
activeProjectObservations(input.observations, input.project.id)
|
|
16253
16909
|
);
|
|
16254
16910
|
try {
|
|
16255
|
-
const {
|
|
16911
|
+
const { MaintenanceTargetStore: MaintenanceTargetStore2 } = await Promise.resolve().then(() => (init_maintenance_targets(), maintenance_targets_exports));
|
|
16912
|
+
new MaintenanceTargetStore2(input.dataDir).register({
|
|
16913
|
+
projectId: input.project.id,
|
|
16914
|
+
projectRoot: input.project.rootPath,
|
|
16915
|
+
dataDir: input.dataDir
|
|
16916
|
+
});
|
|
16917
|
+
const {
|
|
16918
|
+
enqueueClaimRequalification: enqueueClaimRequalification2,
|
|
16919
|
+
enqueueObservationQualification: enqueueObservationQualification2
|
|
16920
|
+
} = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
|
|
16256
16921
|
enqueueClaimRequalification2({
|
|
16257
16922
|
dataDir: input.dataDir,
|
|
16258
16923
|
projectId: input.project.id,
|
|
16259
16924
|
source: "foreground-refresh",
|
|
16260
16925
|
snapshotId: store.latestSnapshot(input.project.id)?.id
|
|
16261
16926
|
});
|
|
16927
|
+
enqueueObservationQualification2({
|
|
16928
|
+
dataDir: input.dataDir,
|
|
16929
|
+
projectId: input.project.id,
|
|
16930
|
+
source: "foreground-refresh"
|
|
16931
|
+
});
|
|
16262
16932
|
} catch {
|
|
16263
16933
|
}
|
|
16264
16934
|
refresh = { ...refresh, backfill };
|
|
@@ -16275,7 +16945,9 @@ async function buildAutoProjectContext(input) {
|
|
|
16275
16945
|
const explain = buildProjectContextExplain({
|
|
16276
16946
|
project: input.project,
|
|
16277
16947
|
store,
|
|
16278
|
-
|
|
16948
|
+
// Binding sees all active evidence, but automatic delivery sees only
|
|
16949
|
+
// evidence that has passed the control-plane admission gate.
|
|
16950
|
+
observations: deliveryEligibleProjectObservations(input.observations, input.project.id),
|
|
16279
16951
|
exclude
|
|
16280
16952
|
});
|
|
16281
16953
|
const overview = explain.overview;
|
|
@@ -16366,7 +17038,8 @@ async function buildAutoProjectContext(input) {
|
|
|
16366
17038
|
suspect: overview.freshness.suspect,
|
|
16367
17039
|
stale: overview.freshness.stale
|
|
16368
17040
|
},
|
|
16369
|
-
runtimeCautions
|
|
17041
|
+
runtimeCautions,
|
|
17042
|
+
...input.deliveryTarget ? { deliveryTarget: input.deliveryTarget } : {}
|
|
16370
17043
|
});
|
|
16371
17044
|
return {
|
|
16372
17045
|
project: input.project,
|
|
@@ -16459,15 +17132,7 @@ function formatCurrentFactsLines(facts) {
|
|
|
16459
17132
|
if (facts.latestChangelog) {
|
|
16460
17133
|
lines.push(`- Latest changelog: ${facts.latestChangelog.version}${facts.latestChangelog.date ? ` (${facts.latestChangelog.date})` : ""}`);
|
|
16461
17134
|
}
|
|
16462
|
-
|
|
16463
|
-
if (facts.git.detached) {
|
|
16464
|
-
gitParts.push("detached HEAD");
|
|
16465
|
-
} else if (facts.git.branch) {
|
|
16466
|
-
gitParts.push(`branch ${facts.git.branch}`);
|
|
16467
|
-
}
|
|
16468
|
-
if (facts.git.commit) gitParts.push(`commit ${facts.git.commit}`);
|
|
16469
|
-
gitParts.push(facts.git.dirty ? "dirty worktree" : "clean worktree");
|
|
16470
|
-
lines.push(`- Git: ${gitParts.join(", ")}`);
|
|
17135
|
+
lines.push("- " + formatGitFact(facts.git));
|
|
16471
17136
|
if (facts.git.latestCommit) lines.push(`- Latest commit: ${facts.git.latestCommit}`);
|
|
16472
17137
|
lines.push("- Current facts above outrank progress/dev-log files when they conflict.");
|
|
16473
17138
|
if (facts.staleNotes.length > 0) {
|
|
@@ -16489,11 +17154,7 @@ function worksetFactLines(facts) {
|
|
|
16489
17154
|
if (facts.latestChangelog) {
|
|
16490
17155
|
lines.push("Latest changelog: " + facts.latestChangelog.version + (facts.latestChangelog.date ? " (" + facts.latestChangelog.date + ")" : ""));
|
|
16491
17156
|
}
|
|
16492
|
-
|
|
16493
|
-
if (facts.git.branch) gitParts.push("branch " + facts.git.branch);
|
|
16494
|
-
if (facts.git.commit) gitParts.push("commit " + facts.git.commit);
|
|
16495
|
-
gitParts.push(facts.git.dirty ? "dirty worktree" : "clean worktree");
|
|
16496
|
-
lines.push("Git: " + gitParts.join(", "));
|
|
17157
|
+
lines.push(formatGitFact(facts.git));
|
|
16497
17158
|
for (const note of facts.staleNotes.slice(0, 1)) {
|
|
16498
17159
|
lines.push(
|
|
16499
17160
|
"Historical note: " + note.path + (note.branchHint ? " (branch hint " + note.branchHint + "; " + note.reason + ")" : " (" + note.reason + ")")
|
|
@@ -16612,6 +17273,7 @@ var init_auto_context = __esm({
|
|
|
16612
17273
|
init_external_provider();
|
|
16613
17274
|
init_project_context();
|
|
16614
17275
|
init_store();
|
|
17276
|
+
init_admission();
|
|
16615
17277
|
init_task_lens();
|
|
16616
17278
|
DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
16617
17279
|
}
|
|
@@ -16804,7 +17466,8 @@ async function attachTaskWorkset(input) {
|
|
|
16804
17466
|
suspect: input.pack.warnings.filter((warning) => warning.status === "suspect").length,
|
|
16805
17467
|
stale: input.pack.warnings.filter((warning) => warning.status === "stale").length
|
|
16806
17468
|
},
|
|
16807
|
-
runtimeCautions: input.runtimeCautions
|
|
17469
|
+
runtimeCautions: input.runtimeCautions,
|
|
17470
|
+
deliveryTarget: "context-pack"
|
|
16808
17471
|
});
|
|
16809
17472
|
return { ...input.pack, workset };
|
|
16810
17473
|
}
|
|
@@ -18216,11 +18879,14 @@ __export(export_import_exports, {
|
|
|
18216
18879
|
exportAsMarkdown: () => exportAsMarkdown,
|
|
18217
18880
|
importFromJson: () => importFromJson
|
|
18218
18881
|
});
|
|
18219
|
-
async function exportAsJson(projectDir2, projectId) {
|
|
18882
|
+
async function exportAsJson(projectDir2, projectId, reader) {
|
|
18220
18883
|
const store = getObservationStore();
|
|
18221
18884
|
const allObs = await store.loadAll();
|
|
18222
18885
|
const allSessions = await getSessionStore().loadAll();
|
|
18223
|
-
const projectObs =
|
|
18886
|
+
const projectObs = filterReadableObservations(
|
|
18887
|
+
allObs.filter((o) => o.projectId === projectId),
|
|
18888
|
+
reader
|
|
18889
|
+
);
|
|
18224
18890
|
const projectSessions = allSessions.filter((s) => s.projectId === projectId);
|
|
18225
18891
|
const typeBreakdown = {};
|
|
18226
18892
|
for (const obs of projectObs) {
|
|
@@ -18239,8 +18905,8 @@ async function exportAsJson(projectDir2, projectId) {
|
|
|
18239
18905
|
}
|
|
18240
18906
|
};
|
|
18241
18907
|
}
|
|
18242
|
-
async function exportAsMarkdown(projectDir2, projectId) {
|
|
18243
|
-
const data = await exportAsJson(projectDir2, projectId);
|
|
18908
|
+
async function exportAsMarkdown(projectDir2, projectId, reader) {
|
|
18909
|
+
const data = await exportAsJson(projectDir2, projectId, reader);
|
|
18244
18910
|
const lines = [];
|
|
18245
18911
|
lines.push(`# Memorix Export: ${projectId}`);
|
|
18246
18912
|
lines.push(`Exported: ${data.exportedAt}`);
|
|
@@ -18342,6 +19008,7 @@ var init_export_import = __esm({
|
|
|
18342
19008
|
"src/memory/export-import.ts"() {
|
|
18343
19009
|
"use strict";
|
|
18344
19010
|
init_esm_shims();
|
|
19011
|
+
init_visibility();
|
|
18345
19012
|
init_obs_store();
|
|
18346
19013
|
init_session_store();
|
|
18347
19014
|
OBSERVATION_ICONS2 = {
|
|
@@ -18518,6 +19185,7 @@ function isEligible(o, projectId) {
|
|
|
18518
19185
|
if (isCommandLog2(o)) return false;
|
|
18519
19186
|
if (isInactive(o)) return false;
|
|
18520
19187
|
if (isOtherProject(o, projectId)) return false;
|
|
19188
|
+
if (!isEligibleForKnowledgePromotion(o)) return false;
|
|
18521
19189
|
if (o.valueCategory === "ephemeral") return false;
|
|
18522
19190
|
if (!contextualHasSubstance(o)) return false;
|
|
18523
19191
|
return true;
|
|
@@ -18666,6 +19334,7 @@ var init_generator = __esm({
|
|
|
18666
19334
|
"src/wiki/generator.ts"() {
|
|
18667
19335
|
"use strict";
|
|
18668
19336
|
init_esm_shims();
|
|
19337
|
+
init_admission();
|
|
18669
19338
|
COMMAND_LOG_TITLE4 = /^(Ran:|Command:|Executed:)\s/i;
|
|
18670
19339
|
SECTION_DEFS = [
|
|
18671
19340
|
{
|
|
@@ -18952,6 +19621,9 @@ function sendError(res, message, status = 500) {
|
|
|
18952
19621
|
function filterByProject(items, projectId) {
|
|
18953
19622
|
return items.filter((item) => item.projectId === projectId);
|
|
18954
19623
|
}
|
|
19624
|
+
function filterDashboardObservations(observations2, projectId) {
|
|
19625
|
+
return filterReadableObservations(observations2, { projectId });
|
|
19626
|
+
}
|
|
18955
19627
|
function isActiveStatus(status) {
|
|
18956
19628
|
return (status ?? "active") === "active";
|
|
18957
19629
|
}
|
|
@@ -18975,13 +19647,8 @@ function prepareDashboardConfig(projectRoot) {
|
|
|
18975
19647
|
}
|
|
18976
19648
|
}
|
|
18977
19649
|
function computeProjectGraphCounts(allEntities, allRelations, projectObs) {
|
|
18978
|
-
const
|
|
18979
|
-
|
|
18980
|
-
);
|
|
18981
|
-
const entities = allEntities.filter((e) => entityNames.has(e.name));
|
|
18982
|
-
const entityNameSet = new Set(entities.map((e) => e.name));
|
|
18983
|
-
const relations = allRelations.filter((r) => entityNameSet.has(r.from) && entityNameSet.has(r.to));
|
|
18984
|
-
return { entities: entities.length, relations: relations.length, entityNames };
|
|
19650
|
+
const scoped = scopeKnowledgeGraphToProject({ entities: allEntities, relations: allRelations }, projectObs);
|
|
19651
|
+
return { entities: scoped.entities.length, relations: scoped.relations.length, entityNames: scoped.entityNames };
|
|
18985
19652
|
}
|
|
18986
19653
|
async function handleApi(req, res, dataDir, projectId, projectName, baseDir, projectRoot, projectResolved, mode = "standalone", port = 3210) {
|
|
18987
19654
|
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
@@ -19004,7 +19671,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19004
19671
|
switch (apiPath) {
|
|
19005
19672
|
case "/projects": {
|
|
19006
19673
|
try {
|
|
19007
|
-
const allObs =
|
|
19674
|
+
const allObs = filterReadableObservations(
|
|
19675
|
+
await getObservationStore().loadAll(),
|
|
19676
|
+
{}
|
|
19677
|
+
);
|
|
19008
19678
|
const projectSet = /* @__PURE__ */ new Map();
|
|
19009
19679
|
for (const obs of allObs) {
|
|
19010
19680
|
if (!isActiveStatus(obs.status)) continue;
|
|
@@ -19053,18 +19723,19 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19053
19723
|
await initGraphStore(effectiveDataDir);
|
|
19054
19724
|
const gStore = getGraphStore();
|
|
19055
19725
|
const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
19056
|
-
const graphObs =
|
|
19057
|
-
|
|
19058
|
-
|
|
19726
|
+
const graphObs = filterDashboardObservations(
|
|
19727
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19728
|
+
effectiveProjectId
|
|
19059
19729
|
);
|
|
19060
|
-
const
|
|
19061
|
-
|
|
19062
|
-
const relations = graph.relations.filter((r) => entityNameSet.has(r.from) && entityNameSet.has(r.to));
|
|
19063
|
-
sendJson(res, { entities, relations });
|
|
19730
|
+
const scoped = scopeKnowledgeGraphToProject(graph, graphObs);
|
|
19731
|
+
sendJson(res, { entities: scoped.entities, relations: scoped.relations });
|
|
19064
19732
|
break;
|
|
19065
19733
|
}
|
|
19066
19734
|
case "/observations": {
|
|
19067
|
-
const observations2 =
|
|
19735
|
+
const observations2 = filterDashboardObservations(
|
|
19736
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19737
|
+
effectiveProjectId
|
|
19738
|
+
);
|
|
19068
19739
|
sendJson(res, observations2);
|
|
19069
19740
|
break;
|
|
19070
19741
|
}
|
|
@@ -19077,7 +19748,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19077
19748
|
case "/stats": {
|
|
19078
19749
|
await initGraphStore(effectiveDataDir);
|
|
19079
19750
|
const graph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
|
|
19080
|
-
const observations2 =
|
|
19751
|
+
const observations2 = filterDashboardObservations(
|
|
19752
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19753
|
+
effectiveProjectId
|
|
19754
|
+
);
|
|
19081
19755
|
const nextId2 = await getObservationStore().loadIdCounter();
|
|
19082
19756
|
const projectGraphCounts = computeProjectGraphCounts(graph.entities, graph.relations, observations2);
|
|
19083
19757
|
const typeCounts = {};
|
|
@@ -19195,7 +19869,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19195
19869
|
break;
|
|
19196
19870
|
}
|
|
19197
19871
|
case "/retention": {
|
|
19198
|
-
const observations2 =
|
|
19872
|
+
const observations2 = filterDashboardObservations(
|
|
19873
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19874
|
+
effectiveProjectId
|
|
19875
|
+
);
|
|
19199
19876
|
const now3 = Date.now();
|
|
19200
19877
|
const scored = observations2.filter((obs) => obs.type !== "probe").map((obs) => {
|
|
19201
19878
|
const age = now3 - new Date(obs.createdAt || now3).getTime();
|
|
@@ -19233,7 +19910,10 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19233
19910
|
const { generateKnowledgeBase: generateKnowledgeBase2 } = await Promise.resolve().then(() => (init_generator(), generator_exports));
|
|
19234
19911
|
const { initMiniSkillStore: initMiniSkillStore2, getMiniSkillStore: getMiniSkillStore2 } = await Promise.resolve().then(() => (init_mini_skill_store(), mini_skill_store_exports));
|
|
19235
19912
|
await initMiniSkillStore2(effectiveDataDir);
|
|
19236
|
-
const allObs =
|
|
19913
|
+
const allObs = filterDashboardObservations(
|
|
19914
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19915
|
+
effectiveProjectId
|
|
19916
|
+
);
|
|
19237
19917
|
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
19238
19918
|
const overview = generateKnowledgeBase2({
|
|
19239
19919
|
projectId: effectiveProjectId,
|
|
@@ -19249,22 +19929,19 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19249
19929
|
const { initGraphStore: initGraphStore2, getGraphStore: getGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
19250
19930
|
await initMiniSkillStore2(effectiveDataDir);
|
|
19251
19931
|
await initGraphStore2(effectiveDataDir);
|
|
19252
|
-
const allObs =
|
|
19932
|
+
const allObs = filterDashboardObservations(
|
|
19933
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
19934
|
+
effectiveProjectId
|
|
19935
|
+
);
|
|
19253
19936
|
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
19254
19937
|
const fullGraph = { entities: getGraphStore2().loadEntities(), relations: getGraphStore2().loadRelations() };
|
|
19255
|
-
const
|
|
19256
|
-
const projectEntityNames = new Set(
|
|
19257
|
-
graphObs.filter((o) => o.entityName).map((o) => o.entityName)
|
|
19258
|
-
);
|
|
19259
|
-
const scopedEntities = fullGraph.entities.filter((e) => projectEntityNames.has(e.name));
|
|
19260
|
-
const scopedEntityNameSet = new Set(scopedEntities.map((e) => e.name));
|
|
19261
|
-
const scopedRelations = fullGraph.relations.filter((r) => scopedEntityNameSet.has(r.from) && scopedEntityNameSet.has(r.to));
|
|
19938
|
+
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
19262
19939
|
const graph = generateKnowledgeGraph2({
|
|
19263
19940
|
projectId: effectiveProjectId,
|
|
19264
19941
|
observations: allObs,
|
|
19265
19942
|
miniSkills: skills,
|
|
19266
|
-
graphEntities:
|
|
19267
|
-
graphRelations:
|
|
19943
|
+
graphEntities: scoped.entities,
|
|
19944
|
+
graphRelations: scoped.relations
|
|
19268
19945
|
});
|
|
19269
19946
|
sendJson(res, graph);
|
|
19270
19947
|
break;
|
|
@@ -19378,7 +20055,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19378
20055
|
break;
|
|
19379
20056
|
}
|
|
19380
20057
|
case "/identity": {
|
|
19381
|
-
const allObs = await getObservationStore().loadAll();
|
|
20058
|
+
const allObs = filterReadableObservations(await getObservationStore().loadAll(), {});
|
|
19382
20059
|
const allProjectIds = [...new Set(allObs.map((o) => o.projectId).filter(Boolean))];
|
|
19383
20060
|
let classifyProjectId2 = () => "real";
|
|
19384
20061
|
let isDirtyProjectId2 = () => false;
|
|
@@ -19463,6 +20140,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19463
20140
|
sendError(res, "Observation not found", 404);
|
|
19464
20141
|
} else if (matchObs.projectId !== effectiveProjectId) {
|
|
19465
20142
|
sendError(res, `Observation #${obsId} belongs to project "${matchObs.projectId}", not "${effectiveProjectId}"`, 403);
|
|
20143
|
+
} else if (!canManageObservation(matchObs, { projectId: effectiveProjectId })) {
|
|
20144
|
+
sendError(res, `Observation #${obsId} is not manageable from the unbound dashboard.`, 403);
|
|
19466
20145
|
} else {
|
|
19467
20146
|
await obsStore.remove(obsId);
|
|
19468
20147
|
try {
|
|
@@ -19484,18 +20163,16 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
19484
20163
|
if (apiPath === "/export") {
|
|
19485
20164
|
await initGraphStore(effectiveDataDir);
|
|
19486
20165
|
const fullGraph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
|
|
19487
|
-
const observations2 =
|
|
19488
|
-
|
|
19489
|
-
|
|
19490
|
-
observations2.filter((o) => (o.status ?? "active") === "active" && o.entityName).map((o) => o.entityName)
|
|
20166
|
+
const observations2 = filterDashboardObservations(
|
|
20167
|
+
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
20168
|
+
effectiveProjectId
|
|
19491
20169
|
);
|
|
19492
|
-
const
|
|
19493
|
-
const
|
|
19494
|
-
const exportRelations = fullGraph.relations.filter((r) => exportEntitySet.has(r.from) && exportEntitySet.has(r.to));
|
|
20170
|
+
const nextId2 = await getObservationStore().loadIdCounter();
|
|
20171
|
+
const scoped = scopeKnowledgeGraphToProject(fullGraph, observations2);
|
|
19495
20172
|
const exportData = {
|
|
19496
20173
|
project: { id: effectiveProjectId, name: effectiveProjectName },
|
|
19497
20174
|
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19498
|
-
graph: { entities:
|
|
20175
|
+
graph: { entities: scoped.entities, relations: scoped.relations },
|
|
19499
20176
|
observations: observations2,
|
|
19500
20177
|
nextId: nextId2
|
|
19501
20178
|
};
|
|
@@ -19812,6 +20489,8 @@ var init_server = __esm({
|
|
|
19812
20489
|
init_dotenv_loader();
|
|
19813
20490
|
init_yaml_loader();
|
|
19814
20491
|
init_yaml_loader();
|
|
20492
|
+
init_graph_scope();
|
|
20493
|
+
init_visibility();
|
|
19815
20494
|
MIME_TYPES = {
|
|
19816
20495
|
".html": "text/html; charset=utf-8",
|
|
19817
20496
|
".css": "text/css; charset=utf-8",
|
|
@@ -20589,6 +21268,11 @@ async function createHandoffArtifact(input, storeObservation2, teamStore) {
|
|
|
20589
21268
|
sourceDetail: "explicit",
|
|
20590
21269
|
valueCategory: "core",
|
|
20591
21270
|
// immune to retention archival
|
|
21271
|
+
// A direct handoff is not a project-wide memo. The sender retains access
|
|
21272
|
+
// and the explicit recipient receives a read grant. Broadcast handoffs
|
|
21273
|
+
// are deliberately visible only to active team members.
|
|
21274
|
+
visibility: input.toAgentId ? "personal" : "team",
|
|
21275
|
+
...input.toAgentId ? { sharedWithAgentIds: [input.toAgentId] } : {},
|
|
20592
21276
|
createdByAgentId: input.fromAgentId
|
|
20593
21277
|
});
|
|
20594
21278
|
try {
|
|
@@ -22528,6 +23212,24 @@ async function createAutoRelations(obs, extracted, graphManager) {
|
|
|
22528
23212
|
const relationType = inferRelationType(obs);
|
|
22529
23213
|
const relations = [];
|
|
22530
23214
|
const selfName = obs.entityName.toLowerCase();
|
|
23215
|
+
const explicitRelated = [...new Set((obs.relatedEntities ?? []).map((name) => name.trim()).filter((name) => name && name.toLowerCase() !== selfName))];
|
|
23216
|
+
if (explicitRelated.length > 0) {
|
|
23217
|
+
await graphManager.createEntities(explicitRelated.map((name) => ({
|
|
23218
|
+
name,
|
|
23219
|
+
entityType: "related",
|
|
23220
|
+
observations: []
|
|
23221
|
+
})));
|
|
23222
|
+
for (const name of explicitRelated) {
|
|
23223
|
+
const matchedEntity = graphManager.findEntityByName(name);
|
|
23224
|
+
if (matchedEntity) {
|
|
23225
|
+
relations.push({
|
|
23226
|
+
from: obs.entityName,
|
|
23227
|
+
to: matchedEntity.name,
|
|
23228
|
+
relationType: "related_entity"
|
|
23229
|
+
});
|
|
23230
|
+
}
|
|
23231
|
+
}
|
|
23232
|
+
}
|
|
22531
23233
|
const candidates = [
|
|
22532
23234
|
...extracted.identifiers,
|
|
22533
23235
|
...extracted.files.map((f) => f.split("/").pop()?.replace(/\.\w+$/, "") ?? ""),
|
|
@@ -22569,6 +23271,8 @@ async function createAutoRelations(obs, extracted, graphManager) {
|
|
|
22569
23271
|
|
|
22570
23272
|
// src/server.ts
|
|
22571
23273
|
init_entity_extractor();
|
|
23274
|
+
init_graph_scope();
|
|
23275
|
+
init_visibility();
|
|
22572
23276
|
|
|
22573
23277
|
// src/compact/engine.ts
|
|
22574
23278
|
init_esm_shims();
|
|
@@ -22694,7 +23398,15 @@ function formatTimeline(timeline) {
|
|
|
22694
23398
|
function formatObservationDetail(doc) {
|
|
22695
23399
|
const icon = getTypeIcon(doc.type);
|
|
22696
23400
|
const lines = [];
|
|
22697
|
-
const header = buildProvenanceHeader(
|
|
23401
|
+
const header = buildProvenanceHeader(
|
|
23402
|
+
doc.sourceDetail,
|
|
23403
|
+
doc.valueCategory,
|
|
23404
|
+
doc.admissionState,
|
|
23405
|
+
doc.admissionReason,
|
|
23406
|
+
doc.source,
|
|
23407
|
+
doc.commitHash,
|
|
23408
|
+
doc.relatedCommits
|
|
23409
|
+
);
|
|
22698
23410
|
if (header) {
|
|
22699
23411
|
lines.push(header);
|
|
22700
23412
|
lines.push("");
|
|
@@ -22729,7 +23441,7 @@ function formatObservationDetail(doc) {
|
|
|
22729
23441
|
}
|
|
22730
23442
|
return lines.join("\n");
|
|
22731
23443
|
}
|
|
22732
|
-
function buildProvenanceHeader(sourceDetail, valueCategory, source, commitHash, relatedCommits) {
|
|
23444
|
+
function buildProvenanceHeader(sourceDetail, valueCategory, admissionState, admissionReason, source, commitHash, relatedCommits) {
|
|
22733
23445
|
const sd = resolveSourceDetail(sourceDetail, source);
|
|
22734
23446
|
if (!sd) return "";
|
|
22735
23447
|
const label = sourceKindLabel(sd);
|
|
@@ -22740,11 +23452,21 @@ function buildProvenanceHeader(sourceDetail, valueCategory, source, commitHash,
|
|
|
22740
23452
|
if (verificationLine) {
|
|
22741
23453
|
lines.push(verificationLine);
|
|
22742
23454
|
}
|
|
22743
|
-
if (valueCategory === "core") {
|
|
23455
|
+
if (valueCategory === "core" && admissionState !== "candidate" && admissionState !== "ephemeral") {
|
|
22744
23456
|
lines.push("[CORE] Core \u2014 immune to decay");
|
|
23457
|
+
} else if (valueCategory === "core") {
|
|
23458
|
+
lines.push("[CORE] Core candidate \u2014 not durable until qualification completes");
|
|
22745
23459
|
} else if (valueCategory === "ephemeral") {
|
|
22746
23460
|
lines.push("[WARN] Ephemeral \u2014 short-lived signal");
|
|
22747
23461
|
}
|
|
23462
|
+
if (admissionState === "candidate") {
|
|
23463
|
+
lines.push("[PENDING] Candidate \u2014 excluded from automatic context until current Code Memory qualifies it");
|
|
23464
|
+
} else if (admissionState === "ephemeral") {
|
|
23465
|
+
lines.push("[TRACE] Ephemeral capture \u2014 excluded from automatic context");
|
|
23466
|
+
} else if (admissionState === "qualified") {
|
|
23467
|
+
lines.push("[QUALIFIED] Automatic evidence passed current Code Memory qualification");
|
|
23468
|
+
}
|
|
23469
|
+
if (admissionReason) lines.push(`Admission: ${redactCredentials(admissionReason)}`);
|
|
22748
23470
|
return lines.join("\n");
|
|
22749
23471
|
}
|
|
22750
23472
|
function sourceKindLabel(sd) {
|
|
@@ -22825,6 +23547,7 @@ init_obs_store();
|
|
|
22825
23547
|
init_mini_skill_store();
|
|
22826
23548
|
init_mini_skills();
|
|
22827
23549
|
init_secret_filter();
|
|
23550
|
+
init_visibility();
|
|
22828
23551
|
function normalizeMemoryBrowseQuery(query) {
|
|
22829
23552
|
const trimmed = query.trim();
|
|
22830
23553
|
if (!trimmed) return "";
|
|
@@ -22867,11 +23590,13 @@ async function compactSearch(options) {
|
|
|
22867
23590
|
);
|
|
22868
23591
|
let formatted = formatIndexTable(entries, searchOptions.query, !options.projectId);
|
|
22869
23592
|
if (entries.length === 0 && options.projectId) {
|
|
22870
|
-
const allObservations = getAllObservations();
|
|
23593
|
+
const allObservations = filterReadableObservations(getAllObservations(), options.reader);
|
|
22871
23594
|
const projectAliases = new Set(await resolveAliases(options.projectId).catch(() => [options.projectId]));
|
|
22872
23595
|
const projectHasStoredMemory = allObservations.some((obs) => obs.projectId && projectAliases.has(obs.projectId));
|
|
22873
23596
|
if (!projectHasStoredMemory) {
|
|
22874
|
-
formatted = `
|
|
23597
|
+
formatted = options.reader ? `No Memorix memories are available to this session yet.
|
|
23598
|
+
|
|
23599
|
+
The tool call worked, but this session has no project-visible memory to retrieve.` : `This project does not have any Memorix memories yet.
|
|
22875
23600
|
|
|
22876
23601
|
It looks like a fresh project: the tool call worked, but there is nothing stored to retrieve yet.
|
|
22877
23602
|
|
|
@@ -22885,8 +23610,8 @@ This project does have stored Memorix memories, but none matched the current que
|
|
|
22885
23610
|
const totalTokens = countTextTokens(formatted);
|
|
22886
23611
|
return { entries, formatted, totalTokens };
|
|
22887
23612
|
}
|
|
22888
|
-
async function compactTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3) {
|
|
22889
|
-
const result = await getTimeline(anchorId, projectId, depthBefore, depthAfter);
|
|
23613
|
+
async function compactTimeline(anchorId, projectId, depthBefore = 3, depthAfter = 3, reader) {
|
|
23614
|
+
const result = await getTimeline(anchorId, projectId, depthBefore, depthAfter, reader);
|
|
22890
23615
|
const timeline = {
|
|
22891
23616
|
anchorId,
|
|
22892
23617
|
anchorEntry: result.anchor,
|
|
@@ -22897,7 +23622,7 @@ async function compactTimeline(anchorId, projectId, depthBefore = 3, depthAfter
|
|
|
22897
23622
|
const totalTokens = countTextTokens(formatted);
|
|
22898
23623
|
return { timeline, formatted, totalTokens };
|
|
22899
23624
|
}
|
|
22900
|
-
async function compactDetail(idsOrRefs) {
|
|
23625
|
+
async function compactDetail(idsOrRefs, options = {}) {
|
|
22901
23626
|
const invalidRefs = [];
|
|
22902
23627
|
const parsedRefs = [];
|
|
22903
23628
|
for (let i = 0; i < idsOrRefs.length; i++) {
|
|
@@ -22954,7 +23679,7 @@ async function compactDetail(idsOrRefs) {
|
|
|
22954
23679
|
const missingRefs = [];
|
|
22955
23680
|
for (const ref of refs) {
|
|
22956
23681
|
const obs = getObservation(ref.id, ref.projectId);
|
|
22957
|
-
if (obs && (ref.projectId ? obs.projectId === ref.projectId : true)) {
|
|
23682
|
+
if (obs && (ref.projectId ? obs.projectId === ref.projectId : true) && canReadObservation(obs, options.reader)) {
|
|
22958
23683
|
documentMap.set(toRefKey(ref), {
|
|
22959
23684
|
id: makeOramaObservationId(obs.projectId, obs.id),
|
|
22960
23685
|
observationId: obs.id,
|
|
@@ -22973,7 +23698,12 @@ async function compactDetail(idsOrRefs) {
|
|
|
22973
23698
|
status: obs.status ?? "active",
|
|
22974
23699
|
source: obs.source ?? "agent",
|
|
22975
23700
|
sourceDetail: obs.sourceDetail ?? "",
|
|
22976
|
-
valueCategory: obs.valueCategory ?? ""
|
|
23701
|
+
valueCategory: obs.valueCategory ?? "",
|
|
23702
|
+
admissionState: obs.admissionState ?? "",
|
|
23703
|
+
admissionReason: obs.admissionReason ?? "",
|
|
23704
|
+
visibility: obs.visibility ?? "project",
|
|
23705
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
23706
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
22977
23707
|
});
|
|
22978
23708
|
} else {
|
|
22979
23709
|
missingRefs.push(ref);
|
|
@@ -22983,17 +23713,17 @@ async function compactDetail(idsOrRefs) {
|
|
|
22983
23713
|
for (const ref of missingRefs) {
|
|
22984
23714
|
const fallbackDocs = await getObservationsByIds([ref.id], ref.projectId);
|
|
22985
23715
|
const doc = fallbackDocs[0];
|
|
22986
|
-
if (doc) {
|
|
23716
|
+
if (doc && canReadObservation(doc, options.reader)) {
|
|
22987
23717
|
documentMap.set(toRefKey(ref), doc);
|
|
22988
23718
|
continue;
|
|
22989
23719
|
}
|
|
22990
23720
|
const stored = await getPersistedObservation(ref);
|
|
22991
|
-
if (stored) {
|
|
23721
|
+
if (stored && canReadObservation(stored, options.reader)) {
|
|
22992
23722
|
documentMap.set(toRefKey(ref), observationToDocument(stored));
|
|
22993
23723
|
}
|
|
22994
23724
|
}
|
|
22995
23725
|
}
|
|
22996
|
-
const allObs = getAllObservations();
|
|
23726
|
+
const allObs = filterReadableObservations(getAllObservations(), options.reader);
|
|
22997
23727
|
const crossRefMap = /* @__PURE__ */ new Map();
|
|
22998
23728
|
for (const ref of refs) {
|
|
22999
23729
|
const obs = getObservation(ref.id, ref.projectId);
|
|
@@ -23107,7 +23837,12 @@ function observationToDocument(obs) {
|
|
|
23107
23837
|
status: obs.status ?? "active",
|
|
23108
23838
|
source: obs.source ?? "agent",
|
|
23109
23839
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23110
|
-
valueCategory: obs.valueCategory ?? ""
|
|
23840
|
+
valueCategory: obs.valueCategory ?? "",
|
|
23841
|
+
admissionState: obs.admissionState ?? "",
|
|
23842
|
+
admissionReason: obs.admissionReason ?? "",
|
|
23843
|
+
visibility: obs.visibility ?? "project",
|
|
23844
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
23845
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
23111
23846
|
};
|
|
23112
23847
|
}
|
|
23113
23848
|
function formatMiniSkillDetail(skill2, provenanceStatus) {
|
|
@@ -23226,7 +23961,9 @@ function auditMemoryQuality(observations2, options) {
|
|
|
23226
23961
|
status: obs.status ?? "active",
|
|
23227
23962
|
source: obs.source ?? "agent",
|
|
23228
23963
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23229
|
-
valueCategory: obs.valueCategory ?? ""
|
|
23964
|
+
valueCategory: obs.valueCategory ?? "",
|
|
23965
|
+
admissionState: obs.admissionState ?? "",
|
|
23966
|
+
admissionReason: obs.admissionReason ?? ""
|
|
23230
23967
|
}, options.referenceTime) !== "active").map((obs) => makeEntry(obs, "Outside active retention zone"));
|
|
23231
23968
|
const summary = {
|
|
23232
23969
|
total: scoped.length,
|
|
@@ -23268,6 +24005,7 @@ function auditMemoryQuality(observations2, options) {
|
|
|
23268
24005
|
|
|
23269
24006
|
// src/memory/graph-context.ts
|
|
23270
24007
|
init_retention();
|
|
24008
|
+
init_admission();
|
|
23271
24009
|
function formatGraphContextPrompt(packet) {
|
|
23272
24010
|
const lines = [
|
|
23273
24011
|
"## Memory Context Packet",
|
|
@@ -23425,7 +24163,9 @@ function scoreForPacket(obs, queryTokens, referenceTime, broadMemoryQuery) {
|
|
|
23425
24163
|
status: obs.status ?? "active",
|
|
23426
24164
|
source: obs.source ?? "agent",
|
|
23427
24165
|
sourceDetail: obs.sourceDetail ?? "",
|
|
23428
|
-
valueCategory: obs.valueCategory ?? ""
|
|
24166
|
+
valueCategory: obs.valueCategory ?? "",
|
|
24167
|
+
admissionState: obs.admissionState ?? "",
|
|
24168
|
+
admissionReason: obs.admissionReason ?? ""
|
|
23429
24169
|
}, referenceTime) !== "active") {
|
|
23430
24170
|
score -= 2;
|
|
23431
24171
|
}
|
|
@@ -23519,8 +24259,11 @@ function buildGraphContextPacket(observations2, options) {
|
|
|
23519
24259
|
...audit.issues.orphans.map((entry) => entry.id),
|
|
23520
24260
|
...audit.issues.retentionCandidates.map((entry) => entry.id)
|
|
23521
24261
|
]);
|
|
23522
|
-
const
|
|
23523
|
-
|
|
24262
|
+
const deliveryEligible = observations2.filter(
|
|
24263
|
+
(obs) => obs.projectId === options.projectId && isEligibleForAutomaticDelivery(obs)
|
|
24264
|
+
);
|
|
24265
|
+
const filteredObservations = deliveryEligible.filter((obs) => !riskIds.has(obs.id));
|
|
24266
|
+
const baseObservations = filteredObservations.length > 0 ? filteredObservations : deliveryEligible;
|
|
23524
24267
|
const memories = pickMemories(baseObservations, options.projectId, options.query, options.limit ?? 5, referenceTime);
|
|
23525
24268
|
const entities = buildEntities(memories);
|
|
23526
24269
|
const entityNames = new Set(entities.map((entity) => entity.name));
|
|
@@ -26007,6 +26750,8 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
26007
26750
|
let projectResolutionError = null;
|
|
26008
26751
|
let explicitProjectBound = false;
|
|
26009
26752
|
let currentAgentId;
|
|
26753
|
+
let teamStore;
|
|
26754
|
+
let initTeamStoreForProject;
|
|
26010
26755
|
if (detectedProject) {
|
|
26011
26756
|
rawProject = detectedProject;
|
|
26012
26757
|
} else {
|
|
@@ -26254,7 +26999,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
26254
26999
|
};
|
|
26255
27000
|
const server = existingServer ?? new McpServer({
|
|
26256
27001
|
name: "memorix",
|
|
26257
|
-
version: true ? "1.2.
|
|
27002
|
+
version: true ? "1.2.2" : "1.0.1"
|
|
26258
27003
|
});
|
|
26259
27004
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
26260
27005
|
server.registerTool = ((name, ...args) => {
|
|
@@ -26274,11 +27019,26 @@ The path should point to a directory containing a .git folder.`
|
|
|
26274
27019
|
}
|
|
26275
27020
|
return originalRegisterTool(name, ...args);
|
|
26276
27021
|
});
|
|
27022
|
+
const getObservationReader = (scope = "project") => {
|
|
27023
|
+
let isTeamMember = false;
|
|
27024
|
+
if (teamFeaturesEnabled && currentAgentId) {
|
|
27025
|
+
try {
|
|
27026
|
+
const agent = teamStore.getAgent(currentAgentId);
|
|
27027
|
+
isTeamMember = agent?.project_id === project.id && agent.status === "active";
|
|
27028
|
+
} catch {
|
|
27029
|
+
}
|
|
27030
|
+
}
|
|
27031
|
+
return {
|
|
27032
|
+
...scope === "project" ? { projectId: project.id } : {},
|
|
27033
|
+
...currentAgentId ? { agentId: currentAgentId } : {},
|
|
27034
|
+
isTeamMember
|
|
27035
|
+
};
|
|
27036
|
+
};
|
|
26277
27037
|
server.registerTool(
|
|
26278
27038
|
"memorix_store",
|
|
26279
27039
|
{
|
|
26280
27040
|
title: "Store Memory",
|
|
26281
|
-
description: "Store a new observation/memory. Automatically indexed for search. Use type to classify: gotcha ([GOTCHA] critical pitfall), decision ([DECISION] architecture choice), problem-solution ([FIX] bug fix), how-it-works ([INFO] explanation), what-changed ([CHANGE] change), discovery ([DISCOVERY] insight), why-it-exists ([WHY] rationale), trade-off ([TRADEOFF] compromise), session-request ([SESSION] original goal).
|
|
27041
|
+
description: "Store a new observation/memory. Automatically indexed for search. Use type to classify: gotcha ([GOTCHA] critical pitfall), decision ([DECISION] architecture choice), problem-solution ([FIX] bug fix), how-it-works ([INFO] explanation), what-changed ([CHANGE] change), discovery ([DISCOVERY] insight), why-it-exists ([WHY] rationale), trade-off ([TRADEOFF] compromise), session-request ([SESSION] original goal). Project visibility is the default. Personal or team visibility requires an explicitly joined coordination identity.",
|
|
26282
27042
|
inputSchema: {
|
|
26283
27043
|
entityName: z2.string().describe('The entity this observation belongs to (e.g., "auth-module", "port-config")'),
|
|
26284
27044
|
type: z2.enum(OBSERVATION_TYPES).describe("Observation type for classification"),
|
|
@@ -26296,361 +27056,214 @@ The path should point to a directory containing a .git folder.`
|
|
|
26296
27056
|
completion: z2.number().optional().describe("Completion percentage 0-100")
|
|
26297
27057
|
}).optional().describe("Progress tracking for task/feature observations"),
|
|
26298
27058
|
relatedCommits: z2.array(z2.string()).optional().describe("Git commit hashes this memory relates to (links ground truth \u2194 reasoning)"),
|
|
26299
|
-
relatedEntities: z2.array(z2.string()).optional().describe("Other entity names this memory cross-references")
|
|
27059
|
+
relatedEntities: z2.array(z2.string()).optional().describe("Other entity names this memory cross-references"),
|
|
27060
|
+
visibility: z2.enum(["personal", "project", "team"]).optional().describe(
|
|
27061
|
+
"Retrieval scope. Project is the normal shared default; personal/team require memorix_session_start with joinTeam=true."
|
|
27062
|
+
)
|
|
26300
27063
|
}
|
|
26301
27064
|
},
|
|
26302
|
-
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities }) => {
|
|
27065
|
+
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility }) => {
|
|
26303
27066
|
const unresolved = requireResolvedProject("store memory in the current project");
|
|
26304
27067
|
if (unresolved) return unresolved;
|
|
26305
|
-
|
|
26306
|
-
|
|
26307
|
-
|
|
26308
|
-
|
|
26309
|
-
|
|
26310
|
-
|
|
26311
|
-
|
|
26312
|
-
|
|
26313
|
-
|
|
26314
|
-
|
|
26315
|
-
|
|
26316
|
-
|
|
26317
|
-
|
|
26318
|
-
|
|
26319
|
-
|
|
26320
|
-
|
|
26321
|
-
|
|
26322
|
-
|
|
26323
|
-
|
|
26324
|
-
|
|
26325
|
-
|
|
26326
|
-
|
|
26327
|
-
|
|
26328
|
-
|
|
26329
|
-
|
|
26330
|
-
|
|
26331
|
-
|
|
26332
|
-
|
|
26333
|
-
|
|
26334
|
-
|
|
26335
|
-
if (event.stageDurationMs !== void 0) {
|
|
26336
|
-
completedFormationStages[event.stage] = event.stageDurationMs;
|
|
26337
|
-
}
|
|
26338
|
-
};
|
|
26339
|
-
try {
|
|
26340
|
-
const formationConfig = {
|
|
26341
|
-
mode: "active",
|
|
26342
|
-
useLLM: isLLMEnabled(),
|
|
26343
|
-
minValueScore: 0.3,
|
|
26344
|
-
searchMemories: async (q, limit, pid) => {
|
|
26345
|
-
const result = await compactSearch({ query: q, limit, projectId: pid, status: "active" });
|
|
26346
|
-
if (result.entries.length === 0) return [];
|
|
26347
|
-
const details = await compactDetail(result.entries.map((e) => e.id));
|
|
26348
|
-
return details.documents.map((d, i) => ({
|
|
26349
|
-
id: Number(d.id.replace("obs-", "")),
|
|
26350
|
-
observationId: d.observationId,
|
|
26351
|
-
title: d.title,
|
|
26352
|
-
narrative: d.narrative,
|
|
26353
|
-
facts: d.facts,
|
|
26354
|
-
entityName: d.entityName,
|
|
26355
|
-
type: d.type,
|
|
26356
|
-
score: result.entries[i]?.score ?? 0
|
|
26357
|
-
}));
|
|
26358
|
-
},
|
|
26359
|
-
getObservation: (id) => {
|
|
26360
|
-
const o = getObservation(id);
|
|
26361
|
-
if (!o) return null;
|
|
26362
|
-
return {
|
|
26363
|
-
id: o.id,
|
|
26364
|
-
entityName: o.entityName,
|
|
26365
|
-
type: o.type,
|
|
26366
|
-
title: o.title,
|
|
26367
|
-
narrative: o.narrative,
|
|
26368
|
-
facts: o.facts,
|
|
26369
|
-
topicKey: o.topicKey
|
|
26370
|
-
};
|
|
26371
|
-
},
|
|
26372
|
-
getEntityNames: () => graphManager.getEntityNames(),
|
|
26373
|
-
onStageEvent: onFormationStageEvent
|
|
26374
|
-
};
|
|
26375
|
-
formationResult = await withTimeout2(
|
|
26376
|
-
runFormation({
|
|
26377
|
-
entityName,
|
|
26378
|
-
type,
|
|
26379
|
-
title,
|
|
26380
|
-
narrative,
|
|
26381
|
-
facts: safeFacts,
|
|
26382
|
-
projectId: project.id,
|
|
26383
|
-
source: "explicit"
|
|
26384
|
-
}, formationConfig),
|
|
26385
|
-
FORMATION_TIMEOUT_MS,
|
|
26386
|
-
"Formation pipeline"
|
|
26387
|
-
);
|
|
26388
|
-
const modeIcon = "[FAST]";
|
|
26389
|
-
formationNote = `
|
|
26390
|
-
${modeIcon} Formation[active]: ${formationResult.evaluation.category} (${formationResult.evaluation.score.toFixed(2)}) | ${formationResult.resolution.action} | ${formationResult.pipeline.durationMs}ms`;
|
|
26391
|
-
if (formationResult.extraction.extractedFacts.length > 0) {
|
|
26392
|
-
formationNote += ` | +${formationResult.extraction.extractedFacts.length} facts`;
|
|
26393
|
-
}
|
|
26394
|
-
if (formationResult.extraction.titleImproved) formationNote += " | title\u2191";
|
|
26395
|
-
if (formationResult.extraction.entityResolved) formationNote += ` | entity\u2192${formationResult.entityName}`;
|
|
26396
|
-
if (formationResult.extraction.typeCorrected) formationNote += ` | type\u2192${formationResult.type}`;
|
|
26397
|
-
} catch (formationErr) {
|
|
26398
|
-
const isTimeout = formationErr instanceof Error && formationErr.message.includes("timed out");
|
|
26399
|
-
const elapsedMs = Date.now() - formationStartTime;
|
|
26400
|
-
const stageSummary = formatFormationStageDurations(completedFormationStages);
|
|
26401
|
-
console.error(
|
|
26402
|
-
`[memorix] Formation ${isTimeout ? "timed out" : "failed"} in memorix_store after ${elapsedMs}ms/${FORMATION_TIMEOUT_MS}ms at stage ${currentFormationStage}${stageSummary ? ` | completed: ${stageSummary}` : ""}`
|
|
26403
|
-
);
|
|
26404
|
-
if (!isTimeout && formationErr instanceof Error) {
|
|
26405
|
-
console.error(`[memorix] Formation error: ${formationErr.message}`);
|
|
27068
|
+
const requestedVisibility = visibility ?? "project";
|
|
27069
|
+
const reader = getObservationReader();
|
|
27070
|
+
if (requestedVisibility !== "project" && !currentAgentId) {
|
|
27071
|
+
return {
|
|
27072
|
+
content: [{ type: "text", text: "Personal or team memory requires memorix_session_start with joinTeam=true so Memorix can bind an owner." }],
|
|
27073
|
+
isError: true
|
|
27074
|
+
};
|
|
27075
|
+
}
|
|
27076
|
+
if (requestedVisibility === "team" && !reader.isTeamMember) {
|
|
27077
|
+
return {
|
|
27078
|
+
content: [{ type: "text", text: "Team memory requires an active coordination membership in the current project." }],
|
|
27079
|
+
isError: true
|
|
27080
|
+
};
|
|
27081
|
+
}
|
|
27082
|
+
try {
|
|
27083
|
+
return await withFreshIndex(async () => {
|
|
27084
|
+
let entityName = rawEntityName;
|
|
27085
|
+
let type = rawType;
|
|
27086
|
+
let title = rawTitle;
|
|
27087
|
+
let safeFacts = facts ? coerceStringArray(facts) : void 0;
|
|
27088
|
+
const safeFiles = filesModified ? coerceStringArray(filesModified) : void 0;
|
|
27089
|
+
const safeConcepts = concepts ? coerceStringArray(concepts) : void 0;
|
|
27090
|
+
let formationMode = "active";
|
|
27091
|
+
if (process.env.MEMORIX_FORMATION_MODE) {
|
|
27092
|
+
formationMode = process.env.MEMORIX_FORMATION_MODE;
|
|
27093
|
+
} else {
|
|
27094
|
+
try {
|
|
27095
|
+
const { getBehaviorConfig: getBehaviorConfig2 } = await Promise.resolve().then(() => (init_behavior(), behavior_exports));
|
|
27096
|
+
formationMode = getBehaviorConfig2({ projectRoot: project.rootPath }).formationMode;
|
|
27097
|
+
} catch {
|
|
26406
27098
|
}
|
|
26407
|
-
formationNote = `
|
|
26408
|
-
[WARN] Formation ${isTimeout ? "timed out" : "failed"} \u2014 storing base observation without enrichment`;
|
|
26409
27099
|
}
|
|
26410
|
-
|
|
26411
|
-
|
|
26412
|
-
|
|
26413
|
-
if (
|
|
26414
|
-
|
|
26415
|
-
|
|
26416
|
-
|
|
26417
|
-
|
|
26418
|
-
|
|
26419
|
-
|
|
26420
|
-
|
|
26421
|
-
|
|
26422
|
-
|
|
26423
|
-
|
|
26424
|
-
|
|
26425
|
-
|
|
26426
|
-
progress,
|
|
26427
|
-
sourceDetail: "explicit",
|
|
26428
|
-
createdByAgentId: currentAgentId
|
|
26429
|
-
});
|
|
26430
|
-
return {
|
|
26431
|
-
content: [{
|
|
26432
|
-
type: "text",
|
|
26433
|
-
text: `[UPDATED] Formation MERGE: merged into #${targetId} (${reason})${formationNote}`
|
|
26434
|
-
}]
|
|
26435
|
-
};
|
|
26436
|
-
}
|
|
26437
|
-
} else if (action2 === "evolve" && targetId) {
|
|
26438
|
-
const targetObs = getObservation(targetId);
|
|
26439
|
-
if (targetObs) {
|
|
26440
|
-
await storeObservation({
|
|
26441
|
-
entityName: targetObs.entityName,
|
|
26442
|
-
type: targetObs.type,
|
|
26443
|
-
title: formationResult.title,
|
|
26444
|
-
narrative: formationResult.narrative,
|
|
26445
|
-
facts: formationResult.facts,
|
|
26446
|
-
filesModified: safeFiles,
|
|
26447
|
-
concepts: safeConcepts,
|
|
26448
|
-
projectId: project.id,
|
|
26449
|
-
topicKey: targetObs.topicKey,
|
|
26450
|
-
progress,
|
|
26451
|
-
sourceDetail: "explicit",
|
|
26452
|
-
createdByAgentId: currentAgentId
|
|
26453
|
-
});
|
|
26454
|
-
return {
|
|
26455
|
-
content: [{
|
|
26456
|
-
type: "text",
|
|
26457
|
-
text: `[UPDATED] Formation EVOLVE: evolved #${targetId} (${reason})${formationNote}`
|
|
26458
|
-
}]
|
|
26459
|
-
};
|
|
26460
|
-
}
|
|
26461
|
-
} else if (action2 === "discard") {
|
|
26462
|
-
return {
|
|
26463
|
-
content: [{
|
|
26464
|
-
type: "text",
|
|
26465
|
-
text: `[SKIP] Formation DISCARD: ${reason}${formationNote}`
|
|
26466
|
-
}]
|
|
27100
|
+
const useFormation = formationMode === "active";
|
|
27101
|
+
let formationResult = null;
|
|
27102
|
+
let formationNote = "";
|
|
27103
|
+
if (useFormation && !topicKey && !progress) {
|
|
27104
|
+
let currentFormationStage = "setup";
|
|
27105
|
+
const completedFormationStages = {};
|
|
27106
|
+
const formationStartTime = Date.now();
|
|
27107
|
+
const onFormationStageEvent = (event) => {
|
|
27108
|
+
if (event.status === "start") {
|
|
27109
|
+
currentFormationStage = event.stage;
|
|
27110
|
+
return;
|
|
27111
|
+
}
|
|
27112
|
+
currentFormationStage = event.stage;
|
|
27113
|
+
if (event.stageDurationMs !== void 0) {
|
|
27114
|
+
completedFormationStages[event.stage] = event.stageDurationMs;
|
|
27115
|
+
}
|
|
26467
27116
|
};
|
|
26468
|
-
|
|
26469
|
-
|
|
26470
|
-
|
|
26471
|
-
|
|
26472
|
-
|
|
26473
|
-
|
|
26474
|
-
|
|
26475
|
-
|
|
26476
|
-
|
|
26477
|
-
|
|
26478
|
-
|
|
26479
|
-
|
|
26480
|
-
|
|
26481
|
-
|
|
26482
|
-
|
|
26483
|
-
|
|
26484
|
-
|
|
26485
|
-
|
|
26486
|
-
|
|
26487
|
-
|
|
26488
|
-
|
|
26489
|
-
|
|
26490
|
-
|
|
26491
|
-
const decision = await withTimeout2(
|
|
26492
|
-
compactOnWrite(
|
|
26493
|
-
{ title, narrative, facts: safeFacts ?? [] },
|
|
26494
|
-
existingMemories
|
|
26495
|
-
),
|
|
26496
|
-
COMPACT_ON_WRITE_TIMEOUT_MS,
|
|
26497
|
-
"Compact-on-write"
|
|
26498
|
-
);
|
|
26499
|
-
if (decision.action === "UPDATE" && decision.targetId) {
|
|
26500
|
-
const targetObs = getObservation(decision.targetId);
|
|
26501
|
-
if (targetObs) {
|
|
26502
|
-
await storeObservation({
|
|
26503
|
-
entityName: targetObs.entityName,
|
|
26504
|
-
type: targetObs.type,
|
|
26505
|
-
title: decision.mergedNarrative ? title : targetObs.title,
|
|
26506
|
-
narrative: decision.mergedNarrative ?? narrative,
|
|
26507
|
-
facts: decision.mergedFacts ?? safeFacts,
|
|
26508
|
-
filesModified: safeFiles,
|
|
26509
|
-
concepts: safeConcepts,
|
|
26510
|
-
projectId: project.id,
|
|
26511
|
-
topicKey: targetObs.topicKey,
|
|
26512
|
-
progress,
|
|
26513
|
-
sourceDetail: "explicit",
|
|
26514
|
-
createdByAgentId: currentAgentId
|
|
26515
|
-
});
|
|
26516
|
-
compactAction = `[UPDATED] Compact UPDATE: merged into #${decision.targetId} (${decision.reason})`;
|
|
26517
|
-
compactMerged = true;
|
|
27117
|
+
try {
|
|
27118
|
+
const formationConfig = {
|
|
27119
|
+
mode: "active",
|
|
27120
|
+
useLLM: isLLMEnabled(),
|
|
27121
|
+
minValueScore: 0.3,
|
|
27122
|
+
searchMemories: async (q, limit, pid) => {
|
|
27123
|
+
const result = await compactSearch({ query: q, limit, projectId: pid, status: "active", reader });
|
|
27124
|
+
if (result.entries.length === 0) return [];
|
|
27125
|
+
const details = await compactDetail(result.entries.map((e) => e.id), { reader });
|
|
27126
|
+
return details.documents.map((d, i) => ({
|
|
27127
|
+
id: Number(d.id.replace("obs-", "")),
|
|
27128
|
+
observationId: d.observationId,
|
|
27129
|
+
title: d.title,
|
|
27130
|
+
narrative: d.narrative,
|
|
27131
|
+
facts: d.facts,
|
|
27132
|
+
entityName: d.entityName,
|
|
27133
|
+
type: d.type,
|
|
27134
|
+
score: result.entries[i]?.score ?? 0
|
|
27135
|
+
}));
|
|
27136
|
+
},
|
|
27137
|
+
getObservation: (id) => {
|
|
27138
|
+
const o = getObservation(id);
|
|
27139
|
+
if (!o || o.projectId !== project.id || !canReadObservation(o, reader)) return null;
|
|
26518
27140
|
return {
|
|
26519
|
-
|
|
26520
|
-
|
|
26521
|
-
|
|
26522
|
-
|
|
26523
|
-
|
|
27141
|
+
id: o.id,
|
|
27142
|
+
entityName: o.entityName,
|
|
27143
|
+
type: o.type,
|
|
27144
|
+
title: o.title,
|
|
27145
|
+
narrative: o.narrative,
|
|
27146
|
+
facts: o.facts,
|
|
27147
|
+
topicKey: o.topicKey
|
|
26524
27148
|
};
|
|
26525
|
-
}
|
|
26526
|
-
|
|
27149
|
+
},
|
|
27150
|
+
getEntityNames: () => graphManager.getEntityNames(),
|
|
27151
|
+
onStageEvent: onFormationStageEvent
|
|
27152
|
+
};
|
|
27153
|
+
formationResult = await withTimeout2(
|
|
27154
|
+
runFormation({
|
|
27155
|
+
entityName,
|
|
27156
|
+
type,
|
|
27157
|
+
title,
|
|
27158
|
+
narrative,
|
|
27159
|
+
facts: safeFacts,
|
|
27160
|
+
projectId: project.id,
|
|
27161
|
+
source: "explicit"
|
|
27162
|
+
}, formationConfig),
|
|
27163
|
+
FORMATION_TIMEOUT_MS,
|
|
27164
|
+
"Formation pipeline"
|
|
27165
|
+
);
|
|
27166
|
+
const modeIcon = "[FAST]";
|
|
27167
|
+
formationNote = `
|
|
27168
|
+
${modeIcon} Formation[active]: ${formationResult.evaluation.category} (${formationResult.evaluation.score.toFixed(2)}) | ${formationResult.resolution.action} | ${formationResult.pipeline.durationMs}ms`;
|
|
27169
|
+
if (formationResult.extraction.extractedFacts.length > 0) {
|
|
27170
|
+
formationNote += ` | +${formationResult.extraction.extractedFacts.length} facts`;
|
|
27171
|
+
}
|
|
27172
|
+
if (formationResult.extraction.titleImproved) formationNote += " | title\u2191";
|
|
27173
|
+
if (formationResult.extraction.entityResolved) formationNote += ` | entity\u2192${formationResult.entityName}`;
|
|
27174
|
+
if (formationResult.extraction.typeCorrected) formationNote += ` | type\u2192${formationResult.type}`;
|
|
27175
|
+
} catch (formationErr) {
|
|
27176
|
+
const isTimeout = formationErr instanceof Error && formationErr.message.includes("timed out");
|
|
27177
|
+
const elapsedMs = Date.now() - formationStartTime;
|
|
27178
|
+
const stageSummary = formatFormationStageDurations(completedFormationStages);
|
|
27179
|
+
console.error(
|
|
27180
|
+
`[memorix] Formation ${isTimeout ? "timed out" : "failed"} in memorix_store after ${elapsedMs}ms/${FORMATION_TIMEOUT_MS}ms at stage ${currentFormationStage}${stageSummary ? ` | completed: ${stageSummary}` : ""}`
|
|
27181
|
+
);
|
|
27182
|
+
if (!isTimeout && formationErr instanceof Error) {
|
|
27183
|
+
console.error(`[memorix] Formation error: ${formationErr.message}`);
|
|
27184
|
+
}
|
|
27185
|
+
formationNote = `
|
|
27186
|
+
[WARN] Formation ${isTimeout ? "timed out" : "failed"} \u2014 storing base observation without enrichment`;
|
|
27187
|
+
}
|
|
27188
|
+
}
|
|
27189
|
+
if (useFormation && formationResult && formationResult.resolution.action !== "new") {
|
|
27190
|
+
const { action: action2, targetId, reason } = formationResult.resolution;
|
|
27191
|
+
if (action2 === "merge" && targetId) {
|
|
27192
|
+
const targetObs = getObservation(targetId);
|
|
27193
|
+
if (targetObs && targetObs.projectId === project.id && canReadObservation(targetObs, reader)) {
|
|
27194
|
+
await storeObservation({
|
|
27195
|
+
entityName: targetObs.entityName,
|
|
27196
|
+
type: targetObs.type,
|
|
27197
|
+
title: formationResult.title,
|
|
27198
|
+
narrative: formationResult.narrative,
|
|
27199
|
+
facts: formationResult.facts,
|
|
27200
|
+
filesModified: safeFiles,
|
|
27201
|
+
concepts: safeConcepts,
|
|
27202
|
+
projectId: project.id,
|
|
27203
|
+
topicKey: targetObs.topicKey,
|
|
27204
|
+
progress,
|
|
27205
|
+
sourceDetail: "explicit",
|
|
27206
|
+
createdByAgentId: currentAgentId,
|
|
27207
|
+
visibility,
|
|
27208
|
+
visibilityReader: reader
|
|
27209
|
+
});
|
|
26527
27210
|
return {
|
|
26528
27211
|
content: [{
|
|
26529
27212
|
type: "text",
|
|
26530
|
-
text: `[
|
|
26531
|
-
Existing memory #${decision.targetId} already covers this.
|
|
26532
|
-
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
27213
|
+
text: `[UPDATED] Formation MERGE: merged into #${targetId} (${reason})${formationNote}`
|
|
26533
27214
|
}]
|
|
26534
27215
|
};
|
|
26535
|
-
} else if (decision.action === "DELETE" && decision.targetId) {
|
|
26536
|
-
const { resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
26537
|
-
await resolveObservations2([decision.targetId], "resolved");
|
|
26538
|
-
compactAction = ` | Compact: resolved outdated #${decision.targetId}`;
|
|
26539
27216
|
}
|
|
26540
|
-
|
|
26541
|
-
|
|
26542
|
-
|
|
26543
|
-
|
|
26544
|
-
|
|
26545
|
-
|
|
27217
|
+
} else if (action2 === "evolve" && targetId) {
|
|
27218
|
+
const targetObs = getObservation(targetId);
|
|
27219
|
+
if (targetObs && targetObs.projectId === project.id && canReadObservation(targetObs, reader)) {
|
|
27220
|
+
await storeObservation({
|
|
27221
|
+
entityName: targetObs.entityName,
|
|
27222
|
+
type: targetObs.type,
|
|
27223
|
+
title: formationResult.title,
|
|
27224
|
+
narrative: formationResult.narrative,
|
|
27225
|
+
facts: formationResult.facts,
|
|
27226
|
+
filesModified: safeFiles,
|
|
27227
|
+
concepts: safeConcepts,
|
|
27228
|
+
projectId: project.id,
|
|
27229
|
+
topicKey: targetObs.topicKey,
|
|
27230
|
+
progress,
|
|
27231
|
+
sourceDetail: "explicit",
|
|
27232
|
+
createdByAgentId: currentAgentId,
|
|
27233
|
+
visibility,
|
|
27234
|
+
visibilityReader: reader
|
|
27235
|
+
});
|
|
27236
|
+
return {
|
|
27237
|
+
content: [{
|
|
27238
|
+
type: "text",
|
|
27239
|
+
text: `[UPDATED] Formation EVOLVE: evolved #${targetId} (${reason})${formationNote}`
|
|
27240
|
+
}]
|
|
27241
|
+
};
|
|
26546
27242
|
}
|
|
27243
|
+
} else if (action2 === "discard") {
|
|
27244
|
+
return {
|
|
27245
|
+
content: [{
|
|
27246
|
+
type: "text",
|
|
27247
|
+
text: `[SKIP] Formation DISCARD: ${reason}${formationNote}`
|
|
27248
|
+
}]
|
|
27249
|
+
};
|
|
26547
27250
|
}
|
|
26548
|
-
} catch {
|
|
26549
|
-
}
|
|
26550
|
-
}
|
|
26551
|
-
if (formationResult && formationResult.resolution.action === "new") {
|
|
26552
|
-
const llmFacts = formationResult.extraction.extractedFacts;
|
|
26553
|
-
if (llmFacts.length > 0) {
|
|
26554
|
-
const currentFacts = safeFacts ?? [];
|
|
26555
|
-
const currentLower = new Set(currentFacts.map((f) => f.toLowerCase().trim()));
|
|
26556
|
-
const newFacts = llmFacts.filter((f) => !currentLower.has(f.toLowerCase().trim()));
|
|
26557
|
-
if (newFacts.length > 0) {
|
|
26558
|
-
safeFacts = [...currentFacts, ...newFacts];
|
|
26559
|
-
}
|
|
26560
|
-
}
|
|
26561
|
-
if (formationResult.extraction.titleImproved && formationResult.title) {
|
|
26562
|
-
title = formationResult.title;
|
|
26563
|
-
}
|
|
26564
|
-
if (formationResult.extraction.typeCorrected && formationResult.type) {
|
|
26565
|
-
type = formationResult.type;
|
|
26566
|
-
}
|
|
26567
|
-
if (formationResult.extraction.entityResolved && formationResult.entityName) {
|
|
26568
|
-
entityName = formationResult.entityName;
|
|
26569
|
-
}
|
|
26570
|
-
}
|
|
26571
|
-
await graphManager.createEntities([
|
|
26572
|
-
{ name: entityName, entityType: "auto", observations: [] }
|
|
26573
|
-
]);
|
|
26574
|
-
let sessionId;
|
|
26575
|
-
try {
|
|
26576
|
-
const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
26577
|
-
const active = await getActiveSession2(projectDir2, project.id);
|
|
26578
|
-
if (active) sessionId = active.id;
|
|
26579
|
-
} catch {
|
|
26580
|
-
}
|
|
26581
|
-
let finalNarrative = narrative;
|
|
26582
|
-
let compressionNote = "";
|
|
26583
|
-
try {
|
|
26584
|
-
const { compressNarrative: compressNarrative2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
|
|
26585
|
-
const { compressed, saved, usedLLM } = await withTimeout2(
|
|
26586
|
-
compressNarrative2(narrative, safeFacts, type),
|
|
26587
|
-
COMPRESSION_TIMEOUT_MS,
|
|
26588
|
-
"Narrative compression"
|
|
26589
|
-
);
|
|
26590
|
-
if (usedLLM && saved > 0) {
|
|
26591
|
-
finalNarrative = compressed;
|
|
26592
|
-
compressionNote = ` | compressed -${saved} tokens`;
|
|
26593
|
-
}
|
|
26594
|
-
} catch {
|
|
26595
|
-
}
|
|
26596
|
-
let attributionWarning = "";
|
|
26597
|
-
try {
|
|
26598
|
-
const attrCheck = await checkProjectAttribution(entityName, project.id, getAllObservations());
|
|
26599
|
-
if (attrCheck.suspicious) {
|
|
26600
|
-
attributionWarning = `
|
|
26601
|
-
[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.`;
|
|
26602
27251
|
}
|
|
26603
|
-
|
|
26604
|
-
|
|
26605
|
-
|
|
26606
|
-
entityName,
|
|
26607
|
-
type,
|
|
26608
|
-
title,
|
|
26609
|
-
narrative: finalNarrative,
|
|
26610
|
-
facts: safeFacts,
|
|
26611
|
-
filesModified: safeFiles,
|
|
26612
|
-
concepts: safeConcepts,
|
|
26613
|
-
projectId: project.id,
|
|
26614
|
-
topicKey,
|
|
26615
|
-
sessionId,
|
|
26616
|
-
progress,
|
|
26617
|
-
relatedCommits,
|
|
26618
|
-
relatedEntities,
|
|
26619
|
-
sourceDetail: "explicit",
|
|
26620
|
-
valueCategory: formationResult?.evaluation.category,
|
|
26621
|
-
createdByAgentId: currentAgentId
|
|
26622
|
-
});
|
|
26623
|
-
await graphManager.addObservations([
|
|
26624
|
-
{ entityName, contents: [`[#${obs.id}] ${title}`] }
|
|
26625
|
-
]);
|
|
26626
|
-
const extracted = extractEntities([title, narrative, ...safeFacts ?? []].join(" "));
|
|
26627
|
-
const autoRelCount = await createAutoRelations(obs, extracted, graphManager);
|
|
26628
|
-
const enrichmentParts = [];
|
|
26629
|
-
const autoFiles = obs.filesModified.filter((f) => !(safeFiles ?? []).includes(f));
|
|
26630
|
-
const autoConcepts = obs.concepts.filter((c) => !(safeConcepts ?? []).includes(c));
|
|
26631
|
-
if (autoFiles.length > 0) enrichmentParts.push(`+${autoFiles.length} files extracted`);
|
|
26632
|
-
if (autoConcepts.length > 0) enrichmentParts.push(`+${autoConcepts.length} concepts enriched`);
|
|
26633
|
-
if (autoRelCount > 0) enrichmentParts.push(`+${autoRelCount} relations auto-created`);
|
|
26634
|
-
if (obs.hasCausalLanguage) enrichmentParts.push("causal language detected");
|
|
26635
|
-
if (upserted) enrichmentParts.push(`topic upserted (rev ${obs.revisionCount ?? 1})`);
|
|
26636
|
-
const enrichment = enrichmentParts.length > 0 ? `
|
|
26637
|
-
Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
26638
|
-
const action = upserted ? "[UPDATED] Updated" : "[OK] Stored";
|
|
26639
|
-
if (!useFormation && !topicKey && !progress) {
|
|
26640
|
-
const shadowFormation = async () => {
|
|
26641
|
-
let oldCompactDecision = null;
|
|
27252
|
+
let compactAction = "";
|
|
27253
|
+
let compactMerged = false;
|
|
27254
|
+
if (!useFormation && !topicKey && !progress) {
|
|
26642
27255
|
try {
|
|
26643
|
-
const compactStart = Date.now();
|
|
26644
27256
|
const searchResult = await compactSearch({
|
|
26645
27257
|
query: `${title} ${narrative.substring(0, 200)}`,
|
|
26646
27258
|
limit: 5,
|
|
26647
27259
|
projectId: project.id,
|
|
26648
|
-
status: "active"
|
|
27260
|
+
status: "active",
|
|
27261
|
+
reader
|
|
26649
27262
|
});
|
|
26650
27263
|
const similarEntries = searchResult.entries.map((e) => e);
|
|
26651
27264
|
if (similarEntries.length > 0) {
|
|
26652
27265
|
const similarIds = similarEntries.map((e) => e.id);
|
|
26653
|
-
const details = await compactDetail(similarIds);
|
|
27266
|
+
const details = await compactDetail(similarIds, { reader });
|
|
26654
27267
|
const existingMemories = details.documents.map((d, i) => ({
|
|
26655
27268
|
id: d.observationId,
|
|
26656
27269
|
title: d.title,
|
|
@@ -26658,78 +27271,266 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
|
26658
27271
|
facts: d.facts,
|
|
26659
27272
|
score: similarEntries[i]?.score ?? 0
|
|
26660
27273
|
}));
|
|
26661
|
-
const decision = await
|
|
26662
|
-
|
|
26663
|
-
|
|
27274
|
+
const decision = await withTimeout2(
|
|
27275
|
+
compactOnWrite(
|
|
27276
|
+
{ title, narrative, facts: safeFacts ?? [] },
|
|
27277
|
+
existingMemories
|
|
27278
|
+
),
|
|
27279
|
+
COMPACT_ON_WRITE_TIMEOUT_MS,
|
|
27280
|
+
"Compact-on-write"
|
|
26664
27281
|
);
|
|
26665
|
-
|
|
26666
|
-
|
|
26667
|
-
|
|
26668
|
-
|
|
26669
|
-
|
|
26670
|
-
|
|
27282
|
+
if (decision.action === "UPDATE" && decision.targetId) {
|
|
27283
|
+
const targetObs = getObservation(decision.targetId);
|
|
27284
|
+
if (targetObs && targetObs.projectId === project.id && canReadObservation(targetObs, reader)) {
|
|
27285
|
+
await storeObservation({
|
|
27286
|
+
entityName: targetObs.entityName,
|
|
27287
|
+
type: targetObs.type,
|
|
27288
|
+
title: decision.mergedNarrative ? title : targetObs.title,
|
|
27289
|
+
narrative: decision.mergedNarrative ?? narrative,
|
|
27290
|
+
facts: decision.mergedFacts ?? safeFacts,
|
|
27291
|
+
filesModified: safeFiles,
|
|
27292
|
+
concepts: safeConcepts,
|
|
27293
|
+
projectId: project.id,
|
|
27294
|
+
topicKey: targetObs.topicKey,
|
|
27295
|
+
progress,
|
|
27296
|
+
sourceDetail: "explicit",
|
|
27297
|
+
createdByAgentId: currentAgentId,
|
|
27298
|
+
visibility,
|
|
27299
|
+
visibilityReader: reader
|
|
27300
|
+
});
|
|
27301
|
+
compactAction = `[UPDATED] Compact UPDATE: merged into #${decision.targetId} (${decision.reason})`;
|
|
27302
|
+
compactMerged = true;
|
|
27303
|
+
return {
|
|
27304
|
+
content: [{
|
|
27305
|
+
type: "text",
|
|
27306
|
+
text: `${compactAction}
|
|
27307
|
+
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
27308
|
+
}]
|
|
27309
|
+
};
|
|
27310
|
+
}
|
|
27311
|
+
} else if (decision.action === "NONE") {
|
|
27312
|
+
return {
|
|
27313
|
+
content: [{
|
|
27314
|
+
type: "text",
|
|
27315
|
+
text: `[SKIP] Compact SKIP: ${decision.reason}
|
|
27316
|
+
Existing memory #${decision.targetId} already covers this.
|
|
27317
|
+
Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
27318
|
+
}]
|
|
27319
|
+
};
|
|
27320
|
+
} else if (decision.action === "DELETE" && decision.targetId) {
|
|
27321
|
+
const { resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27322
|
+
await resolveObservations2([decision.targetId], "resolved");
|
|
27323
|
+
compactAction = ` | Compact: resolved outdated #${decision.targetId}`;
|
|
27324
|
+
}
|
|
27325
|
+
if (decision.enrichedFacts && decision.enrichedFacts.length > 0) {
|
|
27326
|
+
const currentFacts = safeFacts ?? [];
|
|
27327
|
+
const newFacts = decision.enrichedFacts.filter((f) => !currentFacts.includes(f));
|
|
27328
|
+
if (newFacts.length > 0) {
|
|
27329
|
+
compactAction += ` | +${newFacts.length} LLM-extracted facts`;
|
|
27330
|
+
}
|
|
27331
|
+
}
|
|
26671
27332
|
}
|
|
26672
27333
|
} catch {
|
|
26673
27334
|
}
|
|
26674
|
-
|
|
26675
|
-
|
|
26676
|
-
|
|
26677
|
-
|
|
26678
|
-
|
|
26679
|
-
|
|
26680
|
-
|
|
26681
|
-
|
|
26682
|
-
|
|
26683
|
-
|
|
26684
|
-
|
|
26685
|
-
|
|
26686
|
-
|
|
26687
|
-
|
|
26688
|
-
|
|
26689
|
-
|
|
26690
|
-
|
|
26691
|
-
|
|
26692
|
-
|
|
26693
|
-
|
|
26694
|
-
|
|
26695
|
-
|
|
26696
|
-
|
|
26697
|
-
|
|
26698
|
-
|
|
26699
|
-
|
|
26700
|
-
const
|
|
26701
|
-
|
|
26702
|
-
|
|
26703
|
-
|
|
27335
|
+
}
|
|
27336
|
+
if (formationResult && formationResult.resolution.action === "new") {
|
|
27337
|
+
const llmFacts = formationResult.extraction.extractedFacts;
|
|
27338
|
+
if (llmFacts.length > 0) {
|
|
27339
|
+
const currentFacts = safeFacts ?? [];
|
|
27340
|
+
const currentLower = new Set(currentFacts.map((f) => f.toLowerCase().trim()));
|
|
27341
|
+
const newFacts = llmFacts.filter((f) => !currentLower.has(f.toLowerCase().trim()));
|
|
27342
|
+
if (newFacts.length > 0) {
|
|
27343
|
+
safeFacts = [...currentFacts, ...newFacts];
|
|
27344
|
+
}
|
|
27345
|
+
}
|
|
27346
|
+
if (formationResult.extraction.titleImproved && formationResult.title) {
|
|
27347
|
+
title = formationResult.title;
|
|
27348
|
+
}
|
|
27349
|
+
if (formationResult.extraction.typeCorrected && formationResult.type) {
|
|
27350
|
+
type = formationResult.type;
|
|
27351
|
+
}
|
|
27352
|
+
if (formationResult.extraction.entityResolved && formationResult.entityName) {
|
|
27353
|
+
entityName = formationResult.entityName;
|
|
27354
|
+
}
|
|
27355
|
+
}
|
|
27356
|
+
await graphManager.createEntities([
|
|
27357
|
+
{ name: entityName, entityType: "auto", observations: [] }
|
|
27358
|
+
]);
|
|
27359
|
+
let sessionId;
|
|
27360
|
+
try {
|
|
27361
|
+
const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
27362
|
+
const active = await getActiveSession2(projectDir2, project.id);
|
|
27363
|
+
if (active) sessionId = active.id;
|
|
27364
|
+
} catch {
|
|
27365
|
+
}
|
|
27366
|
+
let finalNarrative = narrative;
|
|
27367
|
+
let compressionNote = "";
|
|
27368
|
+
try {
|
|
27369
|
+
const { compressNarrative: compressNarrative2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
|
|
27370
|
+
const { compressed, saved, usedLLM } = await withTimeout2(
|
|
27371
|
+
compressNarrative2(narrative, safeFacts, type),
|
|
27372
|
+
COMPRESSION_TIMEOUT_MS,
|
|
27373
|
+
"Narrative compression"
|
|
26704
27374
|
);
|
|
26705
|
-
|
|
26706
|
-
|
|
26707
|
-
|
|
26708
|
-
formationAction: formed.resolution.action,
|
|
26709
|
-
formationTargetId: formed.resolution.targetId,
|
|
26710
|
-
oldCompactAction: oldCompactDecision.action,
|
|
26711
|
-
oldCompactTargetId: oldCompactDecision.targetId,
|
|
26712
|
-
oldCompactReason: oldCompactDecision.reason,
|
|
26713
|
-
formationValueScore: formed.evaluation.score,
|
|
26714
|
-
formationValueCategory: formed.evaluation.category,
|
|
26715
|
-
formationDurationMs: formed.pipeline.durationMs,
|
|
26716
|
-
compactDurationMs: oldCompactDecision.durationMs
|
|
26717
|
-
});
|
|
27375
|
+
if (usedLLM && saved > 0) {
|
|
27376
|
+
finalNarrative = compressed;
|
|
27377
|
+
compressionNote = ` | compressed -${saved} tokens`;
|
|
26718
27378
|
}
|
|
26719
|
-
}
|
|
26720
|
-
|
|
27379
|
+
} catch {
|
|
27380
|
+
}
|
|
27381
|
+
let attributionWarning = "";
|
|
27382
|
+
try {
|
|
27383
|
+
const attrCheck = await checkProjectAttribution(
|
|
27384
|
+
entityName,
|
|
27385
|
+
project.id,
|
|
27386
|
+
filterReadableObservations(getAllObservations(), reader)
|
|
27387
|
+
);
|
|
27388
|
+
if (attrCheck.suspicious) {
|
|
27389
|
+
attributionWarning = `
|
|
27390
|
+
[WARN] Attribution notice: entity "${entityName}" has 0 observations in "${project.id}" but ${attrCheck.count} in "${attrCheck.knownIn}" (confidence: ${attrCheck.confidence}). Verify the correct project is bound before storing.`;
|
|
27391
|
+
}
|
|
27392
|
+
} catch {
|
|
27393
|
+
}
|
|
27394
|
+
const { observation: obs, upserted } = await storeObservation({
|
|
27395
|
+
entityName,
|
|
27396
|
+
type,
|
|
27397
|
+
title,
|
|
27398
|
+
narrative: finalNarrative,
|
|
27399
|
+
facts: safeFacts,
|
|
27400
|
+
filesModified: safeFiles,
|
|
27401
|
+
concepts: safeConcepts,
|
|
27402
|
+
projectId: project.id,
|
|
27403
|
+
topicKey,
|
|
27404
|
+
sessionId,
|
|
27405
|
+
progress,
|
|
27406
|
+
relatedCommits,
|
|
27407
|
+
relatedEntities,
|
|
27408
|
+
sourceDetail: "explicit",
|
|
27409
|
+
valueCategory: formationResult?.evaluation.category,
|
|
27410
|
+
createdByAgentId: currentAgentId,
|
|
27411
|
+
visibility,
|
|
27412
|
+
visibilityReader: reader
|
|
26721
27413
|
});
|
|
26722
|
-
|
|
26723
|
-
|
|
26724
|
-
|
|
26725
|
-
|
|
26726
|
-
|
|
26727
|
-
|
|
27414
|
+
await graphManager.addObservations([
|
|
27415
|
+
{ entityName, contents: [`[#${obs.id}] ${title}`] }
|
|
27416
|
+
]);
|
|
27417
|
+
const extracted = extractEntities([title, narrative, ...safeFacts ?? []].join(" "));
|
|
27418
|
+
const autoRelCount = await createAutoRelations(obs, extracted, graphManager);
|
|
27419
|
+
const enrichmentParts = [];
|
|
27420
|
+
const autoFiles = obs.filesModified.filter((f) => !(safeFiles ?? []).includes(f));
|
|
27421
|
+
const autoConcepts = obs.concepts.filter((c) => !(safeConcepts ?? []).includes(c));
|
|
27422
|
+
if (autoFiles.length > 0) enrichmentParts.push(`+${autoFiles.length} files extracted`);
|
|
27423
|
+
if (autoConcepts.length > 0) enrichmentParts.push(`+${autoConcepts.length} concepts enriched`);
|
|
27424
|
+
if (autoRelCount > 0) enrichmentParts.push(`+${autoRelCount} relations auto-created`);
|
|
27425
|
+
if (obs.hasCausalLanguage) enrichmentParts.push("causal language detected");
|
|
27426
|
+
if (upserted) enrichmentParts.push(`topic upserted (rev ${obs.revisionCount ?? 1})`);
|
|
27427
|
+
const enrichment = enrichmentParts.length > 0 ? `
|
|
27428
|
+
Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
27429
|
+
const action = upserted ? "[UPDATED] Updated" : "[OK] Stored";
|
|
27430
|
+
if (!useFormation && !topicKey && !progress) {
|
|
27431
|
+
const shadowFormation = async () => {
|
|
27432
|
+
let oldCompactDecision = null;
|
|
27433
|
+
try {
|
|
27434
|
+
const compactStart = Date.now();
|
|
27435
|
+
const searchResult = await compactSearch({
|
|
27436
|
+
query: `${title} ${narrative.substring(0, 200)}`,
|
|
27437
|
+
limit: 5,
|
|
27438
|
+
projectId: project.id,
|
|
27439
|
+
status: "active",
|
|
27440
|
+
reader
|
|
27441
|
+
});
|
|
27442
|
+
const similarEntries = searchResult.entries.map((e) => e);
|
|
27443
|
+
if (similarEntries.length > 0) {
|
|
27444
|
+
const similarIds = similarEntries.map((e) => e.id);
|
|
27445
|
+
const details = await compactDetail(similarIds, { reader });
|
|
27446
|
+
const existingMemories = details.documents.map((d, i) => ({
|
|
27447
|
+
id: d.observationId,
|
|
27448
|
+
title: d.title,
|
|
27449
|
+
narrative: d.narrative,
|
|
27450
|
+
facts: d.facts,
|
|
27451
|
+
score: similarEntries[i]?.score ?? 0
|
|
27452
|
+
}));
|
|
27453
|
+
const decision = await compactOnWrite(
|
|
27454
|
+
{ title, narrative, facts: safeFacts ?? [] },
|
|
27455
|
+
existingMemories
|
|
27456
|
+
);
|
|
27457
|
+
oldCompactDecision = {
|
|
27458
|
+
action: decision.action,
|
|
27459
|
+
targetId: decision.targetId,
|
|
27460
|
+
reason: decision.reason,
|
|
27461
|
+
durationMs: Date.now() - compactStart
|
|
27462
|
+
};
|
|
27463
|
+
}
|
|
27464
|
+
} catch {
|
|
27465
|
+
}
|
|
27466
|
+
const formationConfig = {
|
|
27467
|
+
mode: formationMode,
|
|
27468
|
+
useLLM: isLLMEnabled(),
|
|
27469
|
+
minValueScore: 0.3,
|
|
27470
|
+
searchMemories: async (q, limit, pid) => {
|
|
27471
|
+
const result = await compactSearch({ query: q, limit, projectId: pid, status: "active", reader });
|
|
27472
|
+
if (result.entries.length === 0) return [];
|
|
27473
|
+
const details = await compactDetail(result.entries.map((e) => e.id), { reader });
|
|
27474
|
+
return details.documents.map((d, i) => ({
|
|
27475
|
+
id: Number(d.id.replace("obs-", "")),
|
|
27476
|
+
observationId: d.observationId,
|
|
27477
|
+
title: d.title,
|
|
27478
|
+
narrative: d.narrative,
|
|
27479
|
+
facts: d.facts,
|
|
27480
|
+
entityName: d.entityName,
|
|
27481
|
+
type: d.type,
|
|
27482
|
+
score: result.entries[i]?.score ?? 0
|
|
27483
|
+
}));
|
|
27484
|
+
},
|
|
27485
|
+
getObservation: (id) => {
|
|
27486
|
+
const o = getObservation(id);
|
|
27487
|
+
if (!o || o.projectId !== project.id || !canReadObservation(o, reader)) return null;
|
|
27488
|
+
return { id: o.id, entityName: o.entityName, type: o.type, title: o.title, narrative: o.narrative, facts: o.facts, topicKey: o.topicKey };
|
|
27489
|
+
},
|
|
27490
|
+
getEntityNames: () => graphManager.getEntityNames()
|
|
27491
|
+
};
|
|
27492
|
+
const formed = await withTimeout2(
|
|
27493
|
+
runFormation({ entityName, type, title, narrative, facts: safeFacts, projectId: project.id, source: "explicit", topicKey }, formationConfig),
|
|
27494
|
+
FORMATION_TIMEOUT_MS,
|
|
27495
|
+
"Shadow formation"
|
|
27496
|
+
);
|
|
27497
|
+
const { recordBeforeAfterMetrics: recordBeforeAfterMetrics2 } = await Promise.resolve().then(() => (init_formation(), formation_exports));
|
|
27498
|
+
if (oldCompactDecision) {
|
|
27499
|
+
recordBeforeAfterMetrics2({
|
|
27500
|
+
formationAction: formed.resolution.action,
|
|
27501
|
+
formationTargetId: formed.resolution.targetId,
|
|
27502
|
+
oldCompactAction: oldCompactDecision.action,
|
|
27503
|
+
oldCompactTargetId: oldCompactDecision.targetId,
|
|
27504
|
+
oldCompactReason: oldCompactDecision.reason,
|
|
27505
|
+
formationValueScore: formed.evaluation.score,
|
|
27506
|
+
formationValueCategory: formed.evaluation.category,
|
|
27507
|
+
formationDurationMs: formed.pipeline.durationMs,
|
|
27508
|
+
compactDurationMs: oldCompactDecision.durationMs
|
|
27509
|
+
});
|
|
27510
|
+
}
|
|
27511
|
+
};
|
|
27512
|
+
shadowFormation().catch(() => {
|
|
27513
|
+
});
|
|
27514
|
+
}
|
|
27515
|
+
return {
|
|
27516
|
+
content: [
|
|
27517
|
+
{
|
|
27518
|
+
type: "text",
|
|
27519
|
+
text: `${action} observation #${obs.id} "${title}" (~${obs.tokens} tokens)
|
|
26728
27520
|
Entity: ${entityName} | Type: ${type} | Project: ${project.id}${obs.topicKey ? ` | Topic: ${obs.topicKey}` : ""}${compactAction}${compressionNote}${enrichment}${formationNote}${attributionWarning}`
|
|
26729
|
-
|
|
26730
|
-
|
|
27521
|
+
}
|
|
27522
|
+
]
|
|
27523
|
+
};
|
|
27524
|
+
});
|
|
27525
|
+
} catch (error) {
|
|
27526
|
+
return {
|
|
27527
|
+
content: [{
|
|
27528
|
+
type: "text",
|
|
27529
|
+
text: error instanceof Error ? error.message : "Failed to store memory."
|
|
27530
|
+
}],
|
|
27531
|
+
isError: true
|
|
26731
27532
|
};
|
|
26732
|
-
}
|
|
27533
|
+
}
|
|
26733
27534
|
}
|
|
26734
27535
|
);
|
|
26735
27536
|
server.registerTool(
|
|
@@ -26801,7 +27602,8 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
26801
27602
|
// Use scope: 'global' to explicitly search all projects.
|
|
26802
27603
|
projectId: scope === "global" ? void 0 : project.id,
|
|
26803
27604
|
status: status ?? "active",
|
|
26804
|
-
source
|
|
27605
|
+
source,
|
|
27606
|
+
reader: getObservationReader(scope === "global" ? "global" : "project")
|
|
26805
27607
|
});
|
|
26806
27608
|
const timeoutPromise = new Promise(
|
|
26807
27609
|
(_, reject) => setTimeout(() => reject(new Error(`Search timeout after ${TIMEOUT_MS}ms`)), TIMEOUT_MS)
|
|
@@ -26859,7 +27661,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26859
27661
|
const unresolved = requireResolvedProject("build graph context for the current project");
|
|
26860
27662
|
if (unresolved) return unresolved;
|
|
26861
27663
|
const { getObservationStore: getObservationStore2 } = await Promise.resolve().then(() => (init_obs_store(), obs_store_exports));
|
|
26862
|
-
const observations2 =
|
|
27664
|
+
const observations2 = filterReadableObservations(
|
|
27665
|
+
await getObservationStore2().loadByProject(project.id),
|
|
27666
|
+
getObservationReader()
|
|
27667
|
+
);
|
|
26863
27668
|
const packet = buildGraphContextPacket(observations2, {
|
|
26864
27669
|
projectId: project.id,
|
|
26865
27670
|
query,
|
|
@@ -26911,7 +27716,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26911
27716
|
Promise.resolve().then(() => (init_maintenance_jobs(), maintenance_jobs_exports)),
|
|
26912
27717
|
Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports))
|
|
26913
27718
|
]);
|
|
26914
|
-
const observations2 =
|
|
27719
|
+
const observations2 = filterReadableObservations(
|
|
27720
|
+
await getObservationStore2().loadByProject(project.id, { status: "active" }),
|
|
27721
|
+
getObservationReader()
|
|
27722
|
+
);
|
|
26915
27723
|
const context = await buildAutoProjectContext2({
|
|
26916
27724
|
project,
|
|
26917
27725
|
dataDir: projectDir2,
|
|
@@ -26986,7 +27794,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
26986
27794
|
{ getResolvedConfig: getResolvedConfig2 },
|
|
26987
27795
|
{ getExternalCodeGraphContext: getExternalCodeGraphContext2 },
|
|
26988
27796
|
{ getObservationStore: getObservationStore2 },
|
|
26989
|
-
{ collectCurrentProjectFacts: collectCurrentProjectFacts2 },
|
|
27797
|
+
{ collectCurrentProjectFacts: collectCurrentProjectFacts2, formatGitFact: formatGitFact2 },
|
|
26990
27798
|
{ resolveTaskLens: resolveTaskLens2 }
|
|
26991
27799
|
] = await Promise.all([
|
|
26992
27800
|
Promise.resolve().then(() => (init_store(), store_exports)),
|
|
@@ -27001,7 +27809,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27001
27809
|
await store.init(projectDir2);
|
|
27002
27810
|
const codegraphConfig = getResolvedConfig2({ projectRoot: project.rootPath }).codegraph;
|
|
27003
27811
|
const exclude = codegraphConfig.excludePatterns;
|
|
27004
|
-
const observations2 =
|
|
27812
|
+
const observations2 = filterReadableObservations(
|
|
27813
|
+
await getObservationStore2().loadByProject(project.id, { status: "active" }),
|
|
27814
|
+
getObservationReader()
|
|
27815
|
+
);
|
|
27005
27816
|
observations2.reverse();
|
|
27006
27817
|
const basePack = assembleContextPackForTask2({
|
|
27007
27818
|
store,
|
|
@@ -27027,7 +27838,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27027
27838
|
if (currentFacts.latestChangelog) {
|
|
27028
27839
|
worksetFacts.push("Latest changelog: " + currentFacts.latestChangelog.version + (currentFacts.latestChangelog.date ? " (" + currentFacts.latestChangelog.date + ")" : ""));
|
|
27029
27840
|
}
|
|
27030
|
-
worksetFacts.push(
|
|
27841
|
+
worksetFacts.push(formatGitFact2(currentFacts.git));
|
|
27031
27842
|
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";
|
|
27032
27843
|
const pack = await attachTaskWorkset2({
|
|
27033
27844
|
pack: basePack,
|
|
@@ -27059,11 +27870,13 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27059
27870
|
"memorix_knowledge",
|
|
27060
27871
|
{
|
|
27061
27872
|
title: "Knowledge Workspace",
|
|
27062
|
-
description: "Manage the reviewable project Knowledge Workspace. Use it only for deliberate knowledge operations: initialize a local or versioned workspace,
|
|
27873
|
+
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.",
|
|
27063
27874
|
inputSchema: {
|
|
27064
27875
|
action: z2.enum([
|
|
27065
27876
|
"workspace_init",
|
|
27066
27877
|
"status",
|
|
27878
|
+
"claim_list",
|
|
27879
|
+
"claim_review",
|
|
27067
27880
|
"compile",
|
|
27068
27881
|
"lint",
|
|
27069
27882
|
"proposal_apply",
|
|
@@ -27078,6 +27891,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27078
27891
|
path: z2.string().optional().describe("Explicit versioned workspace path, used only by workspace_init"),
|
|
27079
27892
|
proposalId: z2.string().optional().describe("Pending proposal id for proposal_apply"),
|
|
27080
27893
|
allowManualOverwrite: z2.boolean().optional().default(false).describe("Explicitly allow proposal_apply to replace a manually edited page"),
|
|
27894
|
+
claimId: z2.string().optional().describe("Source-backed claim id for claim_review"),
|
|
27895
|
+
claimReviewState: z2.enum(["approved", "rejected"]).optional().describe("Deliberate review verdict for claim_review"),
|
|
27896
|
+
reviewDetail: z2.string().max(2e3).optional().describe("Evidence check performed before approving or rejecting a claim"),
|
|
27081
27897
|
workflowId: z2.string().optional().describe("Canonical workflow id for workflow preview, apply, or run"),
|
|
27082
27898
|
agent: z2.string().optional().describe("Target agent for a workflow adapter"),
|
|
27083
27899
|
task: z2.string().optional().describe("Task text for workflow selection or a workflow run"),
|
|
@@ -27094,6 +27910,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27094
27910
|
path: workspacePath,
|
|
27095
27911
|
proposalId,
|
|
27096
27912
|
allowManualOverwrite,
|
|
27913
|
+
claimId,
|
|
27914
|
+
claimReviewState,
|
|
27915
|
+
reviewDetail,
|
|
27097
27916
|
workflowId,
|
|
27098
27917
|
agent,
|
|
27099
27918
|
task,
|
|
@@ -27116,6 +27935,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27116
27935
|
};
|
|
27117
27936
|
const [
|
|
27118
27937
|
{ ClaimStore: ClaimStore2 },
|
|
27938
|
+
{ reviewClaim: reviewClaim2 },
|
|
27119
27939
|
{ CodeGraphStore: CodeGraphStore2 },
|
|
27120
27940
|
{ initializeKnowledgeWorkspace: initializeKnowledgeWorkspace2, loadKnowledgeWorkspace: loadKnowledgeWorkspace2 },
|
|
27121
27941
|
{ KnowledgeWorkspaceStore: KnowledgeWorkspaceStore2 },
|
|
@@ -27131,6 +27951,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27131
27951
|
}
|
|
27132
27952
|
] = await Promise.all([
|
|
27133
27953
|
Promise.resolve().then(() => (init_claim_store(), claim_store_exports)),
|
|
27954
|
+
Promise.resolve().then(() => (init_claims(), claims_exports)),
|
|
27134
27955
|
Promise.resolve().then(() => (init_store(), store_exports)),
|
|
27135
27956
|
Promise.resolve().then(() => (init_workspace(), workspace_exports)),
|
|
27136
27957
|
Promise.resolve().then(() => (init_workspace_store(), workspace_store_exports)),
|
|
@@ -27180,10 +28001,52 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27180
28001
|
targetPath: proposal.targetPath,
|
|
27181
28002
|
reason: proposal.reason,
|
|
27182
28003
|
createdAt: proposal.createdAt
|
|
27183
|
-
}))
|
|
28004
|
+
})),
|
|
28005
|
+
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 }))
|
|
27184
28006
|
}
|
|
27185
28007
|
});
|
|
27186
28008
|
}
|
|
28009
|
+
if (action === "claim_list") {
|
|
28010
|
+
return text({
|
|
28011
|
+
claims: claims.listClaims(project.id, { limit: 100 }).map((claim) => ({
|
|
28012
|
+
id: claim.id,
|
|
28013
|
+
subject: claim.subject,
|
|
28014
|
+
predicate: claim.predicate,
|
|
28015
|
+
objectValue: claim.objectValue,
|
|
28016
|
+
status: claim.status,
|
|
28017
|
+
reviewState: claim.reviewState,
|
|
28018
|
+
origin: claim.origin,
|
|
28019
|
+
confidence: claim.confidence,
|
|
28020
|
+
evidenceCount: claims.listEvidence(claim.id).length
|
|
28021
|
+
})),
|
|
28022
|
+
next: "Approve only after checking the linked evidence. Rejected claims stay out of retrieval and publication."
|
|
28023
|
+
});
|
|
28024
|
+
}
|
|
28025
|
+
if (action === "claim_review") {
|
|
28026
|
+
const requestedClaimId = requireText(claimId, "claimId");
|
|
28027
|
+
const detail = requireText(reviewDetail, "reviewDetail");
|
|
28028
|
+
if (!requestedClaimId || !claimReviewState || !detail) {
|
|
28029
|
+
return text({ error: "claimId, claimReviewState, and reviewDetail are required for claim_review." }, true);
|
|
28030
|
+
}
|
|
28031
|
+
const existing = claims.getClaim(requestedClaimId);
|
|
28032
|
+
if (!existing || existing.projectId !== project.id) {
|
|
28033
|
+
return text({ error: "Claim was not found for the current project." }, true);
|
|
28034
|
+
}
|
|
28035
|
+
const claim = reviewClaim2(claims, {
|
|
28036
|
+
claimId: requestedClaimId,
|
|
28037
|
+
reviewState: claimReviewState,
|
|
28038
|
+
detail
|
|
28039
|
+
});
|
|
28040
|
+
return text({
|
|
28041
|
+
claim: {
|
|
28042
|
+
id: claim.id,
|
|
28043
|
+
status: claim.status,
|
|
28044
|
+
reviewState: claim.reviewState,
|
|
28045
|
+
updatedAt: claim.updatedAt
|
|
28046
|
+
},
|
|
28047
|
+
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."
|
|
28048
|
+
});
|
|
28049
|
+
}
|
|
27187
28050
|
if (action === "compile") {
|
|
27188
28051
|
const result = await compileKnowledgeWorkspace2({ workspace, claims });
|
|
27189
28052
|
return text({
|
|
@@ -27222,7 +28085,13 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27222
28085
|
if (action === "workflow_import") {
|
|
27223
28086
|
const result = await importWindsurfWorkflows2({ workspace, projectRoot });
|
|
27224
28087
|
return text({
|
|
27225
|
-
imported: result.imported.map((workflow2) => ({
|
|
28088
|
+
imported: result.imported.map((workflow2) => ({
|
|
28089
|
+
id: workflow2.id,
|
|
28090
|
+
title: workflow2.title,
|
|
28091
|
+
sourcePath: workflow2.sourcePath,
|
|
28092
|
+
importedFrom: workflow2.importedFrom,
|
|
28093
|
+
verificationGates: workflow2.verificationGates
|
|
28094
|
+
})),
|
|
27226
28095
|
skipped: result.skipped
|
|
27227
28096
|
});
|
|
27228
28097
|
}
|
|
@@ -27234,7 +28103,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27234
28103
|
title: workflow2.title,
|
|
27235
28104
|
status: workflow2.status,
|
|
27236
28105
|
taskLenses: workflow2.taskLenses,
|
|
27237
|
-
sourcePath: workflow2.sourcePath
|
|
28106
|
+
sourcePath: workflow2.sourcePath,
|
|
28107
|
+
importedFrom: workflow2.importedFrom,
|
|
28108
|
+
verificationGates: workflow2.verificationGates
|
|
27238
28109
|
})),
|
|
27239
28110
|
parseErrors: synced.errors
|
|
27240
28111
|
});
|
|
@@ -27313,9 +28184,15 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27313
28184
|
}
|
|
27314
28185
|
},
|
|
27315
28186
|
async ({ ids, status }) => {
|
|
27316
|
-
const { resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28187
|
+
const { resolveObservations: resolveObservations2, getObservation: getObservation2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27317
28188
|
const safeIds = (Array.isArray(ids) ? ids : [ids]).map((id) => coerceNumber(id, 0)).filter((id) => id > 0);
|
|
27318
|
-
const
|
|
28189
|
+
const reader = getObservationReader();
|
|
28190
|
+
const authorizedIds = safeIds.filter((id) => {
|
|
28191
|
+
const observation = getObservation2(id, project.id);
|
|
28192
|
+
return observation ? canManageObservation(observation, reader) : false;
|
|
28193
|
+
});
|
|
28194
|
+
const result = await resolveObservations2(authorizedIds, status ?? "resolved");
|
|
28195
|
+
const deniedCount = safeIds.length - authorizedIds.length;
|
|
27319
28196
|
const parts = [];
|
|
27320
28197
|
if (result.resolved.length > 0) {
|
|
27321
28198
|
parts.push(`[OK] Resolved ${result.resolved.length} observation(s): #${result.resolved.join(", #")}`);
|
|
@@ -27323,6 +28200,9 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27323
28200
|
if (result.notFound.length > 0) {
|
|
27324
28201
|
parts.push(`[WARN] Not found: #${result.notFound.join(", #")}`);
|
|
27325
28202
|
}
|
|
28203
|
+
if (deniedCount > 0) {
|
|
28204
|
+
parts.push(`[WARN] Skipped ${deniedCount} observation(s) outside this session's write scope.`);
|
|
28205
|
+
}
|
|
27326
28206
|
parts.push('\nResolved memories are hidden from default search. Use status="all" to include them.');
|
|
27327
28207
|
parts.push('[STATS] Run `memorix_retention` with `action: "report"` to check remaining cleanup status.');
|
|
27328
28208
|
return {
|
|
@@ -27352,6 +28232,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27352
28232
|
async ({ entityName, decision, alternatives, rationale, constraints, expectedOutcome, risks, concepts, filesModified, relatedCommits, relatedEntities }) => {
|
|
27353
28233
|
const unresolved = requireResolvedProject("store reasoning in the current project");
|
|
27354
28234
|
if (unresolved) return unresolved;
|
|
28235
|
+
const reader = getObservationReader();
|
|
27355
28236
|
return withFreshIndex(async () => {
|
|
27356
28237
|
const narrativeParts = [rationale];
|
|
27357
28238
|
if (alternatives && alternatives.length > 0) {
|
|
@@ -27374,7 +28255,11 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27374
28255
|
]);
|
|
27375
28256
|
let reasoningAttributionWarning = "";
|
|
27376
28257
|
try {
|
|
27377
|
-
const attrCheck = await checkProjectAttribution(
|
|
28258
|
+
const attrCheck = await checkProjectAttribution(
|
|
28259
|
+
entityName,
|
|
28260
|
+
project.id,
|
|
28261
|
+
filterReadableObservations(getAllObservations(), reader)
|
|
28262
|
+
);
|
|
27378
28263
|
if (attrCheck.suspicious) {
|
|
27379
28264
|
reasoningAttributionWarning = `
|
|
27380
28265
|
[WARN] Attribution notice: entity "${entityName}" has 0 observations in "${project.id}" but ${attrCheck.count} in "${attrCheck.knownIn}" (confidence: ${attrCheck.confidence}). Verify the correct project is bound before storing.`;
|
|
@@ -27394,7 +28279,8 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
27394
28279
|
relatedCommits,
|
|
27395
28280
|
relatedEntities,
|
|
27396
28281
|
sourceDetail: "explicit",
|
|
27397
|
-
createdByAgentId: currentAgentId
|
|
28282
|
+
createdByAgentId: currentAgentId,
|
|
28283
|
+
visibility: "project"
|
|
27398
28284
|
});
|
|
27399
28285
|
await graphManager.addObservations([
|
|
27400
28286
|
{ entityName, contents: [`[#${obs.id}] [REASONING] ${decision}`] }
|
|
@@ -27426,7 +28312,11 @@ Entity: ${entityName} | ${facts.length} facts | ${obs.tokens} tokens${reasoningA
|
|
|
27426
28312
|
const minCount = threshold ?? 2;
|
|
27427
28313
|
let entries;
|
|
27428
28314
|
try {
|
|
27429
|
-
entries = await auditProjectObservations(
|
|
28315
|
+
entries = await auditProjectObservations(
|
|
28316
|
+
project.id,
|
|
28317
|
+
await withFreshIndex(() => filterReadableObservations(getAllObservations(), getObservationReader())),
|
|
28318
|
+
minCount
|
|
28319
|
+
);
|
|
27430
28320
|
} catch (err) {
|
|
27431
28321
|
return {
|
|
27432
28322
|
content: [{
|
|
@@ -27491,7 +28381,8 @@ Entity: ${entityName} | ${facts.length} facts | ${obs.tokens} tokens${reasoningA
|
|
|
27491
28381
|
limit: safeLimit,
|
|
27492
28382
|
type: "reasoning",
|
|
27493
28383
|
projectId: scope === "global" ? void 0 : project.id,
|
|
27494
|
-
status: "active"
|
|
28384
|
+
status: "active",
|
|
28385
|
+
reader: getObservationReader(scope === "global" ? "global" : "project")
|
|
27495
28386
|
}));
|
|
27496
28387
|
if (result.entries.length === 0) {
|
|
27497
28388
|
return {
|
|
@@ -27516,7 +28407,11 @@ ${result.formatted}` }]
|
|
|
27516
28407
|
},
|
|
27517
28408
|
async ({ query, dryRun }) => {
|
|
27518
28409
|
const { getAllObservations: getAllObservations2, resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27519
|
-
const
|
|
28410
|
+
const reader = getObservationReader();
|
|
28411
|
+
const allObs = await withFreshIndex(() => filterReadableObservations(
|
|
28412
|
+
getAllObservations2().filter((o) => (o.status ?? "active") === "active" && o.projectId === project.id),
|
|
28413
|
+
reader
|
|
28414
|
+
).filter((observation) => canManageObservation(observation, reader)));
|
|
27520
28415
|
if (allObs.length < 2) {
|
|
27521
28416
|
return { content: [{ type: "text", text: "Not enough active memories to deduplicate." }] };
|
|
27522
28417
|
}
|
|
@@ -27530,7 +28425,7 @@ ${result.formatted}` }]
|
|
|
27530
28425
|
}
|
|
27531
28426
|
let candidates;
|
|
27532
28427
|
if (query) {
|
|
27533
|
-
const searchResult = await compactSearch({ query, limit: 20, projectId: project.id, status: "active" });
|
|
28428
|
+
const searchResult = await compactSearch({ query, limit: 20, projectId: project.id, status: "active", reader });
|
|
27534
28429
|
const idSet = new Set(searchResult.entries.map((e) => e.id));
|
|
27535
28430
|
candidates = allObs.filter((o) => idSet.has(o.id));
|
|
27536
28431
|
} else {
|
|
@@ -27620,7 +28515,8 @@ ${actions.join("\n")}`
|
|
|
27620
28515
|
safeAnchor,
|
|
27621
28516
|
project.id,
|
|
27622
28517
|
safeBefore,
|
|
27623
|
-
safeAfter
|
|
28518
|
+
safeAfter,
|
|
28519
|
+
getObservationReader()
|
|
27624
28520
|
);
|
|
27625
28521
|
return {
|
|
27626
28522
|
content: [
|
|
@@ -27654,12 +28550,14 @@ ${actions.join("\n")}`
|
|
|
27654
28550
|
const safeTypedRefs = coerceStringArray(typedRefs);
|
|
27655
28551
|
let result;
|
|
27656
28552
|
try {
|
|
28553
|
+
const hasCrossProjectRef = safeRefs.some((ref) => ref.projectId && ref.projectId !== project.id) || safeTypedRefs.some((ref) => ref.includes("@") && !ref.endsWith(`@${project.id}`));
|
|
28554
|
+
const reader = getObservationReader(hasCrossProjectRef ? "global" : "project");
|
|
27657
28555
|
if (safeTypedRefs.length > 0) {
|
|
27658
|
-
result = await compactDetail(safeTypedRefs);
|
|
28556
|
+
result = await compactDetail(safeTypedRefs, { reader });
|
|
27659
28557
|
} else if (safeRefs.length > 0) {
|
|
27660
|
-
result = await compactDetail(safeRefs);
|
|
28558
|
+
result = await compactDetail(safeRefs, { reader });
|
|
27661
28559
|
} else {
|
|
27662
|
-
result = await compactDetail(safeIds.map((id) => ({ id, projectId: project.id })));
|
|
28560
|
+
result = await compactDetail(safeIds.map((id) => ({ id, projectId: project.id })), { reader });
|
|
27663
28561
|
}
|
|
27664
28562
|
} catch (err) {
|
|
27665
28563
|
return { content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }], isError: true };
|
|
@@ -27685,11 +28583,15 @@ ${actions.join("\n")}`
|
|
|
27685
28583
|
},
|
|
27686
28584
|
async (args) => {
|
|
27687
28585
|
const action = args.action ?? "report";
|
|
27688
|
-
const { getRetentionSummary: getRetentionSummary2, getArchiveCandidates: getArchiveCandidates2, rankByRelevance: rankByRelevance2,
|
|
28586
|
+
const { getRetentionSummary: getRetentionSummary2, getArchiveCandidates: getArchiveCandidates2, rankByRelevance: rankByRelevance2, getRetentionZone: getRetentionZone2, explainRetention: explainRetention2 } = await Promise.resolve().then(() => (init_retention(), retention_exports));
|
|
27689
28587
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_orama_store(), orama_store_exports));
|
|
27690
28588
|
const { search: search2 } = await import("@orama/orama");
|
|
27691
|
-
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
27692
|
-
const
|
|
28589
|
+
const { getAllObservations: getAllObservations2, resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28590
|
+
const reader = getObservationReader();
|
|
28591
|
+
const allObs = await withFreshIndex(() => filterReadableObservations(
|
|
28592
|
+
getAllObservations2().filter((observation) => observation.projectId === project.id),
|
|
28593
|
+
reader
|
|
28594
|
+
));
|
|
27693
28595
|
const accessMap = /* @__PURE__ */ new Map();
|
|
27694
28596
|
try {
|
|
27695
28597
|
const database = await getDb2();
|
|
@@ -27706,20 +28608,6 @@ ${actions.join("\n")}`
|
|
|
27706
28608
|
}
|
|
27707
28609
|
} catch {
|
|
27708
28610
|
}
|
|
27709
|
-
if (action === "archive") {
|
|
27710
|
-
const result = await archiveExpired2(projectDir2, void 0, accessMap, project.id);
|
|
27711
|
-
if (result.archived === 0) {
|
|
27712
|
-
return {
|
|
27713
|
-
content: [{ type: "text", text: "[OK] No expired observations to archive. All memories are within their retention period." }]
|
|
27714
|
-
};
|
|
27715
|
-
}
|
|
27716
|
-
return {
|
|
27717
|
-
content: [{ type: "text", text: `[ARCHIVED] Archived ${result.archived} expired observations (status set to 'archived' in-place)
|
|
27718
|
-
${result.remaining} active observations remaining.
|
|
27719
|
-
|
|
27720
|
-
Archived memories are hidden from default search but can be found with status: "all".` }]
|
|
27721
|
-
};
|
|
27722
|
-
}
|
|
27723
28611
|
const docs = allObs.map((obs) => ({
|
|
27724
28612
|
id: `obs-${obs.id}`,
|
|
27725
28613
|
observationId: obs.id,
|
|
@@ -27738,13 +28626,34 @@ Archived memories are hidden from default search but can be found with status: "
|
|
|
27738
28626
|
status: obs.status ?? "active",
|
|
27739
28627
|
source: obs.source ?? "agent",
|
|
27740
28628
|
sourceDetail: obs.sourceDetail ?? "",
|
|
27741
|
-
valueCategory: obs.valueCategory ?? ""
|
|
28629
|
+
valueCategory: obs.valueCategory ?? "",
|
|
28630
|
+
admissionState: obs.admissionState ?? "",
|
|
28631
|
+
admissionReason: obs.admissionReason ?? "",
|
|
28632
|
+
visibility: obs.visibility ?? "project",
|
|
28633
|
+
createdByAgentId: obs.createdByAgentId ?? "",
|
|
28634
|
+
sharedWithAgentIds: JSON.stringify(obs.sharedWithAgentIds ?? [])
|
|
27742
28635
|
}));
|
|
27743
28636
|
if (docs.length === 0) {
|
|
27744
28637
|
return {
|
|
27745
28638
|
content: [{ type: "text", text: "No observations found for this project." }]
|
|
27746
28639
|
};
|
|
27747
28640
|
}
|
|
28641
|
+
if (action === "archive") {
|
|
28642
|
+
const managedIds = new Set(
|
|
28643
|
+
allObs.filter((observation) => canManageObservation(observation, reader)).map((observation) => observation.id)
|
|
28644
|
+
);
|
|
28645
|
+
const candidates2 = getArchiveCandidates2(docs).filter((document) => managedIds.has(document.observationId));
|
|
28646
|
+
if (candidates2.length === 0) {
|
|
28647
|
+
return {
|
|
28648
|
+
content: [{ type: "text", text: "[OK] No expired memories in this session's write scope to archive." }]
|
|
28649
|
+
};
|
|
28650
|
+
}
|
|
28651
|
+
const result = await resolveObservations2(candidates2.map((document) => document.observationId), "archived");
|
|
28652
|
+
return {
|
|
28653
|
+
content: [{ type: "text", text: `[ARCHIVED] Archived ${result.resolved.length} expired observation(s) in this session's write scope.
|
|
28654
|
+
${Math.max(0, managedIds.size - result.resolved.length)} visible writable observations remaining.` }]
|
|
28655
|
+
};
|
|
28656
|
+
}
|
|
27748
28657
|
if (action === "stale") {
|
|
27749
28658
|
const staleDocs = docs.filter((d) => getRetentionZone2(d) === "stale");
|
|
27750
28659
|
if (staleDocs.length === 0) {
|
|
@@ -28063,13 +28972,14 @@ Archived memories are hidden from default search but can be found with status: "
|
|
|
28063
28972
|
async function scopeGraphToProject(graph) {
|
|
28064
28973
|
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28065
28974
|
const allObs = await withFreshIndex(() => getAllObservations2());
|
|
28066
|
-
const
|
|
28067
|
-
|
|
28975
|
+
const scoped = scopeKnowledgeGraphToProject(
|
|
28976
|
+
graph,
|
|
28977
|
+
filterReadableObservations(
|
|
28978
|
+
allObs.filter((observation) => observation.projectId === project.id),
|
|
28979
|
+
getObservationReader()
|
|
28980
|
+
)
|
|
28068
28981
|
);
|
|
28069
|
-
|
|
28070
|
-
const entityNameSet = new Set(entities.map((e) => e.name));
|
|
28071
|
-
const relations = graph.relations.filter((r) => entityNameSet.has(r.from) && entityNameSet.has(r.to));
|
|
28072
|
-
return { entities, relations };
|
|
28982
|
+
return { entities: scoped.entities, relations: scoped.relations };
|
|
28073
28983
|
}
|
|
28074
28984
|
server.registerTool(
|
|
28075
28985
|
"read_graph",
|
|
@@ -28342,7 +29252,10 @@ ${skill2.content}` }]
|
|
|
28342
29252
|
};
|
|
28343
29253
|
}
|
|
28344
29254
|
const { getObservationStore: getStore } = await Promise.resolve().then(() => (init_obs_store(), obs_store_exports));
|
|
28345
|
-
const allObs =
|
|
29255
|
+
const allObs = filterReadableObservations(
|
|
29256
|
+
(await getStore().loadAll()).filter((observation) => observation.projectId === project.id),
|
|
29257
|
+
getObservationReader()
|
|
29258
|
+
);
|
|
28346
29259
|
const obsData = allObs.map((o) => ({
|
|
28347
29260
|
id: o.id || 0,
|
|
28348
29261
|
entityName: o.entityName || "unknown",
|
|
@@ -28441,7 +29354,11 @@ ${skill2.content}` }]
|
|
|
28441
29354
|
}
|
|
28442
29355
|
const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
28443
29356
|
const allObs = await withFreshIndex(() => getAllObservations2());
|
|
28444
|
-
const
|
|
29357
|
+
const reader = getObservationReader();
|
|
29358
|
+
const matched = filterReadableObservations(
|
|
29359
|
+
allObs.filter((observation) => observation.projectId === project.id && observationIds.includes(observation.id)),
|
|
29360
|
+
reader
|
|
29361
|
+
);
|
|
28445
29362
|
if (matched.length === 0) {
|
|
28446
29363
|
return { content: [{ type: "text", text: `No observations found for IDs: [${observationIds.join(", ")}]. Use \`memorix_search\` to find valid IDs.` }], isError: true };
|
|
28447
29364
|
}
|
|
@@ -28449,6 +29366,13 @@ ${skill2.content}` }]
|
|
|
28449
29366
|
if (nonActive.length > 0) {
|
|
28450
29367
|
return { content: [{ type: "text", text: `Cannot promote: ${nonActive.length} observation(s) are not active: ${nonActive.map((o) => `#${o.id} (${o.status})`).join(", ")}. Only active observations can be promoted to permanent knowledge.` }], isError: true };
|
|
28451
29368
|
}
|
|
29369
|
+
const nonProjectShared = matched.filter((observation) => resolveObservationVisibility(observation) !== "project");
|
|
29370
|
+
if (nonProjectShared.length > 0) {
|
|
29371
|
+
return {
|
|
29372
|
+
content: [{ type: "text", text: `Cannot promote private or team-scoped observations: ${nonProjectShared.map((observation) => `#${observation.id}`).join(", ")}. Promote only deliberate project-shared knowledge.` }],
|
|
29373
|
+
isError: true
|
|
29374
|
+
};
|
|
29375
|
+
}
|
|
28452
29376
|
const skill2 = await promoteToMiniSkill2(projectDir2, project.id, matched, { trigger, instruction, tags });
|
|
28453
29377
|
const lines = [
|
|
28454
29378
|
`[OK] Created mini-skill #${skill2.id}`,
|
|
@@ -28607,8 +29531,11 @@ Ensure the path points to a directory containing a .git folder (or a subdirector
|
|
|
28607
29531
|
const lastSeen = registeredAgent.last_seen_obs_generation;
|
|
28608
29532
|
const store = getObservationStore();
|
|
28609
29533
|
const currentGen = store.getGeneration();
|
|
28610
|
-
const projectObs = await withFreshIndex(() =>
|
|
28611
|
-
(
|
|
29534
|
+
const projectObs = await withFreshIndex(() => filterReadableObservations(
|
|
29535
|
+
getAllObservations().filter(
|
|
29536
|
+
(o) => o.projectId === project.id && (o.writeGeneration ?? 0) > lastSeen
|
|
29537
|
+
),
|
|
29538
|
+
getObservationReader()
|
|
28612
29539
|
));
|
|
28613
29540
|
const wm = computeWatermark2(lastSeen, currentGen, projectObs.length);
|
|
28614
29541
|
if (wm.newObservationCount > 0) {
|
|
@@ -28777,7 +29704,7 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
28777
29704
|
"memorix_transfer",
|
|
28778
29705
|
{
|
|
28779
29706
|
title: "Transfer Memories",
|
|
28780
|
-
description: 'Export or import project memories. Action "export": export observations and sessions (JSON or Markdown). Action "import": import from a JSON export (re-assigns IDs, skips duplicate topicKeys).',
|
|
29707
|
+
description: 'Export or import project memories. Action "export": export observations visible to the current agent and project sessions (JSON or Markdown). Action "import": import from a JSON export (re-assigns IDs, skips duplicate topicKeys).',
|
|
28781
29708
|
inputSchema: {
|
|
28782
29709
|
action: z2.enum(["export", "import"]).describe("Operation: export or import"),
|
|
28783
29710
|
format: z2.enum(["json", "markdown"]).optional().describe("Export format (for export, default: json)"),
|
|
@@ -28788,10 +29715,10 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
28788
29715
|
if (action === "export") {
|
|
28789
29716
|
const { exportAsJson: exportAsJson2, exportAsMarkdown: exportAsMarkdown2 } = await Promise.resolve().then(() => (init_export_import(), export_import_exports));
|
|
28790
29717
|
if (format === "markdown") {
|
|
28791
|
-
const md = await exportAsMarkdown2(projectDir2, project.id);
|
|
29718
|
+
const md = await exportAsMarkdown2(projectDir2, project.id, getObservationReader());
|
|
28792
29719
|
return { content: [{ type: "text", text: md }] };
|
|
28793
29720
|
}
|
|
28794
|
-
const data = await exportAsJson2(projectDir2, project.id);
|
|
29721
|
+
const data = await exportAsJson2(projectDir2, project.id, getObservationReader());
|
|
28795
29722
|
const json = JSON.stringify(data, null, 2);
|
|
28796
29723
|
return {
|
|
28797
29724
|
content: [{
|
|
@@ -28988,8 +29915,6 @@ ${json}
|
|
|
28988
29915
|
}
|
|
28989
29916
|
}
|
|
28990
29917
|
);
|
|
28991
|
-
let teamStore;
|
|
28992
|
-
let initTeamStoreForProject;
|
|
28993
29918
|
if (teamFeaturesEnabled) {
|
|
28994
29919
|
const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
|
|
28995
29920
|
initTeamStoreForProject = initTeamStore2;
|
|
@@ -29262,7 +30187,7 @@ ${lines.join("\n")}` }] };
|
|
|
29262
30187
|
description: `Send, broadcast, or read messages between agents. Durable: messages survive restarts and reach inactive recipients. Action "send": direct message to one agent. Action "broadcast": message all agents. Action "inbox": read this agent's inbox.`,
|
|
29263
30188
|
inputSchema: {
|
|
29264
30189
|
action: z2.enum(["send", "broadcast", "inbox"]).describe("Operation to perform"),
|
|
29265
|
-
from: z2.string().optional().describe("
|
|
30190
|
+
from: z2.string().optional().describe("Your sender agent ID. Omit it to use this session identity."),
|
|
29266
30191
|
to: z2.string().optional().describe("Receiver agent ID (for send)"),
|
|
29267
30192
|
type: z2.enum(["request", "response", "info", "announcement", "contract", "error", "handoff"]).optional().describe("Message type (for send/broadcast)"),
|
|
29268
30193
|
content: z2.string().optional().describe("Message content (for send/broadcast)"),
|
|
@@ -29273,13 +30198,20 @@ ${lines.join("\n")}` }] };
|
|
|
29273
30198
|
}
|
|
29274
30199
|
},
|
|
29275
30200
|
async ({ action, from, to, type: msgType, content, agentId, markRead, toRole, handoffStatus }) => {
|
|
30201
|
+
const requireCurrentTeamAgent = () => {
|
|
30202
|
+
if (!currentAgentId) return null;
|
|
30203
|
+
const agent = teamStore.getAgent(currentAgentId);
|
|
30204
|
+
return agent?.project_id === project.id && agent.status === "active" ? agent : null;
|
|
30205
|
+
};
|
|
29276
30206
|
if (action === "send") {
|
|
29277
|
-
|
|
30207
|
+
const sender = requireCurrentTeamAgent();
|
|
30208
|
+
if (!sender || !msgType || !content) return { content: [{ type: "text", text: "[ERROR] active session identity, type, and content required for send" }], isError: true };
|
|
30209
|
+
if (from && from !== currentAgentId) return { content: [{ type: "text", text: "[ERROR] from must match the current session identity" }], isError: true };
|
|
29278
30210
|
if (!to && !toRole) return { content: [{ type: "text", text: "[ERROR] either to (agent ID) or toRole is required for send" }], isError: true };
|
|
29279
30211
|
if (content.length > 1e4) return { content: [{ type: "text", text: "[ERROR] Message too large (max 10KB)" }], isError: true };
|
|
29280
30212
|
const msg = teamStore.sendMessage({
|
|
29281
30213
|
projectId: project.id,
|
|
29282
|
-
senderAgentId:
|
|
30214
|
+
senderAgentId: currentAgentId,
|
|
29283
30215
|
recipientAgentId: to ?? null,
|
|
29284
30216
|
type: msgType,
|
|
29285
30217
|
content,
|
|
@@ -29291,11 +30223,13 @@ ${lines.join("\n")}` }] };
|
|
|
29291
30223
|
return { content: [{ type: "text", text: `Message sent (${msgType}) to ${target} | ID: ${msg.id.slice(0, 8)}\u2026${toRole ? ` [role: ${toRole}]` : ""}` }] };
|
|
29292
30224
|
}
|
|
29293
30225
|
if (action === "broadcast") {
|
|
29294
|
-
|
|
30226
|
+
const sender = requireCurrentTeamAgent();
|
|
30227
|
+
if (!sender || !msgType || !content) return { content: [{ type: "text", text: "[ERROR] active session identity, type, and content required for broadcast" }], isError: true };
|
|
30228
|
+
if (from && from !== currentAgentId) return { content: [{ type: "text", text: "[ERROR] from must match the current session identity" }], isError: true };
|
|
29295
30229
|
if (content.length > 1e4) return { content: [{ type: "text", text: "[ERROR] Message too large (max 10KB)" }], isError: true };
|
|
29296
30230
|
const msg = teamStore.sendMessage({
|
|
29297
30231
|
projectId: project.id,
|
|
29298
|
-
senderAgentId:
|
|
30232
|
+
senderAgentId: currentAgentId,
|
|
29299
30233
|
recipientAgentId: null,
|
|
29300
30234
|
type: msgType,
|
|
29301
30235
|
content
|
|
@@ -29303,8 +30237,12 @@ ${lines.join("\n")}` }] };
|
|
|
29303
30237
|
if ("error" in msg) return { content: [{ type: "text", text: `[ERROR] ${msg.error}` }], isError: true };
|
|
29304
30238
|
return { content: [{ type: "text", text: `Broadcast (${msgType}) | ID: ${msg.id.slice(0, 8)}\u2026` }] };
|
|
29305
30239
|
}
|
|
29306
|
-
const
|
|
29307
|
-
if (!
|
|
30240
|
+
const inboxAgent = requireCurrentTeamAgent();
|
|
30241
|
+
if (!inboxAgent) return { content: [{ type: "text", text: "[ERROR] active session identity required for inbox" }], isError: true };
|
|
30242
|
+
if (agentId && agentId !== currentAgentId || from && from !== currentAgentId) {
|
|
30243
|
+
return { content: [{ type: "text", text: "[ERROR] inbox access is limited to the current session identity" }], isError: true };
|
|
30244
|
+
}
|
|
30245
|
+
const inboxId = currentAgentId;
|
|
29308
30246
|
const inbox = teamStore.getInbox(project.id, inboxId);
|
|
29309
30247
|
const unread = teamStore.getUnreadCount(project.id, inboxId);
|
|
29310
30248
|
if (inbox.length === 0) return { content: [{ type: "text", text: "Inbox empty" }] };
|
|
@@ -29332,24 +30270,34 @@ ${lines.join("\n")}` }] };
|
|
|
29332
30270
|
},
|
|
29333
30271
|
async ({ agentId, markInboxRead }) => {
|
|
29334
30272
|
const { computeWatermark: computeWatermark2, computePoll: computePoll2 } = await Promise.resolve().then(() => (init_poll(), poll_exports));
|
|
30273
|
+
if (agentId && agentId !== currentAgentId) {
|
|
30274
|
+
return {
|
|
30275
|
+
content: [{ type: "text", text: "[ERROR] agentId must match the current session identity." }],
|
|
30276
|
+
isError: true
|
|
30277
|
+
};
|
|
30278
|
+
}
|
|
30279
|
+
const effectiveAgentId = currentAgentId;
|
|
29335
30280
|
let watermark = computeWatermark2(0, 0, 0);
|
|
29336
|
-
if (
|
|
29337
|
-
const agent = teamStore.getAgent(
|
|
30281
|
+
if (effectiveAgentId) {
|
|
30282
|
+
const agent = teamStore.getAgent(effectiveAgentId);
|
|
29338
30283
|
if (agent) {
|
|
29339
30284
|
const lastSeen = agent.last_seen_obs_generation;
|
|
29340
30285
|
const store = getObservationStore();
|
|
29341
30286
|
const currentGen = store.getGeneration();
|
|
29342
|
-
const projectObs = await withFreshIndex(() =>
|
|
29343
|
-
(
|
|
30287
|
+
const projectObs = await withFreshIndex(() => filterReadableObservations(
|
|
30288
|
+
getAllObservations().filter(
|
|
30289
|
+
(o) => o.projectId === project.id && (o.writeGeneration ?? 0) > lastSeen
|
|
30290
|
+
),
|
|
30291
|
+
getObservationReader()
|
|
29344
30292
|
));
|
|
29345
30293
|
watermark = computeWatermark2(lastSeen, currentGen, projectObs.length);
|
|
29346
|
-
teamStore.updateWatermark(
|
|
29347
|
-
teamStore.heartbeat(
|
|
30294
|
+
teamStore.updateWatermark(effectiveAgentId, currentGen);
|
|
30295
|
+
teamStore.heartbeat(effectiveAgentId);
|
|
29348
30296
|
}
|
|
29349
30297
|
}
|
|
29350
|
-
const poll = computePoll2(teamStore, project.id,
|
|
29351
|
-
if (markInboxRead &&
|
|
29352
|
-
teamStore.markAllRead(project.id,
|
|
30298
|
+
const poll = computePoll2(teamStore, project.id, effectiveAgentId ?? null, watermark);
|
|
30299
|
+
if (markInboxRead && effectiveAgentId) {
|
|
30300
|
+
teamStore.markAllRead(project.id, effectiveAgentId);
|
|
29353
30301
|
}
|
|
29354
30302
|
const lines = [];
|
|
29355
30303
|
if (poll.agent) {
|
|
@@ -29409,7 +30357,7 @@ ${lines.join("\n")}` }] };
|
|
|
29409
30357
|
"memorix_handoff",
|
|
29410
30358
|
{
|
|
29411
30359
|
title: "Team Handoff \u2014 Agent Context Transfer",
|
|
29412
|
-
description: "Create a structured handoff artifact when passing work to another agent. The handoff is stored as a durable observation (
|
|
30360
|
+
description: "Create a structured handoff artifact when passing work to another agent. A targeted handoff is visible only to its sender and recipient; a broadcast handoff is team-visible. The handoff is stored as a durable observation (immune to archival) and a notification message is sent to the recipient. Use this when completing a task and another agent should continue, or when you want to leave context for whoever works on this next.",
|
|
29413
30361
|
inputSchema: {
|
|
29414
30362
|
fromAgentId: z2.string().describe("Your agent ID (from team_manage join or session_start with joinTeam=true)"),
|
|
29415
30363
|
summary: z2.string().describe("Human-readable summary of what you did and what needs to happen next"),
|
|
@@ -29422,6 +30370,34 @@ ${lines.join("\n")}` }] };
|
|
|
29422
30370
|
},
|
|
29423
30371
|
async ({ fromAgentId, summary, context, toAgentId, taskId, filesModified, concepts }) => {
|
|
29424
30372
|
const { createHandoffArtifact: createHandoffArtifact2 } = await Promise.resolve().then(() => (init_handoff(), handoff_exports));
|
|
30373
|
+
if (!currentAgentId) {
|
|
30374
|
+
return {
|
|
30375
|
+
content: [{ type: "text", text: "Create a coordination identity first: call memorix_session_start with joinTeam=true." }],
|
|
30376
|
+
isError: true
|
|
30377
|
+
};
|
|
30378
|
+
}
|
|
30379
|
+
if (fromAgentId !== currentAgentId) {
|
|
30380
|
+
return {
|
|
30381
|
+
content: [{ type: "text", text: "fromAgentId must match the identity returned for this session. Memorix will not create a handoff on behalf of another agent." }],
|
|
30382
|
+
isError: true
|
|
30383
|
+
};
|
|
30384
|
+
}
|
|
30385
|
+
const sender = teamStore.getAgent(currentAgentId);
|
|
30386
|
+
if (!sender || sender.project_id !== project.id || sender.status !== "active") {
|
|
30387
|
+
return {
|
|
30388
|
+
content: [{ type: "text", text: "The current coordination identity is not an active member of this project." }],
|
|
30389
|
+
isError: true
|
|
30390
|
+
};
|
|
30391
|
+
}
|
|
30392
|
+
if (toAgentId) {
|
|
30393
|
+
const recipient = teamStore.getAgent(toAgentId);
|
|
30394
|
+
if (!recipient || recipient.project_id !== project.id) {
|
|
30395
|
+
return {
|
|
30396
|
+
content: [{ type: "text", text: "The handoff recipient must be an agent registered in the current project." }],
|
|
30397
|
+
isError: true
|
|
30398
|
+
};
|
|
30399
|
+
}
|
|
30400
|
+
}
|
|
29425
30401
|
const result = await createHandoffArtifact2(
|
|
29426
30402
|
{
|
|
29427
30403
|
projectId: project.id,
|