sdl-mcp 0.11.13 → 0.12.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.
Files changed (99) hide show
  1. package/README.md +1 -1
  2. package/config/sdlmcp.config.example.json +2 -2
  3. package/config/sdlmcp.config.schema.json +9 -5
  4. package/dist/.tsbuildinfo +1 -1
  5. package/dist/cli/commands/init.js +5 -5
  6. package/dist/cli/commands/tool-actions.d.ts.map +1 -1
  7. package/dist/cli/commands/tool-actions.js +28 -3
  8. package/dist/cli/commands/tool-actions.js.map +1 -1
  9. package/dist/code-mode/action-catalog.js +1 -1
  10. package/dist/code-mode/action-catalog.js.map +1 -1
  11. package/dist/code-mode/descriptions.d.ts.map +1 -1
  12. package/dist/code-mode/descriptions.js +1 -1
  13. package/dist/code-mode/descriptions.js.map +1 -1
  14. package/dist/code-mode/etag-cache.d.ts +9 -2
  15. package/dist/code-mode/etag-cache.d.ts.map +1 -1
  16. package/dist/code-mode/etag-cache.js +45 -2
  17. package/dist/code-mode/etag-cache.js.map +1 -1
  18. package/dist/code-mode/index.d.ts +8 -0
  19. package/dist/code-mode/index.d.ts.map +1 -1
  20. package/dist/code-mode/index.js +3 -1
  21. package/dist/code-mode/index.js.map +1 -1
  22. package/dist/code-mode/manual-generator.js +11 -11
  23. package/dist/code-mode/manual-generator.js.map +1 -1
  24. package/dist/code-mode/types.d.ts +6 -3
  25. package/dist/code-mode/types.d.ts.map +1 -1
  26. package/dist/code-mode/types.js +4 -2
  27. package/dist/code-mode/types.js.map +1 -1
  28. package/dist/code-mode/workflow-executor.d.ts.map +1 -1
  29. package/dist/code-mode/workflow-executor.js +33 -6
  30. package/dist/code-mode/workflow-executor.js.map +1 -1
  31. package/dist/code-mode/workflow-parser.d.ts +1 -1
  32. package/dist/code-mode/workflow-parser.d.ts.map +1 -1
  33. package/dist/code-mode/workflow-parser.js +13 -1
  34. package/dist/code-mode/workflow-parser.js.map +1 -1
  35. package/dist/config/types.d.ts +4 -4
  36. package/dist/config/types.d.ts.map +1 -1
  37. package/dist/config/types.js +5 -5
  38. package/dist/config/types.js.map +1 -1
  39. package/dist/indexer/summary-generator.d.ts +5 -0
  40. package/dist/indexer/summary-generator.d.ts.map +1 -1
  41. package/dist/indexer/summary-generator.js +240 -86
  42. package/dist/indexer/summary-generator.js.map +1 -1
  43. package/dist/indexer/symbol-embedding-text.d.ts +5 -10
  44. package/dist/indexer/symbol-embedding-text.d.ts.map +1 -1
  45. package/dist/indexer/symbol-embedding-text.js +200 -0
  46. package/dist/indexer/symbol-embedding-text.js.map +1 -1
  47. package/dist/mcp/context-response-projection.d.ts +0 -10
  48. package/dist/mcp/context-response-projection.d.ts.map +1 -1
  49. package/dist/mcp/context-response-projection.js +554 -35
  50. package/dist/mcp/context-response-projection.js.map +1 -1
  51. package/dist/mcp/response-compression.d.ts +3 -0
  52. package/dist/mcp/response-compression.d.ts.map +1 -1
  53. package/dist/mcp/response-compression.js +21 -14
  54. package/dist/mcp/response-compression.js.map +1 -1
  55. package/dist/mcp/server-instructions.js +7 -7
  56. package/dist/mcp/server-instructions.js.map +1 -1
  57. package/dist/mcp/token-usage.d.ts +5 -0
  58. package/dist/mcp/token-usage.d.ts.map +1 -1
  59. package/dist/mcp/token-usage.js +17 -0
  60. package/dist/mcp/token-usage.js.map +1 -1
  61. package/dist/mcp/tool-call-formatter.d.ts.map +1 -1
  62. package/dist/mcp/tool-call-formatter.js +4 -6
  63. package/dist/mcp/tool-call-formatter.js.map +1 -1
  64. package/dist/mcp/tools/repo.d.ts.map +1 -1
  65. package/dist/mcp/tools/repo.js +20 -19
  66. package/dist/mcp/tools/repo.js.map +1 -1
  67. package/dist/mcp/tools/response.d.ts.map +1 -1
  68. package/dist/mcp/tools/response.js +17 -2
  69. package/dist/mcp/tools/response.js.map +1 -1
  70. package/dist/mcp/tools/runtime.d.ts.map +1 -1
  71. package/dist/mcp/tools/runtime.js +7 -6
  72. package/dist/mcp/tools/runtime.js.map +1 -1
  73. package/dist/mcp/tools/usage.d.ts.map +1 -1
  74. package/dist/mcp/tools/usage.js +5 -0
  75. package/dist/mcp/tools/usage.js.map +1 -1
  76. package/dist/mcp/tools.d.ts +43 -61
  77. package/dist/mcp/tools.d.ts.map +1 -1
  78. package/dist/mcp/tools.js +50 -18
  79. package/dist/mcp/tools.js.map +1 -1
  80. package/dist/observability/aggregator.d.ts +3 -0
  81. package/dist/observability/aggregator.d.ts.map +1 -1
  82. package/dist/observability/aggregator.js +27 -0
  83. package/dist/observability/aggregator.js.map +1 -1
  84. package/dist/observability/event-tap.d.ts +6 -0
  85. package/dist/observability/event-tap.d.ts.map +1 -1
  86. package/dist/observability/event-tap.js.map +1 -1
  87. package/dist/observability/types.d.ts +9 -0
  88. package/dist/observability/types.d.ts.map +1 -1
  89. package/dist/observability/types.js.map +1 -1
  90. package/dist/runtime/response-artifacts.d.ts +14 -2
  91. package/dist/runtime/response-artifacts.d.ts.map +1 -1
  92. package/dist/runtime/response-artifacts.js +52 -17
  93. package/dist/runtime/response-artifacts.js.map +1 -1
  94. package/dist/server.d.ts +1 -1
  95. package/dist/server.d.ts.map +1 -1
  96. package/dist/server.js +118 -24
  97. package/dist/server.js.map +1 -1
  98. package/package.json +5 -5
  99. package/templates/SDL.md +6 -7
