gitmem-mcp 1.0.3 → 1.0.5
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 +14 -14
- package/dist/commands/check.d.ts +1 -1
- package/dist/commands/check.js +4 -3
- package/dist/diagnostics/anonymizer.d.ts +1 -1
- package/dist/diagnostics/anonymizer.js +1 -1
- package/dist/diagnostics/channels.d.ts +1 -1
- package/dist/diagnostics/channels.js +1 -1
- package/dist/diagnostics/collector.d.ts +1 -1
- package/dist/diagnostics/collector.js +1 -1
- package/dist/diagnostics/index.d.ts +1 -1
- package/dist/diagnostics/index.js +1 -1
- package/dist/hooks/quick-retrieve.js +2 -1
- package/dist/index.js +0 -0
- package/dist/schemas/active-sessions.d.ts +9 -9
- package/dist/schemas/active-sessions.js +1 -1
- package/dist/schemas/common.d.ts +2 -5
- package/dist/schemas/common.js +13 -7
- package/dist/schemas/create-learning.js +1 -1
- package/dist/schemas/session-close.d.ts +5 -8
- package/dist/schemas/session-close.js +7 -3
- package/dist/schemas/session-start.d.ts +4 -4
- package/dist/schemas/session-start.js +1 -1
- package/dist/schemas/thread.d.ts +1 -1
- package/dist/schemas/thread.js +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +21 -10
- package/dist/services/active-sessions.js +3 -2
- package/dist/services/agent-briefing.d.ts +1 -1
- package/dist/services/agent-briefing.js +1 -1
- package/dist/services/agent-detection.d.ts +1 -0
- package/dist/services/agent-detection.js +21 -12
- package/dist/services/analytics.d.ts +1 -1
- package/dist/services/analytics.js +3 -3
- package/dist/services/behavioral-decay.js +2 -2
- package/dist/services/cache.d.ts +1 -1
- package/dist/services/cache.js +1 -1
- package/dist/services/compliance-validator.d.ts +1 -1
- package/dist/services/compliance-validator.js +5 -5
- package/dist/services/config.d.ts +1 -1
- package/dist/services/config.js +1 -1
- package/dist/services/file-lock.js +12 -0
- package/dist/services/gitmem-dir.d.ts +17 -4
- package/dist/services/gitmem-dir.js +43 -9
- package/dist/services/local-file-storage.d.ts +1 -1
- package/dist/services/local-file-storage.js +4 -3
- package/dist/services/local-vector-search.d.ts +1 -1
- package/dist/services/local-vector-search.js +2 -2
- package/dist/services/metrics.d.ts +6 -6
- package/dist/services/metrics.js +8 -8
- package/dist/services/session-state.d.ts +7 -7
- package/dist/services/session-state.js +19 -7
- package/dist/services/startup.d.ts +1 -1
- package/dist/services/startup.js +3 -2
- package/dist/services/supabase-client.d.ts +17 -6
- package/dist/services/supabase-client.js +44 -8
- package/dist/services/thread-manager.d.ts +1 -1
- package/dist/services/thread-manager.js +5 -5
- package/dist/services/thread-supabase.d.ts +1 -1
- package/dist/services/thread-supabase.js +2 -2
- package/dist/services/transcript-chunker.d.ts +1 -1
- package/dist/services/transcript-chunker.js +1 -1
- package/dist/services/variant-assignment.d.ts +6 -6
- package/dist/services/variant-assignment.js +9 -8
- package/dist/tools/analyze.d.ts +2 -2
- package/dist/tools/analyze.js +2 -2
- package/dist/tools/archive-learning.js +36 -22
- package/dist/tools/confirm-scars.js +1 -1
- package/dist/tools/create-decision.d.ts +1 -1
- package/dist/tools/create-decision.js +2 -2
- package/dist/tools/create-learning.d.ts +1 -1
- package/dist/tools/create-learning.js +4 -4
- package/dist/tools/create-linear-issue.d.ts +18 -0
- package/dist/tools/create-linear-issue.js +197 -0
- package/dist/tools/create-thread.d.ts +1 -1
- package/dist/tools/create-thread.js +2 -2
- package/dist/tools/definitions.js +50 -48
- package/dist/tools/get-transcript.d.ts +1 -1
- package/dist/tools/get-transcript.js +1 -1
- package/dist/tools/graph-traverse.d.ts +1 -1
- package/dist/tools/graph-traverse.js +18 -16
- package/dist/tools/list-threads.d.ts +2 -2
- package/dist/tools/list-threads.js +4 -4
- package/dist/tools/log.d.ts +1 -1
- package/dist/tools/log.js +1 -1
- package/dist/tools/prepare-context.d.ts +1 -1
- package/dist/tools/prepare-context.js +1 -1
- package/dist/tools/recall.d.ts +4 -4
- package/dist/tools/recall.js +25 -25
- package/dist/tools/record-scar-usage-batch.js +2 -2
- package/dist/tools/record-scar-usage.d.ts +1 -1
- package/dist/tools/record-scar-usage.js +3 -3
- package/dist/tools/resolve-thread.d.ts +2 -2
- package/dist/tools/resolve-thread.js +3 -3
- package/dist/tools/save-transcript.d.ts +1 -1
- package/dist/tools/save-transcript.js +1 -1
- package/dist/tools/search.d.ts +1 -1
- package/dist/tools/search.js +1 -1
- package/dist/tools/session-close.d.ts +1 -1
- package/dist/tools/session-close.js +31 -31
- package/dist/tools/session-start.d.ts +5 -5
- package/dist/tools/session-start.js +63 -61
- package/dist/types/index.d.ts +13 -13
- package/hooks/.claude-plugin/plugin.json +1 -1
- package/hooks/scripts/post-tool-use.sh +1 -1
- package/hooks/scripts/recall-check.sh +1 -1
- package/hooks/scripts/session-close-check.sh +1 -1
- package/hooks/scripts/session-start.sh +1 -1
- package/package.json +6 -3
- package/dist/commands/check.d.ts.map +0 -1
- package/dist/commands/check.js.map +0 -1
- package/dist/constants/closing-questions.d.ts.map +0 -1
- package/dist/constants/closing-questions.js.map +0 -1
- package/dist/diagnostics/anonymizer.d.ts.map +0 -1
- package/dist/diagnostics/anonymizer.js.map +0 -1
- package/dist/diagnostics/channels.d.ts.map +0 -1
- package/dist/diagnostics/channels.js.map +0 -1
- package/dist/diagnostics/collector.d.ts.map +0 -1
- package/dist/diagnostics/collector.js.map +0 -1
- package/dist/diagnostics/index.d.ts.map +0 -1
- package/dist/diagnostics/index.js.map +0 -1
- package/dist/hooks/format-utils.d.ts.map +0 -1
- package/dist/hooks/format-utils.js.map +0 -1
- package/dist/hooks/quick-retrieve.d.ts.map +0 -1
- package/dist/hooks/quick-retrieve.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/schemas/absorb-observations.d.ts.map +0 -1
- package/dist/schemas/absorb-observations.js.map +0 -1
- package/dist/schemas/active-sessions.d.ts.map +0 -1
- package/dist/schemas/active-sessions.js.map +0 -1
- package/dist/schemas/analyze.d.ts.map +0 -1
- package/dist/schemas/analyze.js.map +0 -1
- package/dist/schemas/common.d.ts.map +0 -1
- package/dist/schemas/common.js.map +0 -1
- package/dist/schemas/create-decision.d.ts.map +0 -1
- package/dist/schemas/create-decision.js.map +0 -1
- package/dist/schemas/create-learning.d.ts.map +0 -1
- package/dist/schemas/create-learning.js.map +0 -1
- package/dist/schemas/get-transcript.d.ts.map +0 -1
- package/dist/schemas/get-transcript.js.map +0 -1
- package/dist/schemas/index.d.ts.map +0 -1
- package/dist/schemas/index.js.map +0 -1
- package/dist/schemas/log.d.ts.map +0 -1
- package/dist/schemas/log.js.map +0 -1
- package/dist/schemas/prepare-context.d.ts.map +0 -1
- package/dist/schemas/prepare-context.js.map +0 -1
- package/dist/schemas/recall.d.ts.map +0 -1
- package/dist/schemas/recall.js.map +0 -1
- package/dist/schemas/record-scar-usage-batch.d.ts.map +0 -1
- package/dist/schemas/record-scar-usage-batch.js.map +0 -1
- package/dist/schemas/record-scar-usage.d.ts.map +0 -1
- package/dist/schemas/record-scar-usage.js.map +0 -1
- package/dist/schemas/registry.d.ts.map +0 -1
- package/dist/schemas/registry.js.map +0 -1
- package/dist/schemas/save-transcript.d.ts.map +0 -1
- package/dist/schemas/save-transcript.js.map +0 -1
- package/dist/schemas/search-transcripts.d.ts.map +0 -1
- package/dist/schemas/search-transcripts.js.map +0 -1
- package/dist/schemas/search.d.ts.map +0 -1
- package/dist/schemas/search.js.map +0 -1
- package/dist/schemas/session-close.d.ts.map +0 -1
- package/dist/schemas/session-close.js.map +0 -1
- package/dist/schemas/session-start.d.ts.map +0 -1
- package/dist/schemas/session-start.js.map +0 -1
- package/dist/schemas/thread.d.ts.map +0 -1
- package/dist/schemas/thread.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/services/active-sessions.d.ts.map +0 -1
- package/dist/services/active-sessions.js.map +0 -1
- package/dist/services/agent-briefing.d.ts.map +0 -1
- package/dist/services/agent-briefing.js.map +0 -1
- package/dist/services/agent-detection.d.ts.map +0 -1
- package/dist/services/agent-detection.js.map +0 -1
- package/dist/services/analytics.d.ts.map +0 -1
- package/dist/services/analytics.js.map +0 -1
- package/dist/services/behavioral-decay.d.ts.map +0 -1
- package/dist/services/behavioral-decay.js.map +0 -1
- package/dist/services/bm25.d.ts.map +0 -1
- package/dist/services/bm25.js.map +0 -1
- package/dist/services/cache.d.ts.map +0 -1
- package/dist/services/cache.js.map +0 -1
- package/dist/services/cache.test.d.ts +0 -8
- package/dist/services/cache.test.d.ts.map +0 -1
- package/dist/services/cache.test.js +0 -267
- package/dist/services/cache.test.js.map +0 -1
- package/dist/services/compliance-validator.d.ts.map +0 -1
- package/dist/services/compliance-validator.js.map +0 -1
- package/dist/services/config.d.ts.map +0 -1
- package/dist/services/config.js.map +0 -1
- package/dist/services/display-protocol.d.ts.map +0 -1
- package/dist/services/display-protocol.js.map +0 -1
- package/dist/services/effect-tracker.d.ts.map +0 -1
- package/dist/services/effect-tracker.js.map +0 -1
- package/dist/services/embedding.d.ts.map +0 -1
- package/dist/services/embedding.js.map +0 -1
- package/dist/services/file-lock.d.ts.map +0 -1
- package/dist/services/file-lock.js.map +0 -1
- package/dist/services/gitmem-dir.d.ts.map +0 -1
- package/dist/services/gitmem-dir.js.map +0 -1
- package/dist/services/local-file-storage.d.ts.map +0 -1
- package/dist/services/local-file-storage.js.map +0 -1
- package/dist/services/local-vector-search.d.ts.map +0 -1
- package/dist/services/local-vector-search.js.map +0 -1
- package/dist/services/metrics.d.ts.map +0 -1
- package/dist/services/metrics.js.map +0 -1
- package/dist/services/session-state.d.ts.map +0 -1
- package/dist/services/session-state.js.map +0 -1
- package/dist/services/startup.d.ts.map +0 -1
- package/dist/services/startup.js.map +0 -1
- package/dist/services/storage.d.ts.map +0 -1
- package/dist/services/storage.js.map +0 -1
- package/dist/services/supabase-client.d.ts.map +0 -1
- package/dist/services/supabase-client.js.map +0 -1
- package/dist/services/thread-dedup.d.ts.map +0 -1
- package/dist/services/thread-dedup.js.map +0 -1
- package/dist/services/thread-manager.d.ts.map +0 -1
- package/dist/services/thread-manager.js.map +0 -1
- package/dist/services/thread-suggestions.d.ts.map +0 -1
- package/dist/services/thread-suggestions.js.map +0 -1
- package/dist/services/thread-supabase.d.ts.map +0 -1
- package/dist/services/thread-supabase.js.map +0 -1
- package/dist/services/thread-vitality.d.ts.map +0 -1
- package/dist/services/thread-vitality.js.map +0 -1
- package/dist/services/tier.d.ts.map +0 -1
- package/dist/services/tier.js.map +0 -1
- package/dist/services/timezone.d.ts.map +0 -1
- package/dist/services/timezone.js.map +0 -1
- package/dist/services/transcript-chunker.d.ts.map +0 -1
- package/dist/services/transcript-chunker.js.map +0 -1
- package/dist/services/triple-writer.d.ts.map +0 -1
- package/dist/services/triple-writer.js.map +0 -1
- package/dist/services/variant-assignment.d.ts.map +0 -1
- package/dist/services/variant-assignment.js.map +0 -1
- package/dist/services/variant-generation.d.ts.map +0 -1
- package/dist/services/variant-generation.js.map +0 -1
- package/dist/tools/absorb-observations.d.ts.map +0 -1
- package/dist/tools/absorb-observations.js.map +0 -1
- package/dist/tools/analyze.d.ts.map +0 -1
- package/dist/tools/analyze.js.map +0 -1
- package/dist/tools/archive-learning.d.ts.map +0 -1
- package/dist/tools/archive-learning.js.map +0 -1
- package/dist/tools/cleanup-threads.d.ts.map +0 -1
- package/dist/tools/cleanup-threads.js.map +0 -1
- package/dist/tools/confirm-scars.d.ts.map +0 -1
- package/dist/tools/confirm-scars.js.map +0 -1
- package/dist/tools/create-decision.d.ts.map +0 -1
- package/dist/tools/create-decision.js.map +0 -1
- package/dist/tools/create-learning.d.ts.map +0 -1
- package/dist/tools/create-learning.js.map +0 -1
- package/dist/tools/create-thread.d.ts.map +0 -1
- package/dist/tools/create-thread.js.map +0 -1
- package/dist/tools/definitions.d.ts.map +0 -1
- package/dist/tools/definitions.js.map +0 -1
- package/dist/tools/dismiss-suggestion.d.ts.map +0 -1
- package/dist/tools/dismiss-suggestion.js.map +0 -1
- package/dist/tools/get-transcript.d.ts.map +0 -1
- package/dist/tools/get-transcript.js.map +0 -1
- package/dist/tools/graph-traverse.d.ts.map +0 -1
- package/dist/tools/graph-traverse.js.map +0 -1
- package/dist/tools/list-threads.d.ts.map +0 -1
- package/dist/tools/list-threads.js.map +0 -1
- package/dist/tools/log.d.ts.map +0 -1
- package/dist/tools/log.js.map +0 -1
- package/dist/tools/prepare-context.d.ts.map +0 -1
- package/dist/tools/prepare-context.js.map +0 -1
- package/dist/tools/promote-suggestion.d.ts.map +0 -1
- package/dist/tools/promote-suggestion.js.map +0 -1
- package/dist/tools/recall.d.ts.map +0 -1
- package/dist/tools/recall.js.map +0 -1
- package/dist/tools/recall.test.d.ts +0 -5
- package/dist/tools/recall.test.d.ts.map +0 -1
- package/dist/tools/recall.test.js +0 -155
- package/dist/tools/recall.test.js.map +0 -1
- package/dist/tools/record-scar-usage-batch.d.ts.map +0 -1
- package/dist/tools/record-scar-usage-batch.js.map +0 -1
- package/dist/tools/record-scar-usage.d.ts.map +0 -1
- package/dist/tools/record-scar-usage.js.map +0 -1
- package/dist/tools/resolve-thread.d.ts.map +0 -1
- package/dist/tools/resolve-thread.js.map +0 -1
- package/dist/tools/save-transcript.d.ts.map +0 -1
- package/dist/tools/save-transcript.js.map +0 -1
- package/dist/tools/search-transcripts.d.ts.map +0 -1
- package/dist/tools/search-transcripts.js.map +0 -1
- package/dist/tools/search.d.ts.map +0 -1
- package/dist/tools/search.js.map +0 -1
- package/dist/tools/session-close.d.ts.map +0 -1
- package/dist/tools/session-close.js.map +0 -1
- package/dist/tools/session-start.d.ts.map +0 -1
- package/dist/tools/session-start.js.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/hooks/tests/test-hooks.sh +0 -577
|
@@ -48,33 +48,27 @@ export function detectAgent() {
|
|
|
48
48
|
let agent;
|
|
49
49
|
if (entrypoint === "cli") {
|
|
50
50
|
if (docker) {
|
|
51
|
-
|
|
52
|
-
agent = "CLI";
|
|
51
|
+
agent = "cli";
|
|
53
52
|
}
|
|
54
53
|
else if (process.env.GITMEM_AGENT_HOSTNAME && (hostname === process.env.GITMEM_AGENT_HOSTNAME)) {
|
|
55
|
-
|
|
56
|
-
agent = "CODA-1";
|
|
54
|
+
agent = "autonomous";
|
|
57
55
|
}
|
|
58
56
|
else {
|
|
59
|
-
|
|
60
|
-
agent = "CLI";
|
|
57
|
+
agent = "cli";
|
|
61
58
|
}
|
|
62
59
|
}
|
|
63
60
|
else if (entrypoint === "claude-desktop") {
|
|
64
|
-
|
|
65
|
-
agent = "DAC";
|
|
61
|
+
agent = "desktop";
|
|
66
62
|
}
|
|
67
63
|
else if (!entrypoint) {
|
|
68
|
-
// No entrypoint - could be Brain Local or Brain Cloud
|
|
69
64
|
if (hasFilesystemAccess()) {
|
|
70
|
-
agent = "
|
|
65
|
+
agent = "local";
|
|
71
66
|
}
|
|
72
67
|
else {
|
|
73
|
-
agent = "
|
|
68
|
+
agent = "cloud";
|
|
74
69
|
}
|
|
75
70
|
}
|
|
76
71
|
else {
|
|
77
|
-
// Unknown entrypoint
|
|
78
72
|
agent = "Unknown";
|
|
79
73
|
}
|
|
80
74
|
return {
|
|
@@ -84,6 +78,21 @@ export function detectAgent() {
|
|
|
84
78
|
agent,
|
|
85
79
|
};
|
|
86
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Normalize legacy agent names to new generic names.
|
|
83
|
+
* Accepts both old (CLI, DAC, CODA-1, Brain_Local, Brain_Cloud)
|
|
84
|
+
* and new (cli, desktop, autonomous, local, cloud) formats.
|
|
85
|
+
*/
|
|
86
|
+
const LEGACY_MAP = {
|
|
87
|
+
"CLI": "cli",
|
|
88
|
+
"DAC": "desktop",
|
|
89
|
+
"CODA-1": "autonomous",
|
|
90
|
+
"Brain_Local": "local",
|
|
91
|
+
"Brain_Cloud": "cloud",
|
|
92
|
+
};
|
|
93
|
+
export function normalizeAgent(input) {
|
|
94
|
+
return LEGACY_MAP[input] || input;
|
|
95
|
+
}
|
|
87
96
|
/**
|
|
88
97
|
* Get just the agent identity (convenience function)
|
|
89
98
|
*/
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Analytics Service
|
|
2
|
+
* Analytics Service
|
|
3
3
|
*
|
|
4
4
|
* Shared analytics engine for session insights. Powers both the
|
|
5
5
|
* gitmem-analyze MCP tool (CLI) and the GitMem Console dashboard.
|
|
6
6
|
*
|
|
7
7
|
* Uses directQuery for raw Supabase REST access (no ww-mcp dependency).
|
|
8
8
|
*/
|
|
9
|
-
import { directQuery, directQueryAll } from "./supabase-client.js";
|
|
9
|
+
import { directQuery, directQueryAll, safeInFilter } from "./supabase-client.js";
|
|
10
10
|
import { getCache } from "./cache.js";
|
|
11
11
|
// --- Query Layer ---
|
|
12
12
|
/**
|
|
@@ -97,7 +97,7 @@ export async function enrichScarUsageTitles(usages) {
|
|
|
97
97
|
const learnings = await directQuery("orchestra_learnings", {
|
|
98
98
|
select: "id,title,severity",
|
|
99
99
|
filters: {
|
|
100
|
-
id:
|
|
100
|
+
id: safeInFilter(ids),
|
|
101
101
|
},
|
|
102
102
|
});
|
|
103
103
|
// Build lookup map
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* 2. fetchDismissalCounts() — queries scar_usage for inline archival hints
|
|
11
11
|
* (called from recall to annotate frequently-dismissed scars)
|
|
12
12
|
*/
|
|
13
|
-
import { isConfigured } from "./supabase-client.js";
|
|
13
|
+
import { isConfigured, safeInFilter } from "./supabase-client.js";
|
|
14
14
|
const SUPABASE_URL = process.env.SUPABASE_URL || "";
|
|
15
15
|
const SUPABASE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_KEY || "";
|
|
16
16
|
const SUPABASE_REST_URL = SUPABASE_URL ? `${SUPABASE_URL}/rest/v1` : "";
|
|
@@ -76,7 +76,7 @@ export async function fetchDismissalCounts(scarIds) {
|
|
|
76
76
|
// Query scar_usage for the given scar IDs (last 90 days)
|
|
77
77
|
const url = new URL(`${SUPABASE_REST_URL}/scar_usage`);
|
|
78
78
|
url.searchParams.set("select", "scar_id,reference_type");
|
|
79
|
-
url.searchParams.set("scar_id",
|
|
79
|
+
url.searchParams.set("scar_id", safeInFilter(scarIds));
|
|
80
80
|
url.searchParams.set("surfaced_at", `gte.${new Date(Date.now() - 90 * 86400000).toISOString()}`);
|
|
81
81
|
url.searchParams.set("limit", "1000");
|
|
82
82
|
const response = await fetch(url.toString(), {
|
package/dist/services/cache.d.ts
CHANGED
package/dist/services/cache.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Caches search results to avoid repeated ww-mcp calls.
|
|
6
6
|
*
|
|
7
7
|
* Design: docs/systems/gitmem-caching.md
|
|
8
|
-
*
|
|
8
|
+
*
|
|
9
9
|
*/
|
|
10
10
|
import { createHash } from "crypto";
|
|
11
11
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, unlinkSync, statSync } from "fs";
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Validates session close compliance based on close type.
|
|
5
5
|
*
|
|
6
|
-
* Standard close requires
|
|
6
|
+
* Standard close requires:
|
|
7
7
|
* - task_completion object with timestamps proving each step was done
|
|
8
8
|
* - All 6 closing questions answered
|
|
9
9
|
* - human_corrections field present (even if empty string)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Validates session close compliance based on close type.
|
|
5
5
|
*
|
|
6
|
-
* Standard close requires
|
|
6
|
+
* Standard close requires:
|
|
7
7
|
* - task_completion object with timestamps proving each step was done
|
|
8
8
|
* - All 6 closing questions answered
|
|
9
9
|
* - human_corrections field present (even if empty string)
|
|
@@ -17,7 +17,7 @@ import { CLOSING_QUESTIONS } from "../constants/closing-questions.js";
|
|
|
17
17
|
/** Minimum time (ms) between asking human and receiving response */
|
|
18
18
|
const MIN_HUMAN_RESPONSE_GAP_MS = 3000;
|
|
19
19
|
/**
|
|
20
|
-
* Validate task_completion timestamps
|
|
20
|
+
* Validate task_completion timestamps
|
|
21
21
|
*
|
|
22
22
|
* Ensures:
|
|
23
23
|
* 1. All timestamps are valid ISO strings
|
|
@@ -83,14 +83,14 @@ function validateTaskCompletion(tc) {
|
|
|
83
83
|
/**
|
|
84
84
|
* Validate standard close parameters
|
|
85
85
|
*
|
|
86
|
-
*
|
|
86
|
+
* Now requires task_completion with timestamps proving each step was done.
|
|
87
87
|
*/
|
|
88
88
|
function validateStandardClose(params) {
|
|
89
89
|
const errors = [];
|
|
90
90
|
const warnings = [];
|
|
91
|
-
//
|
|
91
|
+
// task_completion is REQUIRED for standard close
|
|
92
92
|
if (!params.task_completion) {
|
|
93
|
-
errors.push("Standard close requires task_completion object
|
|
93
|
+
errors.push("Standard close requires task_completion object. " +
|
|
94
94
|
"You must complete each step: display questions → answer → ask human → wait for response");
|
|
95
95
|
}
|
|
96
96
|
else {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - GITMEM_STALE_CHECK: "true" | "false" - Check for stale data (default: "true")
|
|
11
11
|
* - SUPABASE_URL: Supabase project URL
|
|
12
12
|
*
|
|
13
|
-
*
|
|
13
|
+
*
|
|
14
14
|
*/
|
|
15
15
|
import type { GitMemTier } from "./tier.js";
|
|
16
16
|
export type SearchMode = "local" | "remote" | "auto";
|
package/dist/services/config.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - GITMEM_STALE_CHECK: "true" | "false" - Check for stale data (default: "true")
|
|
11
11
|
* - SUPABASE_URL: Supabase project URL
|
|
12
12
|
*
|
|
13
|
-
*
|
|
13
|
+
*
|
|
14
14
|
*/
|
|
15
15
|
import { getTier, hasSupabase, getTablePrefix } from "./tier.js";
|
|
16
16
|
// Private IP patterns for on-prem detection
|
|
@@ -66,6 +66,18 @@ export function acquireLockSync(lockPath, timeoutMs = DEFAULT_TIMEOUT_MS, retryM
|
|
|
66
66
|
if (error.code !== "EEXIST") {
|
|
67
67
|
throw new Error(`[file-lock] Failed to create lock file ${lockPath}: ${error.message}`);
|
|
68
68
|
}
|
|
69
|
+
// Lock file exists — check if we already hold it (reentrance detection)
|
|
70
|
+
try {
|
|
71
|
+
const raw = fs.readFileSync(lockPath, "utf-8");
|
|
72
|
+
const holder = JSON.parse(raw);
|
|
73
|
+
if (holder.pid === process.pid && holder.hostname === os.hostname()) {
|
|
74
|
+
// Same process already holds the lock — reentrant call, allow through
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Can't read lock — fall through to stale check
|
|
80
|
+
}
|
|
69
81
|
// Lock file exists — check if stale
|
|
70
82
|
if (isLockStale(lockPath)) {
|
|
71
83
|
try {
|
|
@@ -2,20 +2,33 @@
|
|
|
2
2
|
* Resolved .gitmem directory path
|
|
3
3
|
*
|
|
4
4
|
* Solves: process.cwd() changes when agents cd into other repos (e.g., /workspace/gitmem),
|
|
5
|
-
* but .gitmem/ was created in the project root
|
|
5
|
+
* but .gitmem/ was created in the project root.
|
|
6
6
|
* The MCP server is long-running, so we resolve the path once and cache it.
|
|
7
7
|
*
|
|
8
8
|
* Resolution order:
|
|
9
|
-
* 1.
|
|
10
|
-
* 2.
|
|
11
|
-
* 3.
|
|
9
|
+
* 1. GITMEM_DIR env var (explicit override)
|
|
10
|
+
* 2. Cached path from session_start (most reliable — session_start created the directory)
|
|
11
|
+
* 3. Walk up from process.cwd() looking for existing .gitmem/ sentinels (backward compat)
|
|
12
|
+
* 4. Fall back to ~/.gitmem (developer-scoped, survives across projects/containers)
|
|
12
13
|
*/
|
|
14
|
+
/**
|
|
15
|
+
* Validate a string intended for use as a single path component (directory name or filename).
|
|
16
|
+
* Rejects path traversal sequences, directory separators, and null bytes.
|
|
17
|
+
* Throws on invalid input — callers should validate before reaching this layer.
|
|
18
|
+
*/
|
|
19
|
+
export declare function sanitizePathComponent(value: string, label: string): string;
|
|
13
20
|
/**
|
|
14
21
|
* Set the .gitmem directory path (called by session_start after creating it)
|
|
15
22
|
*/
|
|
16
23
|
export declare function setGitmemDir(dir: string): void;
|
|
17
24
|
/**
|
|
18
25
|
* Get the resolved .gitmem directory path
|
|
26
|
+
*
|
|
27
|
+
* Resolution order:
|
|
28
|
+
* 1. GITMEM_DIR env var (explicit override)
|
|
29
|
+
* 2. Cached path from session_start (most reliable)
|
|
30
|
+
* 3. Walk up from CWD looking for existing .gitmem/ sentinels (backward compat)
|
|
31
|
+
* 4. Fall back to ~/.gitmem (developer-scoped, survives across projects/containers)
|
|
19
32
|
*/
|
|
20
33
|
export declare function getGitmemDir(): string;
|
|
21
34
|
/**
|
|
@@ -2,16 +2,32 @@
|
|
|
2
2
|
* Resolved .gitmem directory path
|
|
3
3
|
*
|
|
4
4
|
* Solves: process.cwd() changes when agents cd into other repos (e.g., /workspace/gitmem),
|
|
5
|
-
* but .gitmem/ was created in the project root
|
|
5
|
+
* but .gitmem/ was created in the project root.
|
|
6
6
|
* The MCP server is long-running, so we resolve the path once and cache it.
|
|
7
7
|
*
|
|
8
8
|
* Resolution order:
|
|
9
|
-
* 1.
|
|
10
|
-
* 2.
|
|
11
|
-
* 3.
|
|
9
|
+
* 1. GITMEM_DIR env var (explicit override)
|
|
10
|
+
* 2. Cached path from session_start (most reliable — session_start created the directory)
|
|
11
|
+
* 3. Walk up from process.cwd() looking for existing .gitmem/ sentinels (backward compat)
|
|
12
|
+
* 4. Fall back to ~/.gitmem (developer-scoped, survives across projects/containers)
|
|
12
13
|
*/
|
|
13
14
|
import * as path from "path";
|
|
14
15
|
import * as fs from "fs";
|
|
16
|
+
import * as os from "os";
|
|
17
|
+
/**
|
|
18
|
+
* Validate a string intended for use as a single path component (directory name or filename).
|
|
19
|
+
* Rejects path traversal sequences, directory separators, and null bytes.
|
|
20
|
+
* Throws on invalid input — callers should validate before reaching this layer.
|
|
21
|
+
*/
|
|
22
|
+
export function sanitizePathComponent(value, label) {
|
|
23
|
+
if (!value || typeof value !== "string") {
|
|
24
|
+
throw new Error(`${label} must be a non-empty string`);
|
|
25
|
+
}
|
|
26
|
+
if (value.includes("..") || value.includes("/") || value.includes("\\") || value.includes("\0")) {
|
|
27
|
+
throw new Error(`${label} contains invalid characters (path traversal rejected)`);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
15
31
|
let cachedGitmemDir = null;
|
|
16
32
|
/**
|
|
17
33
|
* Set the .gitmem directory path (called by session_start after creating it)
|
|
@@ -22,13 +38,29 @@ export function setGitmemDir(dir) {
|
|
|
22
38
|
}
|
|
23
39
|
/**
|
|
24
40
|
* Get the resolved .gitmem directory path
|
|
41
|
+
*
|
|
42
|
+
* Resolution order:
|
|
43
|
+
* 1. GITMEM_DIR env var (explicit override)
|
|
44
|
+
* 2. Cached path from session_start (most reliable)
|
|
45
|
+
* 3. Walk up from CWD looking for existing .gitmem/ sentinels (backward compat)
|
|
46
|
+
* 4. Fall back to ~/.gitmem (developer-scoped, survives across projects/containers)
|
|
25
47
|
*/
|
|
26
48
|
export function getGitmemDir() {
|
|
27
|
-
// 1.
|
|
49
|
+
// 1. GITMEM_DIR env var (explicit override, highest priority)
|
|
50
|
+
const envDir = process.env.GITMEM_DIR;
|
|
51
|
+
if (envDir) {
|
|
52
|
+
if (!cachedGitmemDir || cachedGitmemDir !== envDir) {
|
|
53
|
+
cachedGitmemDir = envDir;
|
|
54
|
+
console.error(`[gitmem-dir] Using GITMEM_DIR env var: ${envDir}`);
|
|
55
|
+
}
|
|
56
|
+
return envDir;
|
|
57
|
+
}
|
|
58
|
+
// 2. Use cached path from session_start
|
|
28
59
|
if (cachedGitmemDir && fs.existsSync(cachedGitmemDir)) {
|
|
29
60
|
return cachedGitmemDir;
|
|
30
61
|
}
|
|
31
|
-
//
|
|
62
|
+
// 3. Walk up from CWD looking for existing .gitmem directory
|
|
63
|
+
// Backward compat: finds project-scoped .gitmem/ from older installations.
|
|
32
64
|
// Sentinel files checked in priority order:
|
|
33
65
|
// - active-sessions.json (multi-session registry, GIT-19)
|
|
34
66
|
// - config.json (project-level gitmem config)
|
|
@@ -46,9 +78,9 @@ export function getGitmemDir() {
|
|
|
46
78
|
}
|
|
47
79
|
dir = path.dirname(dir);
|
|
48
80
|
}
|
|
49
|
-
//
|
|
50
|
-
const fallback = path.join(
|
|
51
|
-
console.error(`[gitmem-dir] Falling back to
|
|
81
|
+
// 4. Fall back to ~/.gitmem (developer-scoped — survives across projects and containers)
|
|
82
|
+
const fallback = path.join(os.homedir(), ".gitmem");
|
|
83
|
+
console.error(`[gitmem-dir] Falling back to home dir: ${fallback}`);
|
|
52
84
|
return fallback;
|
|
53
85
|
}
|
|
54
86
|
/**
|
|
@@ -62,6 +94,7 @@ export function getGitmemPath(filename) {
|
|
|
62
94
|
* Creates the directory if it doesn't exist.
|
|
63
95
|
*/
|
|
64
96
|
export function getSessionDir(sessionId) {
|
|
97
|
+
sanitizePathComponent(sessionId, "sessionId");
|
|
65
98
|
const sessionsDir = path.join(getGitmemDir(), "sessions", sessionId);
|
|
66
99
|
if (!fs.existsSync(sessionsDir)) {
|
|
67
100
|
fs.mkdirSync(sessionsDir, { recursive: true });
|
|
@@ -73,6 +106,7 @@ export function getSessionDir(sessionId) {
|
|
|
73
106
|
* Get a file path within a per-session directory.
|
|
74
107
|
*/
|
|
75
108
|
export function getSessionPath(sessionId, filename) {
|
|
109
|
+
sanitizePathComponent(filename, "filename");
|
|
76
110
|
return path.join(getSessionDir(sessionId), filename);
|
|
77
111
|
}
|
|
78
112
|
/**
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Local File Storage — Free Tier Backend
|
|
3
3
|
*
|
|
4
4
|
* Stores scars, sessions, decisions, and scar usage as JSON files
|
|
5
|
-
* in the .gitmem/ directory
|
|
5
|
+
* in the .gitmem/ directory (defaults to ~/.gitmem, overridable via GITMEM_DIR).
|
|
6
6
|
*
|
|
7
7
|
* Provides keyword-based search (no embeddings needed).
|
|
8
8
|
*/
|
|
@@ -2,19 +2,20 @@
|
|
|
2
2
|
* Local File Storage — Free Tier Backend
|
|
3
3
|
*
|
|
4
4
|
* Stores scars, sessions, decisions, and scar usage as JSON files
|
|
5
|
-
* in the .gitmem/ directory
|
|
5
|
+
* in the .gitmem/ directory (defaults to ~/.gitmem, overridable via GITMEM_DIR).
|
|
6
6
|
*
|
|
7
7
|
* Provides keyword-based search (no embeddings needed).
|
|
8
8
|
*/
|
|
9
9
|
import * as fs from "fs";
|
|
10
10
|
import * as path from "path";
|
|
11
11
|
import { bm25Search } from "./bm25.js";
|
|
12
|
+
import { getGitmemDir } from "./gitmem-dir.js";
|
|
12
13
|
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
|
|
13
14
|
const WARN_FILE_SIZE = 1 * 1024 * 1024; // 1MB
|
|
14
15
|
export class LocalFileStorage {
|
|
15
16
|
basePath;
|
|
16
17
|
constructor(basePath) {
|
|
17
|
-
this.basePath = basePath ||
|
|
18
|
+
this.basePath = basePath || getGitmemDir();
|
|
18
19
|
this.ensureDir();
|
|
19
20
|
}
|
|
20
21
|
ensureDir() {
|
|
@@ -142,7 +143,7 @@ export class LocalFileStorage {
|
|
|
142
143
|
const l = byId.get(r.id);
|
|
143
144
|
if (!l)
|
|
144
145
|
continue;
|
|
145
|
-
//
|
|
146
|
+
// Deprioritize starter scars (0.7x multiplier)
|
|
146
147
|
const isStarter = !!l.is_starter;
|
|
147
148
|
const adjustedSimilarity = isStarter ? r.similarity * 0.7 : r.similarity;
|
|
148
149
|
mapped.push({
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* - Deterministic results (same model + same data = same results)
|
|
12
12
|
* - Per-container consistency (each loads same data)
|
|
13
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* Cache consistency
|
|
15
15
|
*/
|
|
16
16
|
import type { Project, RelevantScar } from "../types/index.js";
|
|
17
17
|
interface ScarRecord {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* - Deterministic results (same model + same data = same results)
|
|
12
12
|
* - Per-container consistency (each loads same data)
|
|
13
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* Cache consistency
|
|
15
15
|
*/
|
|
16
16
|
import { embed as generateEmbedding, getEmbeddingDim, isEmbeddingAvailable } from "./embedding.js";
|
|
17
17
|
// Embedding dimension — read from provider config at runtime
|
|
@@ -184,7 +184,7 @@ export class LocalVectorSearch {
|
|
|
184
184
|
severity: scar.severity || "medium",
|
|
185
185
|
counter_arguments: scar.counter_arguments || [],
|
|
186
186
|
similarity: Math.round(similarity * 1000) / 1000, // 3 decimal places
|
|
187
|
-
//
|
|
187
|
+
// Include enriched fields for LLM-cooperative enforcement
|
|
188
188
|
why_this_matters: scar.why_this_matters,
|
|
189
189
|
action_protocol: scar.action_protocol,
|
|
190
190
|
self_check_criteria: scar.self_check_criteria,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* GitMem Performance Metrics Service
|
|
3
3
|
*
|
|
4
4
|
* Tracks latency, result counts, and relevance signals for all GitMem tools.
|
|
5
|
-
*
|
|
5
|
+
* Instrumentation layer for performance tracking.
|
|
6
6
|
*/
|
|
7
7
|
/**
|
|
8
8
|
* Tool names that can be tracked
|
|
@@ -15,7 +15,7 @@ export type PhaseTag = "session_start" | "session_refresh" | "session_close" | "
|
|
|
15
15
|
/**
|
|
16
16
|
* Agent identities
|
|
17
17
|
*/
|
|
18
|
-
export type AgentIdentity = "
|
|
18
|
+
export type AgentIdentity = "cli" | "desktop" | "autonomous" | "local" | "cloud";
|
|
19
19
|
/**
|
|
20
20
|
* Metrics data for a query
|
|
21
21
|
*/
|
|
@@ -36,7 +36,7 @@ export interface QueryMetrics {
|
|
|
36
36
|
metadata?: Record<string, unknown>;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
* Performance targets
|
|
39
|
+
* Performance targets
|
|
40
40
|
*/
|
|
41
41
|
export declare const PERFORMANCE_TARGETS: Record<ToolName, number>;
|
|
42
42
|
/**
|
|
@@ -58,16 +58,16 @@ export declare class Timer {
|
|
|
58
58
|
export declare function recordMetrics(metrics: QueryMetrics): Promise<void>;
|
|
59
59
|
/**
|
|
60
60
|
* Re-export performance types from types/index.ts
|
|
61
|
-
*
|
|
61
|
+
* Enhanced instrumentation for test harness validation
|
|
62
62
|
*/
|
|
63
63
|
export type { PerformanceData, PerformanceBreakdown, ComponentPerformance, DataSource, CacheStatus, } from "../types/index.js";
|
|
64
64
|
import type { PerformanceData, PerformanceBreakdown, ComponentPerformance, DataSource, CacheStatus } from "../types/index.js";
|
|
65
65
|
/**
|
|
66
|
-
* Build component performance data
|
|
66
|
+
* Build component performance data
|
|
67
67
|
*/
|
|
68
68
|
export declare function buildComponentPerformance(latencyMs: number, source: DataSource, networkCall: boolean, cacheStatus?: CacheStatus): ComponentPerformance;
|
|
69
69
|
/**
|
|
70
|
-
* Count network calls from breakdown
|
|
70
|
+
* Count network calls from breakdown
|
|
71
71
|
*/
|
|
72
72
|
export declare function countNetworkCalls(breakdown?: PerformanceBreakdown): number;
|
|
73
73
|
export declare function buildPerformanceData(toolName: ToolName, latencyMs: number, resultCount: number, options?: {
|
package/dist/services/metrics.js
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
* GitMem Performance Metrics Service
|
|
3
3
|
*
|
|
4
4
|
* Tracks latency, result counts, and relevance signals for all GitMem tools.
|
|
5
|
-
*
|
|
5
|
+
* Instrumentation layer for performance tracking.
|
|
6
6
|
*/
|
|
7
7
|
import { v4 as uuidv4 } from "uuid";
|
|
8
8
|
import * as supabase from "./supabase-client.js";
|
|
9
9
|
import { getEffectTracker } from "./effect-tracker.js";
|
|
10
10
|
import { hasSupabase } from "./tier.js";
|
|
11
11
|
/**
|
|
12
|
-
* Performance targets
|
|
12
|
+
* Performance targets
|
|
13
13
|
*/
|
|
14
14
|
export const PERFORMANCE_TARGETS = {
|
|
15
15
|
recall: 2000,
|
|
16
16
|
search: 500,
|
|
17
17
|
log: 500,
|
|
18
|
-
session_start: 750, //
|
|
19
|
-
session_refresh: 750, //
|
|
20
|
-
session_close: 1500, //
|
|
18
|
+
session_start: 750, // Lean start (was 1500)
|
|
19
|
+
session_refresh: 750, // Lean refresh (was 1500)
|
|
20
|
+
session_close: 1500, // Tightened (was 3000)
|
|
21
21
|
create_learning: 3000,
|
|
22
22
|
create_decision: 3000,
|
|
23
23
|
record_scar_usage: 1000,
|
|
@@ -84,7 +84,7 @@ export async function recordMetrics(metrics) {
|
|
|
84
84
|
await tracker.track("metrics", metrics.tool_name, () => supabase.directUpsert("gitmem_query_metrics", record));
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
|
-
* Build component performance data
|
|
87
|
+
* Build component performance data
|
|
88
88
|
*/
|
|
89
89
|
export function buildComponentPerformance(latencyMs, source, networkCall, cacheStatus = networkCall ? "miss" : "hit") {
|
|
90
90
|
return {
|
|
@@ -95,7 +95,7 @@ export function buildComponentPerformance(latencyMs, source, networkCall, cacheS
|
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
97
|
/**
|
|
98
|
-
* Count network calls from breakdown
|
|
98
|
+
* Count network calls from breakdown
|
|
99
99
|
*/
|
|
100
100
|
export function countNetworkCalls(breakdown) {
|
|
101
101
|
if (!breakdown)
|
|
@@ -136,7 +136,7 @@ export function buildPerformanceData(toolName, latencyMs, resultCount, options)
|
|
|
136
136
|
cache_hit: options?.cache_hit ?? fullyLocal,
|
|
137
137
|
cache_age_ms: options?.cache_age_ms,
|
|
138
138
|
search_mode: options?.search_mode,
|
|
139
|
-
//
|
|
139
|
+
// Detailed instrumentation for test harness
|
|
140
140
|
total_latency_ms: latencyMs,
|
|
141
141
|
network_calls_made: networkCallsMade,
|
|
142
142
|
fully_local: fullyLocal,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Session State Management
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Track current session context for auto-injecting into recall calls
|
|
4
|
+
* Track surfaced scars for auto-bridging Q6 answers to scar_usage records
|
|
5
5
|
*
|
|
6
6
|
* Maintains in-memory state of the current active session including:
|
|
7
7
|
* - session_id from session_start
|
|
@@ -54,12 +54,12 @@ export declare function getProject(): string | null;
|
|
|
54
54
|
*/
|
|
55
55
|
export declare function hasActiveIssue(): boolean;
|
|
56
56
|
/**
|
|
57
|
-
*
|
|
57
|
+
* Add surfaced scars to tracking (deduplicates by scar_id)
|
|
58
58
|
* Called by session_start and recall when scars are surfaced.
|
|
59
59
|
*/
|
|
60
60
|
export declare function addSurfacedScars(scars: SurfacedScar[]): void;
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
62
|
+
* Get all surfaced scars for the current session
|
|
63
63
|
*/
|
|
64
64
|
export declare function getSurfacedScars(): SurfacedScar[];
|
|
65
65
|
/**
|
|
@@ -105,15 +105,15 @@ export interface SessionActivity {
|
|
|
105
105
|
}
|
|
106
106
|
export declare function getSessionActivity(): SessionActivity | null;
|
|
107
107
|
/**
|
|
108
|
-
*
|
|
108
|
+
* : Set threads for the current session
|
|
109
109
|
*/
|
|
110
110
|
export declare function setThreads(threads: ThreadObject[]): void;
|
|
111
111
|
/**
|
|
112
|
-
*
|
|
112
|
+
* : Get threads for the current session
|
|
113
113
|
*/
|
|
114
114
|
export declare function getThreads(): ThreadObject[];
|
|
115
115
|
/**
|
|
116
|
-
*
|
|
116
|
+
* : Resolve a thread in session state by ID.
|
|
117
117
|
* Returns the resolved thread or null if not found.
|
|
118
118
|
*/
|
|
119
119
|
export declare function resolveThreadInState(threadId: string, resolutionNote?: string): ThreadObject | null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Session State Management
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Track current session context for auto-injecting into recall calls
|
|
4
|
+
* Track surfaced scars for auto-bridging Q6 answers to scar_usage records
|
|
5
5
|
*
|
|
6
6
|
* Maintains in-memory state of the current active session including:
|
|
7
7
|
* - session_id from session_start
|
|
@@ -59,7 +59,7 @@ export function hasActiveIssue() {
|
|
|
59
59
|
return !!(currentSession?.linearIssue);
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
62
|
+
* Add surfaced scars to tracking (deduplicates by scar_id)
|
|
63
63
|
* Called by session_start and recall when scars are surfaced.
|
|
64
64
|
*/
|
|
65
65
|
export function addSurfacedScars(scars) {
|
|
@@ -76,7 +76,7 @@ export function addSurfacedScars(scars) {
|
|
|
76
76
|
console.error(`[session-state] Surfaced scars tracked: ${currentSession.surfacedScars.length} total`);
|
|
77
77
|
}
|
|
78
78
|
/**
|
|
79
|
-
*
|
|
79
|
+
* Get all surfaced scars for the current session
|
|
80
80
|
*/
|
|
81
81
|
export function getSurfacedScars() {
|
|
82
82
|
return currentSession?.surfacedScars || [];
|
|
@@ -121,6 +121,9 @@ export function hasUnconfirmedScars() {
|
|
|
121
121
|
const confirmedIds = new Set(currentSession.confirmations.map(c => c.scar_id));
|
|
122
122
|
return recallScars.some(s => !confirmedIds.has(s.scar_id));
|
|
123
123
|
}
|
|
124
|
+
// Security: cap unbounded arrays to prevent memory exhaustion in long sessions
|
|
125
|
+
const MAX_OBSERVATIONS = 500;
|
|
126
|
+
const MAX_CHILDREN = 100;
|
|
124
127
|
/**
|
|
125
128
|
* v2 Phase 2: Add observations from sub-agents/teammates
|
|
126
129
|
*/
|
|
@@ -134,6 +137,10 @@ export function addObservations(newObs) {
|
|
|
134
137
|
absorbed_at: o.absorbed_at || new Date().toISOString(),
|
|
135
138
|
}));
|
|
136
139
|
currentSession.observations.push(...timestamped);
|
|
140
|
+
// Cap to prevent memory exhaustion — keep most recent
|
|
141
|
+
if (currentSession.observations.length > MAX_OBSERVATIONS) {
|
|
142
|
+
currentSession.observations = currentSession.observations.slice(-MAX_OBSERVATIONS);
|
|
143
|
+
}
|
|
137
144
|
console.error(`[session-state] Observations tracked: ${currentSession.observations.length} total`);
|
|
138
145
|
return timestamped.length;
|
|
139
146
|
}
|
|
@@ -151,6 +158,11 @@ export function addChild(child) {
|
|
|
151
158
|
console.warn("[session-state] Cannot add child: no active session");
|
|
152
159
|
return;
|
|
153
160
|
}
|
|
161
|
+
// Cap to prevent memory exhaustion — reject silently beyond limit
|
|
162
|
+
if (currentSession.children.length >= MAX_CHILDREN) {
|
|
163
|
+
console.warn(`[session-state] Children cap reached (${MAX_CHILDREN}), ignoring new child: ${child.role}`);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
154
166
|
currentSession.children.push(child);
|
|
155
167
|
console.error(`[session-state] Child registered: ${child.role} (${child.type}), total: ${currentSession.children.length}`);
|
|
156
168
|
}
|
|
@@ -173,7 +185,7 @@ export function getSessionActivity() {
|
|
|
173
185
|
};
|
|
174
186
|
}
|
|
175
187
|
/**
|
|
176
|
-
*
|
|
188
|
+
* : Set threads for the current session
|
|
177
189
|
*/
|
|
178
190
|
export function setThreads(threads) {
|
|
179
191
|
if (!currentSession) {
|
|
@@ -184,13 +196,13 @@ export function setThreads(threads) {
|
|
|
184
196
|
console.error(`[session-state] Threads set: ${threads.length} total`);
|
|
185
197
|
}
|
|
186
198
|
/**
|
|
187
|
-
*
|
|
199
|
+
* : Get threads for the current session
|
|
188
200
|
*/
|
|
189
201
|
export function getThreads() {
|
|
190
202
|
return currentSession?.threads || [];
|
|
191
203
|
}
|
|
192
204
|
/**
|
|
193
|
-
*
|
|
205
|
+
* : Resolve a thread in session state by ID.
|
|
194
206
|
* Returns the resolved thread or null if not found.
|
|
195
207
|
*/
|
|
196
208
|
export function resolveThreadInState(threadId, resolutionNote) {
|