@wrongstack/cli 1.0.4 → 1.0.7
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/dist/{acp-AB54FWM2.js → acp-A5EQEXQO.js} +69 -11
- package/dist/{auth-WDMSNZ3S.js → auth-3K6EN5JP.js} +3 -3
- package/dist/boot/container-wiring.d.ts +2 -0
- package/dist/boot/short-circuit-desktop.d.ts +1 -0
- package/dist/{chunk-VUVOMXWP.js → chunk-3VXYY5JC.js} +8 -1
- package/dist/{chunk-U6PGWDMZ.js → chunk-4OLAZIIN.js} +2 -2
- package/dist/{chunk-I7V2OYVH.js → chunk-5EA6OTLJ.js} +3 -6
- package/dist/{chunk-O4A3HHKD.js → chunk-672UXYL3.js} +2 -2
- package/dist/{chunk-ONOT5OJ2.js → chunk-FSZ5QU3M.js} +2 -2
- package/dist/{chunk-KNMIBKLV.js → chunk-JY52HWL6.js} +47 -1
- package/dist/{chunk-BIWE22O5.js → chunk-RLCKKLJA.js} +8 -8
- package/dist/{chunk-VFCJOWJB.js → chunk-RMQNCHGO.js} +8 -7
- package/dist/{chunk-3I5ZJXM5.js → chunk-XIZZ2EXG.js} +161 -62
- package/dist/{cli-context-WFC3BNGM.js → cli-context-TIAP6YQK.js} +95 -15
- package/dist/cli-context.d.ts +2 -0
- package/dist/{cli-main-GU3YZLMO.js → cli-main-AKJHDSHX.js} +1111 -721
- package/dist/config-doctor.d.ts +1 -1
- package/dist/execute-deps.d.ts +3 -1
- package/dist/{execution-RAZDDQFC.js → execution-FJE27VQ6.js} +105 -28
- package/dist/{hq-5XHEIIH6.js → hq-G5PCP325.js} +8 -6
- package/dist/hq-command-controller.d.ts +15 -0
- package/dist/hq-server/auth-state.d.ts +6 -0
- package/dist/hq-server/auth.d.ts +10 -1
- package/dist/hq-server/routes/auth/common.d.ts +34 -0
- package/dist/hq-server/routes/auth/password-routes.d.ts +4 -2
- package/dist/hq-server/types.d.ts +18 -0
- package/dist/{hq-server-CNUTBWTK.js → hq-server-G3VVVMIG.js} +3 -3
- package/dist/index.js +8 -5
- package/dist/input-reader.d.ts +2 -2
- package/dist/{mcp-HOMLC4WE.js → mcp-FX7TKPTV.js} +10 -8
- package/dist/mcp-serve.d.ts +31 -0
- package/dist/{modeldiag-46CMWUNM.js → modeldiag-CJSFHZ3J.js} +55 -31
- package/dist/permission-prompt-mirror.d.ts +70 -0
- package/dist/permission-prompt.d.ts +10 -1
- package/dist/{providers-models-6MOGAOVK.js → providers-models-LFNZ5WI7.js} +2 -2
- package/dist/{short-circuit-flags-YDMLAQ5R.js → short-circuit-flags-RAT6UZVI.js} +2 -2
- package/dist/slash-commands/subagent-models.d.ts +4 -0
- package/dist/subagent-models/panel-service.d.ts +28 -0
- package/dist/subcommands/handlers/modeldiag-test.d.ts +7 -0
- package/dist/{subcommands-7N4RIK6H.js → subcommands-VFVUTYAV.js} +2 -2
- package/dist/webui-server/prefs-seeding.d.ts +8 -0
- package/dist/{webui-server-OBV5JYJV.js → webui-server-3NYXBZHX.js} +36 -12
- package/dist/wiring/hq-telemetry.d.ts +7 -0
- package/package.json +28 -28
|
@@ -23,7 +23,11 @@ import { pathToFileURL } from "node:url";
|
|
|
23
23
|
import { Context } from "@wrongstack/core/agent";
|
|
24
24
|
import { ToolExecutor } from "@wrongstack/core/execution";
|
|
25
25
|
import { ToolRegistry } from "@wrongstack/core/registry";
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
AutoApprovePermissionPolicy,
|
|
28
|
+
DefaultSecretScrubber,
|
|
29
|
+
ToolCapabilities
|
|
30
|
+
} from "@wrongstack/core/security";
|
|
27
31
|
import { normalizeTokenSavingTier } from "@wrongstack/core/types";
|
|
28
32
|
import {
|
|
29
33
|
DEFAULT_MCP_INSERTION_MAX_BYTES,
|
|
@@ -33,11 +37,9 @@ import {
|
|
|
33
37
|
} from "@wrongstack/mcp";
|
|
34
38
|
import { registerBuiltinToolTier } from "@wrongstack/tools/tool-tier";
|
|
35
39
|
import { wireKanbanPorts } from "@wrongstack/runtime";
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
};
|
|
40
|
+
function yoloServePolicy() {
|
|
41
|
+
return new AutoApprovePermissionPolicy(Object.values(ToolCapabilities));
|
|
42
|
+
}
|
|
41
43
|
function parseToolsFlag(flags, positional) {
|
|
42
44
|
const raw = flags["tools"];
|
|
43
45
|
const csv = typeof raw === "string" ? raw : raw === true ? positional?.[0] ?? "" : "";
|
|
@@ -226,7 +228,7 @@ async function serveMcpStdio(deps, positional) {
|
|
|
226
228
|
controller.signal,
|
|
227
229
|
resolveServeFsRestriction(deps.config)
|
|
228
230
|
);
|
|
229
|
-
const permissionPolicy = yolo ?
|
|
231
|
+
const permissionPolicy = yolo ? yoloServePolicy() : new AutoApprovePermissionPolicy();
|
|
230
232
|
const executor = new ToolExecutor(registry, {
|
|
231
233
|
permissionPolicy,
|
|
232
234
|
secretScrubber: new DefaultSecretScrubber(),
|
|
@@ -440,4 +442,4 @@ function isRecord(value) {
|
|
|
440
442
|
export {
|
|
441
443
|
mcpCmd
|
|
442
444
|
};
|
|
443
|
-
//# sourceMappingURL=mcp-
|
|
445
|
+
//# sourceMappingURL=mcp-FX7TKPTV.js.map
|
package/dist/mcp-serve.d.ts
CHANGED
|
@@ -1,8 +1,39 @@
|
|
|
1
1
|
import { Context } from '@wrongstack/core/agent';
|
|
2
2
|
import { ToolRegistry } from '@wrongstack/core/registry';
|
|
3
|
+
import { AutoApprovePermissionPolicy } from '@wrongstack/core/security';
|
|
3
4
|
import type { PermissionPolicy, Tool } from '@wrongstack/core/types';
|
|
4
5
|
import { type MCPServerPrompt, type MCPServerResource } from '@wrongstack/mcp';
|
|
5
6
|
import type { SubcommandDeps } from './subcommands/contracts.js';
|
|
7
|
+
/**
|
|
8
|
+
* `--yolo` for `mcp serve`: widen the CAPABILITY allowlist, keep every guard.
|
|
9
|
+
*
|
|
10
|
+
* This replaced an `AllowAllPermissionPolicy extends AutoApprovePermissionPolicy`
|
|
11
|
+
* whose `evaluate()` returned `{ permission: 'auto' }` unconditionally. That
|
|
12
|
+
* override skipped the whole base implementation — the sensitive-read denial,
|
|
13
|
+
* the leader deny rules, the destructive-command classifier, the agent-state
|
|
14
|
+
* write guard, and `LOCKED_DESTRUCTIVE_KINDS`. It was the only place in the
|
|
15
|
+
* codebase where `agent-state` and `credential-bind` could be un-gated, so a
|
|
16
|
+
* third-party MCP client could have written `hooks` into config.json (boot-time
|
|
17
|
+
* RCE) or `auto: true` into trust.json.
|
|
18
|
+
*
|
|
19
|
+
* It did not actually get there — but only by accident, and the accident is the
|
|
20
|
+
* reason this is a rewrite rather than a patch. The override returned
|
|
21
|
+
* `source: 'default'` where the base class returns `source: 'yolo'`, so
|
|
22
|
+
* `ToolExecutor.capabilityDowngraded` re-armed and forced `confirm`; `mcp serve`
|
|
23
|
+
* has no confirmAwaiter, so the call hard-errored. Changing that one string
|
|
24
|
+
* literal to `'yolo'` — which reads as a trivial consistency cleanup — would
|
|
25
|
+
* have made the bypass live. Safety must not rest on a string nobody knows is
|
|
26
|
+
* load-bearing.
|
|
27
|
+
*
|
|
28
|
+
* Granting every capability is what `--yolo` actually means, and it is what
|
|
29
|
+
* lets `ToolExecutor` trust an `auto` instead of downgrading it (see the
|
|
30
|
+
* `dangerousNotAllowed` reasoning in `auto-approve-policy.ts`). The destructive
|
|
31
|
+
* classifier is deliberately NOT relaxed: `yoloConfirmKinds` is left at its
|
|
32
|
+
* default, which gates every destructive kind. Destructive calls therefore
|
|
33
|
+
* still require a confirmation this surface cannot answer, exactly as before —
|
|
34
|
+
* this change removes a latent bypass without widening anything.
|
|
35
|
+
*/
|
|
36
|
+
export declare function yoloServePolicy(): AutoApprovePermissionPolicy;
|
|
6
37
|
/**
|
|
7
38
|
* Resolve the `--tools` whitelist for `mcp serve`.
|
|
8
39
|
*
|
|
@@ -52,6 +52,17 @@ var MODEL_PROFILES = [
|
|
|
52
52
|
costTier: "premium",
|
|
53
53
|
speedTier: "normal"
|
|
54
54
|
},
|
|
55
|
+
{
|
|
56
|
+
provider: "openai",
|
|
57
|
+
// Must precede /gpt-4/ — every gpt-4o-mini id also contains gpt-4.
|
|
58
|
+
pattern: /gpt-4o-mini/i,
|
|
59
|
+
family: "GPT-4o Mini",
|
|
60
|
+
strengths: ["speed"],
|
|
61
|
+
bestFor: ["lightweight", "docs"],
|
|
62
|
+
avoidFor: ["planning"],
|
|
63
|
+
costTier: "budget",
|
|
64
|
+
speedTier: "fast"
|
|
65
|
+
},
|
|
55
66
|
{
|
|
56
67
|
provider: "openai",
|
|
57
68
|
pattern: /gpt-4/i,
|
|
@@ -62,9 +73,11 @@ var MODEL_PROFILES = [
|
|
|
62
73
|
speedTier: "fast"
|
|
63
74
|
},
|
|
64
75
|
{
|
|
65
|
-
provider: "
|
|
66
|
-
|
|
67
|
-
|
|
76
|
+
provider: "google",
|
|
77
|
+
// Must precede /gemini-(?:2\.5|3)/ — flash variants (gemini-2.5-flash,
|
|
78
|
+
// gemini-3-flash, …) are budget/fast models and need this profile.
|
|
79
|
+
pattern: /gemini.*flash/i,
|
|
80
|
+
family: "Gemini Flash",
|
|
68
81
|
strengths: ["speed"],
|
|
69
82
|
bestFor: ["lightweight", "docs"],
|
|
70
83
|
avoidFor: ["planning"],
|
|
@@ -80,16 +93,6 @@ var MODEL_PROFILES = [
|
|
|
80
93
|
costTier: "standard",
|
|
81
94
|
speedTier: "normal"
|
|
82
95
|
},
|
|
83
|
-
{
|
|
84
|
-
provider: "google",
|
|
85
|
-
pattern: /gemini.*flash/i,
|
|
86
|
-
family: "Gemini Flash",
|
|
87
|
-
strengths: ["speed"],
|
|
88
|
-
bestFor: ["lightweight", "docs"],
|
|
89
|
-
avoidFor: ["planning"],
|
|
90
|
-
costTier: "budget",
|
|
91
|
-
speedTier: "fast"
|
|
92
|
-
},
|
|
93
96
|
{
|
|
94
97
|
provider: "deepseek",
|
|
95
98
|
pattern: /deepseek/i,
|
|
@@ -685,7 +688,11 @@ async function runModeldiagBench(args, deps, providers, config, hasKey) {
|
|
|
685
688
|
|
|
686
689
|
// src/subcommands/handlers/modeldiag-test.ts
|
|
687
690
|
import { color as color4, toErrorMessage as toErrorMessage3 } from "@wrongstack/core/utils";
|
|
688
|
-
import {
|
|
691
|
+
import {
|
|
692
|
+
buildProviderFactoriesFromRegistry,
|
|
693
|
+
makeProviderFromConfig as makeProviderFromConfig2,
|
|
694
|
+
setOAuthTokenPersister
|
|
695
|
+
} from "@wrongstack/providers";
|
|
689
696
|
|
|
690
697
|
// src/subcommands/handlers/model-smoke-test.ts
|
|
691
698
|
import { ProviderError } from "@wrongstack/core/types";
|
|
@@ -876,6 +883,28 @@ async function runModelSmokeTests(params) {
|
|
|
876
883
|
}
|
|
877
884
|
|
|
878
885
|
// src/subcommands/handlers/modeldiag-test.ts
|
|
886
|
+
async function createModelDiagSmokeProvider(params) {
|
|
887
|
+
const { providerId, config, modelsRegistry, providerFactories } = params;
|
|
888
|
+
const saved = config.providers?.[providerId];
|
|
889
|
+
const factoryType = saved?.type ?? providerId;
|
|
890
|
+
const resolved = await modelsRegistry.getProvider(providerId).catch(() => void 0) ?? (factoryType !== providerId ? await modelsRegistry.getProvider(factoryType).catch(() => void 0) : void 0);
|
|
891
|
+
const providerConfig = {
|
|
892
|
+
...providerId === config.provider ? {
|
|
893
|
+
...config.apiKey ? { apiKey: config.apiKey } : {},
|
|
894
|
+
...config.baseUrl ? { baseUrl: config.baseUrl } : {}
|
|
895
|
+
} : {},
|
|
896
|
+
...saved,
|
|
897
|
+
// Keep the user-visible alias on the factory input. The selected factory
|
|
898
|
+
// already captures the canonical models.dev provider and its per-model
|
|
899
|
+
// wire metadata.
|
|
900
|
+
type: providerId,
|
|
901
|
+
...saved?.family ?? resolved?.family ? { family: saved?.family ?? resolved?.family } : {},
|
|
902
|
+
...saved?.baseUrl ?? resolved?.apiBase ? { baseUrl: saved?.baseUrl ?? resolved?.apiBase } : {},
|
|
903
|
+
...saved?.envVars ?? resolved?.envVars ? { envVars: saved?.envVars ?? resolved?.envVars } : {}
|
|
904
|
+
};
|
|
905
|
+
const factory = providerFactories.get(factoryType);
|
|
906
|
+
return factory ? factory.create(providerConfig) : makeProviderFromConfig2(providerId, providerConfig);
|
|
907
|
+
}
|
|
879
908
|
async function runModeldiagTest(args, deps, config) {
|
|
880
909
|
const writeLine = (line = "") => {
|
|
881
910
|
deps.renderer.write(`${line}
|
|
@@ -1010,25 +1039,20 @@ async function runModeldiagTest(args, deps, config) {
|
|
|
1010
1039
|
});
|
|
1011
1040
|
let results;
|
|
1012
1041
|
try {
|
|
1042
|
+
const providerFactories = new Map(
|
|
1043
|
+
(await buildProviderFactoriesFromRegistry({ registry: deps.modelsRegistry })).map(
|
|
1044
|
+
(factory) => [factory.type, factory]
|
|
1045
|
+
)
|
|
1046
|
+
);
|
|
1013
1047
|
results = await runModelSmokeTests({
|
|
1014
1048
|
targets,
|
|
1015
1049
|
options: smokeOptions,
|
|
1016
|
-
createProvider:
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
...config.baseUrl ? { baseUrl: config.baseUrl } : {}
|
|
1023
|
-
} : {},
|
|
1024
|
-
...saved,
|
|
1025
|
-
type: providerId,
|
|
1026
|
-
...saved?.family ?? resolved?.family ? { family: saved?.family ?? resolved?.family } : {},
|
|
1027
|
-
...saved?.baseUrl ?? resolved?.apiBase ? { baseUrl: saved?.baseUrl ?? resolved?.apiBase } : {},
|
|
1028
|
-
...saved?.envVars ?? resolved?.envVars ? { envVars: saved?.envVars ?? resolved?.envVars } : {}
|
|
1029
|
-
};
|
|
1030
|
-
return makeProviderFromConfig2(providerId, providerConfig);
|
|
1031
|
-
},
|
|
1050
|
+
createProvider: (providerId) => createModelDiagSmokeProvider({
|
|
1051
|
+
providerId,
|
|
1052
|
+
config,
|
|
1053
|
+
modelsRegistry: deps.modelsRegistry,
|
|
1054
|
+
providerFactories
|
|
1055
|
+
}),
|
|
1032
1056
|
onTargetComplete: resultLines
|
|
1033
1057
|
});
|
|
1034
1058
|
} finally {
|
|
@@ -1329,4 +1353,4 @@ var modeldiagCmd = async (args, deps) => {
|
|
|
1329
1353
|
export {
|
|
1330
1354
|
modeldiagCmd
|
|
1331
1355
|
};
|
|
1332
|
-
//# sourceMappingURL=modeldiag-
|
|
1356
|
+
//# sourceMappingURL=modeldiag-CJSFHZ3J.js.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mirror the plain REPL's permission prompt to HQ.
|
|
3
|
+
*
|
|
4
|
+
* Every other surface converges on the executor's confirm path, so one
|
|
5
|
+
* listener on the EventBus mirrors them all. The REPL does not: its prompt is
|
|
6
|
+
* asked by the permission policy's own `promptDelegate` inside `evaluate()`,
|
|
7
|
+
* which never reaches that path and raises no event. Without this wrapper the
|
|
8
|
+
* REPL is the one surface whose approvals are invisible from HQ — a partial
|
|
9
|
+
* guarantee that is worse than either a whole one or none, because an operator
|
|
10
|
+
* cannot tell which kind of session they are looking at.
|
|
11
|
+
*
|
|
12
|
+
* Two things make this honest rather than a hack:
|
|
13
|
+
*
|
|
14
|
+
* - **The terminal read is cancellable.** When HQ answers first, the pending
|
|
15
|
+
* `readKey` is aborted so stdin comes out of raw mode and the user's next
|
|
16
|
+
* keystroke is not eaten by a question nobody is asking.
|
|
17
|
+
* - **The mirrored card has a heartbeat, not a fake deadline.** A terminal
|
|
18
|
+
* prompt waits for a keypress however long that takes, so it has no natural
|
|
19
|
+
* deadline to publish — but a card with no deadline breaks the single
|
|
20
|
+
* invariant the dashboard relies on ("pending cannot outlive its
|
|
21
|
+
* deadline"), and a card with a fake one vanishes while the question is
|
|
22
|
+
* still on screen. Renewing a short window on a timer keeps both true.
|
|
23
|
+
*
|
|
24
|
+
* The terminal prompt's own behaviour is deliberately unchanged: it still
|
|
25
|
+
* blocks until answered, with no timeout of its own.
|
|
26
|
+
*
|
|
27
|
+
* @module permission-prompt-mirror
|
|
28
|
+
*/
|
|
29
|
+
import type { ApprovalRegistry } from '@wrongstack/core/hq';
|
|
30
|
+
import type { Tool } from '@wrongstack/core/types';
|
|
31
|
+
type PromptDecision = 'yes' | 'no' | 'always' | 'deny';
|
|
32
|
+
type PromptDelegate = (tool: Tool, input: unknown, suggestedPattern: string) => Promise<PromptDecision>;
|
|
33
|
+
type AbortablePromptDelegate = (tool: Tool, input: unknown, suggestedPattern: string, signal: AbortSignal) => Promise<PromptDecision>;
|
|
34
|
+
/**
|
|
35
|
+
* How long a mirrored REPL card stays pending without a renewal, and how often
|
|
36
|
+
* it is renewed. The gap between them is the worst-case time a card outlives
|
|
37
|
+
* the process that raised it — a laptop that sleeps mid-prompt should not park
|
|
38
|
+
* a permanent card on someone's dashboard.
|
|
39
|
+
*/
|
|
40
|
+
export declare const REPL_MIRROR_TTL_MS = 60000;
|
|
41
|
+
export declare const REPL_MIRROR_HEARTBEAT_MS = 20000;
|
|
42
|
+
export interface MirroredPromptDelegateDeps {
|
|
43
|
+
/** The real terminal prompt. Must honour the abort signal it is handed. */
|
|
44
|
+
inner: AbortablePromptDelegate;
|
|
45
|
+
/**
|
|
46
|
+
* Looked up per call, not captured: the registry is created during boot and
|
|
47
|
+
* this delegate is built before it exists. Returning undefined simply means
|
|
48
|
+
* no mirroring — the terminal prompt still works.
|
|
49
|
+
*/
|
|
50
|
+
getRegistry: () => ApprovalRegistry | undefined;
|
|
51
|
+
/** The session the prompt belongs to, so an HQ answer lands on it. */
|
|
52
|
+
getSessionId?: (() => string | undefined) | undefined;
|
|
53
|
+
now?: (() => number) | undefined;
|
|
54
|
+
}
|
|
55
|
+
export declare function makeMirroredPromptDelegate(deps: MirroredPromptDelegateDeps): PromptDelegate;
|
|
56
|
+
/**
|
|
57
|
+
* Late-bound handle to the approval registry.
|
|
58
|
+
*
|
|
59
|
+
* The prompt delegate is built during container wiring; the registry is
|
|
60
|
+
* created later, once the HQ telemetry layer comes up. Rather than reorder
|
|
61
|
+
* boot for a feature that is optional by nature, the delegate reads through
|
|
62
|
+
* this holder at call time — a prompt only ever fires long after both exist.
|
|
63
|
+
* Empty simply means no mirroring.
|
|
64
|
+
*/
|
|
65
|
+
export interface ApprovalMirrorRef {
|
|
66
|
+
current?: ApprovalRegistry | undefined;
|
|
67
|
+
sessionId?: (() => string | undefined) | undefined;
|
|
68
|
+
}
|
|
69
|
+
export {};
|
|
70
|
+
//# sourceMappingURL=permission-prompt-mirror.d.ts.map
|
|
@@ -2,7 +2,16 @@ import type { InputReader, Tool } from '@wrongstack/core/types';
|
|
|
2
2
|
type PromptDecision = 'yes' | 'no' | 'always' | 'deny';
|
|
3
3
|
/** Signature the Agent expects for confirming tool calls. */
|
|
4
4
|
export type ConfirmAwaiter = (tool: Tool, input: unknown, toolUseId: string, suggestedPattern: string) => Promise<'yes' | 'no' | 'always' | 'deny'>;
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The terminal approval prompt.
|
|
7
|
+
*
|
|
8
|
+
* `signal` exists because the same question can now be answered somewhere else
|
|
9
|
+
* — from the HQ dashboard — while this prompt is on screen. Aborting takes it
|
|
10
|
+
* down and, crucially, gets stdin back out of raw mode; see
|
|
11
|
+
* `permission-prompt-mirror.ts`. Absent, behaviour is exactly as before: the
|
|
12
|
+
* prompt blocks until a key is pressed.
|
|
13
|
+
*/
|
|
14
|
+
export declare function makePromptDelegate(reader: InputReader): (tool: Tool, input: unknown, suggestedPattern: string, signal?: AbortSignal) => Promise<PromptDecision>;
|
|
6
15
|
/**
|
|
7
16
|
* Create a ConfirmAwaiter for the CLI path. Wraps makePromptDelegate
|
|
8
17
|
* with the ConfirmAwaiter type signature expected by the Agent.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
visibleModelIds
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-3VXYY5JC.js";
|
|
4
4
|
import {
|
|
5
5
|
activeProfileConfigPath
|
|
6
6
|
} from "./chunk-YMXXOOFN.js";
|
|
@@ -660,4 +660,4 @@ export {
|
|
|
660
660
|
modelsCmd,
|
|
661
661
|
providersCmd
|
|
662
662
|
};
|
|
663
|
-
//# sourceMappingURL=providers-models-
|
|
663
|
+
//# sourceMappingURL=providers-models-LFNZ5WI7.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handleHelpVersionShortCircuit
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4OLAZIIN.js";
|
|
4
4
|
import "./chunk-6WRKAACA.js";
|
|
5
5
|
import "./chunk-XJXDOF63.js";
|
|
6
6
|
import "./chunk-EK2P53GL.js";
|
|
7
7
|
export {
|
|
8
8
|
handleHelpVersionShortCircuit
|
|
9
9
|
};
|
|
10
|
-
//# sourceMappingURL=short-circuit-flags-
|
|
10
|
+
//# sourceMappingURL=short-circuit-flags-RAT6UZVI.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host bridge for the TUI `/subagent-models` panel.
|
|
3
|
+
*
|
|
4
|
+
* The panel is presentational; the session-scoped plan lives in core and is
|
|
5
|
+
* journaled through the session writer, which only the CLI has. Mutations
|
|
6
|
+
* return an error STRING rather than throwing so a rejected write lands as a
|
|
7
|
+
* panel hint instead of unmounting the TUI.
|
|
8
|
+
*/
|
|
9
|
+
import { setSessionSubagentModelPlan } from '@wrongstack/core/coordination';
|
|
10
|
+
import type { SubagentModelsPanelHost } from '@wrongstack/tui';
|
|
11
|
+
/** The slice of the agent context the plan writer needs. */
|
|
12
|
+
type PlanWriteContext = Parameters<typeof setSessionSubagentModelPlan>[0];
|
|
13
|
+
export interface SubagentModelsPanelServiceDeps {
|
|
14
|
+
/** The session's own provider/model — the target of "use session model". */
|
|
15
|
+
getSessionTarget?: (() => {
|
|
16
|
+
provider?: string;
|
|
17
|
+
model?: string;
|
|
18
|
+
}) | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Live agent context — re-read on every call so a session swap (F1/F10,
|
|
21
|
+
* /resume) is picked up. Typed off the core writer contract rather than the
|
|
22
|
+
* full AgentContext so this bridge only depends on what it actually uses.
|
|
23
|
+
*/
|
|
24
|
+
getContext: () => PlanWriteContext | undefined;
|
|
25
|
+
}
|
|
26
|
+
export declare function createSubagentModelsPanelHost(deps: SubagentModelsPanelServiceDeps): SubagentModelsPanelHost;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=panel-service.d.ts.map
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module subcommands/handlers/modeldiag-test
|
|
5
5
|
*/
|
|
6
|
+
import type { Config, ModelsRegistry, Provider, ProviderFactory } from '@wrongstack/core/types';
|
|
6
7
|
import type { SubcommandDeps } from '../contracts.js';
|
|
8
|
+
export declare function createModelDiagSmokeProvider(params: {
|
|
9
|
+
providerId: string;
|
|
10
|
+
config: Config;
|
|
11
|
+
modelsRegistry: ModelsRegistry;
|
|
12
|
+
providerFactories: ReadonlyMap<string, ProviderFactory>;
|
|
13
|
+
}): Promise<Provider>;
|
|
7
14
|
export declare function runModeldiagTest(args: string[], deps: SubcommandDeps, config: Record<string, unknown>): Promise<number>;
|
|
8
15
|
//# sourceMappingURL=modeldiag-test.d.ts.map
|
|
@@ -10,9 +10,17 @@ interface CliWebUIOptions {
|
|
|
10
10
|
globalConfigPath?: string | undefined;
|
|
11
11
|
profileConfigPath: string;
|
|
12
12
|
appConfig?: {
|
|
13
|
+
/**
|
|
14
|
+
* The live default selection. Carried here so a model switch can move
|
|
15
|
+
* it in-process, not just in config.json — `session.new` reads the
|
|
16
|
+
* live object to stamp a new tab.
|
|
17
|
+
*/
|
|
18
|
+
provider?: string | undefined;
|
|
19
|
+
model?: string | undefined;
|
|
13
20
|
fallbackModels?: string[] | undefined;
|
|
14
21
|
fallbackProfiles?: Record<string, string[]> | undefined;
|
|
15
22
|
favoriteModels?: string[] | undefined;
|
|
23
|
+
disabledModels?: string[] | undefined;
|
|
16
24
|
favoriteModelsOnly?: boolean | undefined;
|
|
17
25
|
modelAvailabilitySchedule?: ModelBlackoutRule[] | undefined;
|
|
18
26
|
fallbackAuto?: boolean | undefined;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createHqCommandDispatcher,
|
|
3
|
-
createProjectKanbanAssignHandler,
|
|
4
|
-
createProjectKanbanTransitionHandler
|
|
5
|
-
} from "./chunk-KNMIBKLV.js";
|
|
6
1
|
import {
|
|
7
2
|
createKanbanRunMirror
|
|
8
3
|
} from "./chunk-VCYZ3WZV.js";
|
|
9
4
|
import {
|
|
10
5
|
WEBUI_SESSION_CHILD_CAPABILITIES
|
|
11
6
|
} from "./chunk-GPPRNAKW.js";
|
|
7
|
+
import {
|
|
8
|
+
createHqCommandDispatcher,
|
|
9
|
+
createProjectKanbanAssignHandler,
|
|
10
|
+
createProjectKanbanTransitionHandler
|
|
11
|
+
} from "./chunk-JY52HWL6.js";
|
|
12
12
|
import {
|
|
13
13
|
startCliHqConnection
|
|
14
14
|
} from "./chunk-TE3Q32JP.js";
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
envFlag,
|
|
38
38
|
findFreePort,
|
|
39
39
|
findInstalledPackageJson,
|
|
40
|
+
integrationConnectSources,
|
|
40
41
|
isStrictPort,
|
|
41
42
|
resolveAuthToken,
|
|
42
43
|
resolvePendingConfirmsForSession,
|
|
@@ -47,8 +48,8 @@ import {
|
|
|
47
48
|
startWebUILiveStatusLogger,
|
|
48
49
|
toSessionHistoryEntries
|
|
49
50
|
} from "@wrongstack/webui-server";
|
|
50
|
-
import { WebSocketServer } from "ws";
|
|
51
51
|
import { verifyClient as verifyWsClient2 } from "@wrongstack/webui-server/server/ws-auth";
|
|
52
|
+
import { WebSocketServer } from "ws";
|
|
52
53
|
|
|
53
54
|
// src/webui-server/client-registration.ts
|
|
54
55
|
import { mailboxSessionTag } from "@wrongstack/core/coordination";
|
|
@@ -69,10 +70,13 @@ function createWebuiClientRegistration(deps) {
|
|
|
69
70
|
capabilities: options.capabilities
|
|
70
71
|
}),
|
|
71
72
|
...control ? {
|
|
72
|
-
createCommandHandler: (mailbox) => createHqCommandDispatcher({
|
|
73
|
+
createCommandHandler: (mailbox, approvals) => createHqCommandDispatcher({
|
|
73
74
|
steerMailbox: mailbox,
|
|
74
75
|
interruptLeader: control.interruptLeader,
|
|
75
76
|
allowRunCommand: control.allowRunCommand,
|
|
77
|
+
// Same registry the approval bridge publishes from, so every
|
|
78
|
+
// prompt HQ shows is one this handler can actually answer.
|
|
79
|
+
resolveApproval: (toolUseId, decision, sessionId) => approvals.resolve(toolUseId, decision, sessionId),
|
|
76
80
|
...deps.projectRoot ? {
|
|
77
81
|
kanbanTransition: createProjectKanbanTransitionHandler(deps.projectRoot),
|
|
78
82
|
kanbanAssign: createProjectKanbanAssignHandler(deps.projectRoot)
|
|
@@ -294,7 +298,7 @@ function startWebuiCredentialWatcher({
|
|
|
294
298
|
}
|
|
295
299
|
} catch {
|
|
296
300
|
}
|
|
297
|
-
const routingChanged = snapshot.fallbackModels !== void 0 || snapshot.fallbackBridge !== void 0 || hadFallbackBridge || snapshot.fallbackProfiles !== void 0 || snapshot.favoriteModels !== void 0 || snapshot.favoriteModelsOnly !== void 0 || snapshot.modelMatrix !== void 0 || snapshot.fallbackAuto !== void 0;
|
|
301
|
+
const routingChanged = snapshot.fallbackModels !== void 0 || snapshot.fallbackBridge !== void 0 || hadFallbackBridge || snapshot.fallbackProfiles !== void 0 || snapshot.favoriteModels !== void 0 || snapshot.disabledModels !== void 0 || snapshot.favoriteModelsOnly !== void 0 || snapshot.modelMatrix !== void 0 || snapshot.fallbackAuto !== void 0;
|
|
298
302
|
if (routingChanged) {
|
|
299
303
|
const configStore = opts.agent.container?.safeResolve?.(TOKENS.ConfigStore);
|
|
300
304
|
configStore?.update({
|
|
@@ -302,6 +306,7 @@ function startWebuiCredentialWatcher({
|
|
|
302
306
|
fallbackBridge: snapshot.fallbackBridge ?? "",
|
|
303
307
|
...snapshot.fallbackProfiles !== void 0 ? { fallbackProfiles: snapshot.fallbackProfiles } : {},
|
|
304
308
|
...snapshot.favoriteModels !== void 0 ? { favoriteModels: snapshot.favoriteModels } : {},
|
|
309
|
+
...snapshot.disabledModels !== void 0 ? { disabledModels: snapshot.disabledModels } : {},
|
|
305
310
|
...snapshot.favoriteModelsOnly !== void 0 ? { favoriteModelsOnly: snapshot.favoriteModelsOnly } : {},
|
|
306
311
|
...snapshot.modelMatrix !== void 0 ? { modelMatrix: snapshot.modelMatrix } : {},
|
|
307
312
|
...snapshot.fallbackAuto !== void 0 ? { fallbackAuto: snapshot.fallbackAuto } : {}
|
|
@@ -782,6 +787,12 @@ function createPrefsSeeding(opts) {
|
|
|
782
787
|
opts.appConfig = { ...opts.appConfig, ...patch };
|
|
783
788
|
};
|
|
784
789
|
const persistPrefs = async (payload) => {
|
|
790
|
+
if (typeof payload["provider"] === "string") {
|
|
791
|
+
patchLiveAppConfig({ provider: payload["provider"] });
|
|
792
|
+
}
|
|
793
|
+
if (typeof payload["model"] === "string") {
|
|
794
|
+
patchLiveAppConfig({ model: payload["model"] });
|
|
795
|
+
}
|
|
785
796
|
if (Array.isArray(payload["fallbackModels"])) {
|
|
786
797
|
patchLiveAppConfig({ fallbackModels: payload["fallbackModels"] });
|
|
787
798
|
}
|
|
@@ -793,6 +804,9 @@ function createPrefsSeeding(opts) {
|
|
|
793
804
|
if (Array.isArray(payload["favoriteModels"])) {
|
|
794
805
|
patchLiveAppConfig({ favoriteModels: payload["favoriteModels"] });
|
|
795
806
|
}
|
|
807
|
+
if (Array.isArray(payload["disabledModels"])) {
|
|
808
|
+
patchLiveAppConfig({ disabledModels: payload["disabledModels"] });
|
|
809
|
+
}
|
|
796
810
|
if (typeof payload["favoriteModelsOnly"] === "boolean") {
|
|
797
811
|
patchLiveAppConfig({ favoriteModelsOnly: payload["favoriteModelsOnly"] });
|
|
798
812
|
}
|
|
@@ -852,7 +866,9 @@ function createPrefsSeeding(opts) {
|
|
|
852
866
|
// src/webui-server/route-contexts.ts
|
|
853
867
|
import * as path5 from "node:path";
|
|
854
868
|
import {
|
|
869
|
+
normalizeSubagentModelPlan,
|
|
855
870
|
seedSessionSubagentPolicy,
|
|
871
|
+
setSessionSubagentModelPlan,
|
|
856
872
|
setSessionSubagentsAllowed
|
|
857
873
|
} from "@wrongstack/core/coordination";
|
|
858
874
|
import { TOKENS as TOKENS2 } from "@wrongstack/core/kernel";
|
|
@@ -980,6 +996,10 @@ function createWebuiRouteContexts({
|
|
|
980
996
|
sessionId ? getSessionAgent(sessionId).ctx : opts.agent.ctx,
|
|
981
997
|
allowed
|
|
982
998
|
),
|
|
999
|
+
setSubagentModelPlan: (plan, sessionId) => setSessionSubagentModelPlan(
|
|
1000
|
+
sessionId ? getSessionAgent(sessionId).ctx : opts.agent.ctx,
|
|
1001
|
+
normalizeSubagentModelPlan(plan)
|
|
1002
|
+
),
|
|
983
1003
|
persist: persistPrefs,
|
|
984
1004
|
setYolo: opts.onYoloSwitch,
|
|
985
1005
|
setAutonomy: opts.onAutonomySwitch,
|
|
@@ -1101,6 +1121,7 @@ function createWebuiRouteContexts({
|
|
|
1101
1121
|
};
|
|
1102
1122
|
const connectionCtx = {
|
|
1103
1123
|
agent: opts.agent,
|
|
1124
|
+
events: opts.events,
|
|
1104
1125
|
getAgent: getSessionAgent,
|
|
1105
1126
|
// Non-creating peek for the hasSession ownership gate (background-tab
|
|
1106
1127
|
// requests are legitimate; arbitrary strings are not).
|
|
@@ -1499,6 +1520,10 @@ async function runWebUI(opts) {
|
|
|
1499
1520
|
requireToken,
|
|
1500
1521
|
deferListen: surface === "simpleui",
|
|
1501
1522
|
strictPort,
|
|
1523
|
+
// HQ / WrongProxy status chips fetch those endpoints straight from the
|
|
1524
|
+
// browser; without their origins in `connect-src` the page's own CSP
|
|
1525
|
+
// blocks the probe and both chips report a healthy server as down.
|
|
1526
|
+
getExtraConnectSrc: () => integrationConnectSources(opts.appConfig),
|
|
1502
1527
|
...opts.getVectorMemoryStore ? { getVectorMemoryStore: opts.getVectorMemoryStore } : {},
|
|
1503
1528
|
...opts.vectorMemoryModelCacheDir ? { vectorMemoryModelCacheDir: opts.vectorMemoryModelCacheDir } : {}
|
|
1504
1529
|
});
|
|
@@ -1511,9 +1536,7 @@ async function runWebUI(opts) {
|
|
|
1511
1536
|
wsHost: host,
|
|
1512
1537
|
expectedToken: wsToken,
|
|
1513
1538
|
requireToken,
|
|
1514
|
-
allowedHostnames: [publicUrl, publicWsUrl].filter(
|
|
1515
|
-
(value) => Boolean(value)
|
|
1516
|
-
),
|
|
1539
|
+
allowedHostnames: [publicUrl, publicWsUrl].filter((value) => Boolean(value)),
|
|
1517
1540
|
allowBrowserUrlToken: Boolean(publicWsUrl),
|
|
1518
1541
|
allowCrossPortLoopbackCookie: process.env["WRONGSTACK_WEBUI_DEV_CROSS_PORT_WS"] === "1"
|
|
1519
1542
|
});
|
|
@@ -1648,6 +1671,7 @@ async function runWebUI(opts) {
|
|
|
1648
1671
|
),
|
|
1649
1672
|
modelsRegistry: opts.modelsRegistry,
|
|
1650
1673
|
providerAuthRegistry: opts.providerAuthRegistry,
|
|
1674
|
+
getDisabledModels: () => opts.appConfig?.disabledModels ?? [],
|
|
1651
1675
|
send,
|
|
1652
1676
|
broadcast,
|
|
1653
1677
|
log: (m) => console.log(m)
|
|
@@ -1974,4 +1998,4 @@ async function runWebUI(opts) {
|
|
|
1974
1998
|
export {
|
|
1975
1999
|
runWebUI
|
|
1976
2000
|
};
|
|
1977
|
-
//# sourceMappingURL=webui-server-
|
|
2001
|
+
//# sourceMappingURL=webui-server-3NYXBZHX.js.map
|
|
@@ -5,6 +5,7 @@ import type { Config, SessionWriter } from '@wrongstack/core/types';
|
|
|
5
5
|
import type { MCPRegistry } from '@wrongstack/mcp';
|
|
6
6
|
import { createHqCommandDispatcher, type HqCommandController } from '../hq-command-controller.js';
|
|
7
7
|
import type { KanbanHqSyncStats } from '../kanban-hq-sync.js';
|
|
8
|
+
import type { ApprovalMirrorRef } from '../permission-prompt-mirror.js';
|
|
8
9
|
/**
|
|
9
10
|
* Mutable holder for the HQ publisher reference. The ref is created in
|
|
10
11
|
* cli-main.ts before `brainMailbox` (which captures it via closure) and
|
|
@@ -28,6 +29,12 @@ interface SetupHqTelemetryDeps {
|
|
|
28
29
|
teardownHandlers: (() => void)[];
|
|
29
30
|
mailboxSessionTag: (sessionId: string) => string;
|
|
30
31
|
hqPublisherRef: HqPublisherRef;
|
|
32
|
+
/**
|
|
33
|
+
* Late-bound handle the plain REPL's prompt delegate reads through. Populated
|
|
34
|
+
* here because this is where the registry is created; until then the REPL
|
|
35
|
+
* prompt simply runs unmirrored.
|
|
36
|
+
*/
|
|
37
|
+
approvalMirror?: ApprovalMirrorRef | undefined;
|
|
31
38
|
mcpRegistry: Pick<MCPRegistry, 'onOperation' | 'operationalHealth'>;
|
|
32
39
|
}
|
|
33
40
|
interface HqTelemetryResult {
|