@openma/deepseek-harness-acp 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridge.js +255 -55
- package/dist/index.js +258 -55
- package/package.json +1 -1
package/dist/bridge.js
CHANGED
|
@@ -23,8 +23,11 @@ import { SessionId } from "@deepseek-ai/dsh-session";
|
|
|
23
23
|
import { foldSessionTitle } from "@deepseek-ai/dsh-session-title";
|
|
24
24
|
import { SANDBOX_MODES, setSandboxMode } from "@deepseek-ai/dsh-sandbox-policy";
|
|
25
25
|
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
26
|
+
import * as agentModule from "@deepseek-ai/dsh-agent";
|
|
26
27
|
import * as mcpClient from "@deepseek-ai/dsh-mcp-client";
|
|
27
28
|
function selfHarness() {
|
|
29
|
+
const agents = agentModule;
|
|
30
|
+
const installModelSelection = agents.installModelSelection ?? agents.ln;
|
|
28
31
|
return {
|
|
29
32
|
createUserMessage,
|
|
30
33
|
errorChain,
|
|
@@ -33,6 +36,7 @@ function selfHarness() {
|
|
|
33
36
|
setSandboxMode,
|
|
34
37
|
sandboxModes: SANDBOX_MODES,
|
|
35
38
|
credentialRef,
|
|
39
|
+
...installModelSelection !== void 0 ? { installModelSelection } : {},
|
|
36
40
|
mcpClient
|
|
37
41
|
};
|
|
38
42
|
}
|
|
@@ -610,6 +614,7 @@ async function apply(ctx, config = {}) {
|
|
|
610
614
|
return { provider: value.slice(0, i), model: value.slice(i + 2) };
|
|
611
615
|
};
|
|
612
616
|
let liveCatalog;
|
|
617
|
+
const effortCache = /* @__PURE__ */ new Map();
|
|
613
618
|
const discoverModels = async () => {
|
|
614
619
|
if (liveCatalog !== void 0) return liveCatalog;
|
|
615
620
|
const llm = ctx.get("llm");
|
|
@@ -640,7 +645,136 @@ async function apply(ctx, config = {}) {
|
|
|
640
645
|
};
|
|
641
646
|
ctx.on("llm/adapters-updated", () => {
|
|
642
647
|
liveCatalog = void 0;
|
|
648
|
+
effortCache.clear();
|
|
643
649
|
});
|
|
650
|
+
const routeOf = (record) => {
|
|
651
|
+
const logged = loggedConfig(record);
|
|
652
|
+
const provider = record.provider ?? defaultProvider() ?? logged?.provider;
|
|
653
|
+
const model = record.model ?? config.model ?? defaultSelection().model ?? logged?.model ?? record.agent.options.model;
|
|
654
|
+
return {
|
|
655
|
+
...provider !== void 0 ? { provider } : {},
|
|
656
|
+
...model !== void 0 ? { model } : {}
|
|
657
|
+
};
|
|
658
|
+
};
|
|
659
|
+
const loggedConfig = (record) => {
|
|
660
|
+
try {
|
|
661
|
+
const header = record.agent.session.requestHeader?.();
|
|
662
|
+
const cfg = header?.config;
|
|
663
|
+
if (cfg === void 0) return void 0;
|
|
664
|
+
return {
|
|
665
|
+
provider: cfg.provider,
|
|
666
|
+
model: cfg.model,
|
|
667
|
+
...cfg.reasoningEffort !== void 0 ? { reasoningEffort: String(cfg.reasoningEffort) } : {}
|
|
668
|
+
};
|
|
669
|
+
} catch {
|
|
670
|
+
return void 0;
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
const effortCatalog = async (provider, model) => {
|
|
674
|
+
if (provider === void 0 || model === void 0) return void 0;
|
|
675
|
+
const key = `${provider}::${model}`;
|
|
676
|
+
if (effortCache.has(key)) return effortCache.get(key);
|
|
677
|
+
const llm = ctx.get("llm");
|
|
678
|
+
let catalog;
|
|
679
|
+
try {
|
|
680
|
+
const reasoning = (await llm?.resolveModelInfo?.(provider, model))?.reasoning;
|
|
681
|
+
if (reasoning !== void 0 && reasoning.efforts.length > 0) {
|
|
682
|
+
catalog = {
|
|
683
|
+
efforts: reasoning.efforts.map((effort) => ({
|
|
684
|
+
id: String(effort.id),
|
|
685
|
+
name: effort.name,
|
|
686
|
+
...effort.description !== void 0 ? { description: effort.description } : {}
|
|
687
|
+
})),
|
|
688
|
+
...reasoning.defaultEffort !== void 0 ? { defaultEffort: String(reasoning.defaultEffort) } : {}
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
} catch (error) {
|
|
692
|
+
logDebug(`resolveModelInfo(${key}) failed: ${String(error)}`);
|
|
693
|
+
}
|
|
694
|
+
effortCache.set(key, catalog);
|
|
695
|
+
return catalog;
|
|
696
|
+
};
|
|
697
|
+
const ensureSelection = (record) => {
|
|
698
|
+
const install = harness.installModelSelection;
|
|
699
|
+
if (install === void 0) return void 0;
|
|
700
|
+
if (record.selection !== void 0) return record.selection;
|
|
701
|
+
let picked;
|
|
702
|
+
const selection = {
|
|
703
|
+
get current() {
|
|
704
|
+
if (picked !== void 0) return picked;
|
|
705
|
+
const logged = loggedConfig(record);
|
|
706
|
+
if (logged !== void 0) return logged;
|
|
707
|
+
const { provider, model } = routeOf(record);
|
|
708
|
+
if (provider === void 0 || model === void 0) return void 0;
|
|
709
|
+
return { provider, model };
|
|
710
|
+
},
|
|
711
|
+
set current(next) {
|
|
712
|
+
picked = next;
|
|
713
|
+
},
|
|
714
|
+
assembled: void 0
|
|
715
|
+
};
|
|
716
|
+
try {
|
|
717
|
+
install(record.agent.ctx, selection);
|
|
718
|
+
} catch (error) {
|
|
719
|
+
logWarn(`installModelSelection failed: ${String(error)}`);
|
|
720
|
+
return void 0;
|
|
721
|
+
}
|
|
722
|
+
record.selection = selection;
|
|
723
|
+
return selection;
|
|
724
|
+
};
|
|
725
|
+
const switchModel = async (record, acpSessionId, value) => {
|
|
726
|
+
if (record.inflight !== void 0) {
|
|
727
|
+
throw invalidParams("cannot switch models while a prompt is running");
|
|
728
|
+
}
|
|
729
|
+
const choice = decodeChoice(value);
|
|
730
|
+
const known = /* @__PURE__ */ new Set([
|
|
731
|
+
...modelCandidates().map((model) => encodeChoice(void 0, model)),
|
|
732
|
+
...(await discoverModels()).map((entry) => encodeChoice(entry.provider, entry.model)),
|
|
733
|
+
encodeChoice(record.provider, record.model ?? "")
|
|
734
|
+
]);
|
|
735
|
+
if (!known.has(value)) throw invalidParams(`unknown model: ${value}`);
|
|
736
|
+
if (choice.model === record.model && choice.provider === (record.provider ?? defaultProvider())) return;
|
|
737
|
+
const sessionId = record.agent.session.id;
|
|
738
|
+
await record.dispose().catch((error) => {
|
|
739
|
+
logWarn(`dispose during model switch failed: ${String(error)}`);
|
|
740
|
+
});
|
|
741
|
+
let handle;
|
|
742
|
+
try {
|
|
743
|
+
handle = await agents.resume({
|
|
744
|
+
resumeSessionId: sessionId,
|
|
745
|
+
agentOptions: agentOptionsFor(choice.model, choice.provider)
|
|
746
|
+
});
|
|
747
|
+
} catch (error) {
|
|
748
|
+
sessions.delete(acpSessionId);
|
|
749
|
+
throw internalError(`model switch failed: ${errorChain2(error)}`);
|
|
750
|
+
}
|
|
751
|
+
record.agent = handle.agent;
|
|
752
|
+
record.dispose = () => handle.dispose();
|
|
753
|
+
record.model = choice.model;
|
|
754
|
+
if (choice.provider !== void 0 && choice.provider !== defaultProvider()) {
|
|
755
|
+
record.provider = choice.provider;
|
|
756
|
+
} else {
|
|
757
|
+
delete record.provider;
|
|
758
|
+
}
|
|
759
|
+
delete record.selection;
|
|
760
|
+
if (record.effort !== void 0) {
|
|
761
|
+
const route = routeOf(record);
|
|
762
|
+
const catalog = await effortCatalog(route.provider, route.model);
|
|
763
|
+
if (catalog?.efforts.some((effort) => effort.id === record.effort) === true && route.provider !== void 0 && route.model !== void 0) {
|
|
764
|
+
const selection = ensureSelection(record);
|
|
765
|
+
if (selection !== void 0) {
|
|
766
|
+
selection.current = {
|
|
767
|
+
provider: route.provider,
|
|
768
|
+
model: route.model,
|
|
769
|
+
reasoningEffort: record.effort
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
} else {
|
|
773
|
+
delete record.effort;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
setApprovalPolicy(record, record.approvals);
|
|
777
|
+
};
|
|
644
778
|
const credentialPresent = async () => {
|
|
645
779
|
if (envCredentialPresent()) return true;
|
|
646
780
|
const brand = harness.credentialRef;
|
|
@@ -698,7 +832,23 @@ async function apply(ctx, config = {}) {
|
|
|
698
832
|
return { id: mode, name: label.name, description: label.description };
|
|
699
833
|
})
|
|
700
834
|
});
|
|
701
|
-
const
|
|
835
|
+
const setApprovalPolicy = (record, policy) => {
|
|
836
|
+
try {
|
|
837
|
+
ctx.get("approval")?.setPolicy?.(record.agent, policy);
|
|
838
|
+
} catch (error) {
|
|
839
|
+
logWarn(`approval policy switch failed: ${String(error)}`);
|
|
840
|
+
}
|
|
841
|
+
record.approvals = policy;
|
|
842
|
+
};
|
|
843
|
+
const applyMode = (record, sessionId, modeId) => {
|
|
844
|
+
const mode = SANDBOX_MODES2.find((candidate) => candidate === modeId);
|
|
845
|
+
if (mode === void 0) throw invalidParams(`unknown mode: ${modeId}`);
|
|
846
|
+
setSandboxMode2(record.agent.session, mode);
|
|
847
|
+
setApprovalPolicy(record, mode === "danger-full-access" ? "never" : "ask");
|
|
848
|
+
record.modeId = mode;
|
|
849
|
+
notify(sessionId, { sessionUpdate: "current_mode_update", currentModeId: mode });
|
|
850
|
+
};
|
|
851
|
+
const modelChoices = async (record) => {
|
|
702
852
|
const seen = /* @__PURE__ */ new Set();
|
|
703
853
|
const options = [];
|
|
704
854
|
const push = (value, name3) => {
|
|
@@ -717,17 +867,67 @@ async function apply(ctx, config = {}) {
|
|
|
717
867
|
push(encodeChoice(choice.provider, choice.model), choice.label);
|
|
718
868
|
}
|
|
719
869
|
for (const model of modelCandidates()) push(encodeChoice(void 0, model), model);
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
870
|
+
return options;
|
|
871
|
+
};
|
|
872
|
+
const configOptions = async (record) => {
|
|
873
|
+
const result = [];
|
|
874
|
+
result.push({
|
|
875
|
+
type: "select",
|
|
876
|
+
id: "mode",
|
|
877
|
+
name: "Mode",
|
|
878
|
+
category: "mode",
|
|
879
|
+
currentValue: record.modeId,
|
|
880
|
+
options: SANDBOX_MODES2.map((mode) => {
|
|
881
|
+
const label = MODE_LABELS[mode] ?? { name: mode, description: "" };
|
|
882
|
+
return {
|
|
883
|
+
value: mode,
|
|
884
|
+
name: label.name,
|
|
885
|
+
...label.description.length > 0 ? { description: label.description } : {}
|
|
886
|
+
};
|
|
887
|
+
})
|
|
888
|
+
});
|
|
889
|
+
const models = await modelChoices(record);
|
|
890
|
+
if (models.length >= 2) {
|
|
891
|
+
result.push({
|
|
723
892
|
type: "select",
|
|
724
893
|
id: "model",
|
|
725
894
|
name: "Model",
|
|
726
895
|
category: "model",
|
|
727
|
-
currentValue:
|
|
728
|
-
options
|
|
729
|
-
}
|
|
730
|
-
|
|
896
|
+
currentValue: models[0].value,
|
|
897
|
+
options: models
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
const route = routeOf(record);
|
|
901
|
+
const efforts = await effortCatalog(route.provider, route.model);
|
|
902
|
+
if (efforts !== void 0 && efforts.efforts.length >= 2) {
|
|
903
|
+
const known = new Set(efforts.efforts.map((effort) => effort.id));
|
|
904
|
+
const preferred = [record.effort, loggedConfig(record)?.reasoningEffort, efforts.defaultEffort].find(
|
|
905
|
+
(candidate) => candidate !== void 0 && known.has(candidate)
|
|
906
|
+
);
|
|
907
|
+
result.push({
|
|
908
|
+
type: "select",
|
|
909
|
+
id: "effort",
|
|
910
|
+
name: "Reasoning",
|
|
911
|
+
category: "thought_level",
|
|
912
|
+
currentValue: preferred ?? efforts.efforts[0].id,
|
|
913
|
+
options: efforts.efforts.map((effort) => ({
|
|
914
|
+
value: effort.id,
|
|
915
|
+
name: effort.name,
|
|
916
|
+
...effort.description !== void 0 ? { description: effort.description } : {}
|
|
917
|
+
}))
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
result.push({
|
|
921
|
+
type: "select",
|
|
922
|
+
id: "approvals",
|
|
923
|
+
name: "Approvals",
|
|
924
|
+
currentValue: record.approvals,
|
|
925
|
+
options: [
|
|
926
|
+
{ value: "ask", name: "Ask", description: "Ask before actions beyond the sandbox mode" },
|
|
927
|
+
{ value: "never", name: "Never ask", description: "Auto-approve every permission request" }
|
|
928
|
+
]
|
|
929
|
+
});
|
|
930
|
+
return result;
|
|
731
931
|
};
|
|
732
932
|
const publishCommands = (sessionId) => {
|
|
733
933
|
notify(sessionId, {
|
|
@@ -809,15 +1009,19 @@ async function apply(ctx, config = {}) {
|
|
|
809
1009
|
};
|
|
810
1010
|
const statusText = async (record) => {
|
|
811
1011
|
const used = record.projection.contextWindow;
|
|
1012
|
+
const route = routeOf(record);
|
|
1013
|
+
const effort = record.effort ?? loggedConfig(record)?.reasoningEffort;
|
|
812
1014
|
const lines = [
|
|
813
1015
|
`**dsh-acp** ${VERSION} \u2014 DeepSeek Harness ACP bridge`,
|
|
814
1016
|
"",
|
|
815
1017
|
`| | |`,
|
|
816
1018
|
`|---|---|`,
|
|
817
|
-
`| Provider | ${
|
|
818
|
-
`| Model | ${
|
|
1019
|
+
`| Provider | ${route.provider ?? "(composition default)"} |`,
|
|
1020
|
+
`| Model | ${route.model ?? "(composition default)"} |`,
|
|
1021
|
+
`| Reasoning | ${effort ?? "(adapter default)"} |`,
|
|
819
1022
|
`| Credential | ${await describeCredential()} |`,
|
|
820
1023
|
`| Permission mode | ${record.modeId} |`,
|
|
1024
|
+
`| Approvals | ${record.approvals} |`,
|
|
821
1025
|
`| Workspace | ${record.agent.session.header.cwd ?? process.cwd()} |`,
|
|
822
1026
|
`| Session | ${String(record.agent.session.id)} |`,
|
|
823
1027
|
...used !== void 0 ? [`| Context window | ${used.toLocaleString()} tokens |`] : []
|
|
@@ -831,6 +1035,7 @@ async function apply(ctx, config = {}) {
|
|
|
831
1035
|
projection: projection ?? new SessionProjection(),
|
|
832
1036
|
modeId,
|
|
833
1037
|
model,
|
|
1038
|
+
approvals: modeId === "danger-full-access" ? "never" : "ask",
|
|
834
1039
|
cancelled: false,
|
|
835
1040
|
inflight: void 0
|
|
836
1041
|
};
|
|
@@ -1095,58 +1300,53 @@ async function apply(ctx, config = {}) {
|
|
|
1095
1300
|
const usage = record.projection.promptUsage();
|
|
1096
1301
|
return { stopReason, ...usage !== void 0 ? { usage } : {} };
|
|
1097
1302
|
},
|
|
1098
|
-
setSessionMode(params) {
|
|
1303
|
+
async setSessionMode(params) {
|
|
1099
1304
|
const record = requireSession(params.sessionId);
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
logWarn(`approval policy switch failed: ${String(error)}`);
|
|
1107
|
-
}
|
|
1108
|
-
record.modeId = mode;
|
|
1109
|
-
notify(params.sessionId, { sessionUpdate: "current_mode_update", currentModeId: mode });
|
|
1110
|
-
return Promise.resolve({});
|
|
1305
|
+
applyMode(record, params.sessionId, params.modeId);
|
|
1306
|
+
notify(params.sessionId, {
|
|
1307
|
+
sessionUpdate: "config_option_update",
|
|
1308
|
+
configOptions: await configOptions(record)
|
|
1309
|
+
});
|
|
1310
|
+
return {};
|
|
1111
1311
|
},
|
|
1112
1312
|
async setSessionConfigOption(params) {
|
|
1113
1313
|
const record = requireSession(params.sessionId);
|
|
1114
|
-
if (params.configId !== "model") throw invalidParams(`unknown config option: ${params.configId}`);
|
|
1115
|
-
if (record.inflight !== void 0) {
|
|
1116
|
-
throw invalidParams("cannot switch models while a prompt is running");
|
|
1117
|
-
}
|
|
1118
1314
|
const value = typeof params.value === "string" ? params.value : void 0;
|
|
1119
|
-
if (value === void 0) throw invalidParams(`
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1315
|
+
if (value === void 0) throw invalidParams(`invalid value: ${String(params.value)}`);
|
|
1316
|
+
switch (params.configId) {
|
|
1317
|
+
case "mode": {
|
|
1318
|
+
applyMode(record, params.sessionId, value);
|
|
1319
|
+
break;
|
|
1320
|
+
}
|
|
1321
|
+
case "approvals": {
|
|
1322
|
+
if (value !== "ask" && value !== "never") throw invalidParams(`unknown approvals: ${value}`);
|
|
1323
|
+
setApprovalPolicy(record, value);
|
|
1324
|
+
break;
|
|
1325
|
+
}
|
|
1326
|
+
case "effort": {
|
|
1327
|
+
const route = routeOf(record);
|
|
1328
|
+
const catalog = await effortCatalog(route.provider, route.model);
|
|
1329
|
+
if (catalog === void 0 || !catalog.efforts.some((effort) => effort.id === value)) {
|
|
1330
|
+
throw invalidParams(`unknown effort: ${value}`);
|
|
1331
|
+
}
|
|
1332
|
+
const selection = ensureSelection(record);
|
|
1333
|
+
if (selection === void 0 || route.provider === void 0 || route.model === void 0) {
|
|
1334
|
+
throw invalidParams("reasoning effort switching is unavailable on this host");
|
|
1335
|
+
}
|
|
1336
|
+
selection.current = {
|
|
1337
|
+
provider: route.provider,
|
|
1338
|
+
model: route.model,
|
|
1339
|
+
reasoningEffort: value
|
|
1340
|
+
};
|
|
1341
|
+
record.effort = value;
|
|
1342
|
+
break;
|
|
1141
1343
|
}
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
if (choice.provider !== void 0 && choice.provider !== defaultProvider()) {
|
|
1146
|
-
record.provider = choice.provider;
|
|
1147
|
-
} else {
|
|
1148
|
-
delete record.provider;
|
|
1344
|
+
case "model": {
|
|
1345
|
+
await switchModel(record, params.sessionId, value);
|
|
1346
|
+
break;
|
|
1149
1347
|
}
|
|
1348
|
+
default:
|
|
1349
|
+
throw invalidParams(`unknown config option: ${params.configId}`);
|
|
1150
1350
|
}
|
|
1151
1351
|
return { configOptions: await configOptions(record) };
|
|
1152
1352
|
},
|
package/dist/index.js
CHANGED
|
@@ -24,8 +24,11 @@ import { SessionId } from "@deepseek-ai/dsh-session";
|
|
|
24
24
|
import { foldSessionTitle } from "@deepseek-ai/dsh-session-title";
|
|
25
25
|
import { SANDBOX_MODES, setSandboxMode } from "@deepseek-ai/dsh-sandbox-policy";
|
|
26
26
|
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
27
|
+
import * as agentModule from "@deepseek-ai/dsh-agent";
|
|
27
28
|
import * as mcpClient from "@deepseek-ai/dsh-mcp-client";
|
|
28
29
|
function selfHarness() {
|
|
30
|
+
const agents = agentModule;
|
|
31
|
+
const installModelSelection = agents.installModelSelection ?? agents.ln;
|
|
29
32
|
return {
|
|
30
33
|
createUserMessage,
|
|
31
34
|
errorChain,
|
|
@@ -34,6 +37,7 @@ function selfHarness() {
|
|
|
34
37
|
setSandboxMode,
|
|
35
38
|
sandboxModes: SANDBOX_MODES,
|
|
36
39
|
credentialRef,
|
|
40
|
+
...installModelSelection !== void 0 ? { installModelSelection } : {},
|
|
37
41
|
mcpClient
|
|
38
42
|
};
|
|
39
43
|
}
|
|
@@ -628,6 +632,7 @@ async function apply(ctx, config = {}) {
|
|
|
628
632
|
return { provider: value.slice(0, i), model: value.slice(i + 2) };
|
|
629
633
|
};
|
|
630
634
|
let liveCatalog;
|
|
635
|
+
const effortCache = /* @__PURE__ */ new Map();
|
|
631
636
|
const discoverModels = async () => {
|
|
632
637
|
if (liveCatalog !== void 0) return liveCatalog;
|
|
633
638
|
const llm = ctx.get("llm");
|
|
@@ -658,7 +663,136 @@ async function apply(ctx, config = {}) {
|
|
|
658
663
|
};
|
|
659
664
|
ctx.on("llm/adapters-updated", () => {
|
|
660
665
|
liveCatalog = void 0;
|
|
666
|
+
effortCache.clear();
|
|
661
667
|
});
|
|
668
|
+
const routeOf = (record) => {
|
|
669
|
+
const logged = loggedConfig(record);
|
|
670
|
+
const provider = record.provider ?? defaultProvider() ?? logged?.provider;
|
|
671
|
+
const model = record.model ?? config.model ?? defaultSelection().model ?? logged?.model ?? record.agent.options.model;
|
|
672
|
+
return {
|
|
673
|
+
...provider !== void 0 ? { provider } : {},
|
|
674
|
+
...model !== void 0 ? { model } : {}
|
|
675
|
+
};
|
|
676
|
+
};
|
|
677
|
+
const loggedConfig = (record) => {
|
|
678
|
+
try {
|
|
679
|
+
const header = record.agent.session.requestHeader?.();
|
|
680
|
+
const cfg = header?.config;
|
|
681
|
+
if (cfg === void 0) return void 0;
|
|
682
|
+
return {
|
|
683
|
+
provider: cfg.provider,
|
|
684
|
+
model: cfg.model,
|
|
685
|
+
...cfg.reasoningEffort !== void 0 ? { reasoningEffort: String(cfg.reasoningEffort) } : {}
|
|
686
|
+
};
|
|
687
|
+
} catch {
|
|
688
|
+
return void 0;
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
const effortCatalog = async (provider, model) => {
|
|
692
|
+
if (provider === void 0 || model === void 0) return void 0;
|
|
693
|
+
const key = `${provider}::${model}`;
|
|
694
|
+
if (effortCache.has(key)) return effortCache.get(key);
|
|
695
|
+
const llm = ctx.get("llm");
|
|
696
|
+
let catalog;
|
|
697
|
+
try {
|
|
698
|
+
const reasoning = (await llm?.resolveModelInfo?.(provider, model))?.reasoning;
|
|
699
|
+
if (reasoning !== void 0 && reasoning.efforts.length > 0) {
|
|
700
|
+
catalog = {
|
|
701
|
+
efforts: reasoning.efforts.map((effort) => ({
|
|
702
|
+
id: String(effort.id),
|
|
703
|
+
name: effort.name,
|
|
704
|
+
...effort.description !== void 0 ? { description: effort.description } : {}
|
|
705
|
+
})),
|
|
706
|
+
...reasoning.defaultEffort !== void 0 ? { defaultEffort: String(reasoning.defaultEffort) } : {}
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
} catch (error) {
|
|
710
|
+
logDebug(`resolveModelInfo(${key}) failed: ${String(error)}`);
|
|
711
|
+
}
|
|
712
|
+
effortCache.set(key, catalog);
|
|
713
|
+
return catalog;
|
|
714
|
+
};
|
|
715
|
+
const ensureSelection = (record) => {
|
|
716
|
+
const install = harness.installModelSelection;
|
|
717
|
+
if (install === void 0) return void 0;
|
|
718
|
+
if (record.selection !== void 0) return record.selection;
|
|
719
|
+
let picked;
|
|
720
|
+
const selection = {
|
|
721
|
+
get current() {
|
|
722
|
+
if (picked !== void 0) return picked;
|
|
723
|
+
const logged = loggedConfig(record);
|
|
724
|
+
if (logged !== void 0) return logged;
|
|
725
|
+
const { provider, model } = routeOf(record);
|
|
726
|
+
if (provider === void 0 || model === void 0) return void 0;
|
|
727
|
+
return { provider, model };
|
|
728
|
+
},
|
|
729
|
+
set current(next) {
|
|
730
|
+
picked = next;
|
|
731
|
+
},
|
|
732
|
+
assembled: void 0
|
|
733
|
+
};
|
|
734
|
+
try {
|
|
735
|
+
install(record.agent.ctx, selection);
|
|
736
|
+
} catch (error) {
|
|
737
|
+
logWarn(`installModelSelection failed: ${String(error)}`);
|
|
738
|
+
return void 0;
|
|
739
|
+
}
|
|
740
|
+
record.selection = selection;
|
|
741
|
+
return selection;
|
|
742
|
+
};
|
|
743
|
+
const switchModel = async (record, acpSessionId, value) => {
|
|
744
|
+
if (record.inflight !== void 0) {
|
|
745
|
+
throw invalidParams("cannot switch models while a prompt is running");
|
|
746
|
+
}
|
|
747
|
+
const choice = decodeChoice(value);
|
|
748
|
+
const known = /* @__PURE__ */ new Set([
|
|
749
|
+
...modelCandidates().map((model) => encodeChoice(void 0, model)),
|
|
750
|
+
...(await discoverModels()).map((entry) => encodeChoice(entry.provider, entry.model)),
|
|
751
|
+
encodeChoice(record.provider, record.model ?? "")
|
|
752
|
+
]);
|
|
753
|
+
if (!known.has(value)) throw invalidParams(`unknown model: ${value}`);
|
|
754
|
+
if (choice.model === record.model && choice.provider === (record.provider ?? defaultProvider())) return;
|
|
755
|
+
const sessionId = record.agent.session.id;
|
|
756
|
+
await record.dispose().catch((error) => {
|
|
757
|
+
logWarn(`dispose during model switch failed: ${String(error)}`);
|
|
758
|
+
});
|
|
759
|
+
let handle;
|
|
760
|
+
try {
|
|
761
|
+
handle = await agents.resume({
|
|
762
|
+
resumeSessionId: sessionId,
|
|
763
|
+
agentOptions: agentOptionsFor(choice.model, choice.provider)
|
|
764
|
+
});
|
|
765
|
+
} catch (error) {
|
|
766
|
+
sessions.delete(acpSessionId);
|
|
767
|
+
throw internalError(`model switch failed: ${errorChain2(error)}`);
|
|
768
|
+
}
|
|
769
|
+
record.agent = handle.agent;
|
|
770
|
+
record.dispose = () => handle.dispose();
|
|
771
|
+
record.model = choice.model;
|
|
772
|
+
if (choice.provider !== void 0 && choice.provider !== defaultProvider()) {
|
|
773
|
+
record.provider = choice.provider;
|
|
774
|
+
} else {
|
|
775
|
+
delete record.provider;
|
|
776
|
+
}
|
|
777
|
+
delete record.selection;
|
|
778
|
+
if (record.effort !== void 0) {
|
|
779
|
+
const route = routeOf(record);
|
|
780
|
+
const catalog = await effortCatalog(route.provider, route.model);
|
|
781
|
+
if (catalog?.efforts.some((effort) => effort.id === record.effort) === true && route.provider !== void 0 && route.model !== void 0) {
|
|
782
|
+
const selection = ensureSelection(record);
|
|
783
|
+
if (selection !== void 0) {
|
|
784
|
+
selection.current = {
|
|
785
|
+
provider: route.provider,
|
|
786
|
+
model: route.model,
|
|
787
|
+
reasoningEffort: record.effort
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
} else {
|
|
791
|
+
delete record.effort;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
setApprovalPolicy(record, record.approvals);
|
|
795
|
+
};
|
|
662
796
|
const credentialPresent = async () => {
|
|
663
797
|
if (envCredentialPresent()) return true;
|
|
664
798
|
const brand = harness.credentialRef;
|
|
@@ -716,7 +850,23 @@ async function apply(ctx, config = {}) {
|
|
|
716
850
|
return { id: mode, name: label.name, description: label.description };
|
|
717
851
|
})
|
|
718
852
|
});
|
|
719
|
-
const
|
|
853
|
+
const setApprovalPolicy = (record, policy) => {
|
|
854
|
+
try {
|
|
855
|
+
ctx.get("approval")?.setPolicy?.(record.agent, policy);
|
|
856
|
+
} catch (error) {
|
|
857
|
+
logWarn(`approval policy switch failed: ${String(error)}`);
|
|
858
|
+
}
|
|
859
|
+
record.approvals = policy;
|
|
860
|
+
};
|
|
861
|
+
const applyMode = (record, sessionId, modeId) => {
|
|
862
|
+
const mode = SANDBOX_MODES2.find((candidate) => candidate === modeId);
|
|
863
|
+
if (mode === void 0) throw invalidParams(`unknown mode: ${modeId}`);
|
|
864
|
+
setSandboxMode2(record.agent.session, mode);
|
|
865
|
+
setApprovalPolicy(record, mode === "danger-full-access" ? "never" : "ask");
|
|
866
|
+
record.modeId = mode;
|
|
867
|
+
notify(sessionId, { sessionUpdate: "current_mode_update", currentModeId: mode });
|
|
868
|
+
};
|
|
869
|
+
const modelChoices = async (record) => {
|
|
720
870
|
const seen = /* @__PURE__ */ new Set();
|
|
721
871
|
const options = [];
|
|
722
872
|
const push = (value, name3) => {
|
|
@@ -735,17 +885,67 @@ async function apply(ctx, config = {}) {
|
|
|
735
885
|
push(encodeChoice(choice.provider, choice.model), choice.label);
|
|
736
886
|
}
|
|
737
887
|
for (const model of modelCandidates()) push(encodeChoice(void 0, model), model);
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
888
|
+
return options;
|
|
889
|
+
};
|
|
890
|
+
const configOptions = async (record) => {
|
|
891
|
+
const result = [];
|
|
892
|
+
result.push({
|
|
893
|
+
type: "select",
|
|
894
|
+
id: "mode",
|
|
895
|
+
name: "Mode",
|
|
896
|
+
category: "mode",
|
|
897
|
+
currentValue: record.modeId,
|
|
898
|
+
options: SANDBOX_MODES2.map((mode) => {
|
|
899
|
+
const label = MODE_LABELS[mode] ?? { name: mode, description: "" };
|
|
900
|
+
return {
|
|
901
|
+
value: mode,
|
|
902
|
+
name: label.name,
|
|
903
|
+
...label.description.length > 0 ? { description: label.description } : {}
|
|
904
|
+
};
|
|
905
|
+
})
|
|
906
|
+
});
|
|
907
|
+
const models = await modelChoices(record);
|
|
908
|
+
if (models.length >= 2) {
|
|
909
|
+
result.push({
|
|
741
910
|
type: "select",
|
|
742
911
|
id: "model",
|
|
743
912
|
name: "Model",
|
|
744
913
|
category: "model",
|
|
745
|
-
currentValue:
|
|
746
|
-
options
|
|
747
|
-
}
|
|
748
|
-
|
|
914
|
+
currentValue: models[0].value,
|
|
915
|
+
options: models
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
const route = routeOf(record);
|
|
919
|
+
const efforts = await effortCatalog(route.provider, route.model);
|
|
920
|
+
if (efforts !== void 0 && efforts.efforts.length >= 2) {
|
|
921
|
+
const known = new Set(efforts.efforts.map((effort) => effort.id));
|
|
922
|
+
const preferred = [record.effort, loggedConfig(record)?.reasoningEffort, efforts.defaultEffort].find(
|
|
923
|
+
(candidate) => candidate !== void 0 && known.has(candidate)
|
|
924
|
+
);
|
|
925
|
+
result.push({
|
|
926
|
+
type: "select",
|
|
927
|
+
id: "effort",
|
|
928
|
+
name: "Reasoning",
|
|
929
|
+
category: "thought_level",
|
|
930
|
+
currentValue: preferred ?? efforts.efforts[0].id,
|
|
931
|
+
options: efforts.efforts.map((effort) => ({
|
|
932
|
+
value: effort.id,
|
|
933
|
+
name: effort.name,
|
|
934
|
+
...effort.description !== void 0 ? { description: effort.description } : {}
|
|
935
|
+
}))
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
result.push({
|
|
939
|
+
type: "select",
|
|
940
|
+
id: "approvals",
|
|
941
|
+
name: "Approvals",
|
|
942
|
+
currentValue: record.approvals,
|
|
943
|
+
options: [
|
|
944
|
+
{ value: "ask", name: "Ask", description: "Ask before actions beyond the sandbox mode" },
|
|
945
|
+
{ value: "never", name: "Never ask", description: "Auto-approve every permission request" }
|
|
946
|
+
]
|
|
947
|
+
});
|
|
948
|
+
return result;
|
|
749
949
|
};
|
|
750
950
|
const publishCommands = (sessionId) => {
|
|
751
951
|
notify(sessionId, {
|
|
@@ -827,15 +1027,19 @@ async function apply(ctx, config = {}) {
|
|
|
827
1027
|
};
|
|
828
1028
|
const statusText = async (record) => {
|
|
829
1029
|
const used = record.projection.contextWindow;
|
|
1030
|
+
const route = routeOf(record);
|
|
1031
|
+
const effort = record.effort ?? loggedConfig(record)?.reasoningEffort;
|
|
830
1032
|
const lines = [
|
|
831
1033
|
`**dsh-acp** ${VERSION} \u2014 DeepSeek Harness ACP bridge`,
|
|
832
1034
|
"",
|
|
833
1035
|
`| | |`,
|
|
834
1036
|
`|---|---|`,
|
|
835
|
-
`| Provider | ${
|
|
836
|
-
`| Model | ${
|
|
1037
|
+
`| Provider | ${route.provider ?? "(composition default)"} |`,
|
|
1038
|
+
`| Model | ${route.model ?? "(composition default)"} |`,
|
|
1039
|
+
`| Reasoning | ${effort ?? "(adapter default)"} |`,
|
|
837
1040
|
`| Credential | ${await describeCredential()} |`,
|
|
838
1041
|
`| Permission mode | ${record.modeId} |`,
|
|
1042
|
+
`| Approvals | ${record.approvals} |`,
|
|
839
1043
|
`| Workspace | ${record.agent.session.header.cwd ?? process.cwd()} |`,
|
|
840
1044
|
`| Session | ${String(record.agent.session.id)} |`,
|
|
841
1045
|
...used !== void 0 ? [`| Context window | ${used.toLocaleString()} tokens |`] : []
|
|
@@ -849,6 +1053,7 @@ async function apply(ctx, config = {}) {
|
|
|
849
1053
|
projection: projection ?? new SessionProjection(),
|
|
850
1054
|
modeId,
|
|
851
1055
|
model,
|
|
1056
|
+
approvals: modeId === "danger-full-access" ? "never" : "ask",
|
|
852
1057
|
cancelled: false,
|
|
853
1058
|
inflight: void 0
|
|
854
1059
|
};
|
|
@@ -1113,58 +1318,53 @@ async function apply(ctx, config = {}) {
|
|
|
1113
1318
|
const usage = record.projection.promptUsage();
|
|
1114
1319
|
return { stopReason, ...usage !== void 0 ? { usage } : {} };
|
|
1115
1320
|
},
|
|
1116
|
-
setSessionMode(params) {
|
|
1321
|
+
async setSessionMode(params) {
|
|
1117
1322
|
const record = requireSession(params.sessionId);
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
logWarn(`approval policy switch failed: ${String(error)}`);
|
|
1125
|
-
}
|
|
1126
|
-
record.modeId = mode;
|
|
1127
|
-
notify(params.sessionId, { sessionUpdate: "current_mode_update", currentModeId: mode });
|
|
1128
|
-
return Promise.resolve({});
|
|
1323
|
+
applyMode(record, params.sessionId, params.modeId);
|
|
1324
|
+
notify(params.sessionId, {
|
|
1325
|
+
sessionUpdate: "config_option_update",
|
|
1326
|
+
configOptions: await configOptions(record)
|
|
1327
|
+
});
|
|
1328
|
+
return {};
|
|
1129
1329
|
},
|
|
1130
1330
|
async setSessionConfigOption(params) {
|
|
1131
1331
|
const record = requireSession(params.sessionId);
|
|
1132
|
-
if (params.configId !== "model") throw invalidParams(`unknown config option: ${params.configId}`);
|
|
1133
|
-
if (record.inflight !== void 0) {
|
|
1134
|
-
throw invalidParams("cannot switch models while a prompt is running");
|
|
1135
|
-
}
|
|
1136
1332
|
const value = typeof params.value === "string" ? params.value : void 0;
|
|
1137
|
-
if (value === void 0) throw invalidParams(`
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1333
|
+
if (value === void 0) throw invalidParams(`invalid value: ${String(params.value)}`);
|
|
1334
|
+
switch (params.configId) {
|
|
1335
|
+
case "mode": {
|
|
1336
|
+
applyMode(record, params.sessionId, value);
|
|
1337
|
+
break;
|
|
1338
|
+
}
|
|
1339
|
+
case "approvals": {
|
|
1340
|
+
if (value !== "ask" && value !== "never") throw invalidParams(`unknown approvals: ${value}`);
|
|
1341
|
+
setApprovalPolicy(record, value);
|
|
1342
|
+
break;
|
|
1343
|
+
}
|
|
1344
|
+
case "effort": {
|
|
1345
|
+
const route = routeOf(record);
|
|
1346
|
+
const catalog = await effortCatalog(route.provider, route.model);
|
|
1347
|
+
if (catalog === void 0 || !catalog.efforts.some((effort) => effort.id === value)) {
|
|
1348
|
+
throw invalidParams(`unknown effort: ${value}`);
|
|
1349
|
+
}
|
|
1350
|
+
const selection = ensureSelection(record);
|
|
1351
|
+
if (selection === void 0 || route.provider === void 0 || route.model === void 0) {
|
|
1352
|
+
throw invalidParams("reasoning effort switching is unavailable on this host");
|
|
1353
|
+
}
|
|
1354
|
+
selection.current = {
|
|
1355
|
+
provider: route.provider,
|
|
1356
|
+
model: route.model,
|
|
1357
|
+
reasoningEffort: value
|
|
1358
|
+
};
|
|
1359
|
+
record.effort = value;
|
|
1360
|
+
break;
|
|
1159
1361
|
}
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
if (choice.provider !== void 0 && choice.provider !== defaultProvider()) {
|
|
1164
|
-
record.provider = choice.provider;
|
|
1165
|
-
} else {
|
|
1166
|
-
delete record.provider;
|
|
1362
|
+
case "model": {
|
|
1363
|
+
await switchModel(record, params.sessionId, value);
|
|
1364
|
+
break;
|
|
1167
1365
|
}
|
|
1366
|
+
default:
|
|
1367
|
+
throw invalidParams(`unknown config option: ${params.configId}`);
|
|
1168
1368
|
}
|
|
1169
1369
|
return { configOptions: await configOptions(record) };
|
|
1170
1370
|
},
|
|
@@ -1600,6 +1800,8 @@ async function apply2(ctx, config) {
|
|
|
1600
1800
|
const p = kit.plugins;
|
|
1601
1801
|
const anyCtx = ctx;
|
|
1602
1802
|
const credentialsSeam = p["credentialsSeam"];
|
|
1803
|
+
const agentsModule = p["agents"];
|
|
1804
|
+
const installModelSelection = agentsModule.installModelSelection ?? agentsModule.ln;
|
|
1603
1805
|
const harness = {
|
|
1604
1806
|
createUserMessage: kit.llm.createUserMessage,
|
|
1605
1807
|
errorChain: kit.llm.errorChain,
|
|
@@ -1607,6 +1809,7 @@ async function apply2(ctx, config) {
|
|
|
1607
1809
|
foldSessionTitle: kit.sessionTitle.foldSessionTitle,
|
|
1608
1810
|
setSandboxMode: kit.sandboxPolicy.setSandboxMode,
|
|
1609
1811
|
sandboxModes: kit.sandboxPolicy.SANDBOX_MODES,
|
|
1812
|
+
...installModelSelection !== void 0 ? { installModelSelection } : {},
|
|
1610
1813
|
...credentialsSeam?.credentialRef !== void 0 ? { credentialRef: credentialsSeam.credentialRef } : {},
|
|
1611
1814
|
...p["mcpClient"] !== void 0 ? { mcpClient: p["mcpClient"] } : {}
|
|
1612
1815
|
};
|