memorix 1.2.2 → 1.2.4
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 +27 -0
- package/README.md +3 -3
- package/README.zh-CN.md +3 -3
- package/TEAM.md +86 -86
- package/dist/cli/index.js +5199 -4726
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +428 -49
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.js +97 -18
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +27 -0
- package/dist/sdk.js +428 -49
- package/dist/sdk.js.map +1 -1
- package/docs/1.2.4-PERSISTENT-MEMORY-DELIVERY.md +86 -0
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +13 -1
- package/docs/API_REFERENCE.md +13 -3
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/dev-log/progress.txt +60 -9
- 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/capability-map.ts +1 -1
- package/src/cli/command-guide.ts +4 -1
- package/src/cli/commands/agent-integrations.ts +5 -1
- package/src/cli/commands/audit-list.ts +89 -89
- package/src/cli/commands/background.ts +659 -659
- package/src/cli/commands/codegraph.ts +1 -1
- package/src/cli/commands/context.ts +9 -1
- package/src/cli/commands/formation.ts +48 -48
- package/src/cli/commands/git-hook-install.ts +111 -111
- package/src/cli/commands/handoff.ts +54 -54
- package/src/cli/commands/hooks-status.ts +63 -63
- package/src/cli/commands/ingest-commit.ts +153 -153
- package/src/cli/commands/ingest-image.ts +66 -66
- package/src/cli/commands/ingest-log.ts +180 -180
- package/src/cli/commands/ingest.ts +44 -44
- package/src/cli/commands/integrate-shared.ts +15 -15
- package/src/cli/commands/lock.ts +82 -82
- package/src/cli/commands/message.ts +104 -104
- package/src/cli/commands/poll.ts +58 -58
- 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 +118 -118
- package/src/cli/commands/resume.ts +31 -0
- package/src/cli/commands/serve-shared.ts +118 -118
- package/src/cli/commands/session.ts +15 -7
- package/src/cli/commands/skills.ts +114 -114
- package/src/cli/commands/task.ts +167 -167
- package/src/cli/commands/transfer.ts +47 -47
- package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
- package/src/cli/index.ts +3 -1
- 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/index.ts +41 -41
- package/src/cli/tui/markdown-render.tsx +371 -371
- package/src/cli/tui/session-service.ts +3 -2
- package/src/cli/tui/use-mouse.ts +157 -157
- package/src/cli/tui/useNavigation.ts +56 -56
- 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 +54 -1
- package/src/codegraph/task-lens.ts +29 -0
- package/src/compact/token-budget.ts +89 -74
- package/src/config/toml-loader.ts +9 -5
- package/src/dashboard/project-classification.ts +64 -64
- 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/handler.ts +127 -66
- package/src/hooks/installers/index.ts +5 -4
- package/src/hooks/official-skills.ts +6 -4
- package/src/hooks/pattern-detector.ts +173 -173
- package/src/hooks/rules/memorix-agent-rules.md +9 -7
- package/src/hooks/significance-filter.ts +250 -250
- package/src/knowledge/context-assembly.ts +4 -1
- package/src/knowledge/workset.ts +89 -1
- 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/attribution-guard.ts +249 -249
- package/src/memory/disclosure-policy.ts +135 -135
- package/src/memory/entity-extractor.ts +197 -197
- 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.ts +197 -197
- package/src/memory/refs.ts +94 -94
- package/src/memory/secret-filter.ts +79 -79
- package/src/memory/session.ts +158 -9
- 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 +343 -343
- package/src/orchestrate/output-budget.ts +80 -80
- package/src/orchestrate/permission.ts +152 -152
- package/src/orchestrate/pipeline-trace.ts +131 -131
- package/src/orchestrate/prompt-builder.ts +155 -155
- package/src/orchestrate/ring-buffer.ts +37 -37
- package/src/orchestrate/task-graph.ts +389 -389
- package/src/orchestrate/worktree.ts +232 -232
- package/src/project/aliases.ts +374 -374
- package/src/project/detector.ts +268 -268
- package/src/rules/adapters/claude-code.ts +99 -99
- package/src/rules/adapters/codex.ts +97 -97
- package/src/rules/adapters/copilot.ts +124 -124
- package/src/rules/adapters/cursor.ts +114 -114
- package/src/rules/adapters/kiro.ts +126 -126
- package/src/rules/adapters/trae.ts +56 -56
- package/src/rules/adapters/windsurf.ts +83 -83
- package/src/rules/syncer.ts +235 -235
- package/src/sdk.ts +299 -299
- 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 +144 -10
- package/src/skills/mini-skills.ts +386 -386
- package/src/store/bun-sqlite-compat.ts +118 -15
- 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/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 +3 -3
- package/src/team/event-bus.ts +76 -76
- package/src/team/file-locks.ts +173 -173
- package/src/team/handoff.ts +161 -161
- package/src/team/messages.ts +203 -203
- package/src/team/poll.ts +132 -132
- package/src/team/tasks.ts +211 -211
- 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
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
export const DEFAULT_FORMATION_TIMEOUT_MS = 12_000;
|
|
2
|
-
export const FORMATION_TIMEOUT_MIN_MS = 1_000;
|
|
3
|
-
export const FORMATION_TIMEOUT_MAX_MS = 300_000;
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Parse and validate MEMORIX_FORMATION_TIMEOUT_MS.
|
|
7
|
-
* - Must be a valid integer in the range 1000-300000ms.
|
|
8
|
-
* - Invalid values fall back to the default and log a warning.
|
|
9
|
-
* - Out-of-range values are clamped to the nearest bound.
|
|
10
|
-
* Default: 12000ms (12s).
|
|
11
|
-
*/
|
|
12
|
-
export function parseFormationTimeoutMs(raw: string | undefined): number {
|
|
13
|
-
const value = raw?.trim();
|
|
14
|
-
if (!value) return DEFAULT_FORMATION_TIMEOUT_MS;
|
|
15
|
-
|
|
16
|
-
const parsed = Number(value);
|
|
17
|
-
if (!Number.isInteger(parsed) || Number.isNaN(parsed)) {
|
|
18
|
-
console.warn(
|
|
19
|
-
`[memorix] MEMORIX_FORMATION_TIMEOUT_MS="${raw}" is invalid (must be a positive integer between ${FORMATION_TIMEOUT_MIN_MS}-${FORMATION_TIMEOUT_MAX_MS}ms). Using default ${DEFAULT_FORMATION_TIMEOUT_MS}ms.`,
|
|
20
|
-
);
|
|
21
|
-
return DEFAULT_FORMATION_TIMEOUT_MS;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (parsed < FORMATION_TIMEOUT_MIN_MS) return FORMATION_TIMEOUT_MIN_MS;
|
|
25
|
-
if (parsed > FORMATION_TIMEOUT_MAX_MS) return FORMATION_TIMEOUT_MAX_MS;
|
|
26
|
-
return parsed;
|
|
27
|
-
}
|
|
1
|
+
export const DEFAULT_FORMATION_TIMEOUT_MS = 12_000;
|
|
2
|
+
export const FORMATION_TIMEOUT_MIN_MS = 1_000;
|
|
3
|
+
export const FORMATION_TIMEOUT_MAX_MS = 300_000;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Parse and validate MEMORIX_FORMATION_TIMEOUT_MS.
|
|
7
|
+
* - Must be a valid integer in the range 1000-300000ms.
|
|
8
|
+
* - Invalid values fall back to the default and log a warning.
|
|
9
|
+
* - Out-of-range values are clamped to the nearest bound.
|
|
10
|
+
* Default: 12000ms (12s).
|
|
11
|
+
*/
|
|
12
|
+
export function parseFormationTimeoutMs(raw: string | undefined): number {
|
|
13
|
+
const value = raw?.trim();
|
|
14
|
+
if (!value) return DEFAULT_FORMATION_TIMEOUT_MS;
|
|
15
|
+
|
|
16
|
+
const parsed = Number(value);
|
|
17
|
+
if (!Number.isInteger(parsed) || Number.isNaN(parsed)) {
|
|
18
|
+
console.warn(
|
|
19
|
+
`[memorix] MEMORIX_FORMATION_TIMEOUT_MS="${raw}" is invalid (must be a positive integer between ${FORMATION_TIMEOUT_MIN_MS}-${FORMATION_TIMEOUT_MAX_MS}ms). Using default ${DEFAULT_FORMATION_TIMEOUT_MS}ms.`,
|
|
20
|
+
);
|
|
21
|
+
return DEFAULT_FORMATION_TIMEOUT_MS;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (parsed < FORMATION_TIMEOUT_MIN_MS) return FORMATION_TIMEOUT_MIN_MS;
|
|
25
|
+
if (parsed > FORMATION_TIMEOUT_MAX_MS) return FORMATION_TIMEOUT_MAX_MS;
|
|
26
|
+
return parsed;
|
|
27
|
+
}
|
package/src/server.ts
CHANGED
|
@@ -228,6 +228,9 @@ export const BOOTSTRAP_SAFE_TOOL_NAMES = new Set([
|
|
|
228
228
|
'memorix_context_pack',
|
|
229
229
|
]);
|
|
230
230
|
|
|
231
|
+
const AUTOPILOT_RETRIEVAL_BOUNDARY_TTL_MS = 2 * 60 * 1000;
|
|
232
|
+
const READ_ONLY_TASK_PATTERN = /\b(?:do not|don't|never)\s+(?:modify|edit|change|write)\b|\bread[- ]only\b|(?:不要|不准|勿|禁止).{0,6}(?:修改|编辑|写入)|只读/i;
|
|
233
|
+
|
|
231
234
|
export function shouldAwaitProjectRuntime(toolName: string): boolean {
|
|
232
235
|
return !BOOTSTRAP_SAFE_TOOL_NAMES.has(toolName);
|
|
233
236
|
}
|
|
@@ -602,6 +605,63 @@ export async function createMemorixServer(
|
|
|
602
605
|
};
|
|
603
606
|
};
|
|
604
607
|
|
|
608
|
+
// A complete Autopilot brief is the normal retrieval boundary for one coding
|
|
609
|
+
// turn. Keep accidental search/detail loops cheap, while preserving an
|
|
610
|
+
// explicit escape hatch when a caller really needs deeper history.
|
|
611
|
+
let autopilotRetrievalBoundary: {
|
|
612
|
+
projectId: string;
|
|
613
|
+
issuedAt: number;
|
|
614
|
+
coveredObservationIds: Set<number>;
|
|
615
|
+
readOnly: boolean;
|
|
616
|
+
} | null = null;
|
|
617
|
+
const getActiveAutopilotRetrievalBoundary = () => {
|
|
618
|
+
const boundary = autopilotRetrievalBoundary;
|
|
619
|
+
if (!boundary) return null;
|
|
620
|
+
if (boundary.projectId === project.id && Date.now() - boundary.issuedAt <= AUTOPILOT_RETRIEVAL_BOUNDARY_TTL_MS) {
|
|
621
|
+
return boundary;
|
|
622
|
+
}
|
|
623
|
+
autopilotRetrievalBoundary = null;
|
|
624
|
+
return null;
|
|
625
|
+
};
|
|
626
|
+
const requireExplicitAutopilotExpansion = (toolName: string, purpose?: string) => {
|
|
627
|
+
if (!getActiveAutopilotRetrievalBoundary() || purpose?.trim()) return null;
|
|
628
|
+
|
|
629
|
+
return {
|
|
630
|
+
content: [{
|
|
631
|
+
type: 'text' as const,
|
|
632
|
+
text:
|
|
633
|
+
'Memorix Autopilot retrieval boundary: the latest `memorix_project_context` already supplied the default bounded brief for this coding turn, so no additional memory was retrieved. ' +
|
|
634
|
+
'Verify the current project first. To intentionally expand beyond that brief, call `' + toolName + '` again with `purpose` naming the specific missing fact or the user\'s explicit request.',
|
|
635
|
+
}],
|
|
636
|
+
};
|
|
637
|
+
};
|
|
638
|
+
const blockCoveredAutopilotEvidence = (toolName: string, observationIds: number[], force?: boolean) => {
|
|
639
|
+
const boundary = getActiveAutopilotRetrievalBoundary();
|
|
640
|
+
if (!boundary || force || observationIds.length === 0 || !observationIds.every(id => boundary.coveredObservationIds.has(id))) {
|
|
641
|
+
return null;
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
644
|
+
content: [{
|
|
645
|
+
type: 'text' as const,
|
|
646
|
+
text:
|
|
647
|
+
'Memorix Autopilot retrieval boundary: every requested memory is already represented in the latest project brief, so no duplicate detail was retrieved. ' +
|
|
648
|
+
'Inspect the current project or seek a new source. Retry with `force: true` only when the user explicitly asks to read the underlying record in full.',
|
|
649
|
+
}],
|
|
650
|
+
};
|
|
651
|
+
};
|
|
652
|
+
const blockReadOnlyAutopilotWrite = (overrideReadOnly?: boolean) => {
|
|
653
|
+
const boundary = getActiveAutopilotRetrievalBoundary();
|
|
654
|
+
if (!boundary?.readOnly || overrideReadOnly) return null;
|
|
655
|
+
return {
|
|
656
|
+
content: [{
|
|
657
|
+
type: 'text' as const,
|
|
658
|
+
text:
|
|
659
|
+
'Memorix write boundary: the latest task is read-only or asks not to modify files, so no memory was stored. ' +
|
|
660
|
+
'Persist a record only when the user explicitly asks to save it, then retry with `overrideReadOnly: true`.',
|
|
661
|
+
}],
|
|
662
|
+
};
|
|
663
|
+
};
|
|
664
|
+
|
|
605
665
|
// ================================================================
|
|
606
666
|
// Memorix Extended Tools (3-layer Progressive Disclosure)
|
|
607
667
|
// ================================================================
|
|
@@ -622,7 +682,8 @@ export async function createMemorixServer(
|
|
|
622
682
|
'problem-solution ([FIX] bug fix), how-it-works ([INFO] explanation), what-changed ([CHANGE] change), ' +
|
|
623
683
|
'discovery ([DISCOVERY] insight), why-it-exists ([WHY] rationale), trade-off ([TRADEOFF] compromise), ' +
|
|
624
684
|
'session-request ([SESSION] original goal). ' +
|
|
625
|
-
'Project visibility is the default. Personal or team visibility requires an explicitly joined coordination identity.'
|
|
685
|
+
'Project visibility is the default. Personal or team visibility requires an explicitly joined coordination identity. ' +
|
|
686
|
+
'For a read-only task, do not store unless the user explicitly asks to save a record.',
|
|
626
687
|
inputSchema: {
|
|
627
688
|
entityName: z.string().describe('The entity this observation belongs to (e.g., "auth-module", "port-config")'),
|
|
628
689
|
type: z.enum(OBSERVATION_TYPES).describe('Observation type for classification'),
|
|
@@ -646,11 +707,16 @@ export async function createMemorixServer(
|
|
|
646
707
|
visibility: z.enum(['personal', 'project', 'team']).optional().describe(
|
|
647
708
|
'Retrieval scope. Project is the normal shared default; personal/team require memorix_session_start with joinTeam=true.',
|
|
648
709
|
),
|
|
710
|
+
overrideReadOnly: z.boolean().optional().describe(
|
|
711
|
+
'Use only when the user explicitly asks to save memory during a read-only or no-modification task.',
|
|
712
|
+
),
|
|
649
713
|
},
|
|
650
714
|
},
|
|
651
|
-
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility }) => {
|
|
715
|
+
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility, overrideReadOnly }) => {
|
|
652
716
|
const unresolved = requireResolvedProject('store memory in the current project');
|
|
653
717
|
if (unresolved) return unresolved;
|
|
718
|
+
const readOnlyBoundary = blockReadOnlyAutopilotWrite(overrideReadOnly);
|
|
719
|
+
if (readOnlyBoundary) return readOnlyBoundary;
|
|
654
720
|
const requestedVisibility = (visibility ?? 'project') as 'personal' | 'project' | 'team';
|
|
655
721
|
const reader = getObservationReader();
|
|
656
722
|
if (requestedVisibility !== 'project' && !currentAgentId) {
|
|
@@ -1224,6 +1290,7 @@ export async function createMemorixServer(
|
|
|
1224
1290
|
title: 'Search Memory',
|
|
1225
1291
|
description:
|
|
1226
1292
|
'Search project memory. Returns a compact index (~50-100 tokens/result). ' +
|
|
1293
|
+
'Do not use as a follow-up to a complete memorix_project_context brief unless a specific fact is still missing or the user asks for deeper history; provide purpose when intentionally expanding. ' +
|
|
1227
1294
|
'Use memorix_detail to fetch full content for specific IDs. ' +
|
|
1228
1295
|
'Use memorix_timeline to see chronological context. ' +
|
|
1229
1296
|
'Searches across all observations stored from any IDE session — enabling cross-session and cross-agent context retrieval.',
|
|
@@ -1243,13 +1310,23 @@ export async function createMemorixServer(
|
|
|
1243
1310
|
source: z.enum(['agent', 'git', 'manual']).optional().describe(
|
|
1244
1311
|
'Filter by memory source. "git" returns only commit-derived ground truth memories. Omit for all sources.',
|
|
1245
1312
|
),
|
|
1313
|
+
purpose: z.string().optional().describe(
|
|
1314
|
+
'Why this must expand beyond the latest Autopilot brief. Name the missing fact or the user\'s explicit request.',
|
|
1315
|
+
),
|
|
1316
|
+
force: z.boolean().optional().describe(
|
|
1317
|
+
'Use only when the user explicitly asks to read a record already represented in the latest Autopilot brief.',
|
|
1318
|
+
),
|
|
1246
1319
|
},
|
|
1247
1320
|
},
|
|
1248
|
-
async ({ query, limit, type, maxTokens, scope, since, until, status, source }) => {
|
|
1321
|
+
async ({ query, limit, type, maxTokens, scope, since, until, status, source, purpose, force }) => {
|
|
1249
1322
|
if (scope !== 'global') {
|
|
1250
1323
|
const unresolved = requireResolvedProject('search the current project');
|
|
1251
1324
|
if (unresolved) return unresolved;
|
|
1252
1325
|
}
|
|
1326
|
+
if (scope !== 'global') {
|
|
1327
|
+
const boundary = requireExplicitAutopilotExpansion('memorix_search', purpose);
|
|
1328
|
+
if (boundary) return boundary;
|
|
1329
|
+
}
|
|
1253
1330
|
return withFreshIndex(async () => {
|
|
1254
1331
|
|
|
1255
1332
|
const safeLimit = limit != null ? coerceNumber(limit, 20) : undefined;
|
|
@@ -1295,6 +1372,18 @@ export async function createMemorixServer(
|
|
|
1295
1372
|
throw error;
|
|
1296
1373
|
}
|
|
1297
1374
|
|
|
1375
|
+
const activeBoundary = getActiveAutopilotRetrievalBoundary();
|
|
1376
|
+
if (
|
|
1377
|
+
scope !== 'global'
|
|
1378
|
+
&& activeBoundary
|
|
1379
|
+
&& !force
|
|
1380
|
+
&& result.entries.length > 0
|
|
1381
|
+
&& result.entries.every(entry => activeBoundary.coveredObservationIds.has(entry.id))
|
|
1382
|
+
) {
|
|
1383
|
+
const duplicate = blockCoveredAutopilotEvidence('memorix_search', result.entries.map(entry => entry.id), force);
|
|
1384
|
+
if (duplicate) return duplicate;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1298
1387
|
// Append retrieval diagnostics only; do not mix workspace-sync guidance into memory results.
|
|
1299
1388
|
let text = result.formatted;
|
|
1300
1389
|
try {
|
|
@@ -1414,6 +1503,7 @@ export async function createMemorixServer(
|
|
|
1414
1503
|
observations,
|
|
1415
1504
|
task,
|
|
1416
1505
|
refresh: refresh ?? 'auto',
|
|
1506
|
+
reader: getObservationReader(),
|
|
1417
1507
|
enqueueRefresh: () => {
|
|
1418
1508
|
enqueueCodegraphRefresh({
|
|
1419
1509
|
dataDir: projectDir,
|
|
@@ -1430,6 +1520,17 @@ export async function createMemorixServer(
|
|
|
1430
1520
|
? formatAutoProjectContextSummary(context)
|
|
1431
1521
|
: formatAutoProjectContextPrompt(context);
|
|
1432
1522
|
|
|
1523
|
+
autopilotRetrievalBoundary = {
|
|
1524
|
+
projectId: project.id,
|
|
1525
|
+
issuedAt: Date.now(),
|
|
1526
|
+
coveredObservationIds: new Set([
|
|
1527
|
+
...(context.workset.continuation?.memories.map(memory => memory.id) ?? []),
|
|
1528
|
+
...context.workset.reliableMemory.map(memory => memory.id),
|
|
1529
|
+
...context.workset.cautionMemory.map(memory => memory.id),
|
|
1530
|
+
]),
|
|
1531
|
+
readOnly: READ_ONLY_TASK_PATTERN.test(task ?? ''),
|
|
1532
|
+
};
|
|
1533
|
+
|
|
1433
1534
|
return {
|
|
1434
1535
|
content: [{ type: 'text' as const, text }],
|
|
1435
1536
|
};
|
|
@@ -1475,18 +1576,24 @@ export async function createMemorixServer(
|
|
|
1475
1576
|
title: 'Context Pack',
|
|
1476
1577
|
description:
|
|
1477
1578
|
'Build a prompt-ready working context pack for a coding task. ' +
|
|
1478
|
-
'Combines relevant memories, CodeGraph Memory facts, freshness warnings, suggested reads, and verification hints.'
|
|
1579
|
+
'Combines relevant memories, CodeGraph Memory facts, freshness warnings, suggested reads, and verification hints. ' +
|
|
1580
|
+
'After a complete memorix_project_context brief, provide purpose only when deliberately expanding beyond it.',
|
|
1479
1581
|
inputSchema: {
|
|
1480
1582
|
task: z.string().describe('Current coding task or question'),
|
|
1481
1583
|
limit: z.preprocess(
|
|
1482
1584
|
value => (typeof value === 'string' && value.trim() !== '' ? Number(value) : value),
|
|
1483
1585
|
z.number().int().positive().max(100),
|
|
1484
1586
|
).optional().describe('Max active memories to inspect before code-ref filtering (default: 20)'),
|
|
1587
|
+
purpose: z.string().optional().describe(
|
|
1588
|
+
'Why this must expand beyond the latest Autopilot brief. Name the missing fact or the user\'s explicit request.',
|
|
1589
|
+
),
|
|
1485
1590
|
},
|
|
1486
1591
|
},
|
|
1487
|
-
async ({ task, limit }) => {
|
|
1592
|
+
async ({ task, limit, purpose }) => {
|
|
1488
1593
|
const unresolved = requireResolvedProject('build a context pack for the current project');
|
|
1489
1594
|
if (unresolved) return unresolved;
|
|
1595
|
+
const boundary = requireExplicitAutopilotExpansion('memorix_context_pack', purpose);
|
|
1596
|
+
if (boundary) return boundary;
|
|
1490
1597
|
|
|
1491
1598
|
const [
|
|
1492
1599
|
{ CodeGraphStore },
|
|
@@ -2380,6 +2487,7 @@ export async function createMemorixServer(
|
|
|
2380
2487
|
description:
|
|
2381
2488
|
'Fetch full observation or mini-skill details — includes source kind (explicit memory / hook trace / git evidence), ' +
|
|
2382
2489
|
'value category, and cross-references (~500-1000 tokens each). ' +
|
|
2490
|
+
'Do not re-fetch content already covered by a complete memorix_project_context brief unless a specific fact is still missing or the user asks for deeper history; provide purpose when intentionally expanding. ' +
|
|
2383
2491
|
'Always use memorix_search first to find relevant IDs, then fetch only what you need. ' +
|
|
2384
2492
|
'Accepts typed refs from search results (e.g. "obs:42", "skill:3") via the typedRefs field, ' +
|
|
2385
2493
|
'or legacy numeric ids / object refs for backward compatibility.',
|
|
@@ -2392,19 +2500,39 @@ export async function createMemorixServer(
|
|
|
2392
2500
|
}),
|
|
2393
2501
|
).optional().describe('Explicit observation refs. Prefer this for global search results.'),
|
|
2394
2502
|
typedRefs: z.array(z.string()).optional().describe('Typed memory refs from search results, e.g. "obs:42", "skill:3", "obs:42@org/proj"'),
|
|
2503
|
+
purpose: z.string().optional().describe(
|
|
2504
|
+
'Why this must expand beyond the latest Autopilot brief. Name the missing fact or the user\'s explicit request.',
|
|
2505
|
+
),
|
|
2506
|
+
force: z.boolean().optional().describe(
|
|
2507
|
+
'Use only when the user explicitly asks to read a record already represented in the latest Autopilot brief.',
|
|
2508
|
+
),
|
|
2395
2509
|
},
|
|
2396
2510
|
},
|
|
2397
|
-
async ({ ids, refs, typedRefs }) => {
|
|
2511
|
+
async ({ ids, refs, typedRefs, purpose, force }) => {
|
|
2398
2512
|
// Defensive coercion: Claude Code CLI + GLM may send "[16]" instead of [16]
|
|
2399
2513
|
const safeIds = coerceNumberArray(ids);
|
|
2400
2514
|
const safeRefs = coerceObservationRefs(refs);
|
|
2401
2515
|
const safeTypedRefs = coerceStringArray(typedRefs);
|
|
2516
|
+
const hasCrossProjectRef = safeRefs.some((ref) => ref.projectId && ref.projectId !== project.id)
|
|
2517
|
+
|| safeTypedRefs.some((ref) => ref.includes('@') && !ref.endsWith(`@${project.id}`));
|
|
2518
|
+
if (!hasCrossProjectRef) {
|
|
2519
|
+
const boundary = requireExplicitAutopilotExpansion('memorix_detail', purpose);
|
|
2520
|
+
if (boundary) return boundary;
|
|
2521
|
+
const requestedObservationIds = [
|
|
2522
|
+
...safeIds,
|
|
2523
|
+
...safeRefs.map(ref => ref.id),
|
|
2524
|
+
...safeTypedRefs.flatMap((ref) => {
|
|
2525
|
+
const match = /^obs:(\d+)(?:@.+)?$/i.exec(ref.trim());
|
|
2526
|
+
return match ? [Number(match[1])] : [];
|
|
2527
|
+
}),
|
|
2528
|
+
];
|
|
2529
|
+
const duplicate = blockCoveredAutopilotEvidence('memorix_detail', requestedObservationIds, force);
|
|
2530
|
+
if (duplicate) return duplicate;
|
|
2531
|
+
}
|
|
2402
2532
|
|
|
2403
2533
|
// Priority: typedRefs > refs > ids (each is a complete, homogeneous input path)
|
|
2404
2534
|
let result;
|
|
2405
2535
|
try {
|
|
2406
|
-
const hasCrossProjectRef = safeRefs.some((ref) => ref.projectId && ref.projectId !== project.id)
|
|
2407
|
-
|| safeTypedRefs.some((ref) => ref.includes('@') && !ref.endsWith(`@${project.id}`));
|
|
2408
2536
|
const reader = getObservationReader(hasCrossProjectRef ? 'global' : 'project');
|
|
2409
2537
|
if (safeTypedRefs.length > 0) {
|
|
2410
2538
|
// Pass typed ref strings directly — compactDetail handles parsing
|
|
@@ -3614,7 +3742,6 @@ export async function createMemorixServer(
|
|
|
3614
3742
|
if (unresolved) return unresolved;
|
|
3615
3743
|
|
|
3616
3744
|
const { startSession } = await import('./memory/session.js');
|
|
3617
|
-
const result = await startSession(projectDir, project.id, { sessionId, agent });
|
|
3618
3745
|
|
|
3619
3746
|
const llmStatus = isLLMEnabled()
|
|
3620
3747
|
? `LLM enhanced mode: ${getLLMConfig()?.provider}/${getLLMConfig()?.model} (fact extraction + auto-dedup active)`
|
|
@@ -3690,6 +3817,12 @@ export async function createMemorixServer(
|
|
|
3690
3817
|
}
|
|
3691
3818
|
} catch { /* team auto-registration is best-effort */ }
|
|
3692
3819
|
|
|
3820
|
+
const result = await startSession(projectDir, project.id, {
|
|
3821
|
+
sessionId,
|
|
3822
|
+
agent,
|
|
3823
|
+
reader: getObservationReader(),
|
|
3824
|
+
});
|
|
3825
|
+
|
|
3693
3826
|
const lines = [
|
|
3694
3827
|
`[OK] Session started: ${result.session.id}`,
|
|
3695
3828
|
`Project: ${project.name} (${project.id})`,
|
|
@@ -3825,7 +3958,7 @@ export async function createMemorixServer(
|
|
|
3825
3958
|
async ({ limit }) => {
|
|
3826
3959
|
const safeLimit = limit != null ? coerceNumber(limit, 3) : 3;
|
|
3827
3960
|
const { getSessionContext, listSessions } = await import('./memory/session.js');
|
|
3828
|
-
const context = await getSessionContext(projectDir, project.id, safeLimit);
|
|
3961
|
+
const context = await getSessionContext(projectDir, project.id, safeLimit, getObservationReader());
|
|
3829
3962
|
const sessions = await listSessions(projectDir, project.id);
|
|
3830
3963
|
|
|
3831
3964
|
const activeSessions = sessions.filter(s => s.status === 'active');
|
|
@@ -4820,6 +4953,7 @@ export async function createMemorixServer(
|
|
|
4820
4953
|
|
|
4821
4954
|
// Phase 4a: clear agent identity — old project's agent is not valid in new project
|
|
4822
4955
|
currentAgentId = undefined;
|
|
4956
|
+
autopilotRetrievalBoundary = null;
|
|
4823
4957
|
|
|
4824
4958
|
// Re-resolve data dir with canonical ID (may differ from raw detected ID)
|
|
4825
4959
|
const canonicalProjectDir = newCanonicalId !== newDetected.id
|