smart-context-mcp 1.6.2 → 1.7.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/package.json +1 -1
- package/scripts/report-metrics.js +2 -2
- package/src/analytics/product-quality.js +176 -1
- package/src/hooks/claude-hooks.js +1 -423
- package/src/hooks/cursor-hooks.js +1 -0
- package/src/orchestration/adapters/claude-adapter.js +426 -0
- package/src/orchestration/adapters/cursor-adapter.js +429 -0
- package/src/orchestration/base-orchestrator.js +242 -0
- package/src/orchestration/headless-wrapper.js +33 -192
- package/src/orchestration/policy/event-policy.js +297 -0
- package/src/task-runner.js +24 -241
package/src/task-runner.js
CHANGED
|
@@ -3,10 +3,16 @@ import { persistMetrics } from './metrics.js';
|
|
|
3
3
|
import { countTokens } from './tokenCounter.js';
|
|
4
4
|
import { projectRoot } from './utils/paths.js';
|
|
5
5
|
import { TASK_RUNNER_QUALITY_ANALYTICS_KIND } from './analytics/product-quality.js';
|
|
6
|
+
import { DEFAULT_END_MAX_TOKENS, DEFAULT_START_MAX_TOKENS, resolveManagedStart } from './orchestration/base-orchestrator.js';
|
|
6
7
|
import { runHeadlessWrapper } from './orchestration/headless-wrapper.js';
|
|
7
|
-
import { smartContext } from './tools/smart-context.js';
|
|
8
8
|
import { smartDoctor } from './tools/smart-doctor.js';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
buildPreflightSummary,
|
|
11
|
+
buildTaskRunnerAutomaticity,
|
|
12
|
+
buildWorkflowPolicyPayload,
|
|
13
|
+
buildWorkflowPromptWithPolicy,
|
|
14
|
+
runWorkflowPreflight,
|
|
15
|
+
} from './orchestration/policy/event-policy.js';
|
|
10
16
|
import { smartStatus } from './tools/smart-status.js';
|
|
11
17
|
import { smartSummary } from './tools/smart-summary.js';
|
|
12
18
|
import { smartTurn } from './tools/smart-turn.js';
|
|
@@ -22,243 +28,9 @@ import {
|
|
|
22
28
|
evaluateRunnerGate,
|
|
23
29
|
} from './task-runner/policy.js';
|
|
24
30
|
|
|
25
|
-
const START_MAX_TOKENS = 350;
|
|
26
|
-
const END_MAX_TOKENS = 350;
|
|
27
31
|
const RUNNER_LOCK_RETRY_ATTEMPTS = 3;
|
|
28
32
|
const RUNNER_LOCK_RETRY_DELAY_MS = 100;
|
|
29
33
|
|
|
30
|
-
const normalizeWhitespace = (value) => String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
31
|
-
const truncate = (value, maxLength = 160) => {
|
|
32
|
-
const normalized = normalizeWhitespace(value);
|
|
33
|
-
if (normalized.length <= maxLength) {
|
|
34
|
-
return normalized;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (maxLength <= 3) {
|
|
38
|
-
return '';
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return `${normalized.slice(0, maxLength - 3)}...`;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const asArray = (value) => Array.isArray(value) ? value : [];
|
|
45
|
-
const uniqueCompact = (values) => [...new Set(asArray(values).map((value) => normalizeWhitespace(value)).filter(Boolean))];
|
|
46
|
-
const extractContextTopFiles = (topFiles) => uniqueCompact(asArray(topFiles).map((item) => {
|
|
47
|
-
if (typeof item === 'string') {
|
|
48
|
-
return item;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return item?.file ?? item?.path ?? '';
|
|
52
|
-
})).slice(0, 3);
|
|
53
|
-
|
|
54
|
-
const extractPreflightTopFiles = (preflightResult) => {
|
|
55
|
-
if (!preflightResult) {
|
|
56
|
-
return [];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (preflightResult.tool === 'smart_context') {
|
|
60
|
-
return uniqueCompact(asArray(preflightResult.result?.context).map((item) => item?.file).filter(Boolean)).slice(0, 3);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (preflightResult.tool === 'smart_search') {
|
|
64
|
-
return extractContextTopFiles(preflightResult.result?.topFiles);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return [];
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const extractPreflightHints = (preflightResult) => {
|
|
71
|
-
if (!preflightResult) {
|
|
72
|
-
return [];
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (preflightResult.tool === 'smart_context') {
|
|
76
|
-
return uniqueCompact(preflightResult.result?.hints).slice(0, 2);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (preflightResult.tool === 'smart_search') {
|
|
80
|
-
const totalMatches = Number(preflightResult.result?.totalMatches ?? 0);
|
|
81
|
-
if (totalMatches > 0) {
|
|
82
|
-
return [`${totalMatches} search match(es) surfaced for the workflow target`];
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return [];
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const buildPreflightSummary = (preflightResult) => {
|
|
90
|
-
if (!preflightResult) {
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const topFiles = extractPreflightTopFiles(preflightResult);
|
|
95
|
-
const hints = extractPreflightHints(preflightResult);
|
|
96
|
-
|
|
97
|
-
return {
|
|
98
|
-
tool: preflightResult.tool,
|
|
99
|
-
topFiles,
|
|
100
|
-
hints,
|
|
101
|
-
totalMatches: Number(preflightResult.result?.totalMatches ?? 0),
|
|
102
|
-
};
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const buildPreflightTask = ({ workflowProfile, prompt, startResult }) => {
|
|
106
|
-
const normalizedPrompt = normalizeWhitespace(prompt);
|
|
107
|
-
const persistedNextStep = normalizeWhitespace(startResult?.summary?.nextStep);
|
|
108
|
-
const currentFocus = normalizeWhitespace(startResult?.summary?.currentFocus);
|
|
109
|
-
const refreshedTopFiles = extractContextTopFiles(startResult?.refreshedContext?.topFiles);
|
|
110
|
-
|
|
111
|
-
if (workflowProfile.commandName === 'continue' || workflowProfile.commandName === 'resume') {
|
|
112
|
-
if (persistedNextStep) {
|
|
113
|
-
return persistedNextStep;
|
|
114
|
-
}
|
|
115
|
-
if (currentFocus) {
|
|
116
|
-
return currentFocus;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (workflowProfile.commandName === 'task' && currentFocus && persistedNextStep) {
|
|
121
|
-
return `${currentFocus}. ${persistedNextStep}`;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (normalizedPrompt) {
|
|
125
|
-
return normalizedPrompt;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (refreshedTopFiles.length > 0) {
|
|
129
|
-
return `Inspect ${refreshedTopFiles.join(', ')} and continue the persisted task`;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return workflowProfile.label;
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
const runWorkflowPreflight = async ({ workflowProfile, prompt, startResult }) => {
|
|
136
|
-
const preflight = workflowProfile.preflight;
|
|
137
|
-
if (!preflight) {
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const preflightTask = buildPreflightTask({ workflowProfile, prompt, startResult });
|
|
142
|
-
|
|
143
|
-
if (preflight.tool === 'smart_context') {
|
|
144
|
-
const result = await smartContext({
|
|
145
|
-
task: preflightTask,
|
|
146
|
-
detail: preflight.detail ?? 'minimal',
|
|
147
|
-
include: preflight.include ?? ['hints'],
|
|
148
|
-
maxTokens: preflight.maxTokens ?? 1200,
|
|
149
|
-
});
|
|
150
|
-
return {
|
|
151
|
-
tool: 'smart_context',
|
|
152
|
-
request: {
|
|
153
|
-
task: preflightTask,
|
|
154
|
-
detail: preflight.detail ?? 'minimal',
|
|
155
|
-
include: preflight.include ?? ['hints'],
|
|
156
|
-
maxTokens: preflight.maxTokens ?? 1200,
|
|
157
|
-
},
|
|
158
|
-
result,
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (preflight.tool === 'smart_search') {
|
|
163
|
-
const result = await smartSearch({
|
|
164
|
-
query: preflightTask,
|
|
165
|
-
intent: preflight.intent ?? workflowProfile.workflowIntent,
|
|
166
|
-
});
|
|
167
|
-
return {
|
|
168
|
-
tool: 'smart_search',
|
|
169
|
-
request: {
|
|
170
|
-
query: preflightTask,
|
|
171
|
-
intent: preflight.intent ?? workflowProfile.workflowIntent,
|
|
172
|
-
},
|
|
173
|
-
result,
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return null;
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
const buildContinuityGuidance = ({ startResult }) => {
|
|
181
|
-
const continuityState = startResult?.continuity?.state ?? 'unknown';
|
|
182
|
-
const lines = [
|
|
183
|
-
`- Continuity: ${continuityState}`,
|
|
184
|
-
];
|
|
185
|
-
const nextStep = normalizeWhitespace(startResult?.summary?.nextStep);
|
|
186
|
-
const currentFocus = normalizeWhitespace(startResult?.summary?.currentFocus);
|
|
187
|
-
const refreshedTopFiles = extractContextTopFiles(startResult?.refreshedContext?.topFiles);
|
|
188
|
-
const recommendedNextTools = asArray(startResult?.recommendedPath?.nextTools)
|
|
189
|
-
.map((tool) => normalizeWhitespace(tool))
|
|
190
|
-
.filter(Boolean)
|
|
191
|
-
.slice(0, 3);
|
|
192
|
-
|
|
193
|
-
if (currentFocus) {
|
|
194
|
-
lines.push(`- Persisted focus: ${truncate(currentFocus, 140)}`);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (nextStep) {
|
|
198
|
-
lines.push(`- Persisted next step: ${truncate(nextStep, 140)}`);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (refreshedTopFiles.length > 0) {
|
|
202
|
-
lines.push(`- Refreshed top files: ${refreshedTopFiles.join(', ')}`);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (recommendedNextTools.length > 0) {
|
|
206
|
-
lines.push(`- smart_turn suggested: ${recommendedNextTools.join(' -> ')}`);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
if (startResult?.isolatedSession) {
|
|
210
|
-
lines.push('- Session handling: smart_turn already isolated this work from the previous session; revalidate before assuming old focus.');
|
|
211
|
-
} else if (continuityState === 'aligned' || continuityState === 'resume') {
|
|
212
|
-
lines.push('- Session handling: reuse the active session context and stay close to the persisted next step unless the task proves otherwise.');
|
|
213
|
-
} else if (continuityState === 'possible_shift' || continuityState === 'context_mismatch') {
|
|
214
|
-
lines.push('- Session handling: treat this as a shifted slice, validate the working set early, and avoid silent context reuse.');
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return lines;
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
const buildWorkflowPromptWithPolicy = ({ prompt, workflowProfile, preflightSummary, startResult }) => {
|
|
221
|
-
const lines = [
|
|
222
|
-
prompt,
|
|
223
|
-
'',
|
|
224
|
-
'Workflow policy:',
|
|
225
|
-
`- Mode: ${workflowProfile.policyMode}`,
|
|
226
|
-
`- Intent: ${workflowProfile.workflowIntent}`,
|
|
227
|
-
`- Prefer this tool order: ${workflowProfile.nextTools.join(' -> ')}`,
|
|
228
|
-
];
|
|
229
|
-
|
|
230
|
-
if (workflowProfile.checkpointStrategy) {
|
|
231
|
-
lines.push(`- Checkpoint rule: ${workflowProfile.checkpointStrategy}`);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
lines.push(...buildContinuityGuidance({ startResult }));
|
|
235
|
-
|
|
236
|
-
if (preflightSummary?.tool) {
|
|
237
|
-
lines.push(`- Preflight: ${preflightSummary.tool}`);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (preflightSummary?.topFiles?.length) {
|
|
241
|
-
lines.push(`- Focus files: ${preflightSummary.topFiles.join(', ')}`);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (preflightSummary?.hints?.length) {
|
|
245
|
-
lines.push(`- Signals: ${preflightSummary.hints.map((hint) => truncate(hint, 120)).join(' | ')}`);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return lines.join('\n');
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
const buildWorkflowPolicyPayload = ({ commandName, workflowProfile, preflightSummary }) => ({
|
|
252
|
-
commandName,
|
|
253
|
-
label: workflowProfile.label,
|
|
254
|
-
policyMode: workflowProfile.policyMode,
|
|
255
|
-
intent: workflowProfile.workflowIntent,
|
|
256
|
-
specialized: workflowProfile.specialized,
|
|
257
|
-
nextTools: [...workflowProfile.nextTools],
|
|
258
|
-
checkpointStrategy: workflowProfile.checkpointStrategy,
|
|
259
|
-
preflight: preflightSummary,
|
|
260
|
-
});
|
|
261
|
-
|
|
262
34
|
const isRetriableLockError = (error) => {
|
|
263
35
|
const issue = error?.storageHealth?.issue ?? error?.cause?.storageHealth?.issue ?? null;
|
|
264
36
|
const retriable = error?.storageHealth?.retriable ?? error?.cause?.storageHealth?.retriable ?? false;
|
|
@@ -302,6 +74,15 @@ const recordRunnerMetrics = async ({
|
|
|
302
74
|
?? null;
|
|
303
75
|
const checkpointPersisted = Boolean(endResult && !endResult.checkpoint?.skipped && !endResult.checkpoint?.blocked);
|
|
304
76
|
const checkpointSkipped = Boolean(endResult?.checkpoint?.skipped);
|
|
77
|
+
const automaticity = buildTaskRunnerAutomaticity({
|
|
78
|
+
isWorkflowCommand: WORKFLOW_COMMANDS.has(commandName),
|
|
79
|
+
startResult,
|
|
80
|
+
endResult,
|
|
81
|
+
workflowPolicy,
|
|
82
|
+
usedWrapper,
|
|
83
|
+
overheadTokens: Number(result?.overheadTokens ?? 0),
|
|
84
|
+
managedByBaseOrchestrator: WORKFLOW_COMMANDS.has(commandName),
|
|
85
|
+
});
|
|
305
86
|
|
|
306
87
|
await persistMetrics({
|
|
307
88
|
tool: 'task_runner',
|
|
@@ -334,6 +115,7 @@ const recordRunnerMetrics = async ({
|
|
|
334
115
|
recommendedPathMode,
|
|
335
116
|
checkpointPersisted,
|
|
336
117
|
checkpointSkipped,
|
|
118
|
+
...automaticity,
|
|
337
119
|
},
|
|
338
120
|
timestamp: new Date().toISOString(),
|
|
339
121
|
});
|
|
@@ -359,13 +141,14 @@ const runWorkflowCommand = async ({
|
|
|
359
141
|
});
|
|
360
142
|
const workflowProfile = buildWorkflowPolicyProfile({ commandName });
|
|
361
143
|
|
|
362
|
-
const
|
|
363
|
-
phase: 'start',
|
|
364
|
-
sessionId,
|
|
144
|
+
const startResolution = await withRunnerLockRetry(() => resolveManagedStart({
|
|
365
145
|
prompt: requestedPrompt,
|
|
146
|
+
sessionId,
|
|
366
147
|
ensureSession: true,
|
|
367
|
-
|
|
148
|
+
allowIsolation: false,
|
|
149
|
+
startMaxTokens: DEFAULT_START_MAX_TOKENS,
|
|
368
150
|
}));
|
|
151
|
+
const start = startResolution.startResult;
|
|
369
152
|
|
|
370
153
|
const gate = evaluateRunnerGate({ startResult: start });
|
|
371
154
|
let preflightSummary = null;
|
|
@@ -485,7 +268,7 @@ const runCheckpointCommand = async ({
|
|
|
485
268
|
sessionId,
|
|
486
269
|
event,
|
|
487
270
|
update,
|
|
488
|
-
maxTokens:
|
|
271
|
+
maxTokens: DEFAULT_END_MAX_TOKENS,
|
|
489
272
|
}));
|
|
490
273
|
await recordRunnerMetrics({
|
|
491
274
|
commandName: 'checkpoint',
|