@@ -4,30 +4,32 @@
4
4
  */
5
5
  /** Fields kept in the compact broad response before final model-content projection.
6
6
  * Shared with context-engine.ts for pre-truncation compaction. */
7
- export const BROAD_VISIBLE_FIELDS = new Set([
8
- "taskId",
7
+ const BROAD_MODEL_VISIBLE_FIELDS = new Set([
9
8
  "taskType",
10
9
  "success",
11
10
  "summary",
12
11
  "answer",
13
12
  "finalEvidence",
14
13
  "nextBestAction",
15
- "retrievalEvidence",
16
- "diagnostics",
17
14
  "error",
18
15
  "truncation",
16
+ ]);
17
+ export const BROAD_VISIBLE_FIELDS = new Set([
18
+ ...BROAD_MODEL_VISIBLE_FIELDS,
19
19
  "etag",
20
- "_displayFooter",
20
+ "diagnostics",
21
+ "retrievalEvidence",
21
22
  ]);
22
23
  /** Tool names eligible for context-specific compaction. */
23
24
  const CONTEXT_TOOLS = new Set(["sdl.context"]);
25
+ const HIDDEN_ETAG_MODEL_FIELDS = new Set(["etag", "etagCache", "sliceEtag"]);
24
26
  const ALWAYS_INTERNAL_MODEL_FIELDS = new Set([
25
27
  "_displayFooter",
28
+ "_packedPayload",
26
29
  "_packedStats",
27
30
  "_rawContext",
28
31
  "_tokenUsage",
29
32
  "actionsTaken",
30
- "backupPath",
31
33
  "indexUpdate",
32
34
  "metrics",
33
35
  "packedStats",
@@ -36,12 +38,160 @@ const ALWAYS_INTERNAL_MODEL_FIELDS = new Set([
36
38
  "serverDiagnostics",
37
39
  "taskId",
38
40
  "timings",
41
+ "tokenEstimate",
39
42
  "totalTokens",
40
- "trace",
43
+ "etagCache",
44
+ "sliceEtag",
45
+ ]);
46
+ const NO_OP_FALSE_FIELDS = new Set([
47
+ "truncated",
48
+ "truncation",
49
+ "blastRadiusTruncated",
50
+ ]);
51
+ const NO_OP_NULL_FIELDS = new Set([
52
+ "signal",
41
53
  ]);
54
+ const NO_OP_EMPTY_ARRAY_FIELDS = new Set([
55
+ "warnings",
56
+ "quotingWarnings",
57
+ "serverDriftWarnings",
58
+ "filesSkipped",
59
+ "filesSkippedByReason",
60
+ "staleSymbols",
61
+ "missedIdentifiers",
62
+ ]);
63
+ const COMPACT_FAILURE_TRACE_FIELDS = new Set([
64
+ "stepIndex",
65
+ "fn",
66
+ "action",
67
+ "kind",
68
+ "status",
69
+ "message",
70
+ "fallbackTools",
71
+ ]);
72
+ const COMPACT_DEBUG_MODEL_FIELDS = new Set([
73
+ "aggregateStats",
74
+ "amplifiers",
75
+ "astFingerprint",
76
+ "bytes",
77
+ "bytesWritten",
78
+ "callResolution",
79
+ "clustersHint",
80
+ "confidenceDistribution",
81
+ "created",
82
+ "deduplicated",
83
+ "detailLevel",
84
+ "detailLevelMetadata",
85
+ "diagnostics",
86
+ "entryPoints",
87
+ "expiresAt",
88
+ "hotspots",
89
+ "lease",
90
+ "ledgerVersion",
91
+ "linesWritten",
92
+ "matchCount",
93
+ "matchedLineNumbers",
94
+ "mtimeMs",
95
+ "originalLines",
96
+ "pprBoosts",
97
+ "processes",
98
+ "relevance",
99
+ "returnedLines",
100
+ "searchedStreams",
101
+ "sessionDelta",
102
+ "sha256",
103
+ "shortId",
104
+ "sliceEtag",
105
+ "stderrPreview",
106
+ "stdinBytes",
107
+ "stdinSha256",
108
+ "stdoutPreview",
109
+ "symbolsRecorded",
110
+ "symbolIndex",
111
+ "symptomType",
112
+ "totalBytes",
113
+ "totalLines",
114
+ "truncationWarning",
115
+ "versionId",
116
+ "visibility",
117
+ "whyApproved",
118
+ ]);
119
+ const REPO_ID_VISIBLE_TOOLS = new Set([
120
+ "repo.status",
121
+ "sdl.repo.status",
122
+ "repo.overview",
123
+ "sdl.repo.overview",
124
+ "delta.get",
125
+ "sdl.delta.get",
126
+ "sdl.workflow",
127
+ "workflow",
128
+ ]);
129
+ const PRECONDITION_MODEL_FIELDS = new Set([
130
+ "astFingerprint",
131
+ "mtimeMs",
132
+ "preconditionSnapshot",
133
+ "sha256",
134
+ ]);
135
+ const PRECONDITION_VISIBLE_TOOLS = new Set([
136
+ "search.edit",
137
+ "sdl.search.edit",
138
+ "symbol.edit",
139
+ "sdl.symbol.edit",
140
+ "symbol.getCard",
141
+ "sdl.symbol.getCard",
142
+ ]);
143
+ const FILE_PRECONDITION_OPS = new Set([
144
+ "searchEditPreview",
145
+ "searchEditApply",
146
+ "symbolEditPreview",
147
+ "symbolEditApply",
148
+ "symbolEditApplyNow",
149
+ ]);
150
+ const WORKFLOW_CHILD_TOOL_NAMES = {
151
+ actionSearch: "action.search",
152
+ codeHotPath: "code.getHotPath",
153
+ codeNeedWindow: "code.needWindow",
154
+ codeSkeleton: "code.getSkeleton",
155
+ deltaGet: "delta.get",
156
+ file: "sdl.file",
157
+ sdlFile: "sdl.file",
158
+ fileRead: "file.read",
159
+ fileWrite: "file.write",
160
+ indexRefresh: "index.refresh",
161
+ policyGet: "policy.get",
162
+ policySet: "policy.set",
163
+ prRiskAnalyze: "pr.risk.analyze",
164
+ repoOverview: "repo.overview",
165
+ repoStatus: "repo.status",
166
+ runtimeExecute: "runtime.execute",
167
+ runtimeQueryOutput: "runtime.queryOutput",
168
+ searchEdit: "search.edit",
169
+ sliceBuild: "slice.build",
170
+ sliceRefresh: "slice.refresh",
171
+ symbolEdit: "symbol.edit",
172
+ symbolGetCard: "symbol.getCard",
173
+ symbolGetCards: "symbol.getCards",
174
+ symbolSearch: "symbol.search",
175
+ usageStats: "usage.stats",
176
+ };
42
177
  function isRecord(value) {
43
178
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
44
179
  }
180
+ function stripEtagFieldsForModel(value) {
181
+ if (Array.isArray(value)) {
182
+ return value.map(stripEtagFieldsForModel);
183
+ }
184
+ if (!isRecord(value)) {
185
+ return value;
186
+ }
187
+ const projected = {};
188
+ for (const [key, itemValue] of Object.entries(value)) {
189
+ if (!HIDDEN_ETAG_MODEL_FIELDS.has(key)) {
190
+ projected[key] = stripEtagFieldsForModel(itemValue);
191
+ }
192
+ }
193
+ return projected;
194
+ }
45
195
  function copyIfPresent(source, target, key) {
46
196
  if (key in source) {
47
197
  target[key] = source[key];
@@ -64,27 +214,121 @@ function projectEvidenceForModel(value) {
64
214
  return projected;
65
215
  });
66
216
  }
217
+ function normalizedDetail(value) {
218
+ return value === "full" || value === "standard" ? value : "compact";
219
+ }
67
220
  function modelOptionsFromArgs(args) {
68
221
  const options = isRecord(args.options) ? args.options : {};
69
222
  return {
70
- includeDiagnostics: args.includeDiagnostics === true,
223
+ detail: normalizedDetail(args.detail ?? options.detail),
224
+ includeDiagnostics: args.includeDiagnostics === true
225
+ || options.includeDiagnostics === true,
71
226
  includeRetrievalEvidence: args.includeRetrievalEvidence === true
72
227
  || options.includeRetrievalEvidence === true,
228
+ includeTelemetry: args.includeTelemetry === true
229
+ || options.includeTelemetry === true,
230
+ includeTrace: args.trace !== undefined,
231
+ includeProcesses: args.includeProcesses === true,
232
+ includeResolutionMetadata: args.includeResolutionMetadata === true,
233
+ fileOp: typeof args.op === "string" ? args.op : undefined,
73
234
  };
74
235
  }
236
+ function isFullDetail(options) {
237
+ return options.detail === "full";
238
+ }
239
+ function isNoOpTruncationObject(value) {
240
+ if (!isRecord(value)) {
241
+ return false;
242
+ }
243
+ if (value.truncated === true
244
+ || value.stdoutTruncated === true
245
+ || value.stderrTruncated === true) {
246
+ return false;
247
+ }
248
+ return Object.keys(value).every((key) => key === "truncated"
249
+ || key === "stdoutTruncated"
250
+ || key === "stderrTruncated"
251
+ || key === "totalStdoutBytes"
252
+ || key === "totalStderrBytes");
253
+ }
254
+ function shouldDropNoOpField(key, value) {
255
+ if (value === false && NO_OP_FALSE_FIELDS.has(key)) {
256
+ return true;
257
+ }
258
+ if (key === "truncation" && isNoOpTruncationObject(value)) {
259
+ return true;
260
+ }
261
+ if (value === null && NO_OP_NULL_FIELDS.has(key)) {
262
+ return true;
263
+ }
264
+ if (Array.isArray(value)
265
+ && value.length === 0
266
+ && NO_OP_EMPTY_ARRAY_FIELDS.has(key)) {
267
+ return true;
268
+ }
269
+ if (key === "rollback" && isRecord(value)) {
270
+ return Object.keys(value).length === 0 || value.triggered === false;
271
+ }
272
+ return false;
273
+ }
274
+ function keepsPreconditionField(toolName, options) {
275
+ return PRECONDITION_VISIBLE_TOOLS.has(toolName)
276
+ || ((toolName === "sdl.file" || toolName === "file")
277
+ && typeof options.fileOp === "string"
278
+ && FILE_PRECONDITION_OPS.has(options.fileOp));
279
+ }
75
280
  function shouldKeepModelField(toolName, key, options) {
76
- if (toolName === "sdl.workflow" && key === "trace") {
281
+ if (isFullDetail(options)) {
282
+ return true;
283
+ }
284
+ if (key === "trace") {
285
+ return toolName === "sdl.workflow" && options.includeTrace;
286
+ }
287
+ if (PRECONDITION_MODEL_FIELDS.has(key) && keepsPreconditionField(toolName, options)) {
77
288
  return true;
78
289
  }
79
290
  if (ALWAYS_INTERNAL_MODEL_FIELDS.has(key)) {
80
291
  return false;
81
292
  }
293
+ if (key === "repoId") {
294
+ return REPO_ID_VISIBLE_TOOLS.has(toolName);
295
+ }
296
+ if (key === "processes") {
297
+ return options.includeProcesses;
298
+ }
299
+ if (key === "callResolution") {
300
+ return options.includeResolutionMetadata;
301
+ }
302
+ if (COMPACT_DEBUG_MODEL_FIELDS.has(key)) {
303
+ return false;
304
+ }
305
+ if (toolName === "slice.build" && key === "budget") {
306
+ return false;
307
+ }
308
+ if ((toolName === "symbol.search" || toolName === "sdl.symbol.search") && key === "symbols") {
309
+ return false;
310
+ }
82
311
  if (key === "diagnostics") {
83
312
  return options.includeDiagnostics;
84
313
  }
85
314
  if (key === "retrievalEvidence") {
86
315
  return options.includeRetrievalEvidence;
87
316
  }
317
+ if (key === "estimatedTokens"
318
+ || key === "originalLines"
319
+ || key === "matchedLineNumbers"
320
+ || key === "generatedAt"
321
+ || key === "tokenMetrics") {
322
+ return false;
323
+ }
324
+ if (options.detail === "compact"
325
+ && !options.includeTelemetry
326
+ && (key === "prefetchStats"
327
+ || key === "strategyMetrics"
328
+ || key === "topStrategies"
329
+ || key === "healthComponents")) {
330
+ return false;
331
+ }
88
332
  return true;
89
333
  }
90
334
  function projectGenericValueForModel(toolName, value, options) {
@@ -96,6 +340,12 @@ function projectGenericValueForModel(toolName, value, options) {
96
340
  }
97
341
  const projected = {};
98
342
  for (const [key, itemValue] of Object.entries(value)) {
343
+ if (HIDDEN_ETAG_MODEL_FIELDS.has(key)) {
344
+ continue;
345
+ }
346
+ if (shouldDropNoOpField(key, itemValue)) {
347
+ continue;
348
+ }
99
349
  if (key === "diagnostics" && options.includeDiagnostics) {
100
350
  projected[key] = itemValue;
101
351
  continue;
@@ -106,7 +356,7 @@ function projectGenericValueForModel(toolName, value, options) {
106
356
  }
107
357
  if (key === "policyDecision") {
108
358
  const decision = isRecord(itemValue) ? itemValue : {};
109
- if (Array.isArray(decision.deniedReasons)) {
359
+ if (Array.isArray(decision.deniedReasons) && decision.deniedReasons.length > 0) {
110
360
  projected.policyDecision = { deniedReasons: decision.deniedReasons };
111
361
  }
112
362
  continue;
@@ -114,7 +364,11 @@ function projectGenericValueForModel(toolName, value, options) {
114
364
  if (!shouldKeepModelField(toolName, key, options)) {
115
365
  continue;
116
366
  }
117
- projected[key] = projectGenericValueForModel(toolName, itemValue, options);
367
+ const projectedValue = projectGenericValueForModel(toolName, itemValue, options);
368
+ if (isRecord(projectedValue) && Object.keys(projectedValue).length === 0) {
369
+ continue;
370
+ }
371
+ projected[key] = projectedValue;
118
372
  }
119
373
  return projected;
120
374
  }
@@ -131,8 +385,9 @@ function projectContextResultForModel(result, options) {
131
385
  }
132
386
  copyIfPresent(result, projected, "nextBestAction");
133
387
  copyIfPresent(result, projected, "error");
134
- copyIfPresent(result, projected, "truncation");
135
- copyIfPresent(result, projected, "etag");
388
+ if (result.truncation !== false) {
389
+ copyIfPresent(result, projected, "truncation");
390
+ }
136
391
  if (options.includeRetrievalEvidence) {
137
392
  copyIfPresent(result, projected, "retrievalEvidence");
138
393
  }
@@ -141,57 +396,302 @@ function projectContextResultForModel(result, options) {
141
396
  }
142
397
  return projected;
143
398
  }
399
+ function projectCompactFailureTrace(value) {
400
+ if (!isRecord(value)) {
401
+ return value;
402
+ }
403
+ const projected = {};
404
+ for (const [key, itemValue] of Object.entries(value)) {
405
+ if (COMPACT_FAILURE_TRACE_FIELDS.has(key)) {
406
+ projected[key] = itemValue;
407
+ }
408
+ }
409
+ return projected;
410
+ }
411
+ function workflowStepArgsAt(workflowArgs, stepIndex) {
412
+ const steps = workflowArgs.steps;
413
+ if (!Array.isArray(steps)) {
414
+ return undefined;
415
+ }
416
+ const step = steps[stepIndex];
417
+ return isRecord(step) ? step.args : undefined;
418
+ }
419
+ function inferWorkflowFileOp(childToolName, result, args) {
420
+ if (isRecord(args) && typeof args.op === "string") {
421
+ return args.op;
422
+ }
423
+ if (childToolName === "sdl.file" && isRecord(result) && "preconditionSnapshot" in result) {
424
+ return "searchEditPreview";
425
+ }
426
+ return undefined;
427
+ }
428
+ function projectWorkflowStepResultForModel(fn, result, options, args) {
429
+ const childToolName = typeof fn === "string"
430
+ ? WORKFLOW_CHILD_TOOL_NAMES[fn] ?? "sdl.workflow"
431
+ : "sdl.workflow";
432
+ const childArgOptions = isRecord(args) ? modelOptionsFromArgs(args) : undefined;
433
+ const fileOp = inferWorkflowFileOp(childToolName, result, args);
434
+ if (isFullDetail(options)) {
435
+ return stripEtagFieldsForModel(result);
436
+ }
437
+ const childOptions = {
438
+ ...options,
439
+ detail: "compact",
440
+ includeDiagnostics: false,
441
+ includeRetrievalEvidence: false,
442
+ includeTelemetry: false,
443
+ includeTrace: false,
444
+ ...(childArgOptions ?? {}),
445
+ fileOp: fileOp ?? childArgOptions?.fileOp ?? options.fileOp,
446
+ };
447
+ if (isFullDetail(childOptions)) {
448
+ return stripEtagFieldsForModel(result);
449
+ }
450
+ if (childToolName === "repo.status") {
451
+ if (childOptions.detail === "compact" && !childOptions.includeTelemetry) {
452
+ return isRecord(result) ? projectRepoStatusForModel(result, childOptions) : result;
453
+ }
454
+ return projectGenericValueForModel(childToolName, result, childOptions);
455
+ }
456
+ if (childToolName === "action.search" && childOptions.detail === "compact") {
457
+ return isRecord(result) ? projectActionSearchForModel(result) : result;
458
+ }
459
+ if (childToolName === "usage.stats") {
460
+ return isRecord(result) ? projectUsageStatsForModel(result) : result;
461
+ }
462
+ return projectGenericValueForModel(childToolName, result, childOptions);
463
+ }
464
+ function projectWorkflowResultForModel(result, options, workflowArgs = {}) {
465
+ const includeWorkflowTelemetry = options.includeTrace
466
+ || options.includeDiagnostics
467
+ || options.includeTelemetry
468
+ || isFullDetail(options);
469
+ const projected = {};
470
+ const rawResults = Array.isArray(result.results) ? result.results : [];
471
+ projected.results = rawResults.map((item, index) => {
472
+ if (!isRecord(item)) {
473
+ return item;
474
+ }
475
+ const status = typeof item.status === "string" ? item.status : "ok";
476
+ const stepIndex = typeof item.stepIndex === "number" ? item.stepIndex : index;
477
+ const stepArgs = workflowStepArgsAt(workflowArgs, stepIndex) ?? item.args;
478
+ if (status === "ok") {
479
+ const successStep = { fn: item.fn };
480
+ if (includeWorkflowTelemetry) {
481
+ copyIfPresent(item, successStep, "stepIndex");
482
+ copyIfPresent(item, successStep, "tokens");
483
+ copyIfPresent(item, successStep, "durationMs");
484
+ copyIfPresent(item, successStep, "status");
485
+ }
486
+ if ("result" in item) {
487
+ successStep.result = projectWorkflowStepResultForModel(item.fn, item.result, options, stepArgs);
488
+ }
489
+ if (item.truncatedResponse) {
490
+ successStep.truncatedResponse = item.truncatedResponse;
491
+ }
492
+ return successStep;
493
+ }
494
+ const failureStep = { fn: item.fn, status };
495
+ copyIfPresent(item, failureStep, "error");
496
+ copyIfPresent(item, failureStep, "fallbackTools");
497
+ copyIfPresent(item, failureStep, "blockedByStep");
498
+ copyIfPresent(item, failureStep, "blockedByFn");
499
+ copyIfPresent(item, failureStep, "blockedByError");
500
+ if ("failureTrace" in item) {
501
+ failureStep.failureTrace = projectCompactFailureTrace(item.failureTrace);
502
+ }
503
+ if ("result" in item && item.result !== null && item.result !== undefined) {
504
+ failureStep.result = projectWorkflowStepResultForModel(item.fn, item.result, options, stepArgs);
505
+ }
506
+ return failureStep;
507
+ });
508
+ if (includeWorkflowTelemetry) {
509
+ copyIfPresent(result, projected, "durationMs");
510
+ copyIfPresent(result, projected, "totalTokens");
511
+ }
512
+ if (result.truncated === true) {
513
+ projected.truncated = true;
514
+ }
515
+ if (options.includeTrace) {
516
+ copyIfPresent(result, projected, "trace");
517
+ }
518
+ if (options.includeDiagnostics) {
519
+ copyIfPresent(result, projected, "diagnostics");
520
+ }
521
+ return projected;
522
+ }
523
+ function projectUsageStatsForModel(result) {
524
+ const projected = {};
525
+ copyIfPresent(result, projected, "formattedSummary");
526
+ return projected;
527
+ }
528
+ function projectDerivedStateForModel(value) {
529
+ if (!isRecord(value)) {
530
+ return value;
531
+ }
532
+ const projected = {};
533
+ copyIfPresent(value, projected, "stale");
534
+ for (const key of [
535
+ "clustersDirty",
536
+ "processesDirty",
537
+ "algorithmsDirty",
538
+ "summariesDirty",
539
+ "embeddingsDirty",
540
+ ]) {
541
+ if (value[key] === true) {
542
+ projected[key] = true;
543
+ }
544
+ }
545
+ copyIfPresent(value, projected, "lastError");
546
+ copyIfPresent(value, projected, "nextBestAction");
547
+ return projected;
548
+ }
549
+ function watcherNeedsAttention(value) {
550
+ return value.running === false
551
+ || value.stale === true
552
+ || (typeof value.errors === "number" && value.errors > 0)
553
+ || Boolean(value.fallbackReason);
554
+ }
555
+ function projectWatcherHealthForModel(value) {
556
+ if (!isRecord(value) || !watcherNeedsAttention(value)) {
557
+ return undefined;
558
+ }
559
+ const projected = {};
560
+ for (const key of [
561
+ "enabled",
562
+ "running",
563
+ "provider",
564
+ "fallbackReason",
565
+ "errors",
566
+ "queueDepth",
567
+ "stale",
568
+ ]) {
569
+ copyIfPresent(value, projected, key);
570
+ }
571
+ return projected;
572
+ }
573
+ function projectRepoStatusForModel(result, options) {
574
+ const projected = {};
575
+ for (const key of [
576
+ "repoId",
577
+ "rootPath",
578
+ "latestVersionId",
579
+ "filesIndexed",
580
+ "symbolsIndexed",
581
+ "lastIndexedAt",
582
+ ]) {
583
+ copyIfPresent(result, projected, key);
584
+ }
585
+ if (result.healthAvailable === true || result.healthScore !== undefined) {
586
+ copyIfPresent(result, projected, "healthAvailable");
587
+ copyIfPresent(result, projected, "healthScore");
588
+ }
589
+ if ("derivedState" in result) {
590
+ projected.derivedState = projectDerivedStateForModel(result.derivedState);
591
+ }
592
+ const compactWatcher = projectWatcherHealthForModel(result.watcherHealth);
593
+ if (compactWatcher !== undefined) {
594
+ projected.watcherHealth = compactWatcher;
595
+ }
596
+ copyIfPresent(result, projected, "nextBestAction");
597
+ if (options.includeDiagnostics) {
598
+ copyIfPresent(result, projected, "diagnostics");
599
+ }
600
+ if (options.includeRetrievalEvidence) {
601
+ copyIfPresent(result, projected, "retrievalEvidence");
602
+ }
603
+ return projected;
604
+ }
605
+ function compactSchemaSummary(value) {
606
+ if (!Array.isArray(value)) {
607
+ return value;
608
+ }
609
+ return value.map((field) => {
610
+ if (!isRecord(field)) {
611
+ return field;
612
+ }
613
+ const projected = {};
614
+ for (const key of ["name", "type", "required", "default", "enumValues", "subFields"]) {
615
+ if (key === "subFields") {
616
+ if ("subFields" in field) {
617
+ projected.subFields = compactSchemaSummary(field.subFields);
618
+ }
619
+ }
620
+ else {
621
+ copyIfPresent(field, projected, key);
622
+ }
623
+ }
624
+ return projected;
625
+ });
626
+ }
627
+ function projectActionSearchForModel(result) {
628
+ const projected = {};
629
+ if (Array.isArray(result.actions)) {
630
+ projected.actions = result.actions.map((action) => {
631
+ if (!isRecord(action)) {
632
+ return action;
633
+ }
634
+ const compact = {};
635
+ copyIfPresent(action, compact, "action");
636
+ copyIfPresent(action, compact, "fn");
637
+ copyIfPresent(action, compact, "requiredParams");
638
+ copyIfPresent(action, compact, "disabled");
639
+ copyIfPresent(action, compact, "disabledReason");
640
+ if (isRecord(action.schemaSummary) && Array.isArray(action.schemaSummary.fields)) {
641
+ compact.schemaSummary = {
642
+ fields: compactSchemaSummary(action.schemaSummary.fields),
643
+ };
644
+ }
645
+ return compact;
646
+ });
647
+ }
648
+ copyIfPresent(result, projected, "summary");
649
+ copyIfPresent(result, projected, "total");
650
+ copyIfPresent(result, projected, "hasMore");
651
+ copyIfPresent(result, projected, "nextCursor");
652
+ copyIfPresent(result, projected, "disabledHint");
653
+ copyIfPresent(result, projected, "schemaHint");
654
+ return projected;
655
+ }
144
656
  /**
145
657
  * Returns true when the result looks like a broad context response that
146
658
  * should be compacted.
147
659
  */
148
660
  export function isBroadContextResult(toolName, result) {
149
- if (!CONTEXT_TOOLS.has(toolName))
661
+ if (!CONTEXT_TOOLS.has(toolName)) {
150
662
  return false;
151
- if (!isRecord(result))
663
+ }
664
+ if (!isRecord(result)) {
152
665
  return false;
666
+ }
153
667
  const r = result;
154
- // Broad results have actionsTaken + path + metrics (precise mode strips some of these
155
- // but still has actionsTaken). The key differentiator: broad mode has `answer` field.
156
- // Also check it's not an error result (which should pass through unchanged).
157
668
  return ("taskId" in r &&
158
669
  "actionsTaken" in r &&
159
670
  "answer" in r &&
160
671
  r.success !== undefined);
161
672
  }
162
- /**
163
- * Project a broad context result to its compact internal response form.
164
- * Returns the original result unchanged if it doesn't qualify.
165
- */
166
673
  export function projectBroadContextResult(toolName, result) {
167
- if (!isBroadContextResult(toolName, result))
674
+ if (!isBroadContextResult(toolName, result)) {
168
675
  return result;
676
+ }
169
677
  const r = result;
170
678
  const projected = {};
171
679
  for (const key of Object.keys(r)) {
172
- if (BROAD_VISIBLE_FIELDS.has(key)) {
680
+ if (BROAD_MODEL_VISIBLE_FIELDS.has(key)) {
173
681
  projected[key] = r[key];
174
682
  }
175
683
  }
176
684
  return projected;
177
685
  }
178
- /**
179
- * Project tool responses before token accounting while keeping the internal raw
180
- * baseline hint that `computeTokenUsage()` needs.
181
- */
182
686
  export function projectContextResultForUsageAccounting(toolName, result, args = {}) {
183
687
  const projected = projectToolResultForModelContent(toolName, result, args);
184
- if (!isRecord(projected) || projected === result) {
688
+ if (!isRecord(projected)) {
185
689
  return result;
186
690
  }
187
- const accountingResult = projected;
188
691
  if ("_rawContext" in result) {
189
- return {
190
- ...accountingResult,
191
- _rawContext: result._rawContext,
192
- };
692
+ return { ...projected, _rawContext: result._rawContext };
193
693
  }
194
- return accountingResult;
694
+ return projected;
195
695
  }
196
696
  /**
197
697
  * Project the payload serialized into MCP text content for the model/user.
@@ -203,9 +703,28 @@ export function projectToolResultForModelContent(toolName, result, args = {}) {
203
703
  return result;
204
704
  }
205
705
  const options = modelOptionsFromArgs(args);
706
+ if (toolName === "sdl.workflow" || toolName === "workflow") {
707
+ return projectWorkflowResultForModel(result, options, args);
708
+ }
709
+ if (isFullDetail(options)) {
710
+ return result;
711
+ }
206
712
  if (CONTEXT_TOOLS.has(toolName) && ("answer" in result || "finalEvidence" in result)) {
207
713
  return projectContextResultForModel(result, options);
208
714
  }
715
+ if (toolName === "usage.stats" || toolName === "sdl.usage.stats") {
716
+ return projectUsageStatsForModel(result);
717
+ }
718
+ if (toolName === "repo.status" || toolName === "sdl.repo.status") {
719
+ if (options.detail === "compact" && !options.includeTelemetry) {
720
+ return projectRepoStatusForModel(result, options);
721
+ }
722
+ }
723
+ if (toolName === "action.search" || toolName === "sdl.action.search") {
724
+ if (options.detail === "compact") {
725
+ return projectActionSearchForModel(result);
726
+ }
727
+ }
209
728
  return projectGenericValueForModel(toolName, result, options);
210
729
  }
211
730
  //# sourceMappingURL=context-response-projection.js.map