smart-context-mcp 1.4.0 → 1.6.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.
@@ -0,0 +1,631 @@
1
+ import { persistMetrics } from './metrics.js';
2
+ import { countTokens } from './tokenCounter.js';
3
+ import { TASK_RUNNER_QUALITY_ANALYTICS_KIND } from './analytics/product-quality.js';
4
+ import { runHeadlessWrapper } from './orchestration/headless-wrapper.js';
5
+ import { smartContext } from './tools/smart-context.js';
6
+ import { smartDoctor } from './tools/smart-doctor.js';
7
+ import { smartSearch } from './tools/smart-search.js';
8
+ import { smartStatus } from './tools/smart-status.js';
9
+ import { smartSummary } from './tools/smart-summary.js';
10
+ import { smartTurn } from './tools/smart-turn.js';
11
+ import {
12
+ RUNNER_COMMANDS,
13
+ SPECIALIZED_WORKFLOW_COMMANDS,
14
+ WORKFLOW_COMMANDS,
15
+ WORKFLOW_DEFINITIONS,
16
+ buildCleanupPlan,
17
+ buildWorkflowPolicyProfile,
18
+ buildRunnerBlockedResult,
19
+ buildWorkflowPrompt,
20
+ evaluateRunnerGate,
21
+ } from './task-runner/policy.js';
22
+
23
+ const START_MAX_TOKENS = 350;
24
+ const END_MAX_TOKENS = 350;
25
+
26
+ const normalizeWhitespace = (value) => String(value ?? '').replace(/\s+/g, ' ').trim();
27
+ const truncate = (value, maxLength = 160) => {
28
+ const normalized = normalizeWhitespace(value);
29
+ if (normalized.length <= maxLength) {
30
+ return normalized;
31
+ }
32
+
33
+ if (maxLength <= 3) {
34
+ return '';
35
+ }
36
+
37
+ return `${normalized.slice(0, maxLength - 3)}...`;
38
+ };
39
+
40
+ const asArray = (value) => Array.isArray(value) ? value : [];
41
+ const uniqueCompact = (values) => [...new Set(asArray(values).map((value) => normalizeWhitespace(value)).filter(Boolean))];
42
+
43
+ const extractPreflightTopFiles = (preflightResult) => {
44
+ if (!preflightResult) {
45
+ return [];
46
+ }
47
+
48
+ if (preflightResult.tool === 'smart_context') {
49
+ return uniqueCompact(asArray(preflightResult.result?.context).map((item) => item?.file).filter(Boolean)).slice(0, 3);
50
+ }
51
+
52
+ if (preflightResult.tool === 'smart_search') {
53
+ return uniqueCompact(asArray(preflightResult.result?.topFiles).map((item) => {
54
+ if (typeof item === 'string') {
55
+ return item;
56
+ }
57
+ return item?.file ?? item?.path ?? '';
58
+ })).slice(0, 3);
59
+ }
60
+
61
+ return [];
62
+ };
63
+
64
+ const extractPreflightHints = (preflightResult) => {
65
+ if (!preflightResult) {
66
+ return [];
67
+ }
68
+
69
+ if (preflightResult.tool === 'smart_context') {
70
+ return uniqueCompact(preflightResult.result?.hints).slice(0, 2);
71
+ }
72
+
73
+ if (preflightResult.tool === 'smart_search') {
74
+ const totalMatches = Number(preflightResult.result?.totalMatches ?? 0);
75
+ if (totalMatches > 0) {
76
+ return [`${totalMatches} search match(es) surfaced for the workflow target`];
77
+ }
78
+ }
79
+
80
+ return [];
81
+ };
82
+
83
+ const buildPreflightSummary = (preflightResult) => {
84
+ if (!preflightResult) {
85
+ return null;
86
+ }
87
+
88
+ const topFiles = extractPreflightTopFiles(preflightResult);
89
+ const hints = extractPreflightHints(preflightResult);
90
+
91
+ return {
92
+ tool: preflightResult.tool,
93
+ topFiles,
94
+ hints,
95
+ totalMatches: Number(preflightResult.result?.totalMatches ?? 0),
96
+ };
97
+ };
98
+
99
+ const buildPreflightTask = ({ workflowProfile, prompt, startResult }) => {
100
+ const normalizedPrompt = normalizeWhitespace(prompt);
101
+ const persistedNextStep = normalizeWhitespace(startResult?.summary?.nextStep);
102
+ const currentFocus = normalizeWhitespace(startResult?.summary?.currentFocus);
103
+ const refreshedTopFiles = uniqueCompact(startResult?.refreshedContext?.topFiles).slice(0, 3);
104
+
105
+ if (workflowProfile.commandName === 'continue' || workflowProfile.commandName === 'resume') {
106
+ if (persistedNextStep) {
107
+ return persistedNextStep;
108
+ }
109
+ if (currentFocus) {
110
+ return currentFocus;
111
+ }
112
+ }
113
+
114
+ if (workflowProfile.commandName === 'task' && currentFocus && persistedNextStep) {
115
+ return `${currentFocus}. ${persistedNextStep}`;
116
+ }
117
+
118
+ if (normalizedPrompt) {
119
+ return normalizedPrompt;
120
+ }
121
+
122
+ if (refreshedTopFiles.length > 0) {
123
+ return `Inspect ${refreshedTopFiles.join(', ')} and continue the persisted task`;
124
+ }
125
+
126
+ return workflowProfile.label;
127
+ };
128
+
129
+ const runWorkflowPreflight = async ({ workflowProfile, prompt, startResult }) => {
130
+ const preflight = workflowProfile.preflight;
131
+ if (!preflight) {
132
+ return null;
133
+ }
134
+
135
+ const preflightTask = buildPreflightTask({ workflowProfile, prompt, startResult });
136
+
137
+ if (preflight.tool === 'smart_context') {
138
+ const result = await smartContext({
139
+ task: preflightTask,
140
+ detail: preflight.detail ?? 'minimal',
141
+ include: preflight.include ?? ['hints'],
142
+ maxTokens: preflight.maxTokens ?? 1200,
143
+ });
144
+ return {
145
+ tool: 'smart_context',
146
+ request: {
147
+ task: preflightTask,
148
+ detail: preflight.detail ?? 'minimal',
149
+ include: preflight.include ?? ['hints'],
150
+ maxTokens: preflight.maxTokens ?? 1200,
151
+ },
152
+ result,
153
+ };
154
+ }
155
+
156
+ if (preflight.tool === 'smart_search') {
157
+ const result = await smartSearch({
158
+ query: preflightTask,
159
+ intent: preflight.intent ?? workflowProfile.workflowIntent,
160
+ });
161
+ return {
162
+ tool: 'smart_search',
163
+ request: {
164
+ query: preflightTask,
165
+ intent: preflight.intent ?? workflowProfile.workflowIntent,
166
+ },
167
+ result,
168
+ };
169
+ }
170
+
171
+ return null;
172
+ };
173
+
174
+ const buildContinuityGuidance = ({ startResult }) => {
175
+ const continuityState = startResult?.continuity?.state ?? 'unknown';
176
+ const lines = [
177
+ `- Continuity: ${continuityState}`,
178
+ ];
179
+ const nextStep = normalizeWhitespace(startResult?.summary?.nextStep);
180
+ const currentFocus = normalizeWhitespace(startResult?.summary?.currentFocus);
181
+ const refreshedTopFiles = uniqueCompact(startResult?.refreshedContext?.topFiles).slice(0, 3);
182
+ const recommendedNextTools = asArray(startResult?.recommendedPath?.nextTools)
183
+ .map((tool) => normalizeWhitespace(tool))
184
+ .filter(Boolean)
185
+ .slice(0, 3);
186
+
187
+ if (currentFocus) {
188
+ lines.push(`- Persisted focus: ${truncate(currentFocus, 140)}`);
189
+ }
190
+
191
+ if (nextStep) {
192
+ lines.push(`- Persisted next step: ${truncate(nextStep, 140)}`);
193
+ }
194
+
195
+ if (refreshedTopFiles.length > 0) {
196
+ lines.push(`- Refreshed top files: ${refreshedTopFiles.join(', ')}`);
197
+ }
198
+
199
+ if (recommendedNextTools.length > 0) {
200
+ lines.push(`- smart_turn suggested: ${recommendedNextTools.join(' -> ')}`);
201
+ }
202
+
203
+ if (startResult?.isolatedSession) {
204
+ lines.push('- Session handling: smart_turn already isolated this work from the previous session; revalidate before assuming old focus.');
205
+ } else if (continuityState === 'aligned' || continuityState === 'resume') {
206
+ lines.push('- Session handling: reuse the active session context and stay close to the persisted next step unless the task proves otherwise.');
207
+ } else if (continuityState === 'possible_shift' || continuityState === 'context_mismatch') {
208
+ lines.push('- Session handling: treat this as a shifted slice, validate the working set early, and avoid silent context reuse.');
209
+ }
210
+
211
+ return lines;
212
+ };
213
+
214
+ const buildWorkflowPromptWithPolicy = ({ prompt, workflowProfile, preflightSummary, startResult }) => {
215
+ const lines = [
216
+ prompt,
217
+ '',
218
+ 'Workflow policy:',
219
+ `- Mode: ${workflowProfile.policyMode}`,
220
+ `- Intent: ${workflowProfile.workflowIntent}`,
221
+ `- Prefer this tool order: ${workflowProfile.nextTools.join(' -> ')}`,
222
+ ];
223
+
224
+ if (workflowProfile.checkpointStrategy) {
225
+ lines.push(`- Checkpoint rule: ${workflowProfile.checkpointStrategy}`);
226
+ }
227
+
228
+ lines.push(...buildContinuityGuidance({ startResult }));
229
+
230
+ if (preflightSummary?.tool) {
231
+ lines.push(`- Preflight: ${preflightSummary.tool}`);
232
+ }
233
+
234
+ if (preflightSummary?.topFiles?.length) {
235
+ lines.push(`- Focus files: ${preflightSummary.topFiles.join(', ')}`);
236
+ }
237
+
238
+ if (preflightSummary?.hints?.length) {
239
+ lines.push(`- Signals: ${preflightSummary.hints.map((hint) => truncate(hint, 120)).join(' | ')}`);
240
+ }
241
+
242
+ return lines.join('\n');
243
+ };
244
+
245
+ const buildWorkflowPolicyPayload = ({ commandName, workflowProfile, preflightSummary }) => ({
246
+ commandName,
247
+ label: workflowProfile.label,
248
+ policyMode: workflowProfile.policyMode,
249
+ intent: workflowProfile.workflowIntent,
250
+ specialized: workflowProfile.specialized,
251
+ nextTools: [...workflowProfile.nextTools],
252
+ checkpointStrategy: workflowProfile.checkpointStrategy,
253
+ preflight: preflightSummary,
254
+ });
255
+
256
+ const recordRunnerMetrics = async ({
257
+ commandName,
258
+ client,
259
+ result,
260
+ usedWrapper = false,
261
+ blocked = false,
262
+ doctorIssued = false,
263
+ }) => {
264
+ const startResult = result?.start ?? result?.startResult ?? null;
265
+ const endResult = result?.end ?? (result?.phase === 'end' ? result : null);
266
+ const workflowPolicy = result?.workflowPolicy ?? null;
267
+ const preflight = workflowPolicy?.preflight ?? null;
268
+ const mutationBlocked = Boolean(result?.mutationSafety?.blocked ?? startResult?.mutationSafety?.blocked ?? endResult?.mutationSafety?.blocked);
269
+ const storageIssue = result?.storageHealth?.issue ?? startResult?.storageHealth?.issue ?? endResult?.storageHealth?.issue ?? 'ok';
270
+ const recommendedPathMode = result?.recommendedPath?.mode
271
+ ?? startResult?.recommendedPath?.mode
272
+ ?? endResult?.recommendedPath?.mode
273
+ ?? null;
274
+ const checkpointPersisted = Boolean(endResult && !endResult.checkpoint?.skipped && !endResult.checkpoint?.blocked);
275
+ const checkpointSkipped = Boolean(endResult?.checkpoint?.skipped);
276
+
277
+ await persistMetrics({
278
+ tool: 'task_runner',
279
+ action: commandName,
280
+ sessionId: result?.sessionId ?? result?.start?.sessionId ?? null,
281
+ rawTokens: 0,
282
+ compressedTokens: countTokens(JSON.stringify(result ?? {})),
283
+ savedTokens: 0,
284
+ savingsPct: 0,
285
+ metadata: {
286
+ analyticsKind: TASK_RUNNER_QUALITY_ANALYTICS_KIND,
287
+ client,
288
+ usedWrapper,
289
+ blocked,
290
+ doctorIssued,
291
+ dryRun: Boolean(result?.dryRun),
292
+ allowDegraded: Boolean(result?.allowDegraded),
293
+ isWorkflowCommand: WORKFLOW_COMMANDS.has(commandName),
294
+ specializedWorkflow: SPECIALIZED_WORKFLOW_COMMANDS.has(commandName),
295
+ workflowIntent: workflowPolicy?.intent ?? null,
296
+ workflowPolicyMode: workflowPolicy?.policyMode ?? null,
297
+ workflowNextToolsCount: workflowPolicy?.nextTools?.length ?? 0,
298
+ workflowHasCheckpointRule: Boolean(workflowPolicy?.checkpointStrategy),
299
+ workflowPreflightTool: preflight?.tool ?? null,
300
+ workflowPreflightTopFiles: preflight?.topFiles?.length ?? 0,
301
+ workflowPreflightHints: preflight?.hints?.length ?? 0,
302
+ continuityState: startResult?.continuity?.state ?? null,
303
+ mutationBlocked,
304
+ storageIssue,
305
+ recommendedPathMode,
306
+ checkpointPersisted,
307
+ checkpointSkipped,
308
+ },
309
+ timestamp: new Date().toISOString(),
310
+ });
311
+ };
312
+
313
+ const runWorkflowCommand = async ({
314
+ commandName,
315
+ client,
316
+ prompt,
317
+ sessionId,
318
+ event,
319
+ stdinPrompt = false,
320
+ dryRun = false,
321
+ streamOutput = false,
322
+ command = '',
323
+ args = [],
324
+ runCommand,
325
+ allowDegraded = false,
326
+ }) => {
327
+ const requestedPrompt = buildWorkflowPrompt({
328
+ commandName,
329
+ prompt,
330
+ });
331
+ const workflowProfile = buildWorkflowPolicyProfile({ commandName });
332
+
333
+ const start = await smartTurn({
334
+ phase: 'start',
335
+ sessionId,
336
+ prompt: requestedPrompt,
337
+ ensureSession: true,
338
+ maxTokens: START_MAX_TOKENS,
339
+ });
340
+
341
+ const gate = evaluateRunnerGate({ startResult: start });
342
+ let preflightSummary = null;
343
+
344
+ if (!gate.requiresDoctor || allowDegraded) {
345
+ const preflightResult = await runWorkflowPreflight({
346
+ workflowProfile,
347
+ prompt: requestedPrompt,
348
+ startResult: start,
349
+ });
350
+ preflightSummary = buildPreflightSummary(preflightResult);
351
+ }
352
+
353
+ const workflowPolicy = buildWorkflowPolicyPayload({
354
+ commandName,
355
+ workflowProfile,
356
+ preflightSummary,
357
+ });
358
+ const effectivePrompt = buildWorkflowPromptWithPolicy({
359
+ prompt: requestedPrompt,
360
+ workflowProfile,
361
+ preflightSummary,
362
+ startResult: start,
363
+ });
364
+
365
+ if (gate.requiresDoctor && !allowDegraded) {
366
+ const doctor = await smartDoctor();
367
+ const blockedResult = buildRunnerBlockedResult({
368
+ commandName,
369
+ client,
370
+ prompt: effectivePrompt,
371
+ startResult: start,
372
+ gate,
373
+ doctorResult: doctor,
374
+ allowDegraded,
375
+ workflowPolicy,
376
+ });
377
+ await recordRunnerMetrics({
378
+ commandName,
379
+ client,
380
+ result: blockedResult,
381
+ usedWrapper: false,
382
+ blocked: true,
383
+ doctorIssued: true,
384
+ });
385
+ return blockedResult;
386
+ }
387
+
388
+ const wrapperResult = await runHeadlessWrapper({
389
+ client,
390
+ prompt: effectivePrompt,
391
+ command,
392
+ args,
393
+ sessionId: start.sessionId ?? sessionId,
394
+ event: event ?? WORKFLOW_DEFINITIONS[commandName]?.defaultEvent,
395
+ stdinPrompt,
396
+ dryRun,
397
+ streamOutput,
398
+ runCommand,
399
+ preparedStartResult: start,
400
+ });
401
+
402
+ const result = {
403
+ success: true,
404
+ usedWrapper: true,
405
+ ...wrapperResult,
406
+ command: commandName,
407
+ client,
408
+ prompt: effectivePrompt,
409
+ requestedPrompt,
410
+ gate,
411
+ workflowPolicy,
412
+ };
413
+
414
+ await recordRunnerMetrics({
415
+ commandName,
416
+ client,
417
+ result,
418
+ usedWrapper: true,
419
+ blocked: false,
420
+ doctorIssued: false,
421
+ });
422
+ return result;
423
+ };
424
+
425
+ const runDoctorCommand = async ({ verifyIntegrity = true, client }) => {
426
+ const result = await smartDoctor({ verifyIntegrity });
427
+ await recordRunnerMetrics({
428
+ commandName: 'doctor',
429
+ client,
430
+ result,
431
+ usedWrapper: false,
432
+ blocked: result.overall === 'error',
433
+ doctorIssued: true,
434
+ });
435
+ return result;
436
+ };
437
+
438
+ const runStatusCommand = async ({ format = 'compact', maxItems = 10, client }) => {
439
+ const result = await smartStatus({ format, maxItems });
440
+ await recordRunnerMetrics({
441
+ commandName: 'status',
442
+ client,
443
+ result,
444
+ });
445
+ return result;
446
+ };
447
+
448
+ const runCheckpointCommand = async ({
449
+ client,
450
+ sessionId,
451
+ event = 'milestone',
452
+ update = {},
453
+ }) => {
454
+ const result = await smartTurn({
455
+ phase: 'end',
456
+ sessionId,
457
+ event,
458
+ update,
459
+ maxTokens: END_MAX_TOKENS,
460
+ });
461
+ await recordRunnerMetrics({
462
+ commandName: 'checkpoint',
463
+ client,
464
+ result,
465
+ });
466
+ return result;
467
+ };
468
+
469
+ const runCleanupCommand = async ({
470
+ client,
471
+ cleanupMode = 'compact',
472
+ apply = false,
473
+ retentionDays = 30,
474
+ keepLatestEventsPerSession = 20,
475
+ keepLatestMetrics = 1000,
476
+ vacuum = false,
477
+ }) => {
478
+ const plan = buildCleanupPlan({
479
+ mode: cleanupMode,
480
+ apply,
481
+ retentionDays,
482
+ keepLatestEventsPerSession,
483
+ keepLatestMetrics,
484
+ vacuum,
485
+ });
486
+
487
+ if (cleanupMode === 'legacy') {
488
+ const result = await smartSummary({
489
+ action: 'cleanup_legacy',
490
+ apply,
491
+ });
492
+ const payload = {
493
+ command: 'cleanup',
494
+ cleanupMode,
495
+ plan,
496
+ result,
497
+ };
498
+ await recordRunnerMetrics({
499
+ commandName: 'cleanup',
500
+ client,
501
+ result: payload,
502
+ });
503
+ return payload;
504
+ }
505
+
506
+ if (cleanupMode === 'all') {
507
+ const compact = await smartSummary({
508
+ action: 'compact',
509
+ retentionDays,
510
+ keepLatestEventsPerSession,
511
+ keepLatestMetrics,
512
+ vacuum,
513
+ });
514
+ const legacy = await smartSummary({
515
+ action: 'cleanup_legacy',
516
+ apply,
517
+ });
518
+ const payload = {
519
+ command: 'cleanup',
520
+ cleanupMode,
521
+ plan,
522
+ result: {
523
+ compact,
524
+ legacy,
525
+ },
526
+ };
527
+ await recordRunnerMetrics({
528
+ commandName: 'cleanup',
529
+ client,
530
+ result: payload,
531
+ });
532
+ return payload;
533
+ }
534
+
535
+ const result = await smartSummary({
536
+ action: 'compact',
537
+ retentionDays,
538
+ keepLatestEventsPerSession,
539
+ keepLatestMetrics,
540
+ vacuum,
541
+ });
542
+ const payload = {
543
+ command: 'cleanup',
544
+ cleanupMode,
545
+ plan,
546
+ result,
547
+ };
548
+ await recordRunnerMetrics({
549
+ commandName: 'cleanup',
550
+ client,
551
+ result: payload,
552
+ });
553
+ return payload;
554
+ };
555
+
556
+ export const runTaskRunner = async ({
557
+ commandName = 'task',
558
+ client = 'generic',
559
+ prompt = '',
560
+ sessionId,
561
+ event,
562
+ stdinPrompt = false,
563
+ dryRun = false,
564
+ streamOutput = false,
565
+ command = '',
566
+ args = [],
567
+ runCommand,
568
+ allowDegraded = false,
569
+ verifyIntegrity = true,
570
+ format = 'compact',
571
+ maxItems = 10,
572
+ cleanupMode = 'compact',
573
+ apply = false,
574
+ retentionDays = 30,
575
+ keepLatestEventsPerSession = 20,
576
+ keepLatestMetrics = 1000,
577
+ vacuum = false,
578
+ update = {},
579
+ } = {}) => {
580
+ if (!RUNNER_COMMANDS.includes(commandName)) {
581
+ throw new Error(`Unsupported task-runner command: ${commandName}`);
582
+ }
583
+
584
+ if (WORKFLOW_COMMANDS.has(commandName)) {
585
+ return runWorkflowCommand({
586
+ commandName,
587
+ client,
588
+ prompt,
589
+ sessionId,
590
+ event,
591
+ stdinPrompt,
592
+ dryRun,
593
+ streamOutput,
594
+ command,
595
+ args,
596
+ runCommand,
597
+ allowDegraded,
598
+ });
599
+ }
600
+
601
+ if (commandName === 'doctor') {
602
+ return runDoctorCommand({ verifyIntegrity, client });
603
+ }
604
+
605
+ if (commandName === 'status') {
606
+ return runStatusCommand({ format, maxItems, client });
607
+ }
608
+
609
+ if (commandName === 'checkpoint') {
610
+ return runCheckpointCommand({
611
+ client,
612
+ sessionId,
613
+ event,
614
+ update,
615
+ });
616
+ }
617
+
618
+ if (commandName === 'cleanup') {
619
+ return runCleanupCommand({
620
+ client,
621
+ cleanupMode,
622
+ apply,
623
+ retentionDays,
624
+ keepLatestEventsPerSession,
625
+ keepLatestMetrics,
626
+ vacuum,
627
+ });
628
+ }
629
+
630
+ throw new Error(`Command not implemented: ${commandName}`);
631
+ };