@jigyasudham/veto 1.4.0 → 1.4.3
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/README.md +218 -17
- package/dist/council/decision-engine.d.ts.map +1 -1
- package/dist/council/decision-engine.js +20 -0
- package/dist/council/decision-engine.js.map +1 -1
- package/dist/council/decision-extractor.d.ts +13 -0
- package/dist/council/decision-extractor.d.ts.map +1 -0
- package/dist/council/decision-extractor.js +66 -0
- package/dist/council/decision-extractor.js.map +1 -0
- package/dist/council/devil-advocate.d.ts.map +1 -1
- package/dist/council/devil-advocate.js +12 -1
- package/dist/council/devil-advocate.js.map +1 -1
- package/dist/council/lead-developer.d.ts.map +1 -1
- package/dist/council/lead-developer.js +34 -14
- package/dist/council/lead-developer.js.map +1 -1
- package/dist/council/legal-compliance.d.ts.map +1 -1
- package/dist/council/legal-compliance.js +44 -24
- package/dist/council/legal-compliance.js.map +1 -1
- package/dist/council/llm-council.d.ts +3 -0
- package/dist/council/llm-council.d.ts.map +1 -1
- package/dist/council/llm-council.js +127 -21
- package/dist/council/llm-council.js.map +1 -1
- package/dist/council/product-manager.d.ts.map +1 -1
- package/dist/council/product-manager.js +33 -13
- package/dist/council/product-manager.js.map +1 -1
- package/dist/council/security.d.ts.map +1 -1
- package/dist/council/security.js +44 -24
- package/dist/council/security.js.map +1 -1
- package/dist/council/session-summarizer.d.ts +25 -0
- package/dist/council/session-summarizer.d.ts.map +1 -0
- package/dist/council/session-summarizer.js +130 -0
- package/dist/council/session-summarizer.js.map +1 -0
- package/dist/council/system-architect.d.ts.map +1 -1
- package/dist/council/system-architect.js +33 -13
- package/dist/council/system-architect.js.map +1 -1
- package/dist/council/ux-designer.d.ts.map +1 -1
- package/dist/council/ux-designer.js +47 -26
- package/dist/council/ux-designer.js.map +1 -1
- package/dist/server.js +109 -13
- package/dist/server.js.map +1 -1
- package/dist/skills/memory/skill-session-restore.d.ts.map +1 -1
- package/dist/skills/memory/skill-session-restore.js +19 -24
- package/dist/skills/memory/skill-session-restore.js.map +1 -1
- package/dist/skills/memory/skill-session-save.d.ts.map +1 -1
- package/dist/skills/memory/skill-session-save.js +5 -9
- package/dist/skills/memory/skill-session-save.js.map +1 -1
- package/dist/tools/definitions.d.ts +164 -6
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +24 -6
- package/dist/tools/definitions.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
export interface GeneratedSession {
|
|
3
|
+
summary: string;
|
|
4
|
+
context: string;
|
|
5
|
+
task_state: string;
|
|
6
|
+
auto_summarized: true;
|
|
7
|
+
}
|
|
8
|
+
export interface AgenticSummarizePrompt {
|
|
9
|
+
mode: 'agentic';
|
|
10
|
+
instruction: string;
|
|
11
|
+
summarize_prompt: string;
|
|
12
|
+
template: {
|
|
13
|
+
auto_summarize: false;
|
|
14
|
+
summary: string;
|
|
15
|
+
context: string;
|
|
16
|
+
task_state: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export declare function buildAgenticSummarizePrompt(): AgenticSummarizePrompt;
|
|
20
|
+
export declare function autoSummarizeSession(server: Server, hints: {
|
|
21
|
+
summary?: string;
|
|
22
|
+
context?: string;
|
|
23
|
+
task_state?: string;
|
|
24
|
+
}): Promise<GeneratedSession | AgenticSummarizePrompt | null>;
|
|
25
|
+
//# sourceMappingURL=session-summarizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-summarizer.d.ts","sourceRoot":"","sources":["../../src/council/session-summarizer.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAExE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,IAAI,CAAC;CACvB;AA+ED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE;QACR,cAAc,EAAE,KAAK,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAmBD,wBAAgB,2BAA2B,IAAI,sBAAsB,CAapE;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACjE,OAAO,CAAC,gBAAgB,GAAG,sBAAsB,GAAG,IAAI,CAAC,CAoB3D"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// LLM-backed session summarizer — uses MCP Sampling with full conversation context
|
|
2
|
+
// to generate a structured, accurate session checkpoint without the calling AI
|
|
3
|
+
// having to manually write summary/context/task_state.
|
|
4
|
+
const SYSTEM_PROMPT = `You are a session archivist for a software development AI assistant.
|
|
5
|
+
Your job: read the conversation above and produce a precise, compact session checkpoint
|
|
6
|
+
that lets a DIFFERENT AI resume this exact work in a new session — without re-reading any source files.
|
|
7
|
+
|
|
8
|
+
Return ONLY valid JSON with exactly this shape (no markdown, no prose):
|
|
9
|
+
{
|
|
10
|
+
"summary": "one sentence: what was accomplished or attempted",
|
|
11
|
+
"phase": "planning|implementing|reviewing|blocked|complete",
|
|
12
|
+
"context": {
|
|
13
|
+
"task": "original task description verbatim",
|
|
14
|
+
"decisions": [
|
|
15
|
+
{ "decision": "what was decided", "rationale": "why" }
|
|
16
|
+
],
|
|
17
|
+
"findings": [
|
|
18
|
+
"specific file path + what matters about it (e.g. src/server.ts:302 — veto_session_save handler, add validation here)"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"task_state": {
|
|
22
|
+
"completed": ["list of finished subtasks, specific"],
|
|
23
|
+
"inProgress": ["current subtask being worked on"],
|
|
24
|
+
"remaining": ["subtasks still to do"],
|
|
25
|
+
"blockers": ["anything requiring human input before continuing"],
|
|
26
|
+
"nextAction": "concrete, file-specific instruction: e.g. Edit src/server.ts line 302 — add zod .max(2000) on summary field"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
Rules:
|
|
31
|
+
- nextAction MUST be actionable without opening any file — include filename, line number, and exact change
|
|
32
|
+
- findings MUST name specific files and what is relevant about them, not generic descriptions
|
|
33
|
+
- Keep total JSON under 1500 tokens
|
|
34
|
+
- Do not include large code blocks — reference file+line instead`;
|
|
35
|
+
function buildUserMessage(hints) {
|
|
36
|
+
const parts = [
|
|
37
|
+
'The conversation context above is the full work session to checkpoint.',
|
|
38
|
+
'Extract a session save from it. Focus on: which specific files were touched (with line numbers), what decisions were made and why, and make nextAction a concrete file+line instruction.',
|
|
39
|
+
];
|
|
40
|
+
if (hints.summary)
|
|
41
|
+
parts.push(`\nHint from calling AI (summary): ${hints.summary}`);
|
|
42
|
+
if (hints.context)
|
|
43
|
+
parts.push(`\nHint from calling AI (context): ${hints.context}`);
|
|
44
|
+
if (hints.task_state)
|
|
45
|
+
parts.push(`\nHint from calling AI (task_state): ${hints.task_state}`);
|
|
46
|
+
if (hints.summary || hints.context || hints.task_state) {
|
|
47
|
+
parts.push('\nUse these hints as supplementary input. The conversation is the primary source — improve on the hints where the conversation provides more detail.');
|
|
48
|
+
}
|
|
49
|
+
return parts.join('\n');
|
|
50
|
+
}
|
|
51
|
+
function parseGeneratedSession(raw) {
|
|
52
|
+
try {
|
|
53
|
+
const match = raw.match(/\{[\s\S]*\}/);
|
|
54
|
+
if (!match)
|
|
55
|
+
return null;
|
|
56
|
+
const parsed = JSON.parse(match[0]);
|
|
57
|
+
if (typeof parsed.summary !== 'string')
|
|
58
|
+
return null;
|
|
59
|
+
const summary = String(parsed.summary).slice(0, 2000);
|
|
60
|
+
const context = JSON.stringify({
|
|
61
|
+
task: parsed.context?.task ?? '',
|
|
62
|
+
phase: parsed.phase ?? 'implementing',
|
|
63
|
+
decisions: Array.isArray(parsed.context?.decisions) ? parsed.context.decisions : [],
|
|
64
|
+
findings: Array.isArray(parsed.context?.findings) ? parsed.context.findings : [],
|
|
65
|
+
}).slice(0, 50_000);
|
|
66
|
+
const task_state = JSON.stringify({
|
|
67
|
+
completed: Array.isArray(parsed.task_state?.completed) ? parsed.task_state.completed : [],
|
|
68
|
+
inProgress: Array.isArray(parsed.task_state?.inProgress) ? parsed.task_state.inProgress : [],
|
|
69
|
+
remaining: Array.isArray(parsed.task_state?.remaining) ? parsed.task_state.remaining : [],
|
|
70
|
+
blockers: Array.isArray(parsed.task_state?.blockers) ? parsed.task_state.blockers : [],
|
|
71
|
+
nextAction: typeof parsed.task_state?.nextAction === 'string' ? parsed.task_state.nextAction : '',
|
|
72
|
+
}).slice(0, 20_000);
|
|
73
|
+
return { summary, context, task_state };
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const AGENTIC_TEMPLATE = {
|
|
80
|
+
auto_summarize: false,
|
|
81
|
+
summary: '<one sentence: what was accomplished or is in progress>',
|
|
82
|
+
context: JSON.stringify({
|
|
83
|
+
task: '<original task description verbatim>',
|
|
84
|
+
decisions: [{ decision: '<what was decided>', rationale: '<why>' }],
|
|
85
|
+
findings: ['<src/file.ts:N — what matters about this file>'],
|
|
86
|
+
}),
|
|
87
|
+
task_state: JSON.stringify({
|
|
88
|
+
completed: ['<finished subtask>'],
|
|
89
|
+
inProgress: ['<current subtask>'],
|
|
90
|
+
remaining: ['<subtask still to do>'],
|
|
91
|
+
blockers: [],
|
|
92
|
+
nextAction: '<concrete file+line instruction: Edit src/X.ts line N — do Y>',
|
|
93
|
+
}),
|
|
94
|
+
};
|
|
95
|
+
export function buildAgenticSummarizePrompt() {
|
|
96
|
+
return {
|
|
97
|
+
mode: 'agentic',
|
|
98
|
+
instruction: 'MCP Sampling is unavailable on this platform. Generate the session summary yourself from the conversation above, then call veto_session_save again with the filled-in fields. Use the template below — replace every <placeholder> with real content from the conversation.',
|
|
99
|
+
summarize_prompt: `Review the conversation above and produce a session checkpoint. Requirements:
|
|
100
|
+
- summary: one sentence describing what was accomplished
|
|
101
|
+
- context.task: the original task verbatim
|
|
102
|
+
- context.decisions: decisions made and why (only non-obvious ones)
|
|
103
|
+
- context.findings: specific file paths with line numbers and what matters there
|
|
104
|
+
- task_state.nextAction: MUST be a concrete file+line instruction — e.g. "Edit src/server.ts line 302 — add zod .max(2000) on summary field". NOT vague like "add validation".
|
|
105
|
+
- Keep total JSON under 1500 tokens`,
|
|
106
|
+
template: AGENTIC_TEMPLATE,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export async function autoSummarizeSession(server, hints) {
|
|
110
|
+
try {
|
|
111
|
+
const result = await server.createMessage({
|
|
112
|
+
messages: [{ role: 'user', content: { type: 'text', text: buildUserMessage(hints) } }],
|
|
113
|
+
systemPrompt: SYSTEM_PROMPT,
|
|
114
|
+
maxTokens: 800,
|
|
115
|
+
includeContext: 'allServers',
|
|
116
|
+
});
|
|
117
|
+
const raw = result.content.type === 'text' ? result.content.text : '';
|
|
118
|
+
if (!raw)
|
|
119
|
+
return buildAgenticSummarizePrompt();
|
|
120
|
+
const parsed = parseGeneratedSession(raw);
|
|
121
|
+
if (!parsed)
|
|
122
|
+
return buildAgenticSummarizePrompt();
|
|
123
|
+
return { ...parsed, auto_summarized: true };
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// MCP Sampling unavailable — return agentic prompt so the calling AI can do it
|
|
127
|
+
return buildAgenticSummarizePrompt();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=session-summarizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-summarizer.js","sourceRoot":"","sources":["../../src/council/session-summarizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-architect.d.ts","sourceRoot":"","sources":["../../src/council/system-architect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"system-architect.d.ts","sourceRoot":"","sources":["../../src/council/system-architect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAsI5C,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAqD/C"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { extractDecision, pickSide, reframeVote } from './decision-extractor.js';
|
|
1
2
|
const BLOCK_RULES = [
|
|
2
3
|
{
|
|
3
4
|
pattern: /\bdrop\s+table\b/i,
|
|
@@ -143,32 +144,51 @@ export function analyze(task) {
|
|
|
143
144
|
recommendations.push(rule.recommendation);
|
|
144
145
|
}
|
|
145
146
|
}
|
|
147
|
+
let vote;
|
|
146
148
|
if (blocks.length > 0) {
|
|
147
|
-
|
|
149
|
+
vote = {
|
|
148
150
|
verdict: 'block',
|
|
149
151
|
reason: blocks[0].reason,
|
|
150
152
|
concerns: blocks.slice(1).map(b => b.reason),
|
|
151
153
|
recommendation: blocks.map(b => b.recommendation).join(' | '),
|
|
152
154
|
};
|
|
153
155
|
}
|
|
154
|
-
if (concerns.length > 0) {
|
|
155
|
-
|
|
156
|
+
else if (concerns.length > 0) {
|
|
157
|
+
vote = {
|
|
156
158
|
verdict: 'warn',
|
|
157
159
|
reason: `${concerns.length} structural concern${concerns.length > 1 ? 's' : ''} — review before building.`,
|
|
158
160
|
concerns,
|
|
159
161
|
recommendation: recommendations[0],
|
|
160
162
|
};
|
|
161
163
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
164
|
+
else {
|
|
165
|
+
const matched = TOPIC_INSIGHTS.filter(t => t.pattern.test(task));
|
|
166
|
+
if (matched.length > 0) {
|
|
167
|
+
const top = matched.slice(0, 2);
|
|
168
|
+
vote = {
|
|
169
|
+
verdict: 'warn',
|
|
170
|
+
reason: top[0].concern,
|
|
171
|
+
concerns: top.slice(1).map(t => t.concern),
|
|
172
|
+
recommendation: top.map(t => t.recommendation).join(' | '),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
vote = { verdict: 'approve', reason: 'Architecture looks sound. No structural concerns identified.', concerns: [] };
|
|
177
|
+
}
|
|
171
178
|
}
|
|
172
|
-
return
|
|
179
|
+
return applyDecisionStance(vote, task);
|
|
180
|
+
}
|
|
181
|
+
// Architect risk: options that tighten coupling or introduce new architectural layers
|
|
182
|
+
const ARCH_RISK = /\b(bundl|integrat\s+into|embed|inline|http|express|new\s+(server|layer|transport|protocol)|microservice)\b/i;
|
|
183
|
+
function applyDecisionStance(vote, task) {
|
|
184
|
+
const ctx = extractDecision(task);
|
|
185
|
+
if (!ctx.isDecisionTask)
|
|
186
|
+
return vote;
|
|
187
|
+
const { optionA, optionB } = ctx;
|
|
188
|
+
const side = pickSide(optionA, optionB, ARCH_RISK);
|
|
189
|
+
const advice = side
|
|
190
|
+
? `"${side.preferred}" preserves separation of concerns and keeps the boundary clean. "${side.avoided}" creates coupling that is difficult to undo later — the caller now depends on implementation details.`
|
|
191
|
+
: `Evaluate which option maintains the cleaner boundary. Prefer the path that adds no new coupling between existing components.`;
|
|
192
|
+
return reframeVote(vote, ctx, side?.preferred ?? null, advice);
|
|
173
193
|
}
|
|
174
194
|
//# sourceMappingURL=system-architect.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-architect.js","sourceRoot":"","sources":["../../src/council/system-architect.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"system-architect.js","sourceRoot":"","sources":["../../src/council/system-architect.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ux-designer.d.ts","sourceRoot":"","sources":["../../src/council/ux-designer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"ux-designer.d.ts","sourceRoot":"","sources":["../../src/council/ux-designer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAsE5C,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAkG/C"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { extractDecision, pickSide, reframeVote } from './decision-extractor.js';
|
|
1
2
|
// Backend-only tasks: UX approves without friction
|
|
2
3
|
const BACKEND_ONLY = /\b(api|backend|server|database|db|query|schema|migration|auth.*middleware|jwt|oauth|cron|cli|script|worker|queue|cache|redis|sql|index|trigger|webhook)\b/i;
|
|
3
4
|
const FRONTEND_SIGNALS = /\b(ui|ux|form|button|modal|page|screen|component|view|layout|menu|nav|input|field|label|error.?message|loading|user.?interface|frontend|react|vue|svelte|html|css)\b/i;
|
|
@@ -65,14 +66,15 @@ export function analyze(task) {
|
|
|
65
66
|
if (TRIVIAL.test(task.trim())) {
|
|
66
67
|
return { verdict: 'approve', reason: 'Trivial change — no UX concerns.', concerns: [] };
|
|
67
68
|
}
|
|
68
|
-
// Pure backend tasks get a light pass
|
|
69
|
+
// Pure backend tasks get a light pass — but still apply decision stance
|
|
69
70
|
const isBackend = BACKEND_ONLY.test(task) && !FRONTEND_SIGNALS.test(task);
|
|
70
71
|
if (isBackend) {
|
|
71
|
-
|
|
72
|
+
const backendVote = {
|
|
72
73
|
verdict: 'approve',
|
|
73
74
|
reason: 'Backend task — no direct UX concerns. Ensure API errors surface clearly to users.',
|
|
74
75
|
concerns: [],
|
|
75
76
|
};
|
|
77
|
+
return applyDecisionStance(backendVote, task);
|
|
76
78
|
}
|
|
77
79
|
const blocks = [];
|
|
78
80
|
const concerns = [];
|
|
@@ -88,22 +90,6 @@ export function analyze(task) {
|
|
|
88
90
|
recommendations.push(rule.recommendation);
|
|
89
91
|
}
|
|
90
92
|
}
|
|
91
|
-
if (blocks.length > 0) {
|
|
92
|
-
return {
|
|
93
|
-
verdict: 'block',
|
|
94
|
-
reason: blocks[0].reason,
|
|
95
|
-
concerns: blocks.slice(1).map(b => b.reason),
|
|
96
|
-
recommendation: blocks.map(b => b.recommendation).join(' | '),
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
if (concerns.length > 0) {
|
|
100
|
-
return {
|
|
101
|
-
verdict: 'warn',
|
|
102
|
-
reason: `${concerns.length} UX concern${concerns.length > 1 ? 's' : ''} — real users will notice this.`,
|
|
103
|
-
concerns,
|
|
104
|
-
recommendation: recommendations[0],
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
93
|
// Topic-based UX analysis for tasks with no direct frontend signals
|
|
108
94
|
const TOPIC_INSIGHTS = [
|
|
109
95
|
{
|
|
@@ -137,16 +123,51 @@ export function analyze(task) {
|
|
|
137
123
|
recommendation: 'Show progress for any operation over 500ms. For long operations, stream partial results or show a "working..." status. Never leave the user in silence.',
|
|
138
124
|
},
|
|
139
125
|
];
|
|
140
|
-
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
|
|
126
|
+
let vote;
|
|
127
|
+
if (blocks.length > 0) {
|
|
128
|
+
vote = {
|
|
129
|
+
verdict: 'block',
|
|
130
|
+
reason: blocks[0].reason,
|
|
131
|
+
concerns: blocks.slice(1).map(b => b.reason),
|
|
132
|
+
recommendation: blocks.map(b => b.recommendation).join(' | '),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
else if (concerns.length > 0) {
|
|
136
|
+
vote = {
|
|
144
137
|
verdict: 'warn',
|
|
145
|
-
reason:
|
|
146
|
-
concerns
|
|
147
|
-
recommendation:
|
|
138
|
+
reason: `${concerns.length} UX concern${concerns.length > 1 ? 's' : ''} — real users will notice this.`,
|
|
139
|
+
concerns,
|
|
140
|
+
recommendation: recommendations[0],
|
|
148
141
|
};
|
|
149
142
|
}
|
|
150
|
-
|
|
143
|
+
else {
|
|
144
|
+
const topicMatched = TOPIC_INSIGHTS.filter(t => t.pattern.test(task));
|
|
145
|
+
if (topicMatched.length > 0) {
|
|
146
|
+
const top = topicMatched.slice(0, 2);
|
|
147
|
+
vote = {
|
|
148
|
+
verdict: 'warn',
|
|
149
|
+
reason: top[0].concern,
|
|
150
|
+
concerns: top.slice(1).map(t => t.concern),
|
|
151
|
+
recommendation: top.map(t => t.recommendation).join(' | '),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
vote = { verdict: 'approve', reason: 'UX looks solid. No user experience concerns identified.', concerns: [] };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return applyDecisionStance(vote, task);
|
|
159
|
+
}
|
|
160
|
+
// UX risk: options that add setup friction, credentials, or configuration overhead
|
|
161
|
+
const UX_RISK = /\b(http|api.?key|auth|oauth|port|server\s+config|credentials|token|setup.{0,15}required|install.{0,15}extra)\b/i;
|
|
162
|
+
function applyDecisionStance(vote, task) {
|
|
163
|
+
const ctx = extractDecision(task);
|
|
164
|
+
if (!ctx.isDecisionTask)
|
|
165
|
+
return vote;
|
|
166
|
+
const { optionA, optionB } = ctx;
|
|
167
|
+
const side = pickSide(optionA, optionB, UX_RISK);
|
|
168
|
+
const advice = side
|
|
169
|
+
? `"${side.preferred}" keeps the developer experience simpler — no extra setup or credentials. "${side.avoided}" adds friction that will silently reduce adoption.`
|
|
170
|
+
: `Evaluate which option requires fewer steps for a first-time user. The shorter happy path wins on developer experience.`;
|
|
171
|
+
return reframeVote(vote, ctx, side?.preferred ?? null, advice);
|
|
151
172
|
}
|
|
152
173
|
//# sourceMappingURL=ux-designer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ux-designer.js","sourceRoot":"","sources":["../../src/council/ux-designer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ux-designer.js","sourceRoot":"","sources":["../../src/council/ux-designer.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -9,7 +9,8 @@ import { buildContextString } from './context/reader.js';
|
|
|
9
9
|
import { TOOL_DEFINITIONS } from './tools/definitions.js';
|
|
10
10
|
import { saveSession, restoreSession, listSessions, closeSession, getDbPath, saveCouncilOutcome, storeKnowledge, searchKnowledge, deleteKnowledge, updateProjectMap, getProjectMap, upsertPattern, getPatterns, getContextStatus, fetchAndCacheDocs, saveTaskPlan, getTaskPlan, getUsageStatus, getAuditLog, getHealthStats, CONTEXT_WINDOWS, logUsage, getUsageLogs, getDb, storeScanDiagnostics, clearScanDiagnostics, updateSession, resolveContextWindow, getMetrics, } from './memory/local.js';
|
|
11
11
|
import { exportMemory, importMemory } from './memory/sync.js';
|
|
12
|
-
import { runLlmDebate } from './council/llm-council.js';
|
|
12
|
+
import { runLlmDebate, buildAgenticDebatePrompt, parseAgentResponses, runFromAgentResponses } from './council/llm-council.js';
|
|
13
|
+
import { autoSummarizeSession } from './council/session-summarizer.js';
|
|
13
14
|
import { routeTask, getRateStatus, trackTokens, recordOutcome, getLearningStats, getLearnedThresholds, applyLearnedThresholds, getAgentPerformanceStats, getTaskTypeBreakdown, getCouncilInsights, getRecommendedAgent } from './router/index.js';
|
|
14
15
|
import { getConfig, setConfig } from './memory/config.js';
|
|
15
16
|
import { executeParallel, executeOne } from './agents/executor.js';
|
|
@@ -252,9 +253,47 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
252
253
|
if (sessionProjectDir)
|
|
253
254
|
activeProjectDir = sessionProjectDir;
|
|
254
255
|
const savePlatform = args?.platform ? String(args.platform) : 'claude';
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
256
|
+
const shouldAutoSummarize = args?.auto_summarize === true;
|
|
257
|
+
// Auto-summarize: try MCP Sampling first, fall back to agentic prompt for host AI
|
|
258
|
+
let autoSummaryResult = null;
|
|
259
|
+
if (shouldAutoSummarize) {
|
|
260
|
+
autoSummaryResult = await autoSummarizeSession(server, {
|
|
261
|
+
summary: args?.summary ? String(args.summary) : undefined,
|
|
262
|
+
context: args?.context ? String(args.context) : undefined,
|
|
263
|
+
task_state: args?.task_state ? String(args.task_state) : undefined,
|
|
264
|
+
});
|
|
265
|
+
// If agentic prompt returned, short-circuit — return it so the AI can fill it in
|
|
266
|
+
if (autoSummaryResult && 'mode' in autoSummaryResult && autoSummaryResult.mode === 'agentic') {
|
|
267
|
+
return {
|
|
268
|
+
content: [{
|
|
269
|
+
type: 'text',
|
|
270
|
+
text: JSON.stringify({
|
|
271
|
+
success: false,
|
|
272
|
+
auto_summarized: false,
|
|
273
|
+
...autoSummaryResult,
|
|
274
|
+
}, null, 2),
|
|
275
|
+
}],
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Enforce size limits — unbounded strings exhaust SQLite page cache and memory
|
|
280
|
+
const SUMMARY_LIMIT = 2_000;
|
|
281
|
+
const CONTEXT_LIMIT = 50_000;
|
|
282
|
+
const TASK_STATE_LIMIT = 20_000;
|
|
283
|
+
const generatedSession = autoSummaryResult && 'auto_summarized' in autoSummaryResult ? autoSummaryResult : null;
|
|
284
|
+
const RAW_SUMMARY = generatedSession?.summary ?? String(args?.summary ?? '');
|
|
285
|
+
const RAW_CONTEXT = generatedSession?.context ?? String(args?.context ?? '');
|
|
286
|
+
const RAW_TASK_STATE = generatedSession?.task_state ?? (args?.task_state ? String(args.task_state) : undefined);
|
|
287
|
+
const saveSummary = RAW_SUMMARY.slice(0, SUMMARY_LIMIT);
|
|
288
|
+
const saveContext = RAW_CONTEXT.slice(0, CONTEXT_LIMIT);
|
|
289
|
+
const saveTaskState = RAW_TASK_STATE ? RAW_TASK_STATE.slice(0, TASK_STATE_LIMIT) : undefined;
|
|
290
|
+
const truncationWarnings = [];
|
|
291
|
+
if (RAW_SUMMARY.length > SUMMARY_LIMIT)
|
|
292
|
+
truncationWarnings.push(`summary truncated to ${SUMMARY_LIMIT} chars (was ${RAW_SUMMARY.length})`);
|
|
293
|
+
if (RAW_CONTEXT.length > CONTEXT_LIMIT)
|
|
294
|
+
truncationWarnings.push(`context truncated to ${CONTEXT_LIMIT} chars (was ${RAW_CONTEXT.length})`);
|
|
295
|
+
if (RAW_TASK_STATE && RAW_TASK_STATE.length > TASK_STATE_LIMIT)
|
|
296
|
+
truncationWarnings.push(`task_state truncated to ${TASK_STATE_LIMIT} chars (was ${RAW_TASK_STATE.length})`);
|
|
258
297
|
const existingId = args?.session_id ? String(args.session_id) : undefined;
|
|
259
298
|
const sessionInput = {
|
|
260
299
|
summary: saveSummary,
|
|
@@ -287,6 +326,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
287
326
|
autoSave.cached = { summary: saveSummary, context: saveContext, task_state: saveTaskState, platform: savePlatform, project_dir: sessionProjectDir, context_window: resolvedWindow };
|
|
288
327
|
autoSave.last_save_at = result.saved_at;
|
|
289
328
|
autoSave.last_session_id = result.session_id;
|
|
329
|
+
const autoSumFailed = shouldAutoSummarize && !autoSummaryResult;
|
|
290
330
|
const responseObj = {
|
|
291
331
|
success: true,
|
|
292
332
|
message: wasUpdate
|
|
@@ -297,7 +337,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
297
337
|
session_id: result.session_id,
|
|
298
338
|
saved_at: result.saved_at,
|
|
299
339
|
updated: wasUpdate,
|
|
340
|
+
auto_summarized: autoSummaryResult ? true : false,
|
|
341
|
+
...(autoSumFailed ? { auto_summarize_warning: 'MCP Sampling unavailable — saved provided values instead. For best results use Claude Code or another host that supports sampling.' } : {}),
|
|
300
342
|
...(wasUpdate ? {} : { usage_pct: result.usage_pct, context_warning: result.context_warning }),
|
|
343
|
+
...(truncationWarnings.length > 0 ? { truncation_warnings: truncationWarnings } : {}),
|
|
301
344
|
};
|
|
302
345
|
if (result.continuation_prompt)
|
|
303
346
|
responseObj.continuation_prompt = result.continuation_prompt;
|
|
@@ -321,22 +364,41 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
321
364
|
const s = result.session;
|
|
322
365
|
if (s.project_dir)
|
|
323
366
|
activeProjectDir = s.project_dir;
|
|
367
|
+
const parsedTaskState = s.task_state ? (() => { try {
|
|
368
|
+
return JSON.parse(s.task_state);
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
return s.task_state;
|
|
372
|
+
} })() : null;
|
|
373
|
+
const nextAction = (typeof parsedTaskState === 'object' && parsedTaskState !== null)
|
|
374
|
+
? (parsedTaskState.nextAction ?? parsedTaskState.next_action ?? null)
|
|
375
|
+
: null;
|
|
376
|
+
const resumeInstructions = [
|
|
377
|
+
'Context restored from previous session. Trust the summary, context, and task_state above — they were written by the AI that last worked on this.',
|
|
378
|
+
'Do NOT re-read source files to orient yourself. That defeats the purpose of session restore and wastes tokens.',
|
|
379
|
+
'Only open a file if you are about to EDIT it — not to "verify" or "familiarize yourself" with it.',
|
|
380
|
+
nextAction ? `Start immediately with: ${nextAction}` : 'Read task_state.nextAction (or context) for where to start.',
|
|
381
|
+
'If context seems stale (e.g. you find a file has changed), read only that file, update the context, and continue.',
|
|
382
|
+
].join(' ');
|
|
324
383
|
return {
|
|
325
384
|
content: [
|
|
326
385
|
{
|
|
327
386
|
type: 'text',
|
|
328
387
|
text: JSON.stringify({
|
|
329
388
|
success: true,
|
|
389
|
+
resume_instructions: resumeInstructions,
|
|
330
390
|
session_id: s.id,
|
|
331
391
|
created_by: s.platform,
|
|
332
|
-
|
|
333
|
-
last_resumed_at: s.last_resumed_at,
|
|
334
|
-
started_at: s.started_at,
|
|
335
|
-
ended_at: s.ended_at,
|
|
392
|
+
saved_at: s.started_at,
|
|
336
393
|
project_dir: s.project_dir,
|
|
337
394
|
summary: s.summary,
|
|
338
|
-
context: s.context ?
|
|
339
|
-
|
|
395
|
+
context: s.context ? (() => { try {
|
|
396
|
+
return JSON.parse(s.context);
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
return s.context;
|
|
400
|
+
} })() : null,
|
|
401
|
+
task_state: parsedTaskState,
|
|
340
402
|
token_count: s.token_count,
|
|
341
403
|
}, null, 2),
|
|
342
404
|
},
|
|
@@ -409,13 +471,36 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
409
471
|
const strictnessArg = (['fast', 'standard', 'strict'].includes(String(args?.strictness ?? '')))
|
|
410
472
|
? String(args.strictness)
|
|
411
473
|
: 'standard';
|
|
412
|
-
const
|
|
413
|
-
const result = await runLlmDebate(server, {
|
|
474
|
+
const debateInput = {
|
|
414
475
|
task,
|
|
415
476
|
context: args?.context ? String(args.context) : undefined,
|
|
416
477
|
project_dir: args?.project_dir ? String(args.project_dir) : undefined,
|
|
417
478
|
strictness: strictnessArg,
|
|
418
|
-
}
|
|
479
|
+
};
|
|
480
|
+
// Phase 2: agent_responses provided — run verdict engine on LLM-generated votes
|
|
481
|
+
const rawAgentResponses = args?.agent_responses;
|
|
482
|
+
if (rawAgentResponses && typeof rawAgentResponses === 'object') {
|
|
483
|
+
const parsed = parseAgentResponses(JSON.stringify(rawAgentResponses), task);
|
|
484
|
+
if (parsed) {
|
|
485
|
+
const debateStart = Date.now();
|
|
486
|
+
const result = runFromAgentResponses(debateInput, parsed);
|
|
487
|
+
const debateDuration = Date.now() - debateStart;
|
|
488
|
+
const sessionId = args?.session_id ? String(args.session_id) : undefined;
|
|
489
|
+
const outcomeId = saveCouncilOutcome({
|
|
490
|
+
session_id: sessionId, task, verdict: result.final_verdict,
|
|
491
|
+
lead_dev: JSON.stringify(result.votes.lead_dev), pm: JSON.stringify(result.votes.pm),
|
|
492
|
+
architect: JSON.stringify(result.votes.architect), ux: JSON.stringify(result.votes.ux),
|
|
493
|
+
devil: JSON.stringify(result.votes.devil), legal: JSON.stringify(result.votes.legal),
|
|
494
|
+
security: JSON.stringify(result.votes.security), recommended: result.recommended,
|
|
495
|
+
duration_ms: debateDuration,
|
|
496
|
+
});
|
|
497
|
+
const payload = { outcome_id: outcomeId, llm_backed: true, final_verdict: result.final_verdict, block_reasons: result.block_reasons, warnings: result.warnings, recommended: result.recommended, debated_at: result.debated_at, votes: result.votes };
|
|
498
|
+
return { content: [{ type: 'text', text: result.formatted_output + '\n\n' + JSON.stringify(payload, null, 2) }] };
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
// Phase 1: run deterministic debate + attach llm_upgrade prompt for host AI
|
|
502
|
+
const debateStart = Date.now();
|
|
503
|
+
const result = await runLlmDebate(server, debateInput);
|
|
419
504
|
const debateDuration = Date.now() - debateStart;
|
|
420
505
|
const sessionId = args?.session_id ? String(args.session_id) : undefined;
|
|
421
506
|
const outcomeId = saveCouncilOutcome({
|
|
@@ -465,8 +550,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
465
550
|
relevance: isRed ? 1.0 : 0.8,
|
|
466
551
|
});
|
|
467
552
|
}
|
|
553
|
+
// Build agentic upgrade prompt so host AI can provide real LLM reasoning on any platform
|
|
554
|
+
const enrichedCtx = buildContextString(debateInput.project_dir, debateInput.context);
|
|
555
|
+
const { optionA, optionB, isDecisionTask } = (await import('./council/decision-extractor.js')).extractDecision(task);
|
|
556
|
+
const decisionCtx = isDecisionTask ? `Option A: "${optionA}" vs Option B: "${optionB}"` : undefined;
|
|
557
|
+
const agenticPrompt = buildAgenticDebatePrompt(task, enrichedCtx, decisionCtx);
|
|
468
558
|
const responsePayload = {
|
|
469
559
|
outcome_id: outcomeId,
|
|
560
|
+
llm_backed: false,
|
|
470
561
|
final_verdict: result.final_verdict,
|
|
471
562
|
block_reasons: result.block_reasons,
|
|
472
563
|
warnings: result.warnings,
|
|
@@ -481,6 +572,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
481
572
|
legal: result.votes.legal,
|
|
482
573
|
security: result.votes.security,
|
|
483
574
|
},
|
|
575
|
+
llm_upgrade: {
|
|
576
|
+
available: true,
|
|
577
|
+
instruction: 'The verdict above is deterministic. For LLM-backed analysis: (1) read debate_prompt and reason as all 7 agents, generating the agent_responses JSON; (2) call veto_council_debate again with { task, agent_responses } to get the final LLM-backed verdict. Works on Claude Code, Gemini CLI, and Codex CLI with no API keys.',
|
|
578
|
+
debate_prompt: agenticPrompt,
|
|
579
|
+
},
|
|
484
580
|
};
|
|
485
581
|
const fullText = result.formatted_output + '\n\n' + JSON.stringify(responsePayload, null, 2);
|
|
486
582
|
if (typeof args?.max_tokens === 'number') {
|