@mars-sea/dsh-commandcode-provider 0.11.11 → 0.11.13
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/CHANGELOG.md +20 -0
- package/README.md +34 -31
- package/README.zh-CN.md +28 -31
- package/lib/client.js +2073 -1837
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +55 -86
- package/lib/index.js +350 -390
- package/lib/index.js.map +1 -1
- package/package.json +40 -48
package/lib/index.js
CHANGED
|
@@ -2,8 +2,7 @@ import { homedir, tmpdir } from "node:os";
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import z from "@deepseek-ai/schemastery";
|
|
4
4
|
import { MAX_TIMER_DELAY_MS } from "@deepseek-ai/dsh-timeout";
|
|
5
|
-
import
|
|
6
|
-
import { CONTEXT_WINDOW_EXCEEDED_CODE, LlmAdapter, LlmError, ReasoningEffortId, ToolCallId, assertUsableApiKey, attributionHeaders, errorChain, isContextWindowExceededError, offloadedImageText, resolveRetryPolicy, textOnlyImageText } from "@deepseek-ai/dsh-llm";
|
|
5
|
+
import { CONTEXT_WINDOW_EXCEEDED_CODE, IMAGE_OFFLOAD_REQUIRED_CODE, LlmAdapter, LlmError, ReasoningEffortId, ToolCallId, assertUsableApiKey, attributionHeaders, errorChain, isContextWindowExceededError, offloadedImageText, projectOffloadedImages, requiredImageOffload, resolveRetryPolicy, textOnlyImageText } from "@deepseek-ai/dsh-llm";
|
|
7
6
|
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
8
7
|
import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
|
|
9
8
|
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
@@ -309,6 +308,11 @@ const KNOWN_EFFORTS = {
|
|
|
309
308
|
"low",
|
|
310
309
|
"medium",
|
|
311
310
|
"high"
|
|
311
|
+
],
|
|
312
|
+
"stealth/space-bunny-alpha": [
|
|
313
|
+
"low",
|
|
314
|
+
"medium",
|
|
315
|
+
"high"
|
|
312
316
|
]
|
|
313
317
|
};
|
|
314
318
|
/**
|
|
@@ -374,6 +378,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
374
378
|
"moonshotai/Kimi-K2.7-Code-Highspeed",
|
|
375
379
|
"moonshotai/Kimi-K3",
|
|
376
380
|
"sakana/fugu-ultra",
|
|
381
|
+
"stealth/space-bunny-alpha",
|
|
377
382
|
"stepfun/Step-3.7-Flash",
|
|
378
383
|
"stepfun/Step-5-Preview",
|
|
379
384
|
"thinkingmachines/inkling",
|
|
@@ -390,7 +395,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
390
395
|
]);
|
|
391
396
|
/**
|
|
392
397
|
* Models WITHOUT a zero-data-retention upstream, per the official CLI's own
|
|
393
|
-
* registry (`command-code@1.
|
|
398
|
+
* registry (`command-code@1.65.0` `dist/cli.mjs`: `modelSupportsZdr(id)` is
|
|
394
399
|
* exactly `!nonZdrSet.has(canonicalize(id))`, and `knownModelSupportsZdr`
|
|
395
400
|
* carries the same membership in the sibling route table — the union is this
|
|
396
401
|
* set). The official docs (commandcode.ai/docs/resources/zdr) put it in prose
|
|
@@ -416,9 +421,12 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
|
416
421
|
* is the snapshot of the exclusion set and nothing more. The 1.62.0 → 1.64.0
|
|
417
422
|
* diff of that union is EMPTY: both anchors were extracted from both bundles
|
|
418
423
|
* during the 2026-09-23 check and each carries the same 20 members — which is
|
|
419
|
-
* how rare a change here is expected to be.
|
|
420
|
-
*
|
|
421
|
-
*
|
|
424
|
+
* how rare a change here is expected to be. The 1.64.0 → 1.65.0 diff (2026-09-24)
|
|
425
|
+
* is the counterexample that proves the check still runs: exactly one member
|
|
426
|
+
* joins (`stealth/space-bunny-alpha`), and it is in both anchors. `meituan/
|
|
427
|
+
* LongCat-2.0` is the one member the two anchors disagree about (it is in
|
|
428
|
+
* `modelSupportsZdr`'s set in both releases and in neither `knownModelSupportsZdr`
|
|
429
|
+
* set), so the union is
|
|
422
430
|
* what this table follows; reading only the sibling route table would drop it.
|
|
423
431
|
*/
|
|
424
432
|
const KNOWN_NON_ZDR_MODELS = /* @__PURE__ */ new Set([
|
|
@@ -433,6 +441,7 @@ const KNOWN_NON_ZDR_MODELS = /* @__PURE__ */ new Set([
|
|
|
433
441
|
"minimax/minimax-m3-free",
|
|
434
442
|
"poolside/laguna-s-2.1-free",
|
|
435
443
|
"sakana/fugu-ultra",
|
|
444
|
+
"stealth/space-bunny-alpha",
|
|
436
445
|
"stepfun/Step-3.7-Flash",
|
|
437
446
|
"stepfun/Step-5-Preview",
|
|
438
447
|
"xai/grok-4.5",
|
|
@@ -565,7 +574,11 @@ function requiresMessagesEndpoint(modelId) {
|
|
|
565
574
|
* and 1.60.0 added `stepfun/Step-5-Preview` (Go); command-code@1.62.0 added the
|
|
566
575
|
* MiMo V2.6 family — `xiaomi/mimo-v2.6-flash` + `xiaomi/mimo-v2.6-pro` (Go) and
|
|
567
576
|
* `xiaomi/mimo-v2.6-pro-ultraspeed` (GOAT) — so the 1.58.0 -> 1.62.0 window's
|
|
568
|
-
* only tier changes are additions and the superset chain still holds.
|
|
577
|
+
* only tier changes are additions and the superset chain still holds. The
|
|
578
|
+
* 1.62.0 -> 1.65.0 window continues the pattern: 1.64.0 added
|
|
579
|
+
* `claude-opus-5-5` (Provider/Max), `gpt-6-sol` (Pro) and `gpt-6-luna` (Go),
|
|
580
|
+
* and 1.65.0 added `stealth/space-bunny-alpha` (Go, every tier) — additions
|
|
581
|
+
* only, no tier moves.
|
|
569
582
|
*
|
|
570
583
|
* The Provider API exposes no plan metadata, so this snapshot is the source of
|
|
571
584
|
* truth for the picker's plan annotation — it answers "which plan do I need to
|
|
@@ -610,6 +623,7 @@ const KNOWN_PLANS = {
|
|
|
610
623
|
"stepfun/Step-3.5-Flash": "go",
|
|
611
624
|
"stepfun/Step-3.7-Flash": "go",
|
|
612
625
|
"stepfun/Step-5-Preview": "go",
|
|
626
|
+
"stealth/space-bunny-alpha": "go",
|
|
613
627
|
"tencent/hy3-paid": "go",
|
|
614
628
|
"tencent/hy4-preview": "go",
|
|
615
629
|
"thinkingmachines/inkling": "go",
|
|
@@ -817,6 +831,10 @@ const KNOWN_DEALS = {
|
|
|
817
831
|
"inclusionai/ling-3.0-flash-sante:free": {
|
|
818
832
|
label: "FREE",
|
|
819
833
|
free: true
|
|
834
|
+
},
|
|
835
|
+
"stealth/space-bunny-alpha": {
|
|
836
|
+
label: "FREE",
|
|
837
|
+
free: true
|
|
820
838
|
}
|
|
821
839
|
};
|
|
822
840
|
/**
|
|
@@ -2600,9 +2618,11 @@ const REQUEST_IMAGE_MAX_ENCODED_BYTES = 1048576;
|
|
|
2600
2618
|
* never returns a zero dimension (a 1-px degenerate side is what the round of
|
|
2601
2619
|
* an extreme aspect ratio can produce, and a 0 would make the encoder refuse).
|
|
2602
2620
|
*
|
|
2603
|
-
*
|
|
2604
|
-
*
|
|
2605
|
-
*
|
|
2621
|
+
* `dsh-attachment` exports an equivalent `longEdgeDimensions`, but it does not
|
|
2622
|
+
* guard its inputs: a reference whose declared size is 0 or NaN — a malformed
|
|
2623
|
+
* or fabricated attachment record — would project to a zero-sized target and
|
|
2624
|
+
* make the encoder refuse the whole request. The guard is the reason this stays
|
|
2625
|
+
* local; `tests/image-tokens.test.ts` pins the degenerate cases.
|
|
2606
2626
|
*/
|
|
2607
2627
|
function longEdgeDimensions(width, height, longEdge) {
|
|
2608
2628
|
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return {
|
|
@@ -2623,16 +2643,11 @@ function longEdgeDimensions(width, height, longEdge) {
|
|
|
2623
2643
|
}
|
|
2624
2644
|
/**
|
|
2625
2645
|
* The request target for one normalized attachment: its aspect ratio under the
|
|
2626
|
-
* long-edge budget, plus the byte budget
|
|
2627
|
-
*
|
|
2628
|
-
* The area is computed from the PROJECTED dimensions rather than the source's,
|
|
2629
|
-
* so ≤0.1.5's own pixel projection lands on the same geometry instead of
|
|
2630
|
-
* re-deriving a slightly different one from the original.
|
|
2646
|
+
* long-edge budget, plus the encoded-byte budget.
|
|
2631
2647
|
*/
|
|
2632
2648
|
function requestImageTarget(ref) {
|
|
2633
2649
|
const projected = longEdgeDimensions(ref.width, ref.height, REQUEST_IMAGE_MAX_LONG_EDGE);
|
|
2634
2650
|
return {
|
|
2635
|
-
maxPixels: projected.width * projected.height,
|
|
2636
2651
|
width: projected.width,
|
|
2637
2652
|
height: projected.height,
|
|
2638
2653
|
maxBytes: REQUEST_IMAGE_MAX_ENCODED_BYTES
|
|
@@ -3002,7 +3017,7 @@ function openStreamTrace(scope, options = {}) {
|
|
|
3002
3017
|
* per-request options thunk and an API-key resolver from the plugin entry
|
|
3003
3018
|
* (src/index.ts), so a settings change reaches the very next request.
|
|
3004
3019
|
*/
|
|
3005
|
-
const COMMAND_CODE_CLI_VERSION = "1.
|
|
3020
|
+
const COMMAND_CODE_CLI_VERSION = "1.65.0";
|
|
3006
3021
|
const DEFAULT_API_BASE = "https://api.commandcode.ai";
|
|
3007
3022
|
const DEFAULT_GENERATE_MAX_TOKENS = 64e3;
|
|
3008
3023
|
const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
|
|
@@ -3383,32 +3398,24 @@ async function writeModelsCache(cachePath, models) {
|
|
|
3383
3398
|
}
|
|
3384
3399
|
}
|
|
3385
3400
|
/**
|
|
3386
|
-
*
|
|
3387
|
-
* content blocks. Older engines wrap those fields in the first tool-result
|
|
3388
|
-
* block of a role:'user' message. Read structurally so the bundle still builds
|
|
3389
|
-
* and loads against older peers. Pairing and emission MUST use this same view:
|
|
3390
|
-
* otherwise a dropped result either drops its call too (making the model
|
|
3391
|
-
* forget completed work), or leaves an unanswered call on the wire.
|
|
3401
|
+
* The tool result one message answers, or undefined when it answers none.
|
|
3392
3402
|
*
|
|
3393
|
-
*
|
|
3394
|
-
*
|
|
3395
|
-
*
|
|
3403
|
+
* The harness models a tool result as its own `role: 'tool'` message carrying
|
|
3404
|
+
* `toolCallId`/`isError` beside raw content blocks (a closed role map:
|
|
3405
|
+
* `MessageRoleMap.tool` is the only role a tool result can arrive on). Pairing
|
|
3406
|
+
* and emission MUST agree on this same view: otherwise a dropped result either
|
|
3407
|
+
* drops its call too (making the model forget completed work), or leaves an
|
|
3408
|
+
* unanswered call on the wire.
|
|
3396
3409
|
*/
|
|
3397
3410
|
function toolResultOf(message) {
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
}
|
|
3407
|
-
if (message.role !== "user") return void 0;
|
|
3408
|
-
const kind = message.source?.kind;
|
|
3409
|
-
if (kind !== void 0 && kind !== "tool") return void 0;
|
|
3410
|
-
const block = message.content[0];
|
|
3411
|
-
return block?.type === "tool-result" ? block : void 0;
|
|
3411
|
+
if (message.role !== "tool") return void 0;
|
|
3412
|
+
const { toolCallId, content } = message;
|
|
3413
|
+
if (typeof toolCallId !== "string" || toolCallId === "") return void 0;
|
|
3414
|
+
return {
|
|
3415
|
+
toolCallId,
|
|
3416
|
+
isError: message.isError ?? false,
|
|
3417
|
+
content
|
|
3418
|
+
};
|
|
3412
3419
|
}
|
|
3413
3420
|
/**
|
|
3414
3421
|
* Collect the tool calls that have a paired tool result, plus each call's
|
|
@@ -3481,23 +3488,16 @@ function toolResultMedia(block) {
|
|
|
3481
3488
|
const chunks = [];
|
|
3482
3489
|
const images = [];
|
|
3483
3490
|
const seen = /* @__PURE__ */ new Set();
|
|
3484
|
-
const
|
|
3485
|
-
|
|
3486
|
-
if (nested.
|
|
3487
|
-
|
|
3488
|
-
continue;
|
|
3489
|
-
}
|
|
3490
|
-
if (nested.type === "image") {
|
|
3491
|
-
if (!seen.has(nested.attachment.attachmentId)) {
|
|
3492
|
-
seen.add(nested.attachment.attachmentId);
|
|
3493
|
-
images.push(nested.attachment);
|
|
3494
|
-
}
|
|
3495
|
-
continue;
|
|
3496
|
-
}
|
|
3497
|
-
if (nested.type === "tool-result") walk(nested.content);
|
|
3491
|
+
for (const nested of block.content) {
|
|
3492
|
+
if (nested.type === "text" || nested.type === "reasoning") {
|
|
3493
|
+
if (nested.text) chunks.push(nested.text);
|
|
3494
|
+
continue;
|
|
3498
3495
|
}
|
|
3499
|
-
|
|
3500
|
-
|
|
3496
|
+
if (nested.type === "image" && !seen.has(nested.attachment.attachmentId)) {
|
|
3497
|
+
seen.add(nested.attachment.attachmentId);
|
|
3498
|
+
images.push(nested.attachment);
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
3501
|
return {
|
|
3502
3502
|
text: chunks.join("\n"),
|
|
3503
3503
|
images
|
|
@@ -3542,8 +3542,7 @@ function toolResultImageNote(media, toolCallId) {
|
|
|
3542
3542
|
return `${lead} ${media.images.length > 1 ? `${media.images.length} images, ` : ""}${dimensions}`;
|
|
3543
3543
|
}
|
|
3544
3544
|
function hasImageContent(message) {
|
|
3545
|
-
|
|
3546
|
-
return check(message.content);
|
|
3545
|
+
return message.content.some((block) => block.type === "image");
|
|
3547
3546
|
}
|
|
3548
3547
|
/** The budget ladder: the primary rung first, the 413 eviction rung second. */
|
|
3549
3548
|
const REQUEST_IMAGE_BUDGETS = [{
|
|
@@ -3557,68 +3556,13 @@ const REQUEST_IMAGE_BUDGETS = [{
|
|
|
3557
3556
|
byteQuantum: 8388608,
|
|
3558
3557
|
countQuantum: 12
|
|
3559
3558
|
}];
|
|
3559
|
+
/** The engine's own helpers: what a process that injects nothing speaks. */
|
|
3560
|
+
const ENGINE_IMAGE_POLICY = {
|
|
3561
|
+
projectOffloadedImages,
|
|
3562
|
+
requiredImageOffload
|
|
3563
|
+
};
|
|
3560
3564
|
/**
|
|
3561
|
-
*
|
|
3562
|
-
* oldest images past it become placeholder text in place, the newest keep their
|
|
3563
|
-
* bytes, and a history already inside the budget is returned as the SAME array
|
|
3564
|
-
* — the caller uses that identity to tell "nothing evicted" from "evicted".
|
|
3565
|
-
*
|
|
3566
|
-
* Nested tool-result images ride the same core walk, which is exactly right
|
|
3567
|
-
* for this adapter: an evicted `read_image` result surfaces its placeholder as
|
|
3568
|
-
* the tool message's own text, so no carrier user message is emitted for it.
|
|
3569
|
-
*/
|
|
3570
|
-
function projectRequestImages(messages, budget, policy) {
|
|
3571
|
-
const offload = policy.offloadRequestImagesWithPolicy;
|
|
3572
|
-
if (typeof offload !== "function") return messages;
|
|
3573
|
-
return offload(messages, {
|
|
3574
|
-
representation: "base64",
|
|
3575
|
-
maxBytes: budget.maxBytes,
|
|
3576
|
-
maxImages: budget.maxImages,
|
|
3577
|
-
byteQuantum: budget.byteQuantum,
|
|
3578
|
-
countQuantum: budget.countQuantum,
|
|
3579
|
-
placeholder: (ref) => offloadedImageText(ref)
|
|
3580
|
-
});
|
|
3581
|
-
}
|
|
3582
|
-
/**
|
|
3583
|
-
* The request options one budget produced: the caller's own object when the
|
|
3584
|
-
* history was already inside the budget, else a copy carrying the projection.
|
|
3585
|
-
* The next rung of the ladder is applied to the CURRENT projection, never to
|
|
3586
|
-
* the raw history — otherwise a retry could reintroduce images the first rung
|
|
3587
|
-
* had already evicted.
|
|
3588
|
-
*/
|
|
3589
|
-
function withImageBudget(options, budget, policy) {
|
|
3590
|
-
const projected = projectRequestImages(options.messages, budget, policy);
|
|
3591
|
-
return projected === options.messages ? options : {
|
|
3592
|
-
...options,
|
|
3593
|
-
messages: [...projected]
|
|
3594
|
-
};
|
|
3595
|
-
}
|
|
3596
|
-
/**
|
|
3597
|
-
* Stable provider-neutral code for "a request still carries more images than
|
|
3598
|
-
* this route will send; offload N of them and retry". Declared locally rather
|
|
3599
|
-
* than imported: the constant does not exist on the 0.1.2–0.1.5 peers this
|
|
3600
|
-
* plugin still supports, and importing it statically is the link-time failure
|
|
3601
|
-
* described above the budget constants.
|
|
3602
|
-
*/
|
|
3603
|
-
const IMAGE_OFFLOAD_REQUIRED_CODE = "IMAGE_OFFLOAD_REQUIRED";
|
|
3604
|
-
/** The installed engine's image policy; see {@link CoreImagePolicy}. */
|
|
3605
|
-
const CORE_IMAGE_POLICY = dshLlm;
|
|
3606
|
-
/**
|
|
3607
|
-
* The durable-offload contract this engine speaks, or undefined on ≤0.1.5.
|
|
3608
|
-
*
|
|
3609
|
-
* Both members are required together: a half-populated engine would either
|
|
3610
|
-
* lose the surface's marks (re-sending evicted images as pixels) or lose the
|
|
3611
|
-
* count (silently sending an over-budget body), and neither is worth a partial
|
|
3612
|
-
* opt-in. Absent, the adapter keeps its own transient projection instead.
|
|
3613
|
-
*/
|
|
3614
|
-
function surfaceImagePolicy(api = CORE_IMAGE_POLICY) {
|
|
3615
|
-
return typeof api.projectOffloadedImages === "function" && typeof api.requiredImageOffload === "function" ? {
|
|
3616
|
-
projectOffloadedImages: api.projectOffloadedImages,
|
|
3617
|
-
requiredImageOffload: api.requiredImageOffload
|
|
3618
|
-
} : void 0;
|
|
3619
|
-
}
|
|
3620
|
-
/**
|
|
3621
|
-
* The failure a route raises instead of evicting on its own (≥0.1.6). The
|
|
3565
|
+
* The failure a route raises instead of evicting on its own. The
|
|
3622
3566
|
* count is the ONE thing the harness needs: `dsh-compaction-image-offload`
|
|
3623
3567
|
* records it as an `image/offload` event, marks that many oldest retained
|
|
3624
3568
|
* occurrences, and retries the step.
|
|
@@ -3647,10 +3591,14 @@ function coreBudget(budget) {
|
|
|
3647
3591
|
*/
|
|
3648
3592
|
const imageVersionBytes = (block) => block.attachment.bytes;
|
|
3649
3593
|
/**
|
|
3650
|
-
*
|
|
3651
|
-
* and an over-budget history FAILS with the count to offload
|
|
3652
|
-
* evicted here. Throws rather than returning, so no caller can
|
|
3653
|
-
* send a body this route already knows is too large.
|
|
3594
|
+
* Request options under one budget rung: the surface's durable marks become
|
|
3595
|
+
* placeholder text, and an over-budget history FAILS with the count to offload
|
|
3596
|
+
* instead of being evicted here. Throws rather than returning, so no caller can
|
|
3597
|
+
* accidentally send a body this route already knows is too large.
|
|
3598
|
+
*
|
|
3599
|
+
* The next rung is applied to the CURRENT projection, never to the raw history —
|
|
3600
|
+
* otherwise the 413 retry could reintroduce images the first rung had already
|
|
3601
|
+
* evicted.
|
|
3654
3602
|
*/
|
|
3655
3603
|
function withSurfaceOffload(options, policy, budget) {
|
|
3656
3604
|
const marked = policy.projectOffloadedImages(options.messages, (ref) => offloadedImageText(ref));
|
|
@@ -3663,21 +3611,17 @@ function withSurfaceOffload(options, policy, budget) {
|
|
|
3663
3611
|
};
|
|
3664
3612
|
}
|
|
3665
3613
|
/**
|
|
3666
|
-
* One image's bytes for the wire: the attachment service's REQUEST VERSION
|
|
3667
|
-
* it can produce one, else the normalized original.
|
|
3614
|
+
* One image's bytes for the wire: the attachment service's REQUEST VERSION.
|
|
3668
3615
|
*
|
|
3669
3616
|
* `readImage()` answers the stored original, which is what admission accepted —
|
|
3670
3617
|
* not what a provider request should carry. `readImageRequest()` re-encodes to
|
|
3671
3618
|
* the route target in `./image-request.ts` and caches the result under it, so
|
|
3672
3619
|
* the same attachment costs one encode per target instead of one per occurrence,
|
|
3673
3620
|
* and the base64 the wire expands is bounded (both the pixel budget and, more to
|
|
3674
|
-
* the point here, the encoded bytes).
|
|
3675
|
-
* not implement the abstract method; every engine this plugin declares support
|
|
3676
|
-
* for does.
|
|
3621
|
+
* the point here, the encoded bytes).
|
|
3677
3622
|
*/
|
|
3678
3623
|
async function readRequestImage(attachments, ref) {
|
|
3679
|
-
|
|
3680
|
-
return (await attachments.readImage(ref)).data;
|
|
3624
|
+
return (await attachments.readImageRequest(ref, requestImageTarget(ref))).data;
|
|
3681
3625
|
}
|
|
3682
3626
|
/**
|
|
3683
3627
|
* Price one request occurrence.
|
|
@@ -3686,13 +3630,12 @@ async function readRequestImage(attachments, ref) {
|
|
|
3686
3630
|
* model-visible TEXT so the caller's estimator can charge that instead: a model
|
|
3687
3631
|
* that accepts text only (the harness projects those images to placeholder text
|
|
3688
3632
|
* before the request), and an occurrence the session surface has already
|
|
3689
|
-
* offloaded (
|
|
3690
|
-
*
|
|
3691
|
-
*
|
|
3633
|
+
* offloaded (the same placeholder text rides in every later request). A retained
|
|
3634
|
+
* occurrence on this route contributes no text at all — the pixels are inlined —
|
|
3635
|
+
* so its price is the family's visual-token count and nothing else.
|
|
3692
3636
|
*/
|
|
3693
|
-
function priceRequestImage(
|
|
3694
|
-
const
|
|
3695
|
-
const ref = block.attachment ?? occurrence;
|
|
3637
|
+
function priceRequestImage(block, model, imageCapable) {
|
|
3638
|
+
const ref = block.attachment;
|
|
3696
3639
|
if (!imageCapable) return {
|
|
3697
3640
|
visualTokens: 0,
|
|
3698
3641
|
text: textOnlyImageText(ref)
|
|
@@ -4225,13 +4168,9 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
4225
4168
|
catalog = [];
|
|
4226
4169
|
fetchImpl;
|
|
4227
4170
|
resolveAttachments;
|
|
4228
|
-
/** The engine's request-image policy, resolved once (tests may inject one). */
|
|
4229
|
-
imagePolicy;
|
|
4230
4171
|
/**
|
|
4231
|
-
* The
|
|
4232
|
-
*
|
|
4233
|
-
* speaks cannot change while it runs, and resolving here keeps every request
|
|
4234
|
-
* on the same branch (see {@link surfaceImagePolicy}).
|
|
4172
|
+
* The durable-offload contract, resolved once (tests may inject a stub). The
|
|
4173
|
+
* contract cannot change while the process runs.
|
|
4235
4174
|
*/
|
|
4236
4175
|
surfaceOffload;
|
|
4237
4176
|
billingAccess = /* @__PURE__ */ new Map();
|
|
@@ -4242,8 +4181,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
4242
4181
|
this.deps = deps;
|
|
4243
4182
|
this.fetchImpl = deps.fetchImpl ?? fetch;
|
|
4244
4183
|
this.resolveAttachments = deps.resolveAttachments;
|
|
4245
|
-
this.
|
|
4246
|
-
this.surfaceOffload = surfaceImagePolicy(this.imagePolicy);
|
|
4184
|
+
this.surfaceOffload = deps.imageOffload ?? ENGINE_IMAGE_POLICY;
|
|
4247
4185
|
}
|
|
4248
4186
|
/**
|
|
4249
4187
|
* Display metadata for the picker's provider group header. The base class
|
|
@@ -4727,8 +4665,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
4727
4665
|
const reasoningEffort = effort && effort !== "off" && supported?.includes(effort) ? effort : void 0;
|
|
4728
4666
|
const systemText = [options.system ?? "", ...options.messages.filter((m) => m.role === "system").map((m) => m.content.map(blockText).filter(Boolean).join("\n"))].filter(Boolean).join("\n\n");
|
|
4729
4667
|
let protocol = this.resolveProtocol(apiKey, options.model);
|
|
4730
|
-
let requestOptions =
|
|
4731
|
-
let imageBudgetIndex = 0;
|
|
4668
|
+
let requestOptions = withSurfaceOffload(options, this.surfaceOffload, REQUEST_IMAGE_BUDGETS[0]);
|
|
4732
4669
|
const buildBody = async (target) => target === "cli" ? buildCliBody(requestOptions, connection, {
|
|
4733
4670
|
maxTokens,
|
|
4734
4671
|
reasoningEffort,
|
|
@@ -4761,19 +4698,9 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
4761
4698
|
body = await buildBody("cli");
|
|
4762
4699
|
continue;
|
|
4763
4700
|
}
|
|
4764
|
-
if (attempt.status === 413 &&
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
if (missing > 0) throw imageOffloadRequired(missing);
|
|
4768
|
-
} else {
|
|
4769
|
-
const tightened = withImageBudget(requestOptions, REQUEST_IMAGE_BUDGETS[imageBudgetIndex + 1], this.imagePolicy);
|
|
4770
|
-
if (tightened !== requestOptions) {
|
|
4771
|
-
imageBudgetIndex += 1;
|
|
4772
|
-
requestOptions = tightened;
|
|
4773
|
-
body = await buildBody(protocol);
|
|
4774
|
-
continue;
|
|
4775
|
-
}
|
|
4776
|
-
}
|
|
4701
|
+
if (attempt.status === 413 && REQUEST_IMAGE_BUDGETS.length > 1) {
|
|
4702
|
+
const missing = this.surfaceOffload.requiredImageOffload(requestOptions.messages, coreBudget(REQUEST_IMAGE_BUDGETS[1]), imageVersionBytes);
|
|
4703
|
+
if (missing > 0) throw imageOffloadRequired(missing);
|
|
4777
4704
|
}
|
|
4778
4705
|
const rotate = this.deps.rotateApiKey;
|
|
4779
4706
|
const rejection = classifyAccountRejection(attempt.status, attempt.errText);
|
|
@@ -5700,7 +5627,6 @@ function makeStrictCodec(typeSymbol, schema) {
|
|
|
5700
5627
|
return {
|
|
5701
5628
|
mode: "strict",
|
|
5702
5629
|
typeSymbol,
|
|
5703
|
-
schema,
|
|
5704
5630
|
create: () => schema
|
|
5705
5631
|
};
|
|
5706
5632
|
}
|
|
@@ -5712,7 +5638,6 @@ function makeStrictCodec(typeSymbol, schema) {
|
|
|
5712
5638
|
* only its own facts.
|
|
5713
5639
|
*/
|
|
5714
5640
|
function makeRemoteDescriptor(endpoint, method, typeSymbol, schema) {
|
|
5715
|
-
const result = makeStrictCodec(typeSymbol, schema);
|
|
5716
5641
|
return {
|
|
5717
5642
|
id: `${REMOTE_PACKAGE}#${endpoint}`,
|
|
5718
5643
|
service: REMOTE_SERVICE,
|
|
@@ -5720,7 +5645,7 @@ function makeRemoteDescriptor(endpoint, method, typeSymbol, schema) {
|
|
|
5720
5645
|
method,
|
|
5721
5646
|
invocation: { kind: "direct" },
|
|
5722
5647
|
parameters: [],
|
|
5723
|
-
result
|
|
5648
|
+
result: makeStrictCodec(typeSymbol, schema)
|
|
5724
5649
|
};
|
|
5725
5650
|
}
|
|
5726
5651
|
//#endregion
|
|
@@ -6643,30 +6568,6 @@ const COMMANDCODE_SEARCH_PROVIDER_ID = "commandcode";
|
|
|
6643
6568
|
* modsearch even with Command Code search turned off (issue #26).
|
|
6644
6569
|
*/
|
|
6645
6570
|
const DEFAULT_WEB_SEARCH_PROVIDER_ID = "deepseek-official";
|
|
6646
|
-
/**
|
|
6647
|
-
* Point the web seam's search selection at this plugin's provider (`commandcode`).
|
|
6648
|
-
* Sets the runtime field; the next search call honours it because `search()`
|
|
6649
|
-
* re-reads `searchProviderId` each time. Returns the prior id (or undefined).
|
|
6650
|
-
* Never throws: a hardened/frozen runtime shape must not break the
|
|
6651
|
-
* settings-save path that calls this — the provider simply stays
|
|
6652
|
-
* registered-but-unselected (the boot-time `searchProvider: commandcode`
|
|
6653
|
-
* cordis patch is the durable alternative).
|
|
6654
|
-
*
|
|
6655
|
-
* @deprecated Prefer {@link applyCommandCodeSearchSelection}: this overload
|
|
6656
|
-
* always overwrites the displaced backend with the factory default on
|
|
6657
|
-
* disable, so turning Command Code search off silences whichever provider
|
|
6658
|
-
* was selected before (e.g. modsearch) instead of restoring it (issue #26).
|
|
6659
|
-
*/
|
|
6660
|
-
function selectCommandCodeSearchProvider(web, enable) {
|
|
6661
|
-
try {
|
|
6662
|
-
const field = web;
|
|
6663
|
-
const prior = field.searchProviderId;
|
|
6664
|
-
field.searchProviderId = enable ? COMMANDCODE_SEARCH_PROVIDER_ID : DEFAULT_WEB_SEARCH_PROVIDER_ID;
|
|
6665
|
-
return prior;
|
|
6666
|
-
} catch {
|
|
6667
|
-
return;
|
|
6668
|
-
}
|
|
6669
|
-
}
|
|
6670
6571
|
/** Fresh selection state: the plugin starts out not owning the selection. */
|
|
6671
6572
|
function commandCodeSearchSelection() {
|
|
6672
6573
|
return {
|
|
@@ -7049,8 +6950,8 @@ function buildCommandCodeTuiSection(deps) {
|
|
|
7049
6950
|
kind: "boolean",
|
|
7050
6951
|
label: "AI command guard",
|
|
7051
6952
|
descriptions: { zh: "AI 命令安全预判" },
|
|
7052
|
-
hint: "typesafe/jev judges whether a shell command is safe and
|
|
7053
|
-
hintDescriptions: { zh: "由 typesafe/jev 判断 shell
|
|
6953
|
+
hint: "typesafe/jev judges whether a shell command is safe; sandbox escalations also need separate scope and necessity verdicts. All must be confident enough; everything else asks as usual. Off by default.",
|
|
6954
|
+
hintDescriptions: { zh: "由 typesafe/jev 判断 shell 命令是否安全;沙箱提权还要分别判断范围与必要性,三项都足够确定才自动放行,其余照常弹窗。默认关闭。" },
|
|
7054
6955
|
format: (value) => value === true ? "true" : "false",
|
|
7055
6956
|
parse: (text) => ({
|
|
7056
6957
|
kind: "set",
|
|
@@ -7058,38 +6959,36 @@ function buildCommandCodeTuiSection(deps) {
|
|
|
7058
6959
|
})
|
|
7059
6960
|
},
|
|
7060
6961
|
{
|
|
7061
|
-
path: ["
|
|
7062
|
-
group: "advanced",
|
|
7063
|
-
kind: "number",
|
|
7064
|
-
label: "Auto-approve threshold (0.5-1)",
|
|
7065
|
-
descriptions: { zh: "自动放行阈值(0.5-1)" },
|
|
7066
|
-
hint: "Approve when typesafe/jev rates the command \"safe\" at least this likely; default 0.9.",
|
|
7067
|
-
hintDescriptions: { zh: "typesafe/jev 判定\"安全\"的概率达到该值即放行,默认 0.9。" },
|
|
7068
|
-
parse: (text) => {
|
|
7069
|
-
const trimmed = text.trim();
|
|
7070
|
-
if (trimmed === "") return { kind: "clear" };
|
|
7071
|
-
const parsed = Number(trimmed);
|
|
7072
|
-
return Number.isFinite(parsed) && parsed >= .5 && parsed <= 1 ? {
|
|
7073
|
-
kind: "set",
|
|
7074
|
-
value: parsed
|
|
7075
|
-
} : void 0;
|
|
7076
|
-
}
|
|
7077
|
-
},
|
|
7078
|
-
{
|
|
7079
|
-
path: ["commandGuardTimeoutMs"],
|
|
6962
|
+
path: ["commandGuardLevel"],
|
|
7080
6963
|
group: "advanced",
|
|
7081
|
-
kind: "
|
|
7082
|
-
label: "
|
|
7083
|
-
descriptions: { zh: "
|
|
7084
|
-
hint: "
|
|
7085
|
-
hintDescriptions: { zh: "typesafe/jev
|
|
6964
|
+
kind: "select",
|
|
6965
|
+
label: "Auto-approve threshold",
|
|
6966
|
+
descriptions: { zh: "自动放行阈值" },
|
|
6967
|
+
hint: "How confident typesafe/jev must be before a command runs without asking. Default: medium.",
|
|
6968
|
+
hintDescriptions: { zh: "typesafe/jev 需要多确定才免弹窗放行;默认「中」。" },
|
|
6969
|
+
options: [
|
|
6970
|
+
{
|
|
6971
|
+
value: "high",
|
|
6972
|
+
label: "High (0.95, approves least)",
|
|
6973
|
+
descriptions: { zh: "高(0.95,放行最少)" }
|
|
6974
|
+
},
|
|
6975
|
+
{
|
|
6976
|
+
value: "medium",
|
|
6977
|
+
label: "Medium (0.9)",
|
|
6978
|
+
descriptions: { zh: "中(0.9)" }
|
|
6979
|
+
},
|
|
6980
|
+
{
|
|
6981
|
+
value: "low",
|
|
6982
|
+
label: "Low (0.8, approves most)",
|
|
6983
|
+
descriptions: { zh: "低(0.8,放行最多)" }
|
|
6984
|
+
}
|
|
6985
|
+
],
|
|
6986
|
+
format: (value) => value === "high" || value === "low" ? value : "medium",
|
|
7086
6987
|
parse: (text) => {
|
|
7087
6988
|
const trimmed = text.trim();
|
|
7088
|
-
|
|
7089
|
-
const parsed = Number(trimmed);
|
|
7090
|
-
return Number.isFinite(parsed) && parsed >= 200 && parsed <= 1e4 ? {
|
|
6989
|
+
return trimmed === "high" || trimmed === "medium" || trimmed === "low" ? {
|
|
7091
6990
|
kind: "set",
|
|
7092
|
-
value:
|
|
6991
|
+
value: trimmed
|
|
7093
6992
|
} : void 0;
|
|
7094
6993
|
}
|
|
7095
6994
|
},
|
|
@@ -7378,8 +7277,8 @@ function transportBudgetMessage(failureMessage, maxRetries) {
|
|
|
7378
7277
|
* as an internal advisor instead: the command guard in `./command-guard.ts`
|
|
7379
7278
|
* asks it whether a shell command is safe to auto-approve.
|
|
7380
7279
|
*
|
|
7381
|
-
* The three question types are the whole vocabulary (command-code@1.
|
|
7382
|
-
* `systemOneRequestSchema
|
|
7280
|
+
* The three question types are the whole vocabulary (command-code@1.65.0
|
|
7281
|
+
* `systemOneRequestSchema`, unchanged since 1.64.0):
|
|
7383
7282
|
*
|
|
7384
7283
|
* - `noul` — "is this true?", optional `criteria: { true, false }` describing
|
|
7385
7284
|
* each verdict. Answers with `noul`, the probability of *yes*.
|
|
@@ -7410,8 +7309,6 @@ function transportBudgetMessage(failureMessage, maxRetries) {
|
|
|
7410
7309
|
*/
|
|
7411
7310
|
/** Path of the decision endpoint, relative to `apiBase`. */
|
|
7412
7311
|
const SYSTEMONE_ROUTE = "/provider/v1/systemone";
|
|
7413
|
-
/** Milliseconds a decision may take before the caller gives up on it. */
|
|
7414
|
-
const SYSTEMONE_DEFAULT_TIMEOUT_MS = 1500;
|
|
7415
7312
|
/** The one error type this client throws. Callers fail closed on it. */
|
|
7416
7313
|
var SystemOneError = class extends Error {
|
|
7417
7314
|
kind;
|
|
@@ -7669,13 +7566,40 @@ async function runSystemOne(deps, request, options = {}) {
|
|
|
7669
7566
|
//#region src/command-guard.ts
|
|
7670
7567
|
/** Tool names whose `arguments.command` this guard judges. */
|
|
7671
7568
|
const COMMAND_GUARD_SHELL_TOOLS = ["bash", "pwsh"];
|
|
7672
|
-
/** Question id the safety verdict is keyed by. */
|
|
7569
|
+
/** Question id the primary safety verdict is keyed by. */
|
|
7673
7570
|
const COMMAND_GUARD_QUESTION_ID = "safe";
|
|
7674
|
-
/**
|
|
7675
|
-
const
|
|
7676
|
-
/**
|
|
7677
|
-
const
|
|
7678
|
-
|
|
7571
|
+
/** Question id for the narrow-purpose part of a sandbox escalation. */
|
|
7572
|
+
const COMMAND_GUARD_ESCALATION_SCOPE_QUESTION_ID = "escalation_scope";
|
|
7573
|
+
/** Question id for the necessity/proportionality part of a sandbox escalation. */
|
|
7574
|
+
const COMMAND_GUARD_ESCALATION_NECESSITY_QUESTION_ID = "escalation_necessity";
|
|
7575
|
+
/** The selectable levels, strictest first (the order the settings surfaces list them in). */
|
|
7576
|
+
const COMMAND_GUARD_LEVELS = [
|
|
7577
|
+
"high",
|
|
7578
|
+
"medium",
|
|
7579
|
+
"low"
|
|
7580
|
+
];
|
|
7581
|
+
/** Each level's probability threshold. */
|
|
7582
|
+
const COMMAND_GUARD_LEVEL_THRESHOLDS = {
|
|
7583
|
+
high: .95,
|
|
7584
|
+
medium: .9,
|
|
7585
|
+
low: .8
|
|
7586
|
+
};
|
|
7587
|
+
/** The level an unset config reads as. */
|
|
7588
|
+
const COMMAND_GUARD_DEFAULT_LEVEL = "medium";
|
|
7589
|
+
/** Probability of "safe" at or above which the guard grants `allowed-once` by default. */
|
|
7590
|
+
const COMMAND_GUARD_DEFAULT_THRESHOLD = COMMAND_GUARD_LEVEL_THRESHOLDS[COMMAND_GUARD_DEFAULT_LEVEL];
|
|
7591
|
+
/** The threshold for a stored level; anything unrecognized reads as the default level. */
|
|
7592
|
+
function commandGuardThreshold(level) {
|
|
7593
|
+
return typeof level === "string" && Object.hasOwn(COMMAND_GUARD_LEVEL_THRESHOLDS, level) ? COMMAND_GUARD_LEVEL_THRESHOLDS[level] : COMMAND_GUARD_DEFAULT_THRESHOLD;
|
|
7594
|
+
}
|
|
7595
|
+
/**
|
|
7596
|
+
* Decision budget; the approval prompt must not wait longer than this. Fixed
|
|
7597
|
+
* rather than configurable: it only bounds how long a late verdict can delay
|
|
7598
|
+
* the prompt, and every failure (a timeout included) already falls back to
|
|
7599
|
+
* asking the user, so there is nothing for a user to tune. 3 s leaves room for
|
|
7600
|
+
* credential resolution plus one decision round trip on a slow link.
|
|
7601
|
+
*/
|
|
7602
|
+
const COMMAND_GUARD_TIMEOUT_MS = 3e3;
|
|
7679
7603
|
/**
|
|
7680
7604
|
* Longest command the guard will send for judgement. A longer one is delegated
|
|
7681
7605
|
* instead of truncated: a verdict on the first N characters of a command is a
|
|
@@ -7714,7 +7638,7 @@ const COMMAND_GUARD_DENY_LIST = [
|
|
|
7714
7638
|
},
|
|
7715
7639
|
{
|
|
7716
7640
|
id: "device-redirect",
|
|
7717
|
-
pattern:
|
|
7641
|
+
pattern: />{1,2}\s*\/dev\/(?:sd|disk|nvme|hd)/
|
|
7718
7642
|
},
|
|
7719
7643
|
{
|
|
7720
7644
|
id: "fork-bomb",
|
|
@@ -7854,17 +7778,21 @@ function shellCommandOf(name, args) {
|
|
|
7854
7778
|
/**
|
|
7855
7779
|
* Build the decision request for one command.
|
|
7856
7780
|
*
|
|
7857
|
-
*
|
|
7858
|
-
*
|
|
7859
|
-
*
|
|
7860
|
-
*
|
|
7861
|
-
*
|
|
7781
|
+
* A normal approval asks one question. A call that explicitly requests a wider
|
|
7782
|
+
* sandbox gets the same safety question plus two veto questions. This keeps the
|
|
7783
|
+
* common case cheap while making the broader permission change an explicit
|
|
7784
|
+
* decision: JEV must agree that the extra access has a narrow purpose and is
|
|
7785
|
+
* necessary, not merely that the command text looks familiar.
|
|
7786
|
+
*
|
|
7787
|
+
* The agent's own `description` and the harness's `reason` are included as
|
|
7788
|
+
* context, but the criteria say the command text itself decides — a
|
|
7862
7789
|
* model-written description is a claim, not evidence.
|
|
7863
7790
|
*
|
|
7864
7791
|
* @param fact - the command and its context.
|
|
7865
7792
|
* @returns the request to send.
|
|
7866
7793
|
*/
|
|
7867
7794
|
function buildSafetyRequest(fact) {
|
|
7795
|
+
const escalated = fact.sandboxPermissions !== void 0;
|
|
7868
7796
|
const lines = [
|
|
7869
7797
|
`Tool: ${fact.tool}`,
|
|
7870
7798
|
"Command:",
|
|
@@ -7876,24 +7804,43 @@ function buildSafetyRequest(fact) {
|
|
|
7876
7804
|
];
|
|
7877
7805
|
const question = {
|
|
7878
7806
|
type: "noul",
|
|
7879
|
-
instructions: "Is it safe to run this command right now WITHOUT asking the user first? Judge the command text itself; a description is only a claim about it.",
|
|
7807
|
+
instructions: escalated ? "Is it safe to run this command right now WITHOUT asking the user first, including the requested sandbox escalation? Judge the command text itself; a description or stated reason is only a claim about it." : "Is it safe to run this command right now WITHOUT asking the user first? Judge the command text itself; a description is only a claim about it.",
|
|
7880
7808
|
criteria: {
|
|
7881
|
-
true: "Read-only, or its effects stay inside the current project and are trivially reversible: no data leaves the machine, nothing outside the project is modified, and no credentials, git history or remote state are touched.",
|
|
7882
|
-
false: "Anything else.
|
|
7809
|
+
true: escalated ? "A normal, non-obfuscated read, build, or test operation whose outside-workspace effects are limited to conventional build/dependency caches or temporary files and are easily reversible: no unrelated user or system data is accessed, no data leaves the machine, and no credentials, git history or remote state are touched." : "Read-only, or its effects stay inside the current project and are trivially reversible: no data leaves the machine, nothing outside the project is modified, and no credentials, git history or remote state are touched.",
|
|
7810
|
+
false: "Anything else. Broad or unrelated outside-workspace access, deleting or overwriting files outside the project, rewriting or publishing git history, uploading data anywhere, installing system or global packages, changing permissions, ownership or credentials, killing processes, running as another user or as root, piping a download into a shell, or a command obfuscated enough that its effect cannot be read."
|
|
7883
7811
|
}
|
|
7884
7812
|
};
|
|
7813
|
+
const questions = { [COMMAND_GUARD_QUESTION_ID]: question };
|
|
7814
|
+
if (escalated) {
|
|
7815
|
+
questions[COMMAND_GUARD_ESCALATION_SCOPE_QUESTION_ID] = {
|
|
7816
|
+
type: "noul",
|
|
7817
|
+
instructions: "Is the requested wider sandbox access limited to a narrow, conventional purpose supported by this command, such as a build cache, dependency cache, or temporary directory, rather than unrelated user files, credentials, other projects, or the network?",
|
|
7818
|
+
criteria: {
|
|
7819
|
+
true: "The wider access is plausibly limited to conventional local build/dependency caches or temporary files required by the command, with no unrelated host data or remote transfer in scope.",
|
|
7820
|
+
false: "The command needs broad or unrelated host access, cannot establish a narrow purpose, or would use the wider permission to reach credentials, other projects, user files, or the network."
|
|
7821
|
+
}
|
|
7822
|
+
};
|
|
7823
|
+
questions[COMMAND_GUARD_ESCALATION_NECESSITY_QUESTION_ID] = {
|
|
7824
|
+
type: "noul",
|
|
7825
|
+
instructions: "Is the requested sandbox escalation necessary and proportionate for this one command, rather than broader than the command needs? Treat the stated reason as a claim to check against the command, not as proof.",
|
|
7826
|
+
criteria: {
|
|
7827
|
+
true: "The command and its stated purpose support a one-shot escalation that is needed for the task and is no broader than the requested effect requires.",
|
|
7828
|
+
false: "The wider mode is unnecessary, broader than needed, unsupported by the command, or the command is too ambiguous to justify the extra authority."
|
|
7829
|
+
}
|
|
7830
|
+
};
|
|
7831
|
+
}
|
|
7885
7832
|
return {
|
|
7886
7833
|
state: lines.join("\n"),
|
|
7887
|
-
questions
|
|
7834
|
+
questions
|
|
7888
7835
|
};
|
|
7889
7836
|
}
|
|
7890
7837
|
/**
|
|
7891
|
-
* Read
|
|
7838
|
+
* Read a probability out of a decision response.
|
|
7892
7839
|
*
|
|
7893
7840
|
* @param response - the parsed response.
|
|
7894
7841
|
* @param questionId - the question id to read (defaults to the guard's own).
|
|
7895
|
-
* @returns the probability
|
|
7896
|
-
*
|
|
7842
|
+
* @returns the probability, or undefined when the answer is missing or is not
|
|
7843
|
+
* a `noul` verdict.
|
|
7897
7844
|
*/
|
|
7898
7845
|
function safeProbabilityOf(response, questionId = COMMAND_GUARD_QUESTION_ID) {
|
|
7899
7846
|
const answer = response.answers[questionId];
|
|
@@ -8000,7 +7947,7 @@ var CommandGuard = class {
|
|
|
8000
7947
|
const agentDecisions = request.agent === void 0 ? void 0 : this.decisions.get(request.agent);
|
|
8001
7948
|
const memo = agentDecisions?.get(memoKey);
|
|
8002
7949
|
if (memo !== void 0) {
|
|
8003
|
-
if (now - memo.at < 6e5) return this.verdict(memo
|
|
7950
|
+
if (now - memo.at < 6e5) return this.verdict(memo, settings.threshold, "memo", excerpt, full.sandboxPermissions !== void 0);
|
|
8004
7951
|
agentDecisions?.delete(memoKey);
|
|
8005
7952
|
}
|
|
8006
7953
|
let response;
|
|
@@ -8022,12 +7969,28 @@ var CommandGuard = class {
|
|
|
8022
7969
|
reason: "the decision carried no safe verdict",
|
|
8023
7970
|
command: excerpt
|
|
8024
7971
|
};
|
|
7972
|
+
let escalation;
|
|
7973
|
+
if (full.sandboxPermissions !== void 0) {
|
|
7974
|
+
const scope = safeProbabilityOf(response, COMMAND_GUARD_ESCALATION_SCOPE_QUESTION_ID);
|
|
7975
|
+
const necessity = safeProbabilityOf(response, COMMAND_GUARD_ESCALATION_NECESSITY_QUESTION_ID);
|
|
7976
|
+
if (scope === void 0 || necessity === void 0) return {
|
|
7977
|
+
kind: "delegate",
|
|
7978
|
+
reason: "the decision carried no complete sandbox-escalation verdict",
|
|
7979
|
+
command: excerpt
|
|
7980
|
+
};
|
|
7981
|
+
escalation = {
|
|
7982
|
+
scope,
|
|
7983
|
+
necessity
|
|
7984
|
+
};
|
|
7985
|
+
}
|
|
7986
|
+
const memoEntry = {
|
|
7987
|
+
probability,
|
|
7988
|
+
...escalation === void 0 ? {} : { escalation },
|
|
7989
|
+
at: now
|
|
7990
|
+
};
|
|
8025
7991
|
if (request.agent !== void 0) {
|
|
8026
7992
|
const decisions = agentDecisions ?? /* @__PURE__ */ new Map();
|
|
8027
|
-
decisions.set(memoKey,
|
|
8028
|
-
probability,
|
|
8029
|
-
at: now
|
|
8030
|
-
});
|
|
7993
|
+
decisions.set(memoKey, memoEntry);
|
|
8031
7994
|
while (decisions.size > 64) {
|
|
8032
7995
|
const oldest = decisions.keys().next();
|
|
8033
7996
|
if (oldest.done === true) break;
|
|
@@ -8035,19 +7998,44 @@ var CommandGuard = class {
|
|
|
8035
7998
|
}
|
|
8036
7999
|
this.decisions.set(request.agent, decisions);
|
|
8037
8000
|
}
|
|
8038
|
-
return this.verdict(
|
|
8001
|
+
return this.verdict(memoEntry, settings.threshold, "model", excerpt, full.sandboxPermissions !== void 0);
|
|
8039
8002
|
}
|
|
8040
|
-
/** Apply the threshold to
|
|
8041
|
-
verdict(
|
|
8042
|
-
|
|
8003
|
+
/** Apply the threshold to the primary verdict and any escalation vetoes. */
|
|
8004
|
+
verdict(entry, threshold, source, command, escalated) {
|
|
8005
|
+
if (entry.probability < threshold) return {
|
|
8006
|
+
kind: "delegate",
|
|
8007
|
+
reason: `safe probability ${entry.probability.toFixed(3)} below ${threshold}`,
|
|
8008
|
+
probability: entry.probability,
|
|
8009
|
+
command
|
|
8010
|
+
};
|
|
8011
|
+
if (!escalated) return {
|
|
8043
8012
|
kind: "approve",
|
|
8044
|
-
probability,
|
|
8013
|
+
probability: entry.probability,
|
|
8045
8014
|
source,
|
|
8046
8015
|
command
|
|
8047
|
-
}
|
|
8016
|
+
};
|
|
8017
|
+
if (entry.escalation === void 0) return {
|
|
8048
8018
|
kind: "delegate",
|
|
8049
|
-
reason:
|
|
8050
|
-
|
|
8019
|
+
reason: "the cached sandbox-escalation verdict was incomplete",
|
|
8020
|
+
command
|
|
8021
|
+
};
|
|
8022
|
+
if (entry.escalation.scope < threshold) return {
|
|
8023
|
+
kind: "delegate",
|
|
8024
|
+
reason: `sandbox scope probability ${entry.escalation.scope.toFixed(3)} below ${threshold}`,
|
|
8025
|
+
probability: entry.probability,
|
|
8026
|
+
command
|
|
8027
|
+
};
|
|
8028
|
+
if (entry.escalation.necessity < threshold) return {
|
|
8029
|
+
kind: "delegate",
|
|
8030
|
+
reason: `sandbox necessity probability ${entry.escalation.necessity.toFixed(3)} below ${threshold}`,
|
|
8031
|
+
probability: entry.probability,
|
|
8032
|
+
command
|
|
8033
|
+
};
|
|
8034
|
+
return {
|
|
8035
|
+
kind: "approve",
|
|
8036
|
+
probability: entry.probability,
|
|
8037
|
+
source,
|
|
8038
|
+
...entry.escalation === void 0 ? {} : { escalation: entry.escalation },
|
|
8051
8039
|
command
|
|
8052
8040
|
};
|
|
8053
8041
|
}
|
|
@@ -8085,7 +8073,7 @@ function applyCommandGuard(ctx, deps) {
|
|
|
8085
8073
|
return next();
|
|
8086
8074
|
}
|
|
8087
8075
|
if (verdict.kind === "approve") {
|
|
8088
|
-
deps.log?.(`command guard auto-approved a ${request.toolName} call (safe probability ${verdict.probability.toFixed(3)}, ${verdict.source}): ${verdict.command ?? "(command unknown)"}`);
|
|
8076
|
+
deps.log?.(`command guard auto-approved a ${request.toolName} call (safe probability ${verdict.probability.toFixed(3)}${verdict.escalation === void 0 ? "" : `, sandbox scope ${verdict.escalation.scope.toFixed(3)}, necessity ${verdict.escalation.necessity.toFixed(3)}`}, ${verdict.source}): ${verdict.command ?? "(command unknown)"}`);
|
|
8089
8077
|
return "allowed-once";
|
|
8090
8078
|
}
|
|
8091
8079
|
deps.log?.(`command guard delegated a ${request.toolName} call: ${verdict.reason}${verdict.command === void 0 ? "" : ` — ${verdict.command}`}`);
|
|
@@ -8099,25 +8087,58 @@ function applyCommandGuard(ctx, deps) {
|
|
|
8099
8087
|
//#endregion
|
|
8100
8088
|
//#region src/config-volatile.ts
|
|
8101
8089
|
/**
|
|
8102
|
-
*
|
|
8103
|
-
*
|
|
8104
|
-
*
|
|
8105
|
-
*
|
|
8090
|
+
* Volatile-config helpers for dsh 0.1.7's schema-derived profile Config.
|
|
8091
|
+
*
|
|
8092
|
+
* 0.1.7 rewrote settings around profile Config: `settings.describe()` projects
|
|
8093
|
+
* each active entry's schema through `volatileForm()`, so a form contains ONLY
|
|
8094
|
+
* fields whose schema nodes carry `meta.volatile`, and a form edit is refused
|
|
8095
|
+
* unless its path lies beneath a marked node. On that generation the loader
|
|
8096
|
+
* resolves each marked top-level field into a readonly `Volatile<T>` reference
|
|
8097
|
+
* (`{ get() }`, frozen, written in place by the owning runtime) instead of a
|
|
8098
|
+
* plain value, commits later changes WITHOUT remounting the fiber, and
|
|
8099
|
+
* dispatches `loader/volatile-update` to the owning fiber.
|
|
8100
|
+
*
|
|
8101
|
+
* Two helpers keep the rest of the plugin reading a PLAIN `Config`:
|
|
8102
|
+
*
|
|
8103
|
+
* - `markVolatile(schema)` applies `.volatile()`, so 0.1.7's forms see every
|
|
8104
|
+
* field we mark. The declared type is preserved through the mark on purpose:
|
|
8105
|
+
* it changes what the schema PARSES to (a live reference), not the
|
|
8106
|
+
* plugin-facing `Config` shape every consumer reads.
|
|
8107
|
+
* - `unwrapVolatileConfig(config)` reads every top-level field through its
|
|
8108
|
+
* reference when one is present — returning a FRESH object per call, since
|
|
8109
|
+
* a reference's identity is stable while its value changes — and returns the
|
|
8110
|
+
* config untouched when none is.
|
|
8111
|
+
*
|
|
8112
|
+
* Only TOP-LEVEL fields are marked (form writes name top-level paths such as
|
|
8113
|
+
* `accounts` or `modelVisibility.<id>`, whose first segment is the marked
|
|
8114
|
+
* node), so one unwrap level is complete. The literal `apiKey` secret stays
|
|
8115
|
+
* unmarked on purpose: no settings surface writes it — the settings page and
|
|
8116
|
+
* the dsh-TUI section both write keys through the credentials seam — so it
|
|
8117
|
+
* keeps composition-only semantics, and a config-file edit to it reloading the
|
|
8118
|
+
* fiber is the correct behavior for a secret literal.
|
|
8119
|
+
*
|
|
8120
|
+
* @module dsh-commandcode-provider/config-volatile
|
|
8121
|
+
*/
|
|
8122
|
+
/**
|
|
8123
|
+
* Mark one schema field volatile.
|
|
8124
|
+
*
|
|
8125
|
+
* `.volatile()` is unconditional: the mark is a schemastery 3.18.3 feature and
|
|
8126
|
+
* the only engine this bundle supports (`dsh 0.1.7-rc.1`) pins `~3.18.4`, so
|
|
8127
|
+
* the method is always present. It is reached through a structural member
|
|
8128
|
+
* rather than schemastery's own typing so the function can keep the caller's
|
|
8129
|
+
* declared field type, which is what lets `Config` stay the plain shape every
|
|
8130
|
+
* consumer reads while the schema parses to live references.
|
|
8131
|
+
*
|
|
8132
|
+
* @param schema - The field schema.
|
|
8133
|
+
* @returns The schema carrying `meta.volatile`.
|
|
8106
8134
|
*/
|
|
8107
8135
|
function markVolatile(schema) {
|
|
8108
|
-
|
|
8109
|
-
if (typeof candidate.volatile !== "function") return schema;
|
|
8110
|
-
return candidate.volatile();
|
|
8136
|
+
return schema.volatile();
|
|
8111
8137
|
}
|
|
8112
8138
|
/**
|
|
8113
8139
|
* Mark every field of a Config field dict volatile, except the named
|
|
8114
8140
|
* composition-only secrets (`apiKey` here, which no settings surface writes).
|
|
8115
8141
|
*
|
|
8116
|
-
* The result is a NEW dict of schema instances: callers that also need an
|
|
8117
|
-
* UNMARKED schema for the legacy settings generation must build the fields
|
|
8118
|
-
* twice (`configFields()`), never share one dict — `.volatile()` clones per
|
|
8119
|
-
* call, and a shared instance would carry the mark into the legacy schema.
|
|
8120
|
-
*
|
|
8121
8142
|
* @param fields - Freshly built field schemas.
|
|
8122
8143
|
* @param skip - Field names that must stay unmarked.
|
|
8123
8144
|
* @returns The field dict with every non-skipped field marked.
|
|
@@ -8138,24 +8159,21 @@ function isVolatileRef(value) {
|
|
|
8138
8159
|
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.isFrozen(value) && typeof value.get === "function";
|
|
8139
8160
|
}
|
|
8140
8161
|
/**
|
|
8141
|
-
* Read one config object as PLAIN values
|
|
8162
|
+
* Read one config object as PLAIN values.
|
|
8142
8163
|
*
|
|
8143
|
-
*
|
|
8164
|
+
* Every top-level field found to be a volatile reference is read through
|
|
8144
8165
|
* `.get()` — freshly on every call, so a live update lands on the very next
|
|
8145
|
-
* read.
|
|
8146
|
-
*
|
|
8147
|
-
*
|
|
8166
|
+
* read. The loader resolves EVERY marked node into a reference, unset fields
|
|
8167
|
+
* included, so a loader-parsed config always yields a fresh object here; the
|
|
8168
|
+
* pass-through for a field that is not a reference is what keeps the unmarked
|
|
8169
|
+
* `apiKey` literal readable.
|
|
8148
8170
|
*/
|
|
8149
8171
|
function unwrapVolatileConfig(config) {
|
|
8150
8172
|
const source = config;
|
|
8151
8173
|
if (typeof source !== "object" || source === null) return config;
|
|
8152
|
-
let found = false;
|
|
8153
8174
|
const plain = {};
|
|
8154
|
-
for (const [field, value] of Object.entries(source))
|
|
8155
|
-
|
|
8156
|
-
found = true;
|
|
8157
|
-
} else plain[field] = value;
|
|
8158
|
-
return found ? plain : config;
|
|
8175
|
+
for (const [field, value] of Object.entries(source)) plain[field] = isVolatileRef(value) ? value.get() : value;
|
|
8176
|
+
return plain;
|
|
8159
8177
|
}
|
|
8160
8178
|
//#endregion
|
|
8161
8179
|
//#region src/index.ts
|
|
@@ -8167,10 +8185,9 @@ function unwrapVolatileConfig(config) {
|
|
|
8167
8185
|
* the configurable-provider directory, so the web Models page shows a
|
|
8168
8186
|
* "Command Code" card with an API-key field and the model picker lists the
|
|
8169
8187
|
* live Command Code model catalog. Connection facts resolve per request over
|
|
8170
|
-
* the plugin's live
|
|
8171
|
-
*
|
|
8172
|
-
*
|
|
8173
|
-
* reaches the next request without a restart.
|
|
8188
|
+
* the plugin's live profile Config (volatile fields, unwrapped per read) and
|
|
8189
|
+
* the credential seam, so a changed key, endpoint, or cache path reaches the
|
|
8190
|
+
* next request without a restart.
|
|
8174
8191
|
*
|
|
8175
8192
|
* ```yaml
|
|
8176
8193
|
* - id: llm-commandcode
|
|
@@ -8196,87 +8213,54 @@ const PROVIDER = "commandcode";
|
|
|
8196
8213
|
/** Default models cache path (mirrors the pi plugin's on-disk cache). */
|
|
8197
8214
|
const DEFAULT_MODELS_CACHE_PATH = join(homedir(), ".commandcode", "models-cache.json");
|
|
8198
8215
|
/**
|
|
8199
|
-
* The Config schema
|
|
8200
|
-
*
|
|
8201
|
-
* A FACTORY, not a shared dict, and that is load-bearing: the exported
|
|
8202
|
-
* {@link Config} marks every field volatile while {@link LegacySettingsSchema}
|
|
8203
|
-
* must carry no mark at all, and schemastery's schema instances are
|
|
8204
|
-
* single-purpose objects — sharing one dict would leak the marks into the
|
|
8205
|
-
* legacy registration (see `config-volatile.ts` for why that is a boot-time
|
|
8206
|
-
* `ValidationError` on ≤0.1.6 engines whose schemastery resolved to ≥3.18.3).
|
|
8207
|
-
*
|
|
8208
|
-
* @returns One fresh schema instance per Config field.
|
|
8209
|
-
*/
|
|
8210
|
-
function configFields() {
|
|
8211
|
-
return {
|
|
8212
|
-
apiKeyEnv: z.string().role("credential-ref").default(DEFAULT_API_KEY_ENV),
|
|
8213
|
-
apiKey: z.string().role("secret"),
|
|
8214
|
-
apiBase: z.string(),
|
|
8215
|
-
workingDir: z.string(),
|
|
8216
|
-
modelsCachePath: z.string(),
|
|
8217
|
-
requestTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),
|
|
8218
|
-
streamIdleTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),
|
|
8219
|
-
transportMaxRetries: z.number().min(0).max(50),
|
|
8220
|
-
filterModelsByPlan: z.boolean(),
|
|
8221
|
-
visibleModels: z.array(z.string()),
|
|
8222
|
-
/**
|
|
8223
|
-
* Per-model visibility overrides for the terminal settings page's checkbox
|
|
8224
|
-
* list, keyed by catalog id. dsh-TUI addresses a staged edit by its field
|
|
8225
|
-
* PATH, so two checkboxes sharing one path would overwrite each other's
|
|
8226
|
-
* draft and the section's last model would decide every write; a map gives
|
|
8227
|
-
* each checkbox a path of its own. An id listed here wins over
|
|
8228
|
-
* {@link Config.visibleModels}; ids absent here keep following it.
|
|
8229
|
-
*/
|
|
8230
|
-
modelVisibility: z.dict(z.boolean()),
|
|
8231
|
-
webSearch: z.boolean().default(true),
|
|
8232
|
-
showSidebarQuota: z.boolean().default(false),
|
|
8233
|
-
accounts: z.array(z.object({
|
|
8234
|
-
label: z.string(),
|
|
8235
|
-
apiKeyEnv: z.string().role("credential-ref"),
|
|
8236
|
-
/** Literal key for one extra slot; see the top-level `apiKey` secret note. */
|
|
8237
|
-
apiKey: z.string().role("secret")
|
|
8238
|
-
})),
|
|
8239
|
-
activeAccount: z.string(),
|
|
8240
|
-
modelAccountRules: z.array(z.object({
|
|
8241
|
-
models: z.array(z.string()),
|
|
8242
|
-
account: z.string()
|
|
8243
|
-
})),
|
|
8244
|
-
commandGuard: z.boolean().default(false),
|
|
8245
|
-
commandGuardThreshold: z.number().min(COMMAND_GUARD_MIN_THRESHOLD).max(1),
|
|
8246
|
-
commandGuardTimeoutMs: z.number().min(200).max(COMMAND_GUARD_MAX_TIMEOUT_MS),
|
|
8247
|
-
zdr: z.boolean().default(false),
|
|
8248
|
-
lang: z.string().pattern(/^(zh|en)$/).default("zh")
|
|
8249
|
-
};
|
|
8250
|
-
}
|
|
8251
|
-
/**
|
|
8252
|
-
* The 0.1.7-generation schema: every field volatile except the
|
|
8253
|
-
* composition-only `apiKey` secret.
|
|
8216
|
+
* The Config schema: every field volatile except the composition-only `apiKey`
|
|
8217
|
+
* secret.
|
|
8254
8218
|
*
|
|
8255
8219
|
* dsh 0.1.7's settings forms are projected from the schema's `meta.volatile`
|
|
8256
8220
|
* nodes, so an unmarked field would be invisible to AND unwritable from the
|
|
8257
|
-
* settings page and refused by form-edit path validation — and
|
|
8258
|
-
*
|
|
8259
|
-
*
|
|
8260
|
-
*
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
*
|
|
8275
|
-
*
|
|
8276
|
-
*
|
|
8277
|
-
*
|
|
8278
|
-
|
|
8279
|
-
|
|
8221
|
+
* settings page and refused by form-edit path validation — and the loader
|
|
8222
|
+
* hands `apply()` a live reference per marked field, committing later writes
|
|
8223
|
+
* without remounting this fiber (the `loader/volatile-update` listener at the
|
|
8224
|
+
* bottom of `apply` covers the facts that are not re-derived per read).
|
|
8225
|
+
*/
|
|
8226
|
+
const Config = z.object(markVolatileFields({
|
|
8227
|
+
apiKeyEnv: z.string().role("credential-ref").default(DEFAULT_API_KEY_ENV),
|
|
8228
|
+
apiKey: z.string().role("secret"),
|
|
8229
|
+
apiBase: z.string(),
|
|
8230
|
+
workingDir: z.string(),
|
|
8231
|
+
modelsCachePath: z.string(),
|
|
8232
|
+
requestTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),
|
|
8233
|
+
streamIdleTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS),
|
|
8234
|
+
transportMaxRetries: z.number().min(0).max(50),
|
|
8235
|
+
filterModelsByPlan: z.boolean(),
|
|
8236
|
+
visibleModels: z.array(z.string()),
|
|
8237
|
+
/**
|
|
8238
|
+
* Per-model visibility overrides for the terminal settings page's checkbox
|
|
8239
|
+
* list, keyed by catalog id. dsh-TUI addresses a staged edit by its field
|
|
8240
|
+
* PATH, so two checkboxes sharing one path would overwrite each other's
|
|
8241
|
+
* draft and the section's last model would decide every write; a map gives
|
|
8242
|
+
* each checkbox a path of its own. An id listed here wins over
|
|
8243
|
+
* {@link Config.visibleModels}; ids absent here keep following it.
|
|
8244
|
+
*/
|
|
8245
|
+
modelVisibility: z.dict(z.boolean()),
|
|
8246
|
+
webSearch: z.boolean().default(true),
|
|
8247
|
+
showSidebarQuota: z.boolean().default(false),
|
|
8248
|
+
accounts: z.array(z.object({
|
|
8249
|
+
label: z.string(),
|
|
8250
|
+
apiKeyEnv: z.string().role("credential-ref"),
|
|
8251
|
+
/** Literal key for one extra slot; see the top-level `apiKey` secret note. */
|
|
8252
|
+
apiKey: z.string().role("secret")
|
|
8253
|
+
})),
|
|
8254
|
+
activeAccount: z.string(),
|
|
8255
|
+
modelAccountRules: z.array(z.object({
|
|
8256
|
+
models: z.array(z.string()),
|
|
8257
|
+
account: z.string()
|
|
8258
|
+
})),
|
|
8259
|
+
commandGuard: z.boolean().default(false),
|
|
8260
|
+
commandGuardLevel: z.union([...COMMAND_GUARD_LEVELS]).default(COMMAND_GUARD_DEFAULT_LEVEL),
|
|
8261
|
+
zdr: z.boolean().default(false),
|
|
8262
|
+
lang: z.string().pattern(/^(zh|en)$/).default("zh")
|
|
8263
|
+
}, ["apiKey"]));
|
|
8280
8264
|
/**
|
|
8281
8265
|
* The one explicit resolve step from raw config to validated connection
|
|
8282
8266
|
* facts. Programmatic construction may bypass Schemastery normalization, so
|
|
@@ -8311,19 +8295,8 @@ function readModelVisibility(raw) {
|
|
|
8311
8295
|
}
|
|
8312
8296
|
function apply(ctx, config) {
|
|
8313
8297
|
installCostProjection(ctx);
|
|
8314
|
-
|
|
8315
|
-
const
|
|
8316
|
-
let lastRaw;
|
|
8317
|
-
let lastGood;
|
|
8318
|
-
const options = () => {
|
|
8319
|
-
const raw = current();
|
|
8320
|
-
if (raw === lastRaw && lastGood !== void 0) return lastGood;
|
|
8321
|
-
const next = resolveAdapterOptions(raw);
|
|
8322
|
-
lastRaw = raw;
|
|
8323
|
-
lastGood = next;
|
|
8324
|
-
return next;
|
|
8325
|
-
};
|
|
8326
|
-
options();
|
|
8298
|
+
const current = () => unwrapVolatileConfig(config);
|
|
8299
|
+
const options = () => resolveAdapterOptions(current());
|
|
8327
8300
|
const slots = () => {
|
|
8328
8301
|
const raw = current();
|
|
8329
8302
|
const list = [{
|
|
@@ -8515,8 +8488,8 @@ function apply(ctx, config) {
|
|
|
8515
8488
|
const raw = current();
|
|
8516
8489
|
return {
|
|
8517
8490
|
enabled: raw.commandGuard === true,
|
|
8518
|
-
threshold: raw.
|
|
8519
|
-
timeoutMs:
|
|
8491
|
+
threshold: commandGuardThreshold(raw.commandGuardLevel),
|
|
8492
|
+
timeoutMs: COMMAND_GUARD_TIMEOUT_MS
|
|
8520
8493
|
};
|
|
8521
8494
|
};
|
|
8522
8495
|
ctx.inject(["approval"], (approvalCtx) => {
|
|
@@ -8548,23 +8521,10 @@ function apply(ctx, config) {
|
|
|
8548
8521
|
}, "dsh-commandcode-provider: tui settings handle");
|
|
8549
8522
|
});
|
|
8550
8523
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
service.installSection(ctx, NS, LegacySettingsSchema, unwrapVolatileConfig(config), {
|
|
8554
|
-
setSource: (next) => {
|
|
8555
|
-
source = next;
|
|
8556
|
-
},
|
|
8557
|
-
onChange: () => {
|
|
8558
|
-
applySearchSelection(current().webSearch ?? true);
|
|
8559
|
-
refreshTuiSettings?.();
|
|
8560
|
-
}
|
|
8561
|
-
});
|
|
8562
|
-
return;
|
|
8563
|
-
}
|
|
8564
|
-
if (typeof service.configure === "function") settingsCtx.effect(() => {
|
|
8565
|
-
const dispose = service.configure?.({ auto: false }, ctx.fiber);
|
|
8524
|
+
settingsCtx.effect(() => {
|
|
8525
|
+
const dispose = settingsCtx.settings.configure({ auto: false }, ctx.fiber);
|
|
8566
8526
|
return () => {
|
|
8567
|
-
dispose
|
|
8527
|
+
dispose();
|
|
8568
8528
|
};
|
|
8569
8529
|
}, "dsh-commandcode-provider: settings auto-form policy");
|
|
8570
8530
|
});
|
|
@@ -8580,6 +8540,6 @@ function apply(ctx, config) {
|
|
|
8580
8540
|
}, "dsh-commandcode-provider: volatile config updates");
|
|
8581
8541
|
}
|
|
8582
8542
|
//#endregion
|
|
8583
|
-
export { ACTIVE_ACCOUNT_AUTO, BILLING_ACCESS_TTL_MS, COMMANDCODE_SEARCH_PROVIDER_ID, COMMAND_CODE_CLI_VERSION, CommandCodeAccountPool, CommandCodeAdapter, CommandCodeLoginFlow, CommandCodeSearchProvider, CommandCodeUsageService, Config, DEFAULT_API_BASE, DEFAULT_GENERATE_MAX_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_MODELS_CACHE_PATH, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, DEFAULT_WEB_SEARCH_PROVIDER_ID, KNOWN_DEALS, KNOWN_EFFORTS, KNOWN_IMAGE_MODELS, KNOWN_NON_ZDR_MODELS, KNOWN_PEAK_PRICING, KNOWN_PLANS, KNOWN_SUBSCRIPTION_PLANS, KNOWN_THINKING_MODELS, LANG_AUTO, LOGIN_ALLOWED_ORIGINS, LOGIN_BEGIN_ENDPOINT, LOGIN_BODY_LIMIT_BYTES, LOGIN_CANCEL_ENDPOINT, LOGIN_MAX_PORT_ATTEMPTS, LOGIN_START_PORT, LOGIN_STATUS_ENDPOINT, LOGIN_TIMEOUT_MS,
|
|
8543
|
+
export { ACTIVE_ACCOUNT_AUTO, BILLING_ACCESS_TTL_MS, COMMANDCODE_SEARCH_PROVIDER_ID, COMMAND_CODE_CLI_VERSION, CommandCodeAccountPool, CommandCodeAdapter, CommandCodeLoginFlow, CommandCodeSearchProvider, CommandCodeUsageService, Config, DEFAULT_API_BASE, DEFAULT_GENERATE_MAX_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_MODELS_CACHE_PATH, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, DEFAULT_WEB_SEARCH_PROVIDER_ID, KNOWN_DEALS, KNOWN_EFFORTS, KNOWN_IMAGE_MODELS, KNOWN_NON_ZDR_MODELS, KNOWN_PEAK_PRICING, KNOWN_PLANS, KNOWN_SUBSCRIPTION_PLANS, KNOWN_THINKING_MODELS, LANG_AUTO, LOGIN_ALLOWED_ORIGINS, LOGIN_BEGIN_ENDPOINT, LOGIN_BODY_LIMIT_BYTES, LOGIN_CANCEL_ENDPOINT, LOGIN_MAX_PORT_ATTEMPTS, LOGIN_START_PORT, LOGIN_STATUS_ENDPOINT, LOGIN_TIMEOUT_MS, PLAN_LABELS, PLAN_ORDER, PROVIDER, USAGE_REPORT_ENDPOINT, accountUsable, apply, applyCommandCodeSearchSelection, applyCommandCodeTuiSettings, applyCommands, applyUsageRemote, buildCommandAuthUrl, buildCommandCodeTuiSection, capabilityDescription, commandCodeSearchSelection, commandDefinition, compareByPlan, dealLabel, formatContext, inject, loginStatusSchema, matchModelRule, modelVisibleInPlan, name, parseLoginStatus, peakPricingLabel, peakPricingState, planLabel, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey, selectAccountForModel, selectActiveAccount, studioBaseForApiBase, subscriptionPlanInfo, supportsZeroDataRetention, usageReportSchema, validateCommandApiKey };
|
|
8584
8544
|
|
|
8585
8545
|
//# sourceMappingURL=index.js.map
|