gitmem-mcp 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 +5 -0
- package/bin/init-wizard.js +11 -0
- package/dist/schemas/analyze.d.ts +2 -2
- package/dist/server.js +7 -0
- package/dist/services/metrics.d.ts +1 -1
- package/dist/services/metrics.js +1 -0
- package/dist/services/session-state.d.ts +12 -2
- package/dist/services/session-state.js +28 -0
- package/dist/tools/definitions.d.ts +460 -0
- package/dist/tools/definitions.js +81 -2
- package/dist/tools/reflect-scars.d.ts +20 -0
- package/dist/tools/reflect-scars.js +219 -0
- package/dist/tools/session-close.js +28 -3
- package/dist/types/index.d.ts +24 -0
- package/hooks/scripts/auto-retrieve-hook.sh +31 -0
- package/package.json +2 -1
|
@@ -77,6 +77,39 @@ export const TOOLS = [
|
|
|
77
77
|
required: ["confirmations"],
|
|
78
78
|
},
|
|
79
79
|
},
|
|
80
|
+
{
|
|
81
|
+
name: "reflect_scars",
|
|
82
|
+
description: "End-of-session scar reflection — the closing counterpart to confirm_scars. Mirrors CODA-1's [Scar Reflection] protocol. Call BEFORE session_close to provide evidence of how each surfaced scar was handled. OBEYED: concrete evidence of compliance (min 15 chars). REFUTED: why it didn't apply + what was done instead (min 30 chars). Session close uses reflections to set execution_successful accurately.",
|
|
83
|
+
inputSchema: {
|
|
84
|
+
type: "object",
|
|
85
|
+
properties: {
|
|
86
|
+
reflections: {
|
|
87
|
+
type: "array",
|
|
88
|
+
items: {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties: {
|
|
91
|
+
scar_id: {
|
|
92
|
+
type: "string",
|
|
93
|
+
description: "UUID of the surfaced scar (from recall or session_start)",
|
|
94
|
+
},
|
|
95
|
+
outcome: {
|
|
96
|
+
type: "string",
|
|
97
|
+
enum: ["OBEYED", "REFUTED"],
|
|
98
|
+
description: "OBEYED: followed the scar with evidence. REFUTED: scar didn't apply, explain why.",
|
|
99
|
+
},
|
|
100
|
+
evidence: {
|
|
101
|
+
type: "string",
|
|
102
|
+
description: "Concrete evidence of compliance (OBEYED, min 15 chars) or explanation of why scar didn't apply (REFUTED, min 30 chars).",
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
required: ["scar_id", "outcome", "evidence"],
|
|
106
|
+
},
|
|
107
|
+
description: "One reflection per surfaced scar.",
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
required: ["reflections"],
|
|
111
|
+
},
|
|
112
|
+
},
|
|
80
113
|
{
|
|
81
114
|
name: "session_start",
|
|
82
115
|
description: "Initialize session, detect agent, load institutional context (last session, recent decisions, open threads). Scars surface on-demand via recall(). DISPLAY: The result includes a pre-formatted 'display' field visible in the tool result. Output the display field verbatim as your response — tool results are collapsed in the CLI.",
|
|
@@ -768,6 +801,29 @@ export const TOOLS = [
|
|
|
768
801
|
required: ["confirmations"],
|
|
769
802
|
},
|
|
770
803
|
},
|
|
804
|
+
{
|
|
805
|
+
name: "gitmem-rf",
|
|
806
|
+
description: "gitmem-rf (reflect_scars) - End-of-session scar reflection (OBEYED/REFUTED with evidence)",
|
|
807
|
+
inputSchema: {
|
|
808
|
+
type: "object",
|
|
809
|
+
properties: {
|
|
810
|
+
reflections: {
|
|
811
|
+
type: "array",
|
|
812
|
+
items: {
|
|
813
|
+
type: "object",
|
|
814
|
+
properties: {
|
|
815
|
+
scar_id: { type: "string", description: "UUID of the surfaced scar" },
|
|
816
|
+
outcome: { type: "string", enum: ["OBEYED", "REFUTED"], description: "Reflection outcome" },
|
|
817
|
+
evidence: { type: "string", description: "Evidence (OBEYED min 15 chars, REFUTED min 30 chars)" },
|
|
818
|
+
},
|
|
819
|
+
required: ["scar_id", "outcome", "evidence"],
|
|
820
|
+
},
|
|
821
|
+
description: "One reflection per surfaced scar",
|
|
822
|
+
},
|
|
823
|
+
},
|
|
824
|
+
required: ["reflections"],
|
|
825
|
+
},
|
|
826
|
+
},
|
|
771
827
|
{
|
|
772
828
|
name: "gitmem-ss",
|
|
773
829
|
description: "gitmem-ss (session_start) - Initialize session with institutional context. DISPLAY: The result includes a pre-formatted 'display' field. Output the display field verbatim as your response — tool results are collapsed in the CLI.",
|
|
@@ -1454,6 +1510,29 @@ export const TOOLS = [
|
|
|
1454
1510
|
required: ["confirmations"],
|
|
1455
1511
|
},
|
|
1456
1512
|
},
|
|
1513
|
+
{
|
|
1514
|
+
name: "gm-reflect",
|
|
1515
|
+
description: "gm-reflect (reflect_scars) - End-of-session scar reflection",
|
|
1516
|
+
inputSchema: {
|
|
1517
|
+
type: "object",
|
|
1518
|
+
properties: {
|
|
1519
|
+
reflections: {
|
|
1520
|
+
type: "array",
|
|
1521
|
+
items: {
|
|
1522
|
+
type: "object",
|
|
1523
|
+
properties: {
|
|
1524
|
+
scar_id: { type: "string", description: "UUID of the surfaced scar" },
|
|
1525
|
+
outcome: { type: "string", enum: ["OBEYED", "REFUTED"] },
|
|
1526
|
+
evidence: { type: "string", description: "Evidence of compliance or refutation" },
|
|
1527
|
+
},
|
|
1528
|
+
required: ["scar_id", "outcome", "evidence"],
|
|
1529
|
+
},
|
|
1530
|
+
description: "One reflection per surfaced scar",
|
|
1531
|
+
},
|
|
1532
|
+
},
|
|
1533
|
+
required: ["reflections"],
|
|
1534
|
+
},
|
|
1535
|
+
},
|
|
1457
1536
|
{
|
|
1458
1537
|
name: "gm-refresh",
|
|
1459
1538
|
description: "gm-refresh (session_refresh) - Refresh context for the active session without creating a new one. DISPLAY: The result includes a pre-formatted 'display' field. Output the display field verbatim as your response — tool results are collapsed in the CLI.",
|
|
@@ -2117,7 +2196,7 @@ export const TOOLS = [
|
|
|
2117
2196
|
*/
|
|
2118
2197
|
export const ALIAS_TOOL_NAMES = new Set([
|
|
2119
2198
|
// gitmem-* aliases
|
|
2120
|
-
"gitmem-r", "gitmem-cs", "gitmem-ss", "gitmem-sr", "gitmem-sc",
|
|
2199
|
+
"gitmem-r", "gitmem-cs", "gitmem-rf", "gitmem-ss", "gitmem-sr", "gitmem-sc",
|
|
2121
2200
|
"gitmem-cl", "gitmem-cd", "gitmem-rs", "gitmem-rsb",
|
|
2122
2201
|
"gitmem-st", "gitmem-gt", "gitmem-stx",
|
|
2123
2202
|
"gitmem-search", "gitmem-log", "gitmem-analyze",
|
|
@@ -2125,7 +2204,7 @@ export const ALIAS_TOOL_NAMES = new Set([
|
|
|
2125
2204
|
"gitmem-lt", "gitmem-rt", "gitmem-ct", "gitmem-ps", "gitmem-ds",
|
|
2126
2205
|
"gitmem-cleanup", "gitmem-health", "gitmem-al", "gitmem-graph",
|
|
2127
2206
|
// gm-* aliases
|
|
2128
|
-
"gm-open", "gm-confirm", "gm-refresh", "gm-close",
|
|
2207
|
+
"gm-open", "gm-confirm", "gm-reflect", "gm-refresh", "gm-close",
|
|
2129
2208
|
"gm-scar", "gm-search", "gm-log", "gm-analyze",
|
|
2130
2209
|
"gm-pc", "gm-absorb",
|
|
2131
2210
|
"gm-threads", "gm-resolve", "gm-thread-new", "gm-promote", "gm-dismiss",
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* reflect_scars Tool
|
|
3
|
+
*
|
|
4
|
+
* End-of-session scar reflection — the closing counterpart to confirm_scars.
|
|
5
|
+
* Mirrors CODA-1's [Scar Reflection] protocol for CLI/DAC/Brain agents.
|
|
6
|
+
*
|
|
7
|
+
* Flow:
|
|
8
|
+
* recall → confirm_scars (START: "I will...") → work
|
|
9
|
+
* → reflect_scars (END: "I did...") → session_close uses reflections
|
|
10
|
+
*
|
|
11
|
+
* Each scar surfaced during the session should be reflected upon with:
|
|
12
|
+
* OBEYED — Followed the scar, with concrete evidence (min 15 chars)
|
|
13
|
+
* REFUTED — Scar didn't apply or was overridden (min 30 chars)
|
|
14
|
+
*/
|
|
15
|
+
import type { ReflectScarsParams, ReflectScarsResult } from "../types/index.js";
|
|
16
|
+
/**
|
|
17
|
+
* Main tool implementation: reflect_scars
|
|
18
|
+
*/
|
|
19
|
+
export declare function reflectScars(params: ReflectScarsParams): Promise<ReflectScarsResult>;
|
|
20
|
+
//# sourceMappingURL=reflect-scars.d.ts.map
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* reflect_scars Tool
|
|
3
|
+
*
|
|
4
|
+
* End-of-session scar reflection — the closing counterpart to confirm_scars.
|
|
5
|
+
* Mirrors CODA-1's [Scar Reflection] protocol for CLI/DAC/Brain agents.
|
|
6
|
+
*
|
|
7
|
+
* Flow:
|
|
8
|
+
* recall → confirm_scars (START: "I will...") → work
|
|
9
|
+
* → reflect_scars (END: "I did...") → session_close uses reflections
|
|
10
|
+
*
|
|
11
|
+
* Each scar surfaced during the session should be reflected upon with:
|
|
12
|
+
* OBEYED — Followed the scar, with concrete evidence (min 15 chars)
|
|
13
|
+
* REFUTED — Scar didn't apply or was overridden (min 30 chars)
|
|
14
|
+
*/
|
|
15
|
+
import { getCurrentSession, getSurfacedScars, addReflections, getReflections, } from "../services/session-state.js";
|
|
16
|
+
import { Timer, buildPerformanceData } from "../services/metrics.js";
|
|
17
|
+
import { getSessionPath } from "../services/gitmem-dir.js";
|
|
18
|
+
import { wrapDisplay, STATUS, ANSI } from "../services/display-protocol.js";
|
|
19
|
+
import * as fs from "fs";
|
|
20
|
+
// Minimum evidence lengths (matching CODA-1's refute-or-obey.test.js)
|
|
21
|
+
const MIN_OBEYED_LENGTH = 15;
|
|
22
|
+
const MIN_REFUTED_LENGTH = 30;
|
|
23
|
+
/**
|
|
24
|
+
* Validate a single reflection.
|
|
25
|
+
* Returns null if valid, or an error string if invalid.
|
|
26
|
+
*/
|
|
27
|
+
function validateReflection(reflection, scar) {
|
|
28
|
+
const { outcome, evidence } = reflection;
|
|
29
|
+
if (!evidence || evidence.trim().length === 0) {
|
|
30
|
+
return `${scar.scar_title}: Evidence is required for ${outcome}.`;
|
|
31
|
+
}
|
|
32
|
+
switch (outcome) {
|
|
33
|
+
case "OBEYED":
|
|
34
|
+
if (evidence.trim().length < MIN_OBEYED_LENGTH) {
|
|
35
|
+
return `${scar.scar_title}: OBEYED evidence too short (${evidence.trim().length} chars, minimum ${MIN_OBEYED_LENGTH}). Provide concrete evidence of compliance.`;
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
case "REFUTED":
|
|
39
|
+
if (evidence.trim().length < MIN_REFUTED_LENGTH) {
|
|
40
|
+
return `${scar.scar_title}: REFUTED evidence too short (${evidence.trim().length} chars, minimum ${MIN_REFUTED_LENGTH}). Explain why the scar didn't apply and what was done instead.`;
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
default:
|
|
44
|
+
return `${scar.scar_title}: Invalid outcome "${outcome}". Must be OBEYED or REFUTED.`;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Format the reflection result as markdown.
|
|
50
|
+
*/
|
|
51
|
+
function formatResponse(valid, reflections, errors, missingScars) {
|
|
52
|
+
const lines = [];
|
|
53
|
+
if (valid) {
|
|
54
|
+
lines.push(`${STATUS.ok} SCAR REFLECTIONS ACCEPTED`);
|
|
55
|
+
lines.push("");
|
|
56
|
+
for (const ref of reflections) {
|
|
57
|
+
const indicator = ref.outcome === "OBEYED" ? STATUS.pass : `${ANSI.yellow}!${ANSI.reset}`;
|
|
58
|
+
lines.push(`${indicator} **${ref.scar_title}** → ${ref.outcome}`);
|
|
59
|
+
}
|
|
60
|
+
lines.push("");
|
|
61
|
+
lines.push("All surfaced scars reflected upon. Session close will use these for execution_successful.");
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
lines.push(`${STATUS.rejected} SCAR REFLECTIONS REJECTED`);
|
|
65
|
+
lines.push("");
|
|
66
|
+
if (errors.length > 0) {
|
|
67
|
+
lines.push("**Validation errors:**");
|
|
68
|
+
for (const err of errors) {
|
|
69
|
+
lines.push(`- ${err}`);
|
|
70
|
+
}
|
|
71
|
+
lines.push("");
|
|
72
|
+
}
|
|
73
|
+
if (missingScars.length > 0) {
|
|
74
|
+
lines.push("**Unreflected scars (must reflect on all surfaced scars):**");
|
|
75
|
+
for (const title of missingScars) {
|
|
76
|
+
lines.push(`- ${title}`);
|
|
77
|
+
}
|
|
78
|
+
lines.push("");
|
|
79
|
+
}
|
|
80
|
+
lines.push("Fix the errors above and call reflect_scars again.");
|
|
81
|
+
}
|
|
82
|
+
return lines.join("\n");
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Persist reflections to the per-session file.
|
|
86
|
+
*/
|
|
87
|
+
function persistReflectionsToFile(reflections) {
|
|
88
|
+
try {
|
|
89
|
+
const session = getCurrentSession();
|
|
90
|
+
if (!session)
|
|
91
|
+
return;
|
|
92
|
+
const sessionFilePath = getSessionPath(session.sessionId, "session.json");
|
|
93
|
+
if (!fs.existsSync(sessionFilePath))
|
|
94
|
+
return;
|
|
95
|
+
const data = JSON.parse(fs.readFileSync(sessionFilePath, "utf8"));
|
|
96
|
+
data.reflections = reflections;
|
|
97
|
+
fs.writeFileSync(sessionFilePath, JSON.stringify(data, null, 2));
|
|
98
|
+
console.error(`[reflect_scars] Reflections persisted to ${sessionFilePath}`);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
console.warn("[reflect_scars] Failed to persist reflections to file:", error);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Main tool implementation: reflect_scars
|
|
106
|
+
*/
|
|
107
|
+
export async function reflectScars(params) {
|
|
108
|
+
const timer = new Timer();
|
|
109
|
+
// Validate session exists
|
|
110
|
+
const session = getCurrentSession();
|
|
111
|
+
if (!session) {
|
|
112
|
+
const performance = buildPerformanceData("reflect_scars", timer.elapsed(), 0);
|
|
113
|
+
const noSessionMsg = `${STATUS.rejected} No active session. Call session_start before reflect_scars.`;
|
|
114
|
+
return {
|
|
115
|
+
valid: false,
|
|
116
|
+
errors: ["No active session. Call session_start first."],
|
|
117
|
+
reflections: [],
|
|
118
|
+
missing_scars: [],
|
|
119
|
+
formatted_response: noSessionMsg,
|
|
120
|
+
display: wrapDisplay(noSessionMsg),
|
|
121
|
+
performance,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// Get ALL surfaced scars (both session_start and recall — reflections cover the whole session)
|
|
125
|
+
const allSurfacedScars = getSurfacedScars();
|
|
126
|
+
if (allSurfacedScars.length === 0) {
|
|
127
|
+
const performance = buildPerformanceData("reflect_scars", timer.elapsed(), 0);
|
|
128
|
+
const noScarsMsg = `${STATUS.ok} No surfaced scars to reflect upon. Proceed to session close.`;
|
|
129
|
+
return {
|
|
130
|
+
valid: true,
|
|
131
|
+
errors: [],
|
|
132
|
+
reflections: [],
|
|
133
|
+
missing_scars: [],
|
|
134
|
+
formatted_response: noScarsMsg,
|
|
135
|
+
display: wrapDisplay(noScarsMsg),
|
|
136
|
+
performance,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
// Build scar lookup by ID
|
|
140
|
+
const scarById = new Map();
|
|
141
|
+
for (const scar of allSurfacedScars) {
|
|
142
|
+
scarById.set(scar.scar_id, scar);
|
|
143
|
+
}
|
|
144
|
+
// Validate each reflection
|
|
145
|
+
const errors = [];
|
|
146
|
+
const validReflections = [];
|
|
147
|
+
const reflectedIds = new Set();
|
|
148
|
+
if (!params.reflections || params.reflections.length === 0) {
|
|
149
|
+
errors.push("No reflections provided. Each surfaced scar should be reflected upon.");
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
for (const ref of params.reflections) {
|
|
153
|
+
// Check scar exists (try exact match first)
|
|
154
|
+
let scar = scarById.get(ref.scar_id);
|
|
155
|
+
// 8-char prefix match (same as confirm_scars)
|
|
156
|
+
if (!scar && /^[0-9a-f]{8}$/i.test(ref.scar_id)) {
|
|
157
|
+
let matchedId = null;
|
|
158
|
+
for (const [fullId, scarData] of scarById.entries()) {
|
|
159
|
+
if (fullId.startsWith(ref.scar_id)) {
|
|
160
|
+
if (matchedId) {
|
|
161
|
+
errors.push(`Ambiguous scar_id prefix "${ref.scar_id}" matches multiple scars. Use full UUID.`);
|
|
162
|
+
scar = undefined;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
matchedId = fullId;
|
|
166
|
+
scar = scarData;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (!scar) {
|
|
171
|
+
errors.push(`Unknown scar_id "${ref.scar_id}". Only reflect on scars surfaced during this session.`);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
// Validate the reflection
|
|
175
|
+
const error = validateReflection(ref, scar);
|
|
176
|
+
if (error) {
|
|
177
|
+
errors.push(error);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
validReflections.push({
|
|
181
|
+
scar_id: scar.scar_id,
|
|
182
|
+
scar_title: scar.scar_title,
|
|
183
|
+
outcome: ref.outcome,
|
|
184
|
+
evidence: ref.evidence.trim(),
|
|
185
|
+
reflected_at: new Date().toISOString(),
|
|
186
|
+
});
|
|
187
|
+
reflectedIds.add(scar.scar_id);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Check for unreflected scars — advisory, not blocking
|
|
192
|
+
// (Unlike confirm_scars which requires all recall scars, reflect_scars
|
|
193
|
+
// is softer — missing scars are noted but don't invalidate the call)
|
|
194
|
+
const previouslyReflectedIds = new Set(getReflections().map(r => r.scar_id));
|
|
195
|
+
const missingScars = [];
|
|
196
|
+
for (const scar of allSurfacedScars) {
|
|
197
|
+
if (!reflectedIds.has(scar.scar_id) && !previouslyReflectedIds.has(scar.scar_id)) {
|
|
198
|
+
missingScars.push(scar.scar_title);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const valid = errors.length === 0;
|
|
202
|
+
// If valid, persist to session state and file
|
|
203
|
+
if (valid && validReflections.length > 0) {
|
|
204
|
+
addReflections(validReflections);
|
|
205
|
+
persistReflectionsToFile([...getReflections()]);
|
|
206
|
+
}
|
|
207
|
+
const performance = buildPerformanceData("reflect_scars", timer.elapsed(), validReflections.length);
|
|
208
|
+
const formatted_response = formatResponse(valid, validReflections, errors, missingScars);
|
|
209
|
+
return {
|
|
210
|
+
valid,
|
|
211
|
+
errors,
|
|
212
|
+
reflections: validReflections,
|
|
213
|
+
missing_scars: missingScars,
|
|
214
|
+
formatted_response,
|
|
215
|
+
display: wrapDisplay(formatted_response),
|
|
216
|
+
performance,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=reflect-scars.js.map
|
|
@@ -12,7 +12,7 @@ import * as supabase from "../services/supabase-client.js";
|
|
|
12
12
|
import { embed, isEmbeddingAvailable } from "../services/embedding.js";
|
|
13
13
|
import { hasSupabase, getTableName } from "../services/tier.js";
|
|
14
14
|
import { getStorage } from "../services/storage.js";
|
|
15
|
-
import { clearCurrentSession, getSurfacedScars, getConfirmations, getObservations, getChildren, getThreads, getSessionActivity } from "../services/session-state.js";
|
|
15
|
+
import { clearCurrentSession, getSurfacedScars, getConfirmations, getReflections, getObservations, getChildren, getThreads, getSessionActivity } from "../services/session-state.js";
|
|
16
16
|
import { normalizeThreads, mergeThreadStates, migrateStringThread, saveThreadsFile } from "../services/thread-manager.js"; //
|
|
17
17
|
import { deduplicateThreadList } from "../services/thread-dedup.js";
|
|
18
18
|
import { syncThreadsToSupabase, loadOpenThreadEmbeddings } from "../services/thread-supabase.js";
|
|
@@ -573,24 +573,47 @@ function bridgeScarsToUsageRecords(normalizedScarsApplied, sessionId, agentIdent
|
|
|
573
573
|
}
|
|
574
574
|
const autoBridgedScars = [];
|
|
575
575
|
const matchedScarIds = new Set();
|
|
576
|
-
// Load structured confirmations from confirm_scars (
|
|
576
|
+
// Load structured confirmations from confirm_scars (start-of-task)
|
|
577
577
|
const confirmations = getConfirmations();
|
|
578
578
|
const confirmationMap = new Map();
|
|
579
579
|
for (const conf of confirmations) {
|
|
580
580
|
confirmationMap.set(conf.scar_id, conf);
|
|
581
581
|
}
|
|
582
|
+
// Load end-of-session reflections from reflect_scars (end-of-task)
|
|
583
|
+
// Reflections provide the most accurate execution_successful signal
|
|
584
|
+
const reflections = getReflections();
|
|
585
|
+
const reflectionMap = new Map();
|
|
586
|
+
for (const ref of reflections) {
|
|
587
|
+
reflectionMap.set(ref.scar_id, { outcome: ref.outcome, evidence: ref.evidence });
|
|
588
|
+
}
|
|
582
589
|
// First pass: match surfaced scars against structured confirmations
|
|
583
590
|
for (const scar of surfacedScars) {
|
|
584
591
|
const confirmation = confirmationMap.get(scar.scar_id);
|
|
585
592
|
if (confirmation) {
|
|
586
593
|
matchedScarIds.add(scar.scar_id);
|
|
594
|
+
// Prefer reflect_scars outcome over confirmation default
|
|
595
|
+
// reflect_scars gives actual end-of-session evidence; confirm_scars is intent
|
|
596
|
+
const reflection = reflectionMap.get(scar.scar_id);
|
|
597
|
+
let executionSuccessful;
|
|
598
|
+
let context;
|
|
599
|
+
if (reflection) {
|
|
600
|
+
// Reflection provides definitive signal
|
|
601
|
+
executionSuccessful = reflection.outcome === "OBEYED" ? true : false;
|
|
602
|
+
context = `Confirmed: ${confirmation.decision} → Reflected: ${reflection.outcome} — ${reflection.evidence.slice(0, 80)}`;
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
// Fall back to confirmation-based default (Option A)
|
|
606
|
+
executionSuccessful = confirmation.decision === "APPLYING" ? true : undefined;
|
|
607
|
+
context = `Confirmed via confirm_scars: ${confirmation.decision} — ${confirmation.evidence.slice(0, 100)}`;
|
|
608
|
+
}
|
|
587
609
|
autoBridgedScars.push({
|
|
588
610
|
scar_identifier: scar.scar_id,
|
|
589
611
|
session_id: sessionId,
|
|
590
612
|
agent: agentIdentity,
|
|
591
613
|
surfaced_at: scar.surfaced_at,
|
|
592
614
|
reference_type: decisionToRefType(confirmation.decision),
|
|
593
|
-
reference_context:
|
|
615
|
+
reference_context: context,
|
|
616
|
+
execution_successful: executionSuccessful,
|
|
594
617
|
variant_id: scar.variant_id,
|
|
595
618
|
});
|
|
596
619
|
}
|
|
@@ -619,6 +642,7 @@ function bridgeScarsToUsageRecords(normalizedScarsApplied, sessionId, agentIdent
|
|
|
619
642
|
}
|
|
620
643
|
}
|
|
621
644
|
// For surfaced scars NOT matched by either method, record as "none"
|
|
645
|
+
// execution_successful = false — scar was surfaced but ignored entirely
|
|
622
646
|
for (const scar of surfacedScars) {
|
|
623
647
|
if (!matchedScarIds.has(scar.scar_id)) {
|
|
624
648
|
autoBridgedScars.push({
|
|
@@ -628,6 +652,7 @@ function bridgeScarsToUsageRecords(normalizedScarsApplied, sessionId, agentIdent
|
|
|
628
652
|
surfaced_at: scar.surfaced_at,
|
|
629
653
|
reference_type: "none",
|
|
630
654
|
reference_context: `Surfaced during ${scar.source} but not mentioned in closing reflection`,
|
|
655
|
+
execution_successful: false,
|
|
631
656
|
variant_id: scar.variant_id,
|
|
632
657
|
});
|
|
633
658
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -330,6 +330,30 @@ export interface ConfirmScarsResult {
|
|
|
330
330
|
display?: string;
|
|
331
331
|
performance: PerformanceData;
|
|
332
332
|
}
|
|
333
|
+
export type ReflectionOutcome = "OBEYED" | "REFUTED";
|
|
334
|
+
export interface ScarReflection {
|
|
335
|
+
scar_id: string;
|
|
336
|
+
scar_title: string;
|
|
337
|
+
outcome: ReflectionOutcome;
|
|
338
|
+
evidence: string;
|
|
339
|
+
reflected_at: string;
|
|
340
|
+
}
|
|
341
|
+
export interface ReflectScarsParams {
|
|
342
|
+
reflections: Array<{
|
|
343
|
+
scar_id: string;
|
|
344
|
+
outcome: ReflectionOutcome;
|
|
345
|
+
evidence: string;
|
|
346
|
+
}>;
|
|
347
|
+
}
|
|
348
|
+
export interface ReflectScarsResult {
|
|
349
|
+
valid: boolean;
|
|
350
|
+
errors: string[];
|
|
351
|
+
reflections: ScarReflection[];
|
|
352
|
+
missing_scars: string[];
|
|
353
|
+
formatted_response: string;
|
|
354
|
+
display?: string;
|
|
355
|
+
performance: PerformanceData;
|
|
356
|
+
}
|
|
333
357
|
export type ReferenceType = "explicit" | "implicit" | "acknowledged" | "refuted" | "none";
|
|
334
358
|
export interface RecordScarUsageParams {
|
|
335
359
|
scar_id: string;
|
|
@@ -65,6 +65,37 @@ PROMPT_LEN=${#PROMPT}
|
|
|
65
65
|
|
|
66
66
|
RETRIEVAL_LEVEL=""
|
|
67
67
|
|
|
68
|
+
# Priority 0: Knowledge-retrieval queries — route through gitmem, don't inject scars
|
|
69
|
+
# These are queries where the user wants to ACCESS institutional memory, not just
|
|
70
|
+
# have scars passively injected. Output a routing instruction instead of scars.
|
|
71
|
+
IS_KNOWLEDGE_QUERY=false
|
|
72
|
+
|
|
73
|
+
# Explicit recall/remember commands
|
|
74
|
+
if echo "$PROMPT_LOWER" | grep -qE '\b(recall|remember)\b.*(doc|process|tree|structure|how|what|where|our)'; then
|
|
75
|
+
IS_KNOWLEDGE_QUERY=true
|
|
76
|
+
# Process/documentation queries ("what's our process for X", "how do we usually Y")
|
|
77
|
+
elif echo "$PROMPT_LOWER" | grep -qE "(what('s| is) our (process|approach|pattern|method)|how do we (usually|typically|normally)|remind me (how|what|about)|what('s| is) the (process|protocol|procedure) for)"; then
|
|
78
|
+
IS_KNOWLEDGE_QUERY=true
|
|
79
|
+
# Documentation discovery ("show me the docs", "where are the scars")
|
|
80
|
+
elif echo "$PROMPT_LOWER" | grep -qE '\b(show me|find|where (is|are|do)).*(doc(s|umentation)?|process(es)?|pattern(s)?|decision(s)?|scar(s)?|learning(s)?)\b'; then
|
|
81
|
+
IS_KNOWLEDGE_QUERY=true
|
|
82
|
+
# Institutional knowledge queries ("what did we decide about", "what scars exist")
|
|
83
|
+
elif echo "$PROMPT_LOWER" | grep -qE '\b(what did we (decide|learn|document)|what scars|what learnings|past decisions about|institutional (memory|knowledge))\b'; then
|
|
84
|
+
IS_KNOWLEDGE_QUERY=true
|
|
85
|
+
# Direct "recall" as a verb/command at start of prompt
|
|
86
|
+
elif echo "$PROMPT_LOWER" | grep -qE '^\s*recall\b'; then
|
|
87
|
+
IS_KNOWLEDGE_QUERY=true
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
if [ "$IS_KNOWLEDGE_QUERY" = "true" ]; then
|
|
91
|
+
cat <<HOOKJSON
|
|
92
|
+
{
|
|
93
|
+
"additionalContext": "KNOWLEDGE RETRIEVAL DETECTED. Route through GitMem FIRST:\n\n1. gitmem search (query relevant to the question) OR gitmem recall (if action-oriented)\n2. gitmem log (if browsing recent learnings)\n3. ONLY THEN fall back to filesystem reads if gitmem doesn't have the answer\n\nDo NOT start with Read/Glob/Grep on local files. Institutional memory exists precisely so we don't re-explore the filesystem every time."
|
|
94
|
+
}
|
|
95
|
+
HOOKJSON
|
|
96
|
+
exit 0
|
|
97
|
+
fi
|
|
98
|
+
|
|
68
99
|
# Priority 1: Trivial — skip retrieval entirely
|
|
69
100
|
if echo "$PROMPT_LOWER" | grep -qE '^(yes|no|ok|k|y|n|sure|thanks|thank you|continue|go ahead|proceed|correct|right|exactly|got it|sounds good|lgtm|looks good|done|nope|yep|yup|agreed)$'; then
|
|
70
101
|
exit 0
|
package/package.json
CHANGED