@skein-code/cli 0.3.21 → 0.3.23
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/README.md +12 -3
- package/dist/cli.js +220 -41
- package/dist/cli.js.map +1 -1
- package/docs/ARCHITECTURE.md +12 -7
- package/docs/NEXT_STEPS.md +32 -15
- package/docs/PRODUCT_BENCHMARK.md +4 -3
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -57,8 +57,10 @@ inspectable index for code retrieval and no retrieval service dependency.
|
|
|
57
57
|
artifact for bounded readback.
|
|
58
58
|
- **Measurable token economy:** every model request records a privacy-safe
|
|
59
59
|
receipt for stable, dynamic, history, retrieval, tool-result, and tool-schema
|
|
60
|
-
estimates, alongside actual provider usage when available.
|
|
61
|
-
|
|
60
|
+
estimates, alongside actual provider usage when available. OpenAI, Anthropic,
|
|
61
|
+
and Gemini cache/reasoning counters are normalized into session totals and
|
|
62
|
+
structured events when the provider reports them. Receipts retain only counts
|
|
63
|
+
and runtime decisions, never prompt or source content.
|
|
62
64
|
- **Reversible work:** Skein snapshots affected files before mutation without
|
|
63
65
|
touching your Git history.
|
|
64
66
|
- **Resumable by default:** conversations, tasks, usage, and changed files live
|
|
@@ -94,7 +96,7 @@ To build, verify, and install a local package artifact from this checkout:
|
|
|
94
96
|
|
|
95
97
|
```bash
|
|
96
98
|
npm run verify:package -- --output-dir artifacts/package
|
|
97
|
-
npm install -g ./artifacts/package/skein-code-cli-0.3.
|
|
99
|
+
npm install -g ./artifacts/package/skein-code-cli-0.3.23.tgz
|
|
98
100
|
```
|
|
99
101
|
|
|
100
102
|
To install the published package from npm:
|
|
@@ -212,6 +214,13 @@ only to the originating session, expire after seven days, and are removed when
|
|
|
212
214
|
that session is deleted. A receipt marked `source-truncated` is honest about
|
|
213
215
|
bytes already omitted by the producing tool; those bytes cannot be recovered.
|
|
214
216
|
|
|
217
|
+
Normal chat runs discover MCP servers lazily. Startup exposes only a compact
|
|
218
|
+
`mcp_activate` catalog of configured servers; selecting one connects that
|
|
219
|
+
server, runs remote discovery, and loads at most eight request-relevant schemas
|
|
220
|
+
into the live tool registry. All activated MCP tools remain `network`
|
|
221
|
+
operations. The explicit `skein mcp status` diagnostic still connects
|
|
222
|
+
configured servers eagerly.
|
|
223
|
+
|
|
215
224
|
At 96 columns and wider, a fresh session uses a two-column welcome surface. The
|
|
216
225
|
left side keeps Skein's brand, workspace path, and next actions close to the
|
|
217
226
|
composer; the right side reports the actual model, mode, local index file/chunk
|
package/dist/cli.js
CHANGED
|
@@ -220,7 +220,7 @@ function isSqliteBusy(error) {
|
|
|
220
220
|
// package.json
|
|
221
221
|
var package_default = {
|
|
222
222
|
name: "@skein-code/cli",
|
|
223
|
-
version: "0.3.
|
|
223
|
+
version: "0.3.23",
|
|
224
224
|
description: "A context-first, model-agnostic coding agent with an auditable terminal workspace.",
|
|
225
225
|
type: "module",
|
|
226
226
|
license: "MIT",
|
|
@@ -236,6 +236,12 @@ var package_default = {
|
|
|
236
236
|
},
|
|
237
237
|
skein: {
|
|
238
238
|
releaseNotes: [
|
|
239
|
+
"Normal chat startup now exposes a compact mcp_activate catalog instead of eagerly connecting MCP servers",
|
|
240
|
+
"MCP activation connects one selected server, discovers remote tools, and loads at most eight relevant schemas",
|
|
241
|
+
"Explicit MCP diagnostics can still connect eagerly while all MCP activation and remote calls remain network operations",
|
|
242
|
+
"OpenAI, Anthropic, and Gemini usage now normalizes cached input, cache-write input, and reasoning tokens when reported",
|
|
243
|
+
"Token Ledger, session totals, and structured usage events preserve provider cache and reasoning counts without prompt content",
|
|
244
|
+
"Streaming, non-streaming, explicit-zero, and legacy-session compatibility paths are covered without claiming cache activation",
|
|
239
245
|
"Failed configured verification output yields bounded current-run diagnostic ranking without persisting paths",
|
|
240
246
|
"Diagnostic hints cannot create zero-relevance hits and are cleared on success or the next agent run",
|
|
241
247
|
"Python absolute and relative module imports now contribute deterministic offline graph adjacency",
|
|
@@ -1935,6 +1941,7 @@ var agentTeamConfigSchema = z2.object({
|
|
|
1935
1941
|
var mcpServerSchema = z2.object({
|
|
1936
1942
|
enabled: z2.boolean().optional(),
|
|
1937
1943
|
transport: z2.enum(["stdio", "http"]).optional(),
|
|
1944
|
+
description: z2.string().min(1).max(500).optional(),
|
|
1938
1945
|
command: z2.string().min(1).max(512).optional(),
|
|
1939
1946
|
args: z2.array(z2.string().max(4e3)).max(64).optional(),
|
|
1940
1947
|
cwd: z2.string().max(4e3).optional(),
|
|
@@ -5349,10 +5356,7 @@ var AnthropicProvider = class {
|
|
|
5349
5356
|
name: block.name ?? "unknown",
|
|
5350
5357
|
arguments: safeJsonArguments(block.input)
|
|
5351
5358
|
})),
|
|
5352
|
-
usage:
|
|
5353
|
-
...data.usage?.input_tokens !== void 0 ? { inputTokens: data.usage.input_tokens } : {},
|
|
5354
|
-
...data.usage?.output_tokens !== void 0 ? { outputTokens: data.usage.output_tokens } : {}
|
|
5355
|
-
},
|
|
5359
|
+
usage: normalizeAnthropicUsage(data.usage),
|
|
5356
5360
|
...data.stop_reason ? { stopReason: data.stop_reason } : {}
|
|
5357
5361
|
};
|
|
5358
5362
|
}
|
|
@@ -5393,6 +5397,8 @@ var AnthropicProvider = class {
|
|
|
5393
5397
|
let content = "";
|
|
5394
5398
|
let inputTokens;
|
|
5395
5399
|
let outputTokens;
|
|
5400
|
+
let cachedInputTokens;
|
|
5401
|
+
let cacheWriteInputTokens;
|
|
5396
5402
|
let stopReason;
|
|
5397
5403
|
const calls = /* @__PURE__ */ new Map();
|
|
5398
5404
|
for await (const event of parseServerSentEvents(response)) {
|
|
@@ -5402,6 +5408,13 @@ var AnthropicProvider = class {
|
|
|
5402
5408
|
}
|
|
5403
5409
|
if (chunk.usage?.input_tokens !== void 0) inputTokens = chunk.usage.input_tokens;
|
|
5404
5410
|
if (chunk.usage?.output_tokens !== void 0) outputTokens = chunk.usage.output_tokens;
|
|
5411
|
+
const usage = chunk.usage ?? chunk.message?.usage;
|
|
5412
|
+
if (usage?.cache_read_input_tokens !== void 0) {
|
|
5413
|
+
cachedInputTokens = usage.cache_read_input_tokens;
|
|
5414
|
+
}
|
|
5415
|
+
if (usage?.cache_creation_input_tokens !== void 0) {
|
|
5416
|
+
cacheWriteInputTokens = usage.cache_creation_input_tokens;
|
|
5417
|
+
}
|
|
5405
5418
|
if (chunk.delta?.stop_reason) stopReason = chunk.delta.stop_reason;
|
|
5406
5419
|
const index = chunk.index ?? 0;
|
|
5407
5420
|
if (chunk.type === "content_block_start" && chunk.content_block?.type === "text") {
|
|
@@ -5440,7 +5453,9 @@ var AnthropicProvider = class {
|
|
|
5440
5453
|
})),
|
|
5441
5454
|
usage: {
|
|
5442
5455
|
...inputTokens !== void 0 ? { inputTokens } : {},
|
|
5443
|
-
...outputTokens !== void 0 ? { outputTokens } : {}
|
|
5456
|
+
...outputTokens !== void 0 ? { outputTokens } : {},
|
|
5457
|
+
...cachedInputTokens !== void 0 ? { cachedInputTokens } : {},
|
|
5458
|
+
...cacheWriteInputTokens !== void 0 ? { cacheWriteInputTokens } : {}
|
|
5444
5459
|
},
|
|
5445
5460
|
...stopReason ? { stopReason } : {}
|
|
5446
5461
|
}
|
|
@@ -5456,13 +5471,18 @@ function normalizeAnthropicResponse(data) {
|
|
|
5456
5471
|
name: block.name ?? "unknown",
|
|
5457
5472
|
arguments: safeJsonArguments(block.input)
|
|
5458
5473
|
})),
|
|
5459
|
-
usage:
|
|
5460
|
-
...data.usage?.input_tokens !== void 0 ? { inputTokens: data.usage.input_tokens } : {},
|
|
5461
|
-
...data.usage?.output_tokens !== void 0 ? { outputTokens: data.usage.output_tokens } : {}
|
|
5462
|
-
},
|
|
5474
|
+
usage: normalizeAnthropicUsage(data.usage),
|
|
5463
5475
|
...data.stop_reason ? { stopReason: data.stop_reason } : {}
|
|
5464
5476
|
};
|
|
5465
5477
|
}
|
|
5478
|
+
function normalizeAnthropicUsage(usage) {
|
|
5479
|
+
return {
|
|
5480
|
+
...usage?.input_tokens !== void 0 ? { inputTokens: usage.input_tokens } : {},
|
|
5481
|
+
...usage?.output_tokens !== void 0 ? { outputTokens: usage.output_tokens } : {},
|
|
5482
|
+
...usage?.cache_read_input_tokens !== void 0 ? { cachedInputTokens: usage.cache_read_input_tokens } : {},
|
|
5483
|
+
...usage?.cache_creation_input_tokens !== void 0 ? { cacheWriteInputTokens: usage.cache_creation_input_tokens } : {}
|
|
5484
|
+
};
|
|
5485
|
+
}
|
|
5466
5486
|
function toAnthropicMessage(message2) {
|
|
5467
5487
|
if (message2.role === "tool") {
|
|
5468
5488
|
return {
|
|
@@ -5536,10 +5556,7 @@ var GeminiProvider = class {
|
|
|
5536
5556
|
name: part.functionCall?.name ?? "unknown",
|
|
5537
5557
|
arguments: safeJsonArguments(part.functionCall?.args)
|
|
5538
5558
|
})),
|
|
5539
|
-
usage:
|
|
5540
|
-
...data.usageMetadata?.promptTokenCount !== void 0 ? { inputTokens: data.usageMetadata.promptTokenCount } : {},
|
|
5541
|
-
...data.usageMetadata?.candidatesTokenCount !== void 0 ? { outputTokens: data.usageMetadata.candidatesTokenCount } : {}
|
|
5542
|
-
},
|
|
5559
|
+
usage: normalizeGeminiUsage(data.usageMetadata),
|
|
5543
5560
|
...candidate?.finishReason ? { stopReason: candidate.finishReason } : {}
|
|
5544
5561
|
};
|
|
5545
5562
|
}
|
|
@@ -5579,6 +5596,8 @@ var GeminiProvider = class {
|
|
|
5579
5596
|
let content = "";
|
|
5580
5597
|
let inputTokens;
|
|
5581
5598
|
let outputTokens;
|
|
5599
|
+
let cachedInputTokens;
|
|
5600
|
+
let reasoningTokens;
|
|
5582
5601
|
let stopReason;
|
|
5583
5602
|
const toolCalls = [];
|
|
5584
5603
|
for await (const event of parseServerSentEvents(response)) {
|
|
@@ -5590,6 +5609,12 @@ var GeminiProvider = class {
|
|
|
5590
5609
|
if (chunk.usageMetadata?.candidatesTokenCount !== void 0) {
|
|
5591
5610
|
outputTokens = chunk.usageMetadata.candidatesTokenCount;
|
|
5592
5611
|
}
|
|
5612
|
+
if (chunk.usageMetadata?.cachedContentTokenCount !== void 0) {
|
|
5613
|
+
cachedInputTokens = chunk.usageMetadata.cachedContentTokenCount;
|
|
5614
|
+
}
|
|
5615
|
+
if (chunk.usageMetadata?.thoughtsTokenCount !== void 0) {
|
|
5616
|
+
reasoningTokens = chunk.usageMetadata.thoughtsTokenCount;
|
|
5617
|
+
}
|
|
5593
5618
|
const candidate = chunk.candidates?.[0];
|
|
5594
5619
|
if (candidate?.finishReason) stopReason = candidate.finishReason;
|
|
5595
5620
|
for (const part of candidate?.content?.parts ?? []) {
|
|
@@ -5613,7 +5638,9 @@ var GeminiProvider = class {
|
|
|
5613
5638
|
toolCalls,
|
|
5614
5639
|
usage: {
|
|
5615
5640
|
...inputTokens !== void 0 ? { inputTokens } : {},
|
|
5616
|
-
...outputTokens !== void 0 ? { outputTokens } : {}
|
|
5641
|
+
...outputTokens !== void 0 ? { outputTokens } : {},
|
|
5642
|
+
...cachedInputTokens !== void 0 ? { cachedInputTokens } : {},
|
|
5643
|
+
...reasoningTokens !== void 0 ? { reasoningTokens } : {}
|
|
5617
5644
|
},
|
|
5618
5645
|
...stopReason ? { stopReason } : {}
|
|
5619
5646
|
}
|
|
@@ -5630,13 +5657,18 @@ function normalizeGeminiResponse(data) {
|
|
|
5630
5657
|
name: part.functionCall?.name ?? "unknown",
|
|
5631
5658
|
arguments: safeJsonArguments(part.functionCall?.args)
|
|
5632
5659
|
})),
|
|
5633
|
-
usage:
|
|
5634
|
-
...data.usageMetadata?.promptTokenCount !== void 0 ? { inputTokens: data.usageMetadata.promptTokenCount } : {},
|
|
5635
|
-
...data.usageMetadata?.candidatesTokenCount !== void 0 ? { outputTokens: data.usageMetadata.candidatesTokenCount } : {}
|
|
5636
|
-
},
|
|
5660
|
+
usage: normalizeGeminiUsage(data.usageMetadata),
|
|
5637
5661
|
...candidate?.finishReason ? { stopReason: candidate.finishReason } : {}
|
|
5638
5662
|
};
|
|
5639
5663
|
}
|
|
5664
|
+
function normalizeGeminiUsage(usage) {
|
|
5665
|
+
return {
|
|
5666
|
+
...usage?.promptTokenCount !== void 0 ? { inputTokens: usage.promptTokenCount } : {},
|
|
5667
|
+
...usage?.candidatesTokenCount !== void 0 ? { outputTokens: usage.candidatesTokenCount } : {},
|
|
5668
|
+
...usage?.cachedContentTokenCount !== void 0 ? { cachedInputTokens: usage.cachedContentTokenCount } : {},
|
|
5669
|
+
...usage?.thoughtsTokenCount !== void 0 ? { reasoningTokens: usage.thoughtsTokenCount } : {}
|
|
5670
|
+
};
|
|
5671
|
+
}
|
|
5640
5672
|
function toGeminiMessage(message2) {
|
|
5641
5673
|
if (message2.role === "tool") {
|
|
5642
5674
|
return {
|
|
@@ -5764,6 +5796,8 @@ var OpenAIProvider = class {
|
|
|
5764
5796
|
let content = "";
|
|
5765
5797
|
let inputTokens;
|
|
5766
5798
|
let outputTokens;
|
|
5799
|
+
let cachedInputTokens;
|
|
5800
|
+
let reasoningTokens;
|
|
5767
5801
|
let stopReason;
|
|
5768
5802
|
const calls = /* @__PURE__ */ new Map();
|
|
5769
5803
|
for await (const event of parseServerSentEvents(response)) {
|
|
@@ -5771,6 +5805,12 @@ var OpenAIProvider = class {
|
|
|
5771
5805
|
const chunk = JSON.parse(event.data);
|
|
5772
5806
|
if (chunk.usage?.prompt_tokens !== void 0) inputTokens = chunk.usage.prompt_tokens;
|
|
5773
5807
|
if (chunk.usage?.completion_tokens !== void 0) outputTokens = chunk.usage.completion_tokens;
|
|
5808
|
+
if (chunk.usage?.prompt_tokens_details?.cached_tokens !== void 0) {
|
|
5809
|
+
cachedInputTokens = chunk.usage.prompt_tokens_details.cached_tokens;
|
|
5810
|
+
}
|
|
5811
|
+
if (chunk.usage?.completion_tokens_details?.reasoning_tokens !== void 0) {
|
|
5812
|
+
reasoningTokens = chunk.usage.completion_tokens_details.reasoning_tokens;
|
|
5813
|
+
}
|
|
5774
5814
|
const choice = chunk.choices?.[0];
|
|
5775
5815
|
if (choice?.finish_reason) stopReason = choice.finish_reason;
|
|
5776
5816
|
const delta = choice?.delta;
|
|
@@ -5798,7 +5838,9 @@ var OpenAIProvider = class {
|
|
|
5798
5838
|
})),
|
|
5799
5839
|
usage: {
|
|
5800
5840
|
...inputTokens !== void 0 ? { inputTokens } : {},
|
|
5801
|
-
...outputTokens !== void 0 ? { outputTokens } : {}
|
|
5841
|
+
...outputTokens !== void 0 ? { outputTokens } : {},
|
|
5842
|
+
...cachedInputTokens !== void 0 ? { cachedInputTokens } : {},
|
|
5843
|
+
...reasoningTokens !== void 0 ? { reasoningTokens } : {}
|
|
5802
5844
|
},
|
|
5803
5845
|
...stopReason ? { stopReason } : {}
|
|
5804
5846
|
}
|
|
@@ -5816,13 +5858,18 @@ function normalizeOpenAIResponse(data) {
|
|
|
5816
5858
|
name: call.function?.name ?? "unknown",
|
|
5817
5859
|
arguments: safeJsonArguments(call.function?.arguments)
|
|
5818
5860
|
})),
|
|
5819
|
-
usage:
|
|
5820
|
-
...data.usage?.prompt_tokens !== void 0 ? { inputTokens: data.usage.prompt_tokens } : {},
|
|
5821
|
-
...data.usage?.completion_tokens !== void 0 ? { outputTokens: data.usage.completion_tokens } : {}
|
|
5822
|
-
},
|
|
5861
|
+
usage: normalizeOpenAIUsage(data.usage),
|
|
5823
5862
|
...choice?.finish_reason ? { stopReason: choice.finish_reason } : {}
|
|
5824
5863
|
};
|
|
5825
5864
|
}
|
|
5865
|
+
function normalizeOpenAIUsage(usage) {
|
|
5866
|
+
return {
|
|
5867
|
+
...usage?.prompt_tokens !== void 0 ? { inputTokens: usage.prompt_tokens } : {},
|
|
5868
|
+
...usage?.completion_tokens !== void 0 ? { outputTokens: usage.completion_tokens } : {},
|
|
5869
|
+
...usage?.prompt_tokens_details?.cached_tokens !== void 0 ? { cachedInputTokens: usage.prompt_tokens_details.cached_tokens } : {},
|
|
5870
|
+
...usage?.completion_tokens_details?.reasoning_tokens !== void 0 ? { reasoningTokens: usage.completion_tokens_details.reasoning_tokens } : {}
|
|
5871
|
+
};
|
|
5872
|
+
}
|
|
5826
5873
|
function toOpenAIMessage(message2) {
|
|
5827
5874
|
if (message2.role === "tool") {
|
|
5828
5875
|
return {
|
|
@@ -6399,6 +6446,9 @@ var sessionSchema = z5.object({
|
|
|
6399
6446
|
outputSource: z5.enum(["actual", "estimated", "mixed", "unknown"]).optional(),
|
|
6400
6447
|
actualInputTokens: z5.number().nonnegative().optional(),
|
|
6401
6448
|
actualOutputTokens: z5.number().nonnegative().optional(),
|
|
6449
|
+
actualCachedInputTokens: z5.number().nonnegative().optional(),
|
|
6450
|
+
actualCacheWriteInputTokens: z5.number().nonnegative().optional(),
|
|
6451
|
+
actualReasoningTokens: z5.number().nonnegative().optional(),
|
|
6402
6452
|
estimatedInputTokens: z5.number().nonnegative().optional(),
|
|
6403
6453
|
estimatedOutputTokens: z5.number().nonnegative().optional()
|
|
6404
6454
|
}).strict(),
|
|
@@ -6419,7 +6469,10 @@ var sessionSchema = z5.object({
|
|
|
6419
6469
|
}).strict(),
|
|
6420
6470
|
actual: z5.object({
|
|
6421
6471
|
inputTokens: z5.number().nonnegative().optional(),
|
|
6422
|
-
outputTokens: z5.number().nonnegative().optional()
|
|
6472
|
+
outputTokens: z5.number().nonnegative().optional(),
|
|
6473
|
+
cachedInputTokens: z5.number().nonnegative().optional(),
|
|
6474
|
+
cacheWriteInputTokens: z5.number().nonnegative().optional(),
|
|
6475
|
+
reasoningTokens: z5.number().nonnegative().optional()
|
|
6423
6476
|
}).strict(),
|
|
6424
6477
|
inputSource: z5.enum(["actual", "estimated"]),
|
|
6425
6478
|
outputSource: z5.enum(["actual", "estimated"]),
|
|
@@ -10830,6 +10883,9 @@ var AgentRunner = class {
|
|
|
10830
10883
|
const { inputTokens, outputTokens } = turnUsage;
|
|
10831
10884
|
const actualInputTokens = validTokenCount(response.usage?.inputTokens);
|
|
10832
10885
|
const actualOutputTokens = validTokenCount(response.usage?.outputTokens);
|
|
10886
|
+
const actualCachedInputTokens = validTokenCount(response.usage?.cachedInputTokens);
|
|
10887
|
+
const actualCacheWriteInputTokens = validTokenCount(response.usage?.cacheWriteInputTokens);
|
|
10888
|
+
const actualReasoningTokens = validTokenCount(response.usage?.reasoningTokens);
|
|
10833
10889
|
const receipt = recordTokenLedger(this.session, {
|
|
10834
10890
|
requestId: userMessage.id,
|
|
10835
10891
|
turn,
|
|
@@ -10840,7 +10896,10 @@ var AgentRunner = class {
|
|
|
10840
10896
|
},
|
|
10841
10897
|
actual: {
|
|
10842
10898
|
...actualInputTokens === void 0 ? {} : { inputTokens: actualInputTokens },
|
|
10843
|
-
...actualOutputTokens === void 0 ? {} : { outputTokens: actualOutputTokens }
|
|
10899
|
+
...actualOutputTokens === void 0 ? {} : { outputTokens: actualOutputTokens },
|
|
10900
|
+
...actualCachedInputTokens === void 0 ? {} : { cachedInputTokens: actualCachedInputTokens },
|
|
10901
|
+
...actualCacheWriteInputTokens === void 0 ? {} : { cacheWriteInputTokens: actualCacheWriteInputTokens },
|
|
10902
|
+
...actualReasoningTokens === void 0 ? {} : { reasoningTokens: actualReasoningTokens }
|
|
10844
10903
|
},
|
|
10845
10904
|
inputSource: actualInputTokens === void 0 ? "estimated" : "actual",
|
|
10846
10905
|
outputSource: actualOutputTokens === void 0 ? "estimated" : "actual",
|
|
@@ -10859,7 +10918,10 @@ var AgentRunner = class {
|
|
|
10859
10918
|
outputSource: this.session.usage.outputSource ?? "unknown",
|
|
10860
10919
|
actual: {
|
|
10861
10920
|
inputTokens: this.session.usage.actualInputTokens ?? 0,
|
|
10862
|
-
outputTokens: this.session.usage.actualOutputTokens ?? 0
|
|
10921
|
+
outputTokens: this.session.usage.actualOutputTokens ?? 0,
|
|
10922
|
+
...this.session.usage.actualCachedInputTokens === void 0 ? {} : { cachedInputTokens: this.session.usage.actualCachedInputTokens },
|
|
10923
|
+
...this.session.usage.actualCacheWriteInputTokens === void 0 ? {} : { cacheWriteInputTokens: this.session.usage.actualCacheWriteInputTokens },
|
|
10924
|
+
...this.session.usage.actualReasoningTokens === void 0 ? {} : { reasoningTokens: this.session.usage.actualReasoningTokens }
|
|
10863
10925
|
},
|
|
10864
10926
|
estimated: {
|
|
10865
10927
|
inputTokens: this.session.usage.estimatedInputTokens ?? 0,
|
|
@@ -11560,6 +11622,18 @@ function recordTokenUsage(session, providerUsage, estimatedInputTokens, estimate
|
|
|
11560
11622
|
} else {
|
|
11561
11623
|
session.usage.estimatedOutputTokens = (session.usage.estimatedOutputTokens ?? 0) + outputTokens;
|
|
11562
11624
|
}
|
|
11625
|
+
const cachedInputActual = validTokenCount(providerUsage?.cachedInputTokens);
|
|
11626
|
+
const cacheWriteInputActual = validTokenCount(providerUsage?.cacheWriteInputTokens);
|
|
11627
|
+
const reasoningActual = validTokenCount(providerUsage?.reasoningTokens);
|
|
11628
|
+
if (cachedInputActual !== void 0) {
|
|
11629
|
+
session.usage.actualCachedInputTokens = (session.usage.actualCachedInputTokens ?? 0) + cachedInputActual;
|
|
11630
|
+
}
|
|
11631
|
+
if (cacheWriteInputActual !== void 0) {
|
|
11632
|
+
session.usage.actualCacheWriteInputTokens = (session.usage.actualCacheWriteInputTokens ?? 0) + cacheWriteInputActual;
|
|
11633
|
+
}
|
|
11634
|
+
if (reasoningActual !== void 0) {
|
|
11635
|
+
session.usage.actualReasoningTokens = (session.usage.actualReasoningTokens ?? 0) + reasoningActual;
|
|
11636
|
+
}
|
|
11563
11637
|
session.usage.inputSource = mergeMeasurementSource(
|
|
11564
11638
|
priorInputSource,
|
|
11565
11639
|
inputActual === void 0 ? "estimated" : "actual",
|
|
@@ -19870,12 +19944,24 @@ var MAX_TOOLS_PER_SERVER = 256;
|
|
|
19870
19944
|
var MAX_LIST_PAGES = 16;
|
|
19871
19945
|
var DEFAULT_CONNECT_TIMEOUT = 12e3;
|
|
19872
19946
|
var DEFAULT_TOOL_TIMEOUT = 6e4;
|
|
19947
|
+
var LAZY_SCHEMA_LIMIT = 8;
|
|
19873
19948
|
var McpManager = class {
|
|
19874
19949
|
constructor(config, options = {}) {
|
|
19875
19950
|
this.config = config;
|
|
19876
19951
|
this.options = options;
|
|
19877
|
-
|
|
19952
|
+
const entries = Object.entries(config.servers ?? {});
|
|
19953
|
+
for (const [index, [name, server]] of entries.entries()) {
|
|
19878
19954
|
const transport = server.transport ?? "stdio";
|
|
19955
|
+
if (index >= MAX_SERVERS) {
|
|
19956
|
+
this.statuses.set(name, {
|
|
19957
|
+
name,
|
|
19958
|
+
state: "error",
|
|
19959
|
+
transport,
|
|
19960
|
+
toolCount: 0,
|
|
19961
|
+
error: `MCP server limit exceeded (maximum ${MAX_SERVERS})`
|
|
19962
|
+
});
|
|
19963
|
+
continue;
|
|
19964
|
+
}
|
|
19879
19965
|
const state = config.enabled === false || server.enabled === false ? "disabled" : "disconnected";
|
|
19880
19966
|
this.statuses.set(name, { name, state, transport, toolCount: 0 });
|
|
19881
19967
|
}
|
|
@@ -19890,6 +19976,75 @@ var McpManager = class {
|
|
|
19890
19976
|
options;
|
|
19891
19977
|
shutdownController = new AbortController();
|
|
19892
19978
|
closed = false;
|
|
19979
|
+
/** Compact model-visible catalog; transport and remote discovery stay lazy. */
|
|
19980
|
+
activationTool(registry) {
|
|
19981
|
+
const names = this.activatableServerNames();
|
|
19982
|
+
if (!names.length) return;
|
|
19983
|
+
const catalog = names.map((name) => {
|
|
19984
|
+
const server = this.config.servers[name];
|
|
19985
|
+
const description = sanitizeCatalogText(server?.description) || `${server?.transport ?? "stdio"} MCP server`;
|
|
19986
|
+
return `${name}: ${description}`;
|
|
19987
|
+
}).join("; ");
|
|
19988
|
+
return {
|
|
19989
|
+
definition: {
|
|
19990
|
+
name: "mcp_activate",
|
|
19991
|
+
description: `Connect to one configured MCP server only when the current task needs it, discover its tools, and load at most ${LAZY_SCHEMA_LIMIT} relevant schemas. Available servers: ${catalog}`,
|
|
19992
|
+
category: "network",
|
|
19993
|
+
inputSchema: {
|
|
19994
|
+
type: "object",
|
|
19995
|
+
properties: {
|
|
19996
|
+
server: { type: "string", enum: names, description: "Configured MCP server to activate." },
|
|
19997
|
+
query: { type: "string", minLength: 1, maxLength: 500, description: "Capability needed from that server." }
|
|
19998
|
+
},
|
|
19999
|
+
required: ["server", "query"],
|
|
20000
|
+
additionalProperties: false
|
|
20001
|
+
}
|
|
20002
|
+
},
|
|
20003
|
+
permissionCategories: () => ["network"],
|
|
20004
|
+
execute: async (arguments_, context) => {
|
|
20005
|
+
const server = typeof arguments_.server === "string" ? arguments_.server : "";
|
|
20006
|
+
const query = typeof arguments_.query === "string" ? arguments_.query.trim() : "";
|
|
20007
|
+
if (!names.includes(server)) throw new ToolInputError("MCP server is not available for activation");
|
|
20008
|
+
if (!query || query.length > 500) {
|
|
20009
|
+
throw new ToolInputError("MCP activation query must contain 1 to 500 characters");
|
|
20010
|
+
}
|
|
20011
|
+
const result = await this.activate(server, query, registry, context.signal);
|
|
20012
|
+
if (!result.ok) {
|
|
20013
|
+
return {
|
|
20014
|
+
ok: false,
|
|
20015
|
+
content: `MCP server ${server} could not be activated: ${result.status.error ?? result.status.state}`,
|
|
20016
|
+
metadata: activationMetadata(result)
|
|
20017
|
+
};
|
|
20018
|
+
}
|
|
20019
|
+
const loaded = result.registeredTools.length ? result.registeredTools.join(", ") : "no matching tool schemas";
|
|
20020
|
+
const deferred = result.deferredTools ? ` ${result.deferredTools} additional schemas remain deferred; activate again with a narrower query if needed.` : "";
|
|
20021
|
+
return {
|
|
20022
|
+
content: `Activated MCP server ${server}. Loaded: ${loaded}.${deferred}`,
|
|
20023
|
+
metadata: activationMetadata(result)
|
|
20024
|
+
};
|
|
20025
|
+
}
|
|
20026
|
+
};
|
|
20027
|
+
}
|
|
20028
|
+
/** Connect/discover one server, then register only request-relevant schemas. */
|
|
20029
|
+
async activate(name, query, registry, signal) {
|
|
20030
|
+
const connected = await this.connect(name, signal);
|
|
20031
|
+
if (!connected.ok) {
|
|
20032
|
+
return { ...connected, registeredTools: [], availableTools: 0, deferredTools: 0 };
|
|
20033
|
+
}
|
|
20034
|
+
const connection = this.connections.get(name);
|
|
20035
|
+
if (!connection) {
|
|
20036
|
+
return { ...connected, ok: false, registeredTools: [], availableTools: 0, deferredTools: 0 };
|
|
20037
|
+
}
|
|
20038
|
+
const tools = [...connection.tools.values()];
|
|
20039
|
+
const selected = tools.length <= LAZY_SCHEMA_LIMIT ? tools : selectRelevantTools(tools, query, LAZY_SCHEMA_LIMIT);
|
|
20040
|
+
this.registerSelectedTools(registry, selected);
|
|
20041
|
+
return {
|
|
20042
|
+
...connected,
|
|
20043
|
+
registeredTools: selected.map((tool) => tool.definition.name),
|
|
20044
|
+
availableTools: tools.length,
|
|
20045
|
+
deferredTools: Math.max(0, tools.length - selected.length)
|
|
20046
|
+
};
|
|
20047
|
+
}
|
|
19893
20048
|
/** Connect enabled servers with a small concurrency bound. */
|
|
19894
20049
|
async connectAll(signal) {
|
|
19895
20050
|
if (this.closed) throw new Error("MCP manager is closed");
|
|
@@ -19898,15 +20053,6 @@ var McpManager = class {
|
|
|
19898
20053
|
if (this.config.enabled === false) {
|
|
19899
20054
|
return configuredNames.map((name) => this.resultFor(name, false, 0));
|
|
19900
20055
|
}
|
|
19901
|
-
for (const name of configuredNames.slice(MAX_SERVERS)) {
|
|
19902
|
-
const server = this.config.servers[name];
|
|
19903
|
-
this.setStatus(name, {
|
|
19904
|
-
state: "error",
|
|
19905
|
-
transport: server?.transport ?? "stdio",
|
|
19906
|
-
toolCount: 0,
|
|
19907
|
-
error: `MCP server limit exceeded (maximum ${MAX_SERVERS})`
|
|
19908
|
-
});
|
|
19909
|
-
}
|
|
19910
20056
|
const results = [];
|
|
19911
20057
|
let cursor = 0;
|
|
19912
20058
|
const worker = async () => {
|
|
@@ -19922,6 +20068,10 @@ var McpManager = class {
|
|
|
19922
20068
|
/** Connect one configured server. Connection errors are captured in status. */
|
|
19923
20069
|
async connect(name, signal) {
|
|
19924
20070
|
if (this.closed) throw new Error("MCP manager is closed");
|
|
20071
|
+
const status = this.statuses.get(name);
|
|
20072
|
+
if (status?.state === "error" && status.error?.includes("server limit exceeded")) {
|
|
20073
|
+
return this.resultFor(name, false, 0);
|
|
20074
|
+
}
|
|
19925
20075
|
const existing = this.pending.get(name);
|
|
19926
20076
|
if (existing) return existing;
|
|
19927
20077
|
const connectionController = new AbortController();
|
|
@@ -20006,8 +20156,11 @@ var McpManager = class {
|
|
|
20006
20156
|
}
|
|
20007
20157
|
/** Register connected MCP tools, preserving idempotency for the same adapter. */
|
|
20008
20158
|
registerTools(registry) {
|
|
20159
|
+
return this.registerSelectedTools(registry, this.tools());
|
|
20160
|
+
}
|
|
20161
|
+
registerSelectedTools(registry, tools) {
|
|
20009
20162
|
const registered = [];
|
|
20010
|
-
for (const tool of
|
|
20163
|
+
for (const tool of tools) {
|
|
20011
20164
|
const existing = registry.get(tool.definition.name);
|
|
20012
20165
|
if (existing) {
|
|
20013
20166
|
if (existing !== tool) {
|
|
@@ -20020,6 +20173,10 @@ var McpManager = class {
|
|
|
20020
20173
|
}
|
|
20021
20174
|
return registered;
|
|
20022
20175
|
}
|
|
20176
|
+
activatableServerNames() {
|
|
20177
|
+
if (this.config.enabled === false) return [];
|
|
20178
|
+
return [...this.statuses.values()].filter((status) => status.state !== "disabled" && status.state !== "error").map((status) => status.name).sort((left, right) => left.localeCompare(right));
|
|
20179
|
+
}
|
|
20023
20180
|
async connectInternal(name, signal) {
|
|
20024
20181
|
const configured = this.config.servers?.[name];
|
|
20025
20182
|
if (!configured) throw new Error(`Unknown MCP server: ${name}`);
|
|
@@ -20271,6 +20428,28 @@ var McpManager = class {
|
|
|
20271
20428
|
return { name, ok, status: { ...status }, skippedTools };
|
|
20272
20429
|
}
|
|
20273
20430
|
};
|
|
20431
|
+
function activationMetadata(result) {
|
|
20432
|
+
return {
|
|
20433
|
+
mcpServer: result.name,
|
|
20434
|
+
state: result.status.state,
|
|
20435
|
+
availableTools: result.availableTools,
|
|
20436
|
+
loadedTools: result.registeredTools,
|
|
20437
|
+
deferredTools: result.deferredTools,
|
|
20438
|
+
skippedTools: result.skippedTools
|
|
20439
|
+
};
|
|
20440
|
+
}
|
|
20441
|
+
function selectRelevantTools(tools, query, limit) {
|
|
20442
|
+
const terms = new Set(query.toLocaleLowerCase().match(/[\p{L}\p{N}_-]{2,}/gu) ?? []);
|
|
20443
|
+
return tools.map((tool) => {
|
|
20444
|
+
const searchable = `${tool.definition.name.replaceAll("_", " ")} ${tool.definition.description}`.toLocaleLowerCase();
|
|
20445
|
+
let score = 0;
|
|
20446
|
+
for (const term of terms) if (searchable.includes(term)) score += term.length;
|
|
20447
|
+
return { tool, score };
|
|
20448
|
+
}).sort((left, right) => right.score - left.score || left.tool.definition.name.localeCompare(right.tool.definition.name)).slice(0, limit).map(({ tool }) => tool);
|
|
20449
|
+
}
|
|
20450
|
+
function sanitizeCatalogText(value) {
|
|
20451
|
+
return value ? stripAnsi5(value).replace(/[\u0000-\u001f\u007f]/g, " ").replace(/\s+/g, " ").trim().slice(0, 200) : "";
|
|
20452
|
+
}
|
|
20274
20453
|
function requestOptions(timeoutMs, signal) {
|
|
20275
20454
|
return {
|
|
20276
20455
|
timeout: timeoutMs,
|
|
@@ -20780,7 +20959,7 @@ var ExtensionRuntime = class _ExtensionRuntime {
|
|
|
20780
20959
|
throw error;
|
|
20781
20960
|
}
|
|
20782
20961
|
}
|
|
20783
|
-
async initialize(registry,
|
|
20962
|
+
async initialize(registry, _signal) {
|
|
20784
20963
|
if (this.initialized) return;
|
|
20785
20964
|
if (this.memory) {
|
|
20786
20965
|
await this.memory.open();
|
|
@@ -20792,8 +20971,8 @@ var ExtensionRuntime = class _ExtensionRuntime {
|
|
|
20792
20971
|
}
|
|
20793
20972
|
await this.skills?.discover();
|
|
20794
20973
|
if (this.mcp) {
|
|
20795
|
-
|
|
20796
|
-
|
|
20974
|
+
const activationTool = this.mcp.activationTool(registry);
|
|
20975
|
+
if (activationTool) registry.register(activationTool);
|
|
20797
20976
|
}
|
|
20798
20977
|
await this.profiles?.discover();
|
|
20799
20978
|
if (this.config.agents?.enabled && this.profiles && this.options.provider && this.options.contextEngine) {
|