hivemind-context-governance 1.3.0
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 +95 -0
- package/LICENSE +21 -0
- package/README.md +218 -0
- package/dist/cli/init.d.ts +23 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +175 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +162 -0
- package/dist/cli.js.map +1 -0
- package/dist/dashboard/i18n/en.d.ts +43 -0
- package/dist/dashboard/i18n/en.d.ts.map +1 -0
- package/dist/dashboard/i18n/en.js +43 -0
- package/dist/dashboard/i18n/en.js.map +1 -0
- package/dist/dashboard/i18n/index.d.ts +10 -0
- package/dist/dashboard/i18n/index.d.ts.map +1 -0
- package/dist/dashboard/i18n/index.js +14 -0
- package/dist/dashboard/i18n/index.js.map +1 -0
- package/dist/dashboard/i18n/vi.d.ts +43 -0
- package/dist/dashboard/i18n/vi.d.ts.map +1 -0
- package/dist/dashboard/i18n/vi.js +43 -0
- package/dist/dashboard/i18n/vi.js.map +1 -0
- package/dist/dashboard/server.d.ts +23 -0
- package/dist/dashboard/server.d.ts.map +1 -0
- package/dist/dashboard/server.js +282 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/hooks/compaction.d.ts +21 -0
- package/dist/hooks/compaction.d.ts.map +1 -0
- package/dist/hooks/compaction.js +79 -0
- package/dist/hooks/compaction.js.map +1 -0
- package/dist/hooks/index.d.ts +8 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +7 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/session-lifecycle.d.ts +28 -0
- package/dist/hooks/session-lifecycle.d.ts.map +1 -0
- package/dist/hooks/session-lifecycle.js +94 -0
- package/dist/hooks/session-lifecycle.js.map +1 -0
- package/dist/hooks/tool-gate.d.ts +28 -0
- package/dist/hooks/tool-gate.d.ts.map +1 -0
- package/dist/hooks/tool-gate.js +152 -0
- package/dist/hooks/tool-gate.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +159 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/complexity.d.ts +41 -0
- package/dist/lib/complexity.d.ts.map +1 -0
- package/dist/lib/complexity.js +67 -0
- package/dist/lib/complexity.js.map +1 -0
- package/dist/lib/context-refresh.d.ts +46 -0
- package/dist/lib/context-refresh.d.ts.map +1 -0
- package/dist/lib/context-refresh.js +77 -0
- package/dist/lib/context-refresh.js.map +1 -0
- package/dist/lib/index.d.ts +10 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +10 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/logging.d.ts +13 -0
- package/dist/lib/logging.d.ts.map +1 -0
- package/dist/lib/logging.js +40 -0
- package/dist/lib/logging.js.map +1 -0
- package/dist/lib/persistence.d.ts +15 -0
- package/dist/lib/persistence.d.ts.map +1 -0
- package/dist/lib/persistence.js +62 -0
- package/dist/lib/persistence.js.map +1 -0
- package/dist/lib/planning-fs.d.ts +34 -0
- package/dist/lib/planning-fs.d.ts.map +1 -0
- package/dist/lib/planning-fs.js +134 -0
- package/dist/lib/planning-fs.js.map +1 -0
- package/dist/lib/sentiment.d.ts +31 -0
- package/dist/lib/sentiment.d.ts.map +1 -0
- package/dist/lib/sentiment.js +158 -0
- package/dist/lib/sentiment.js.map +1 -0
- package/dist/schemas/brain-state.d.ts +60 -0
- package/dist/schemas/brain-state.d.ts.map +1 -0
- package/dist/schemas/brain-state.js +176 -0
- package/dist/schemas/brain-state.js.map +1 -0
- package/dist/schemas/config.d.ts +51 -0
- package/dist/schemas/config.d.ts.map +1 -0
- package/dist/schemas/config.js +128 -0
- package/dist/schemas/config.js.map +1 -0
- package/dist/schemas/hierarchy.d.ts +22 -0
- package/dist/schemas/hierarchy.d.ts.map +1 -0
- package/dist/schemas/hierarchy.js +27 -0
- package/dist/schemas/hierarchy.js.map +1 -0
- package/dist/schemas/index.d.ts +7 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +7 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/tools/compact-session.d.ts +15 -0
- package/dist/tools/compact-session.d.ts.map +1 -0
- package/dist/tools/compact-session.js +76 -0
- package/dist/tools/compact-session.js.map +1 -0
- package/dist/tools/declare-intent.d.ts +15 -0
- package/dist/tools/declare-intent.d.ts.map +1 -0
- package/dist/tools/declare-intent.js +83 -0
- package/dist/tools/declare-intent.js.map +1 -0
- package/dist/tools/index.d.ts +14 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +14 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/map-context.d.ts +15 -0
- package/dist/tools/map-context.d.ts.map +1 -0
- package/dist/tools/map-context.js +85 -0
- package/dist/tools/map-context.js.map +1 -0
- package/dist/tools/self-rate.d.ts +15 -0
- package/dist/tools/self-rate.d.ts.map +1 -0
- package/dist/tools/self-rate.js +76 -0
- package/dist/tools/self-rate.js.map +1 -0
- package/package.json +71 -0
- package/src/cli/init.ts +214 -0
- package/src/cli.ts +178 -0
- package/src/dashboard/i18n/en.ts +43 -0
- package/src/dashboard/i18n/index.ts +20 -0
- package/src/dashboard/i18n/vi.ts +43 -0
- package/src/dashboard/server.ts +372 -0
- package/src/hooks/compaction.ts +104 -0
- package/src/hooks/index.ts +8 -0
- package/src/hooks/session-lifecycle.ts +133 -0
- package/src/hooks/tool-gate.ts +205 -0
- package/src/index.ts +206 -0
- package/src/lib/complexity.ts +96 -0
- package/src/lib/context-refresh.ts +107 -0
- package/src/lib/index.ts +10 -0
- package/src/lib/logging.ts +53 -0
- package/src/lib/persistence.ts +83 -0
- package/src/lib/planning-fs.ts +187 -0
- package/src/lib/sentiment.ts +183 -0
- package/src/schemas/brain-state.ts +257 -0
- package/src/schemas/config.ts +184 -0
- package/src/schemas/hierarchy.ts +58 -0
- package/src/schemas/index.ts +7 -0
- package/src/tools/compact-session.ts +97 -0
- package/src/tools/declare-intent.ts +104 -0
- package/src/tools/index.ts +14 -0
- package/src/tools/map-context.ts +105 -0
- package/src/tools/self-rate.ts +86 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentiment Detection Module
|
|
3
|
+
*
|
|
4
|
+
* Detects negative signals in agent/user communication to trigger
|
|
5
|
+
* context refresh when drift is detected.
|
|
6
|
+
*
|
|
7
|
+
* Signals:
|
|
8
|
+
* - Negative keywords: stop, wrong, no, bad, incorrect, confused
|
|
9
|
+
* - Agent failure phrases: "I apologize", "you are right", "I was wrong"
|
|
10
|
+
* - Cancellation patterns: "cancel", "abort", "start over"
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface SentimentSignal {
|
|
14
|
+
type: "negative_keyword" | "failure_phrase" | "cancellation" | "confusion"
|
|
15
|
+
matched_text: string
|
|
16
|
+
timestamp: number
|
|
17
|
+
turn_number: number
|
|
18
|
+
source: "user" | "agent"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Negative keywords that indicate problems.
|
|
23
|
+
*
|
|
24
|
+
* Short keywords (≤5 chars) use word-boundary regex to prevent false positives
|
|
25
|
+
* like "no" matching "no issues" or "error" matching "error handling".
|
|
26
|
+
* Benign phrases are excluded via negative-lookahead.
|
|
27
|
+
*/
|
|
28
|
+
const NEGATIVE_KEYWORD_PATTERNS: Array<{ pattern: RegExp; label: string }> = [
|
|
29
|
+
{ pattern: /\bstop\b(?!\s+(worrying|there))/i, label: "stop" },
|
|
30
|
+
{ pattern: /\bwrong\b/i, label: "wrong" },
|
|
31
|
+
{ pattern: /\bno\b(?!\s+(issues?|errors?|problems?|worries|need|thanks|longer|more))/i, label: "no" },
|
|
32
|
+
{ pattern: /\bbad\b(?!\s+(→|->|to))/i, label: "bad" },
|
|
33
|
+
{ pattern: /\bincorrect\b/i, label: "incorrect" },
|
|
34
|
+
{ pattern: /\bconfused\b/i, label: "confused" },
|
|
35
|
+
{ pattern: /\bconfusion\b/i, label: "confusion" },
|
|
36
|
+
{ pattern: /\bmistake\b/i, label: "mistake" },
|
|
37
|
+
{ pattern: /\berror\b(?!\s+(handling|boundary|boundaries|message|code|type|class|object|page|recovery|log))/i, label: "error" },
|
|
38
|
+
{ pattern: /\bbroken\b/i, label: "broken" },
|
|
39
|
+
{ pattern: /not working/i, label: "not working" },
|
|
40
|
+
{ pattern: /doesn't work/i, label: "doesn't work" },
|
|
41
|
+
{ pattern: /isn't working/i, label: "isn't working" },
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
/** Agent failure admission phrases */
|
|
45
|
+
const FAILURE_PHRASES = [
|
|
46
|
+
"i apologize",
|
|
47
|
+
"you are right",
|
|
48
|
+
"i was wrong",
|
|
49
|
+
"my mistake",
|
|
50
|
+
"i misunderstood",
|
|
51
|
+
"i made an error",
|
|
52
|
+
"that was incorrect",
|
|
53
|
+
"i see the issue now",
|
|
54
|
+
"you're correct",
|
|
55
|
+
"i should have",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
/** Cancellation/restart patterns */
|
|
59
|
+
const CANCELLATION_PATTERNS = [
|
|
60
|
+
"cancel",
|
|
61
|
+
"abort",
|
|
62
|
+
"start over",
|
|
63
|
+
"begin again",
|
|
64
|
+
"scratch that",
|
|
65
|
+
"forget that",
|
|
66
|
+
"never mind",
|
|
67
|
+
"nevermind",
|
|
68
|
+
"let's restart",
|
|
69
|
+
"do over",
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
/** Confusion indicators */
|
|
73
|
+
const CONFUSION_PATTERNS = [
|
|
74
|
+
"i'm confused",
|
|
75
|
+
"i am confused",
|
|
76
|
+
"this is confusing",
|
|
77
|
+
"doesn't make sense",
|
|
78
|
+
"not making sense",
|
|
79
|
+
"unclear",
|
|
80
|
+
"i don't understand",
|
|
81
|
+
"i do not understand",
|
|
82
|
+
"what do you mean",
|
|
83
|
+
"can you explain",
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Detect sentiment signals in text
|
|
88
|
+
*/
|
|
89
|
+
export function detectSentiment(
|
|
90
|
+
text: string,
|
|
91
|
+
turnNumber: number,
|
|
92
|
+
source: "user" | "agent" = "user"
|
|
93
|
+
): SentimentSignal[] {
|
|
94
|
+
const signals: SentimentSignal[] = []
|
|
95
|
+
const lowerText = text.toLowerCase()
|
|
96
|
+
const timestamp = Date.now()
|
|
97
|
+
|
|
98
|
+
// Check negative keywords (regex-based with word boundaries)
|
|
99
|
+
for (const { pattern, label } of NEGATIVE_KEYWORD_PATTERNS) {
|
|
100
|
+
if (pattern.test(lowerText)) {
|
|
101
|
+
signals.push({
|
|
102
|
+
type: "negative_keyword",
|
|
103
|
+
matched_text: label,
|
|
104
|
+
timestamp,
|
|
105
|
+
turn_number: turnNumber,
|
|
106
|
+
source,
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Check failure phrases
|
|
112
|
+
for (const phrase of FAILURE_PHRASES) {
|
|
113
|
+
if (lowerText.includes(phrase)) {
|
|
114
|
+
signals.push({
|
|
115
|
+
type: "failure_phrase",
|
|
116
|
+
matched_text: phrase,
|
|
117
|
+
timestamp,
|
|
118
|
+
turn_number: turnNumber,
|
|
119
|
+
source,
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check cancellation patterns
|
|
125
|
+
for (const pattern of CANCELLATION_PATTERNS) {
|
|
126
|
+
if (lowerText.includes(pattern)) {
|
|
127
|
+
signals.push({
|
|
128
|
+
type: "cancellation",
|
|
129
|
+
matched_text: pattern,
|
|
130
|
+
timestamp,
|
|
131
|
+
turn_number: turnNumber,
|
|
132
|
+
source,
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Check confusion patterns
|
|
138
|
+
for (const pattern of CONFUSION_PATTERNS) {
|
|
139
|
+
if (lowerText.includes(pattern)) {
|
|
140
|
+
signals.push({
|
|
141
|
+
type: "confusion",
|
|
142
|
+
matched_text: pattern,
|
|
143
|
+
timestamp,
|
|
144
|
+
turn_number: turnNumber,
|
|
145
|
+
source,
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return signals
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Check if text contains any negative sentiment
|
|
155
|
+
*/
|
|
156
|
+
export function hasNegativeSentiment(text: string): boolean {
|
|
157
|
+
return detectSentiment(text, 0).length > 0
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get sentiment summary for display
|
|
162
|
+
*/
|
|
163
|
+
export function getSentimentSummary(signals: SentimentSignal[]): string {
|
|
164
|
+
if (signals.length === 0) {
|
|
165
|
+
return "No negative signals detected"
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const byType = signals.reduce(
|
|
169
|
+
(acc, signal) => {
|
|
170
|
+
acc[signal.type] = (acc[signal.type] || 0) + 1
|
|
171
|
+
return acc
|
|
172
|
+
},
|
|
173
|
+
{} as Record<string, number>
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
const parts: string[] = []
|
|
177
|
+
if (byType["negative_keyword"]) parts.push(`${byType["negative_keyword"]} negative keywords`)
|
|
178
|
+
if (byType["failure_phrase"]) parts.push(`${byType["failure_phrase"]} failure admissions`)
|
|
179
|
+
if (byType["cancellation"]) parts.push(`${byType["cancellation"]} cancellations`)
|
|
180
|
+
if (byType["confusion"]) parts.push(`${byType["confusion"]} confusion indicators`)
|
|
181
|
+
|
|
182
|
+
return parts.join(", ")
|
|
183
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brain State Schema
|
|
3
|
+
* Core state machine for session governance
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { HiveMindConfig, GovernanceMode } from "./config.js";
|
|
7
|
+
import type { HierarchyState } from "./hierarchy.js";
|
|
8
|
+
import type { SentimentSignal } from "../lib/sentiment.js";
|
|
9
|
+
|
|
10
|
+
export type SessionMode = "plan_driven" | "quick_fix" | "exploration";
|
|
11
|
+
export type GovernanceStatus = "LOCKED" | "OPEN";
|
|
12
|
+
|
|
13
|
+
export interface SessionState {
|
|
14
|
+
id: string;
|
|
15
|
+
mode: SessionMode;
|
|
16
|
+
governance_mode: GovernanceMode;
|
|
17
|
+
governance_status: GovernanceStatus;
|
|
18
|
+
start_time: number;
|
|
19
|
+
last_activity: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface SelfRating {
|
|
23
|
+
score: number; // 1-10
|
|
24
|
+
reason?: string;
|
|
25
|
+
turn_context?: string;
|
|
26
|
+
timestamp: number;
|
|
27
|
+
turn_number: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface MetricsState {
|
|
31
|
+
turn_count: number;
|
|
32
|
+
drift_score: number;
|
|
33
|
+
files_touched: string[];
|
|
34
|
+
context_updates: number;
|
|
35
|
+
ratings: SelfRating[];
|
|
36
|
+
auto_health_score: number; // 0-100, calculated from success rate
|
|
37
|
+
total_tool_calls: number;
|
|
38
|
+
successful_tool_calls: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface BrainState {
|
|
42
|
+
session: SessionState;
|
|
43
|
+
hierarchy: HierarchyState;
|
|
44
|
+
metrics: MetricsState;
|
|
45
|
+
sentiment_signals: SentimentSignal[];
|
|
46
|
+
complexity_nudge_shown: boolean;
|
|
47
|
+
version: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const BRAIN_STATE_VERSION = "1.0.0";
|
|
51
|
+
|
|
52
|
+
export function generateSessionId(): string {
|
|
53
|
+
const timestamp = Date.now();
|
|
54
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
55
|
+
return `session-${timestamp}-${random}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function createBrainState(
|
|
59
|
+
sessionId: string,
|
|
60
|
+
config: HiveMindConfig,
|
|
61
|
+
mode: SessionMode = "plan_driven"
|
|
62
|
+
): BrainState {
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
session: {
|
|
67
|
+
id: sessionId,
|
|
68
|
+
mode,
|
|
69
|
+
governance_mode: config.governance_mode,
|
|
70
|
+
governance_status: config.governance_mode === "strict" ? "LOCKED" : "OPEN",
|
|
71
|
+
start_time: now,
|
|
72
|
+
last_activity: now,
|
|
73
|
+
},
|
|
74
|
+
hierarchy: {
|
|
75
|
+
trajectory: "",
|
|
76
|
+
tactic: "",
|
|
77
|
+
action: "",
|
|
78
|
+
},
|
|
79
|
+
metrics: {
|
|
80
|
+
turn_count: 0,
|
|
81
|
+
drift_score: 100,
|
|
82
|
+
files_touched: [],
|
|
83
|
+
context_updates: 0,
|
|
84
|
+
ratings: [],
|
|
85
|
+
auto_health_score: 100,
|
|
86
|
+
total_tool_calls: 0,
|
|
87
|
+
successful_tool_calls: 0,
|
|
88
|
+
},
|
|
89
|
+
sentiment_signals: [],
|
|
90
|
+
complexity_nudge_shown: false,
|
|
91
|
+
version: BRAIN_STATE_VERSION,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function isSessionLocked(state: BrainState): boolean {
|
|
96
|
+
return state.session.governance_status === "LOCKED";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function unlockSession(state: BrainState): BrainState {
|
|
100
|
+
return {
|
|
101
|
+
...state,
|
|
102
|
+
session: {
|
|
103
|
+
...state.session,
|
|
104
|
+
governance_status: "OPEN",
|
|
105
|
+
last_activity: Date.now(),
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function lockSession(state: BrainState): BrainState {
|
|
111
|
+
return {
|
|
112
|
+
...state,
|
|
113
|
+
session: {
|
|
114
|
+
...state.session,
|
|
115
|
+
governance_status: "LOCKED",
|
|
116
|
+
last_activity: Date.now(),
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function incrementTurnCount(state: BrainState): BrainState {
|
|
122
|
+
return {
|
|
123
|
+
...state,
|
|
124
|
+
metrics: {
|
|
125
|
+
...state.metrics,
|
|
126
|
+
turn_count: state.metrics.turn_count + 1,
|
|
127
|
+
},
|
|
128
|
+
session: {
|
|
129
|
+
...state.session,
|
|
130
|
+
last_activity: Date.now(),
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function resetTurnCount(state: BrainState): BrainState {
|
|
136
|
+
return {
|
|
137
|
+
...state,
|
|
138
|
+
metrics: {
|
|
139
|
+
...state.metrics,
|
|
140
|
+
turn_count: 0,
|
|
141
|
+
drift_score: Math.min(100, state.metrics.drift_score + 10),
|
|
142
|
+
},
|
|
143
|
+
session: {
|
|
144
|
+
...state.session,
|
|
145
|
+
last_activity: Date.now(),
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function updateHierarchy(
|
|
151
|
+
state: BrainState,
|
|
152
|
+
hierarchy: Partial<HierarchyState>
|
|
153
|
+
): BrainState {
|
|
154
|
+
return {
|
|
155
|
+
...state,
|
|
156
|
+
hierarchy: { ...state.hierarchy, ...hierarchy },
|
|
157
|
+
metrics: {
|
|
158
|
+
...state.metrics,
|
|
159
|
+
context_updates: state.metrics.context_updates + 1,
|
|
160
|
+
},
|
|
161
|
+
session: {
|
|
162
|
+
...state.session,
|
|
163
|
+
last_activity: Date.now(),
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function addFileTouched(state: BrainState, filePath: string): BrainState {
|
|
169
|
+
if (state.metrics.files_touched.includes(filePath)) {
|
|
170
|
+
return state;
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
...state,
|
|
174
|
+
metrics: {
|
|
175
|
+
...state.metrics,
|
|
176
|
+
files_touched: [...state.metrics.files_touched, filePath],
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function calculateDriftScore(state: BrainState): number {
|
|
182
|
+
const turnsPenalty = Math.min(50, state.metrics.turn_count * 5);
|
|
183
|
+
const updatesBonus = Math.min(20, state.metrics.context_updates * 2);
|
|
184
|
+
return Math.max(0, Math.min(100, 100 - turnsPenalty + updatesBonus));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function shouldTriggerDriftWarning(
|
|
188
|
+
state: BrainState,
|
|
189
|
+
maxTurns: number
|
|
190
|
+
): boolean {
|
|
191
|
+
return state.metrics.turn_count >= maxTurns && state.metrics.drift_score < 50;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function addSelfRating(
|
|
195
|
+
state: BrainState,
|
|
196
|
+
rating: Omit<SelfRating, "timestamp" | "turn_number">
|
|
197
|
+
): BrainState {
|
|
198
|
+
const newRating: SelfRating = {
|
|
199
|
+
...rating,
|
|
200
|
+
timestamp: Date.now(),
|
|
201
|
+
turn_number: state.metrics.turn_count,
|
|
202
|
+
};
|
|
203
|
+
return {
|
|
204
|
+
...state,
|
|
205
|
+
metrics: {
|
|
206
|
+
...state.metrics,
|
|
207
|
+
ratings: [...state.metrics.ratings, newRating],
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function trackToolCall(
|
|
213
|
+
state: BrainState,
|
|
214
|
+
success: boolean
|
|
215
|
+
): BrainState {
|
|
216
|
+
const total = state.metrics.total_tool_calls + 1;
|
|
217
|
+
const successful = success
|
|
218
|
+
? state.metrics.successful_tool_calls + 1
|
|
219
|
+
: state.metrics.successful_tool_calls;
|
|
220
|
+
const healthScore = total > 0 ? Math.round((successful / total) * 100) : 100;
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
...state,
|
|
224
|
+
metrics: {
|
|
225
|
+
...state.metrics,
|
|
226
|
+
total_tool_calls: total,
|
|
227
|
+
successful_tool_calls: successful,
|
|
228
|
+
auto_health_score: healthScore,
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function addSentimentSignals(
|
|
234
|
+
state: BrainState,
|
|
235
|
+
signals: SentimentSignal[]
|
|
236
|
+
): BrainState {
|
|
237
|
+
if (signals.length === 0) return state;
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
...state,
|
|
241
|
+
sentiment_signals: [...state.sentiment_signals, ...signals],
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function setComplexityNudgeShown(state: BrainState): BrainState {
|
|
246
|
+
return {
|
|
247
|
+
...state,
|
|
248
|
+
complexity_nudge_shown: true,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function resetComplexityNudge(state: BrainState): BrainState {
|
|
253
|
+
return {
|
|
254
|
+
...state,
|
|
255
|
+
complexity_nudge_shown: false,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HiveMind Configuration Schema
|
|
3
|
+
* Defines governance modes, project settings, and agent behavior constraints
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type GovernanceMode = "permissive" | "assisted" | "strict";
|
|
7
|
+
export type Language = "en" | "vi";
|
|
8
|
+
export type ExpertLevel = "beginner" | "intermediate" | "advanced" | "expert";
|
|
9
|
+
export type OutputStyle =
|
|
10
|
+
| "explanatory" // Detailed explanations, teaching mode
|
|
11
|
+
| "outline" // Bullet points, structured summaries
|
|
12
|
+
| "skeptical" // Critical review, challenge assumptions
|
|
13
|
+
| "architecture" // Focus on design patterns, structure first
|
|
14
|
+
| "minimal"; // Brief, code-only responses
|
|
15
|
+
|
|
16
|
+
export interface AgentBehaviorConfig {
|
|
17
|
+
/** Language for all responses */
|
|
18
|
+
language: Language;
|
|
19
|
+
/** Expertise level - affects depth and assumptions */
|
|
20
|
+
expert_level: ExpertLevel;
|
|
21
|
+
/** Output style - affects response format */
|
|
22
|
+
output_style: OutputStyle;
|
|
23
|
+
/** Additional behavioral constraints */
|
|
24
|
+
constraints: {
|
|
25
|
+
/** Require code review before accepting */
|
|
26
|
+
require_code_review: boolean;
|
|
27
|
+
/** Enforce test-driven development */
|
|
28
|
+
enforce_tdd: boolean;
|
|
29
|
+
/** Maximum response length in tokens (approximate) */
|
|
30
|
+
max_response_tokens: number;
|
|
31
|
+
/** Always explain 'why' not just 'what' */
|
|
32
|
+
explain_reasoning: boolean;
|
|
33
|
+
/** Challenge user assumptions */
|
|
34
|
+
be_skeptical: boolean;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface HiveMindConfig {
|
|
39
|
+
governance_mode: GovernanceMode;
|
|
40
|
+
language: Language;
|
|
41
|
+
max_turns_before_warning: number;
|
|
42
|
+
max_active_md_lines: number;
|
|
43
|
+
auto_compact_on_turns: number;
|
|
44
|
+
/** Agent behavior configuration - injected into every session */
|
|
45
|
+
agent_behavior: AgentBehaviorConfig;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const DEFAULT_AGENT_BEHAVIOR: AgentBehaviorConfig = {
|
|
49
|
+
language: "en",
|
|
50
|
+
expert_level: "intermediate",
|
|
51
|
+
output_style: "explanatory",
|
|
52
|
+
constraints: {
|
|
53
|
+
require_code_review: false,
|
|
54
|
+
enforce_tdd: false,
|
|
55
|
+
max_response_tokens: 2000,
|
|
56
|
+
explain_reasoning: true,
|
|
57
|
+
be_skeptical: false,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const DEFAULT_CONFIG: HiveMindConfig = {
|
|
62
|
+
governance_mode: "assisted",
|
|
63
|
+
language: "en",
|
|
64
|
+
max_turns_before_warning: 5,
|
|
65
|
+
max_active_md_lines: 50,
|
|
66
|
+
auto_compact_on_turns: 20,
|
|
67
|
+
agent_behavior: DEFAULT_AGENT_BEHAVIOR,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export function createConfig(overrides: Partial<HiveMindConfig> = {}): HiveMindConfig {
|
|
71
|
+
return {
|
|
72
|
+
...DEFAULT_CONFIG,
|
|
73
|
+
...overrides,
|
|
74
|
+
agent_behavior: {
|
|
75
|
+
...DEFAULT_AGENT_BEHAVIOR,
|
|
76
|
+
...overrides.agent_behavior,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function isValidGovernanceMode(mode: string): mode is GovernanceMode {
|
|
82
|
+
return ["permissive", "assisted", "strict"].includes(mode);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function isValidLanguage(lang: string): lang is Language {
|
|
86
|
+
return ["en", "vi"].includes(lang);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function isValidExpertLevel(level: string): level is ExpertLevel {
|
|
90
|
+
return ["beginner", "intermediate", "advanced", "expert"].includes(level);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function isValidOutputStyle(style: string): style is OutputStyle {
|
|
94
|
+
return ["explanatory", "outline", "skeptical", "architecture", "minimal"].includes(style);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Generates the mandatory agent configuration prompt that is injected
|
|
99
|
+
* at every session opening and persists throughout the conversation.
|
|
100
|
+
*/
|
|
101
|
+
export function generateAgentBehaviorPrompt(config: AgentBehaviorConfig): string {
|
|
102
|
+
const lines: string[] = [];
|
|
103
|
+
|
|
104
|
+
lines.push("<agent-configuration>");
|
|
105
|
+
lines.push("MANDATORY: You MUST obey these constraints for this entire session:");
|
|
106
|
+
lines.push("");
|
|
107
|
+
|
|
108
|
+
// Language enforcement
|
|
109
|
+
const langNames: Record<Language, string> = {
|
|
110
|
+
en: "English",
|
|
111
|
+
vi: "Vietnamese (Tiếng Việt)",
|
|
112
|
+
};
|
|
113
|
+
lines.push(`[LANGUAGE] Respond ONLY in ${langNames[config.language]}. No exceptions.`);
|
|
114
|
+
lines.push("");
|
|
115
|
+
|
|
116
|
+
// Expert level
|
|
117
|
+
const expertDescriptions: Record<ExpertLevel, string> = {
|
|
118
|
+
beginner: "Explain everything simply. Define terms. No assumptions of prior knowledge.",
|
|
119
|
+
intermediate: "Standard technical depth. Some domain knowledge assumed.",
|
|
120
|
+
advanced: "Concise, sophisticated. Assume strong domain expertise.",
|
|
121
|
+
expert: "Terse, reference advanced concepts. Minimal hand-holding.",
|
|
122
|
+
};
|
|
123
|
+
lines.push(`[EXPERT LEVEL] ${config.expert_level.toUpperCase()}: ${expertDescriptions[config.expert_level]}`);
|
|
124
|
+
lines.push("");
|
|
125
|
+
|
|
126
|
+
// Output style
|
|
127
|
+
const styleInstructions: Record<OutputStyle, string[]> = {
|
|
128
|
+
explanatory: [
|
|
129
|
+
"- Explain WHY, not just WHAT",
|
|
130
|
+
"- Provide context and rationale",
|
|
131
|
+
"- Use analogies where helpful",
|
|
132
|
+
"- Answer follow-up questions proactively",
|
|
133
|
+
],
|
|
134
|
+
outline: [
|
|
135
|
+
"- Use bullet points and structured lists",
|
|
136
|
+
"- Headings for organization",
|
|
137
|
+
"- Summary at the top",
|
|
138
|
+
"- Details collapsed or abbreviated",
|
|
139
|
+
],
|
|
140
|
+
skeptical: [
|
|
141
|
+
"- Challenge assumptions in the request",
|
|
142
|
+
"- Point out risks and edge cases",
|
|
143
|
+
"- Ask clarifying questions",
|
|
144
|
+
"- Suggest alternatives",
|
|
145
|
+
"- Verify requirements are complete",
|
|
146
|
+
],
|
|
147
|
+
architecture: [
|
|
148
|
+
"- Start with high-level design",
|
|
149
|
+
"- Discuss patterns and trade-offs",
|
|
150
|
+
"- Component diagrams before code",
|
|
151
|
+
"- Implementation follows design",
|
|
152
|
+
],
|
|
153
|
+
minimal: [
|
|
154
|
+
"- Code only, minimal prose",
|
|
155
|
+
"- No explanations unless asked",
|
|
156
|
+
"- Direct answers",
|
|
157
|
+
],
|
|
158
|
+
};
|
|
159
|
+
lines.push(`[OUTPUT STYLE] ${config.output_style.toUpperCase()}:`);
|
|
160
|
+
styleInstructions[config.output_style].forEach(instruction => lines.push(instruction));
|
|
161
|
+
lines.push("");
|
|
162
|
+
|
|
163
|
+
// Constraints
|
|
164
|
+
lines.push("[CONSTRAINTS]");
|
|
165
|
+
if (config.constraints.require_code_review) {
|
|
166
|
+
lines.push("- MUST review code before accepting: check for bugs, smells, tests");
|
|
167
|
+
}
|
|
168
|
+
if (config.constraints.enforce_tdd) {
|
|
169
|
+
lines.push("- TDD REQUIRED: Write failing test first, then implementation");
|
|
170
|
+
}
|
|
171
|
+
if (config.constraints.explain_reasoning) {
|
|
172
|
+
lines.push("- ALWAYS explain your reasoning");
|
|
173
|
+
}
|
|
174
|
+
if (config.constraints.be_skeptical) {
|
|
175
|
+
lines.push("- BE SKEPTICAL: Question requirements, point out gaps");
|
|
176
|
+
}
|
|
177
|
+
lines.push(`- Maximum response: ~${config.constraints.max_response_tokens} tokens`);
|
|
178
|
+
lines.push("");
|
|
179
|
+
|
|
180
|
+
lines.push("VIOLATION: If you cannot obey these constraints, state why explicitly.");
|
|
181
|
+
lines.push("</agent-configuration>");
|
|
182
|
+
|
|
183
|
+
return lines.join("\n");
|
|
184
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hierarchy Schema
|
|
3
|
+
* 3-level context hierarchy: trajectory → tactic → action
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type HierarchyLevel = "trajectory" | "tactic" | "action";
|
|
7
|
+
export type ContextStatus = "pending" | "active" | "complete" | "blocked";
|
|
8
|
+
|
|
9
|
+
export interface ContextMap {
|
|
10
|
+
level: HierarchyLevel;
|
|
11
|
+
content: string;
|
|
12
|
+
status: ContextStatus;
|
|
13
|
+
timestamp: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface HierarchyState {
|
|
17
|
+
trajectory: string;
|
|
18
|
+
tactic: string;
|
|
19
|
+
action: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createHierarchyState(
|
|
23
|
+
trajectory = "",
|
|
24
|
+
tactic = "",
|
|
25
|
+
action = ""
|
|
26
|
+
): HierarchyState {
|
|
27
|
+
return { trajectory, tactic, action };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function updateHierarchyLevel(
|
|
31
|
+
state: HierarchyState,
|
|
32
|
+
level: HierarchyLevel,
|
|
33
|
+
content: string
|
|
34
|
+
): HierarchyState {
|
|
35
|
+
return { ...state, [level]: content };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function validateHierarchyTransition(
|
|
39
|
+
current: HierarchyLevel | "complete",
|
|
40
|
+
next: HierarchyLevel
|
|
41
|
+
): boolean {
|
|
42
|
+
const levels: HierarchyLevel[] = ["trajectory", "tactic", "action"];
|
|
43
|
+
const currentIndex = levels.indexOf(current as HierarchyLevel);
|
|
44
|
+
const nextIndex = levels.indexOf(next);
|
|
45
|
+
|
|
46
|
+
// Allow same level or moving down (more specific)
|
|
47
|
+
// Allow moving up only if current is complete
|
|
48
|
+
return nextIndex >= currentIndex || current === "complete";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getLevelDepth(level: HierarchyLevel): number {
|
|
52
|
+
const depths: Record<HierarchyLevel, number> = {
|
|
53
|
+
trajectory: 1,
|
|
54
|
+
tactic: 2,
|
|
55
|
+
action: 3,
|
|
56
|
+
};
|
|
57
|
+
return depths[level];
|
|
58
|
+
}
|