smart-context-mcp 1.6.2 → 1.7.1

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';
@@ -21,244 +27,11 @@ import {
21
27
  buildWorkflowPrompt,
22
28
  evaluateRunnerGate,
23
29
  } from './task-runner/policy.js';
30
+ import { detectClient } from './utils/client-detection.js';
24
31
 
25
- const START_MAX_TOKENS = 350;
26
- const END_MAX_TOKENS = 350;
27
32
  const RUNNER_LOCK_RETRY_ATTEMPTS = 3;
28
33
  const RUNNER_LOCK_RETRY_DELAY_MS = 100;
29
34
 
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
35
  const isRetriableLockError = (error) => {
263
36
  const issue = error?.storageHealth?.issue ?? error?.cause?.storageHealth?.issue ?? null;
264
37
  const retriable = error?.storageHealth?.retriable ?? error?.cause?.storageHealth?.retriable ?? false;
@@ -302,6 +75,15 @@ const recordRunnerMetrics = async ({
302
75
  ?? null;
303
76
  const checkpointPersisted = Boolean(endResult && !endResult.checkpoint?.skipped && !endResult.checkpoint?.blocked);
304
77
  const checkpointSkipped = Boolean(endResult?.checkpoint?.skipped);
78
+ const automaticity = buildTaskRunnerAutomaticity({
79
+ isWorkflowCommand: WORKFLOW_COMMANDS.has(commandName),
80
+ startResult,
81
+ endResult,
82
+ workflowPolicy,
83
+ usedWrapper,
84
+ overheadTokens: Number(result?.overheadTokens ?? 0),
85
+ managedByBaseOrchestrator: WORKFLOW_COMMANDS.has(commandName),
86
+ });
305
87
 
306
88
  await persistMetrics({
307
89
  tool: 'task_runner',
@@ -334,6 +116,7 @@ const recordRunnerMetrics = async ({
334
116
  recommendedPathMode,
335
117
  checkpointPersisted,
336
118
  checkpointSkipped,
119
+ ...automaticity,
337
120
  },
338
121
  timestamp: new Date().toISOString(),
339
122
  });
@@ -359,13 +142,14 @@ const runWorkflowCommand = async ({
359
142
  });
360
143
  const workflowProfile = buildWorkflowPolicyProfile({ commandName });
361
144
 
362
- const start = await withRunnerLockRetry(() => smartTurn({
363
- phase: 'start',
364
- sessionId,
145
+ const startResolution = await withRunnerLockRetry(() => resolveManagedStart({
365
146
  prompt: requestedPrompt,
147
+ sessionId,
366
148
  ensureSession: true,
367
- maxTokens: START_MAX_TOKENS,
149
+ allowIsolation: false,
150
+ startMaxTokens: DEFAULT_START_MAX_TOKENS,
368
151
  }));
152
+ const start = startResolution.startResult;
369
153
 
370
154
  const gate = evaluateRunnerGate({ startResult: start });
371
155
  let preflightSummary = null;
@@ -485,7 +269,7 @@ const runCheckpointCommand = async ({
485
269
  sessionId,
486
270
  event,
487
271
  update,
488
- maxTokens: END_MAX_TOKENS,
272
+ maxTokens: DEFAULT_END_MAX_TOKENS,
489
273
  }));
490
274
  await recordRunnerMetrics({
491
275
  commandName: 'checkpoint',
@@ -631,7 +415,7 @@ const runCleanupCommand = async ({
631
415
 
632
416
  export const runTaskRunner = async ({
633
417
  commandName = 'task',
634
- client = 'generic',
418
+ client = null,
635
419
  prompt = '',
636
420
  sessionId,
637
421
  event,
@@ -653,6 +437,8 @@ export const runTaskRunner = async ({
653
437
  vacuum = false,
654
438
  update = {},
655
439
  } = {}) => {
440
+ const resolvedClient = client ?? detectClient();
441
+
656
442
  if (!RUNNER_COMMANDS.includes(commandName)) {
657
443
  throw new Error(`Unsupported task-runner command: ${commandName}`);
658
444
  }
@@ -660,7 +446,7 @@ export const runTaskRunner = async ({
660
446
  if (WORKFLOW_COMMANDS.has(commandName)) {
661
447
  return runWorkflowCommand({
662
448
  commandName,
663
- client,
449
+ client: resolvedClient,
664
450
  prompt,
665
451
  sessionId,
666
452
  event,
@@ -675,16 +461,16 @@ export const runTaskRunner = async ({
675
461
  }
676
462
 
677
463
  if (commandName === 'doctor') {
678
- return runDoctorCommand({ verifyIntegrity, client });
464
+ return runDoctorCommand({ verifyIntegrity, client: resolvedClient });
679
465
  }
680
466
 
681
467
  if (commandName === 'status') {
682
- return runStatusCommand({ format, maxItems, client });
468
+ return runStatusCommand({ format, maxItems, client: resolvedClient });
683
469
  }
684
470
 
685
471
  if (commandName === 'checkpoint') {
686
472
  return runCheckpointCommand({
687
- client,
473
+ client: resolvedClient,
688
474
  sessionId,
689
475
  event,
690
476
  update,
@@ -693,7 +479,7 @@ export const runTaskRunner = async ({
693
479
 
694
480
  if (commandName === 'cleanup') {
695
481
  return runCleanupCommand({
696
- client,
482
+ client: resolvedClient,
697
483
  cleanupMode,
698
484
  apply,
699
485
  retentionDays,
@@ -0,0 +1,33 @@
1
+ const detectClientFromEnv = () => {
2
+ if (process.env.CURSOR_AGENT === '1') {
3
+ return 'cursor';
4
+ }
5
+
6
+ if (process.env.CLAUDE_AGENT === '1') {
7
+ return 'claude';
8
+ }
9
+
10
+ if (process.env.GEMINI_AGENT === '1') {
11
+ return 'gemini';
12
+ }
13
+
14
+ if (process.env.CODEX_AGENT === '1') {
15
+ return 'codex';
16
+ }
17
+
18
+ return 'generic';
19
+ };
20
+
21
+ let cachedClient = null;
22
+
23
+ export const detectClient = () => {
24
+ if (cachedClient === null) {
25
+ cachedClient = detectClientFromEnv();
26
+ }
27
+
28
+ return cachedClient;
29
+ };
30
+
31
+ export const resetClientDetection = () => {
32
+ cachedClient = null;
33
+ };