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.
@@ -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 { smartSearch } from './tools/smart-search.js';
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 start = await withRunnerLockRetry(() => smartTurn({
363
- phase: 'start',
364
- sessionId,
144
+ const startResolution = await withRunnerLockRetry(() => resolveManagedStart({
365
145
  prompt: requestedPrompt,
146
+ sessionId,
366
147
  ensureSession: true,
367
- maxTokens: START_MAX_TOKENS,
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: END_MAX_TOKENS,
271
+ maxTokens: DEFAULT_END_MAX_TOKENS,
489
272
  }));
490
273
  await recordRunnerMetrics({
491
274
  commandName: 'checkpoint',