@openma/deepseek-harness-acp 0.1.0 → 0.3.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/README.md +27 -0
- package/cordis.patch.yml +84 -0
- package/dist/bridge.js +381 -66
- package/dist/index.js +384 -66
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -161,6 +161,33 @@ npm install --prefix /tmp/dsh-host @deepseek-ai/dsh
|
|
|
161
161
|
DSH_ACP_TEST_HOST=/tmp/dsh-host npm test
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
+
### Live iteration: paired profiles
|
|
165
|
+
|
|
166
|
+
Keep the profile you use in your editor on the published package, and point a
|
|
167
|
+
second profile at this worktree via a pnpm symlink:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# stable — what Zed uses day to day
|
|
171
|
+
dsh plugin --profile acp add -w @openma/deepseek-harness-acp
|
|
172
|
+
|
|
173
|
+
# dev — a symlink to this checkout; no packing, no version bumps
|
|
174
|
+
dsh plugin --profile acp-test add -w "link:$PWD"
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The dev loop is then just `npm run build` and a process restart (`dist/` and
|
|
178
|
+
`cordis.patch.yml` are read through the link). Note pnpm treats `file:` as a
|
|
179
|
+
copy install and caches same-version tarballs — `link:` avoids both. In Zed,
|
|
180
|
+
register both entries:
|
|
181
|
+
|
|
182
|
+
```jsonc
|
|
183
|
+
{
|
|
184
|
+
"agent_servers": {
|
|
185
|
+
"DeepSeek Harness": { "command": "dsh", "args": ["--profile", "acp"] },
|
|
186
|
+
"DeepSeek Harness (dev)": { "command": "dsh", "args": ["--profile", "acp-test"] }
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
164
191
|
## License
|
|
165
192
|
|
|
166
193
|
Apache-2.0.
|
package/cordis.patch.yml
CHANGED
|
@@ -20,7 +20,91 @@
|
|
|
20
20
|
- id: hmr
|
|
21
21
|
disabled: true
|
|
22
22
|
|
|
23
|
+
# Model-facing tool rows move off the host plane and into each agent preset's
|
|
24
|
+
# own composition (standard/code/minimal/cordis ship complete agent.cordis.yml
|
|
25
|
+
# files) — the same split the web-app bundle makes, and what gives "minimal"
|
|
26
|
+
# its two-tool surface. Host-plane services (job/skill/goal registries,
|
|
27
|
+
# shell-env, sandbox, approval) stay in dsh-base and serve every preset.
|
|
28
|
+
|
|
29
|
+
- id: tool-bash
|
|
30
|
+
disabled: true
|
|
31
|
+
|
|
32
|
+
- id: tool-pwsh
|
|
33
|
+
disabled: true
|
|
34
|
+
|
|
35
|
+
- id: tool-jobs
|
|
36
|
+
disabled: true
|
|
37
|
+
|
|
38
|
+
- id: tool-fs
|
|
39
|
+
disabled: true
|
|
40
|
+
|
|
41
|
+
- id: tool-fs-search
|
|
42
|
+
disabled: true
|
|
43
|
+
|
|
44
|
+
- id: tool-str-replace-editor
|
|
45
|
+
disabled: true
|
|
46
|
+
|
|
47
|
+
- id: skill-filesystem
|
|
48
|
+
disabled: true
|
|
49
|
+
|
|
50
|
+
- id: tool-skill
|
|
51
|
+
disabled: true
|
|
52
|
+
|
|
53
|
+
- id: tool-goal
|
|
54
|
+
disabled: true
|
|
55
|
+
|
|
56
|
+
- id: plan-mode
|
|
57
|
+
disabled: true
|
|
58
|
+
|
|
59
|
+
- id: compaction-basic
|
|
60
|
+
disabled: true
|
|
61
|
+
|
|
62
|
+
- id: command-compact
|
|
63
|
+
disabled: true
|
|
64
|
+
|
|
65
|
+
- id: tool-result-pruner
|
|
66
|
+
disabled: true
|
|
67
|
+
|
|
68
|
+
- id: tool-subagent-control
|
|
69
|
+
disabled: true
|
|
70
|
+
|
|
71
|
+
- id: tool-subagent-list-agents
|
|
72
|
+
disabled: true
|
|
73
|
+
|
|
74
|
+
- id: tool-subagent
|
|
75
|
+
disabled: true
|
|
76
|
+
|
|
77
|
+
- id: tool-subagent-fork
|
|
78
|
+
disabled: true
|
|
79
|
+
|
|
80
|
+
- id: workflow-worker-thread
|
|
81
|
+
disabled: true
|
|
82
|
+
|
|
83
|
+
- id: tool-workflow
|
|
84
|
+
disabled: true
|
|
85
|
+
|
|
86
|
+
- id: tool-ralph
|
|
87
|
+
disabled: true
|
|
88
|
+
|
|
89
|
+
- id: agent-instructions
|
|
90
|
+
disabled: true
|
|
91
|
+
|
|
92
|
+
- id: tool-todo
|
|
93
|
+
disabled: true
|
|
94
|
+
|
|
95
|
+
- id: tool-web
|
|
96
|
+
disabled: true
|
|
97
|
+
|
|
23
98
|
- insert:
|
|
99
|
+
# Agent presets: per-session model-facing compositions (persona, tools,
|
|
100
|
+
# compaction). The dsh CLI injects its shipped root into any row with this
|
|
101
|
+
# id, so standard/code/minimal/creator are available and ACP session modes
|
|
102
|
+
# surface them (~/.dsh/agent-presets adds user-authored ones).
|
|
103
|
+
- id: agent-presets
|
|
104
|
+
name: '@deepseek-ai/dsh-agent-presets'
|
|
105
|
+
config:
|
|
106
|
+
default: standard
|
|
107
|
+
|
|
24
108
|
- id: acp-bridge
|
|
25
109
|
name: '@openma/deepseek-harness-acp/bridge'
|
|
26
110
|
config:
|
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
|
}
|
|
@@ -54,7 +58,8 @@ import {
|
|
|
54
58
|
} from "@agentclientprotocol/sdk";
|
|
55
59
|
|
|
56
60
|
// src/version.ts
|
|
57
|
-
|
|
61
|
+
import { createRequire } from "node:module";
|
|
62
|
+
var VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
58
63
|
|
|
59
64
|
// src/log.ts
|
|
60
65
|
var PREFIX = "[dsh-acp]";
|
|
@@ -610,6 +615,7 @@ async function apply(ctx, config = {}) {
|
|
|
610
615
|
return { provider: value.slice(0, i), model: value.slice(i + 2) };
|
|
611
616
|
};
|
|
612
617
|
let liveCatalog;
|
|
618
|
+
const effortCache = /* @__PURE__ */ new Map();
|
|
613
619
|
const discoverModels = async () => {
|
|
614
620
|
if (liveCatalog !== void 0) return liveCatalog;
|
|
615
621
|
const llm = ctx.get("llm");
|
|
@@ -640,7 +646,141 @@ async function apply(ctx, config = {}) {
|
|
|
640
646
|
};
|
|
641
647
|
ctx.on("llm/adapters-updated", () => {
|
|
642
648
|
liveCatalog = void 0;
|
|
649
|
+
effortCache.clear();
|
|
643
650
|
});
|
|
651
|
+
const routeOf = (record) => {
|
|
652
|
+
const logged = loggedConfig(record);
|
|
653
|
+
const provider = record.provider ?? defaultProvider() ?? logged?.provider;
|
|
654
|
+
const model = record.model ?? config.model ?? defaultSelection().model ?? logged?.model ?? record.agent.options.model;
|
|
655
|
+
return {
|
|
656
|
+
...provider !== void 0 ? { provider } : {},
|
|
657
|
+
...model !== void 0 ? { model } : {}
|
|
658
|
+
};
|
|
659
|
+
};
|
|
660
|
+
const loggedConfig = (record) => {
|
|
661
|
+
try {
|
|
662
|
+
const header = record.agent.session.requestHeader?.();
|
|
663
|
+
const cfg = header?.config;
|
|
664
|
+
if (cfg === void 0) return void 0;
|
|
665
|
+
return {
|
|
666
|
+
provider: cfg.provider,
|
|
667
|
+
model: cfg.model,
|
|
668
|
+
...cfg.reasoningEffort !== void 0 ? { reasoningEffort: String(cfg.reasoningEffort) } : {}
|
|
669
|
+
};
|
|
670
|
+
} catch {
|
|
671
|
+
return void 0;
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
const effortCatalog = async (provider, model) => {
|
|
675
|
+
if (provider === void 0 || model === void 0) return void 0;
|
|
676
|
+
const key = `${provider}::${model}`;
|
|
677
|
+
if (effortCache.has(key)) return effortCache.get(key);
|
|
678
|
+
const llm = ctx.get("llm");
|
|
679
|
+
let catalog;
|
|
680
|
+
try {
|
|
681
|
+
const reasoning = (await llm?.resolveModelInfo?.(provider, model))?.reasoning;
|
|
682
|
+
if (reasoning !== void 0 && reasoning.efforts.length > 0) {
|
|
683
|
+
catalog = {
|
|
684
|
+
efforts: reasoning.efforts.map((effort) => ({
|
|
685
|
+
id: String(effort.id),
|
|
686
|
+
name: effort.name,
|
|
687
|
+
...effort.description !== void 0 ? { description: effort.description } : {}
|
|
688
|
+
})),
|
|
689
|
+
...reasoning.defaultEffort !== void 0 ? { defaultEffort: String(reasoning.defaultEffort) } : {}
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
} catch (error) {
|
|
693
|
+
logDebug(`resolveModelInfo(${key}) failed: ${String(error)}`);
|
|
694
|
+
}
|
|
695
|
+
effortCache.set(key, catalog);
|
|
696
|
+
return catalog;
|
|
697
|
+
};
|
|
698
|
+
const ensureSelection = (record) => {
|
|
699
|
+
const install = harness.installModelSelection;
|
|
700
|
+
if (install === void 0) return void 0;
|
|
701
|
+
if (record.selection !== void 0) return record.selection;
|
|
702
|
+
let picked;
|
|
703
|
+
const selection = {
|
|
704
|
+
get current() {
|
|
705
|
+
if (picked !== void 0) return picked;
|
|
706
|
+
const logged = loggedConfig(record);
|
|
707
|
+
if (logged !== void 0) return logged;
|
|
708
|
+
const { provider, model } = routeOf(record);
|
|
709
|
+
if (provider === void 0 || model === void 0) return void 0;
|
|
710
|
+
const defaults = defaultSelection();
|
|
711
|
+
const effort = defaults.model === void 0 || defaults.model === model ? defaults.reasoningEffort : void 0;
|
|
712
|
+
return { provider, model, ...effort !== void 0 ? { reasoningEffort: effort } : {} };
|
|
713
|
+
},
|
|
714
|
+
set current(next) {
|
|
715
|
+
picked = next;
|
|
716
|
+
},
|
|
717
|
+
assembled: void 0
|
|
718
|
+
};
|
|
719
|
+
try {
|
|
720
|
+
install(record.agent.ctx, selection);
|
|
721
|
+
} catch (error) {
|
|
722
|
+
logWarn(`installModelSelection failed: ${String(error)}`);
|
|
723
|
+
return void 0;
|
|
724
|
+
}
|
|
725
|
+
record.selection = selection;
|
|
726
|
+
return selection;
|
|
727
|
+
};
|
|
728
|
+
const switchModel = async (record, acpSessionId, value) => {
|
|
729
|
+
if (record.inflight !== void 0) {
|
|
730
|
+
throw invalidParams("cannot switch models while a prompt is running");
|
|
731
|
+
}
|
|
732
|
+
const choice = decodeChoice(value);
|
|
733
|
+
const known = /* @__PURE__ */ new Set([
|
|
734
|
+
...modelCandidates().map((model) => encodeChoice(void 0, model)),
|
|
735
|
+
...(await discoverModels()).map((entry) => encodeChoice(entry.provider, entry.model)),
|
|
736
|
+
encodeChoice(record.provider, record.model ?? "")
|
|
737
|
+
]);
|
|
738
|
+
if (!known.has(value)) throw invalidParams(`unknown model: ${value}`);
|
|
739
|
+
if (choice.model === record.model && choice.provider === (record.provider ?? defaultProvider())) return;
|
|
740
|
+
const sessionId = record.agent.session.id;
|
|
741
|
+
await record.dispose().catch((error) => {
|
|
742
|
+
logWarn(`dispose during model switch failed: ${String(error)}`);
|
|
743
|
+
});
|
|
744
|
+
let handle;
|
|
745
|
+
try {
|
|
746
|
+
const presets = presetsService();
|
|
747
|
+
handle = await agents.resume({
|
|
748
|
+
resumeSessionId: sessionId,
|
|
749
|
+
agentOptions: agentOptionsFor(choice.model, choice.provider),
|
|
750
|
+
...presetSetup(presets, record.preset) !== void 0 ? { setup: presetSetup(presets, record.preset) } : {}
|
|
751
|
+
});
|
|
752
|
+
} catch (error) {
|
|
753
|
+
sessions.delete(acpSessionId);
|
|
754
|
+
throw internalError(`model switch failed: ${errorChain2(error)}`);
|
|
755
|
+
}
|
|
756
|
+
record.agent = handle.agent;
|
|
757
|
+
record.dispose = () => handle.dispose();
|
|
758
|
+
record.model = choice.model;
|
|
759
|
+
if (choice.provider !== void 0 && choice.provider !== defaultProvider()) {
|
|
760
|
+
record.provider = choice.provider;
|
|
761
|
+
} else {
|
|
762
|
+
delete record.provider;
|
|
763
|
+
}
|
|
764
|
+
delete record.selection;
|
|
765
|
+
ensureSelection(record);
|
|
766
|
+
if (record.effort !== void 0) {
|
|
767
|
+
const route = routeOf(record);
|
|
768
|
+
const catalog = await effortCatalog(route.provider, route.model);
|
|
769
|
+
if (catalog?.efforts.some((effort) => effort.id === record.effort) === true && route.provider !== void 0 && route.model !== void 0) {
|
|
770
|
+
const selection = ensureSelection(record);
|
|
771
|
+
if (selection !== void 0) {
|
|
772
|
+
selection.current = {
|
|
773
|
+
provider: route.provider,
|
|
774
|
+
model: route.model,
|
|
775
|
+
reasoningEffort: record.effort
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
} else {
|
|
779
|
+
delete record.effort;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
setApprovalPolicy(record, record.approvals);
|
|
783
|
+
};
|
|
644
784
|
const credentialPresent = async () => {
|
|
645
785
|
if (envCredentialPresent()) return true;
|
|
646
786
|
const brand = harness.credentialRef;
|
|
@@ -691,14 +831,113 @@ async function apply(ctx, config = {}) {
|
|
|
691
831
|
record.inflight = void 0;
|
|
692
832
|
inflight.resolve(reason);
|
|
693
833
|
};
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
834
|
+
const presetsService = () => ctx.get("agentPresets");
|
|
835
|
+
const presetSetup = (presets, presetId) => {
|
|
836
|
+
if (presets === void 0 || presetId === void 0) return void 0;
|
|
837
|
+
return async (agentCtx) => {
|
|
838
|
+
logDebug(`preset setup: mounting "${presetId}"`);
|
|
839
|
+
await presets.mount(agentCtx, presetId);
|
|
840
|
+
logDebug(`preset setup: mounted "${presetId}"`);
|
|
841
|
+
};
|
|
842
|
+
};
|
|
843
|
+
const presetFromLog = (header, events) => {
|
|
844
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
845
|
+
const event = events[index];
|
|
846
|
+
if (event?.type === "agent-preset/selected") {
|
|
847
|
+
return event.data?.agentPreset;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
return header?.agentPreset;
|
|
851
|
+
};
|
|
852
|
+
const presetLabel = (id) => id.charAt(0).toUpperCase() + id.slice(1);
|
|
853
|
+
const modeState = (record) => {
|
|
854
|
+
const permission = permissionService();
|
|
855
|
+
if (permission !== void 0 && permission.names.length > 0) {
|
|
856
|
+
return {
|
|
857
|
+
currentModeId: record.modeId,
|
|
858
|
+
availableModes: permission.names.map((name3) => {
|
|
859
|
+
const spec = permission.resolve(name3);
|
|
860
|
+
return { id: name3, name: spec.name, description: spec.description };
|
|
861
|
+
})
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
return {
|
|
865
|
+
currentModeId: record.modeId,
|
|
866
|
+
availableModes: SANDBOX_MODES2.map((mode) => {
|
|
867
|
+
const label = MODE_LABELS[mode] ?? { name: mode, description: "" };
|
|
868
|
+
return { id: mode, name: label.name, description: label.description };
|
|
869
|
+
})
|
|
870
|
+
};
|
|
871
|
+
};
|
|
872
|
+
const switchPreset = async (record, acpSessionId, presetId) => {
|
|
873
|
+
if (record.inflight !== void 0) {
|
|
874
|
+
throw invalidParams("cannot switch presets while a prompt is running");
|
|
875
|
+
}
|
|
876
|
+
const presets = presetsService();
|
|
877
|
+
if (presets === void 0) throw invalidParams(`unknown mode: ${presetId}`);
|
|
878
|
+
let resolved;
|
|
879
|
+
try {
|
|
880
|
+
resolved = (await presets.resolve(presetId)).id;
|
|
881
|
+
} catch (error) {
|
|
882
|
+
throw invalidParams(`unknown preset: ${presetId} (${errorChain2(error)})`);
|
|
883
|
+
}
|
|
884
|
+
if (resolved === record.preset) return;
|
|
885
|
+
try {
|
|
886
|
+
record.agent.session.append("agent-preset/selected", { agentPreset: resolved });
|
|
887
|
+
} catch (error) {
|
|
888
|
+
logWarn(`recording preset selection failed: ${String(error)}`);
|
|
889
|
+
}
|
|
890
|
+
const sessionId = record.agent.session.id;
|
|
891
|
+
await record.dispose().catch((error) => {
|
|
892
|
+
logWarn(`dispose during preset switch failed: ${String(error)}`);
|
|
893
|
+
});
|
|
894
|
+
let handle;
|
|
895
|
+
try {
|
|
896
|
+
handle = await agents.resume({
|
|
897
|
+
resumeSessionId: sessionId,
|
|
898
|
+
agentOptions: agentOptionsFor(record.model ?? config.model, record.provider),
|
|
899
|
+
setup: presetSetup(presets, resolved)
|
|
900
|
+
});
|
|
901
|
+
} catch (error) {
|
|
902
|
+
sessions.delete(acpSessionId);
|
|
903
|
+
throw internalError(`preset switch failed: ${errorChain2(error)}`);
|
|
904
|
+
}
|
|
905
|
+
record.agent = handle.agent;
|
|
906
|
+
record.dispose = () => handle.dispose();
|
|
907
|
+
record.preset = resolved;
|
|
908
|
+
delete record.selection;
|
|
909
|
+
ensureSelection(record);
|
|
910
|
+
setApprovalPolicy(record, record.approvals);
|
|
911
|
+
};
|
|
912
|
+
const setApprovalPolicy = (record, policy) => {
|
|
913
|
+
try {
|
|
914
|
+
ctx.get("approval")?.setPolicy?.(record.agent, policy);
|
|
915
|
+
} catch (error) {
|
|
916
|
+
logWarn(`approval policy switch failed: ${String(error)}`);
|
|
917
|
+
}
|
|
918
|
+
record.approvals = policy;
|
|
919
|
+
};
|
|
920
|
+
const permissionService = () => ctx.get("permission");
|
|
921
|
+
const applyMode = (record, sessionId, modeId) => {
|
|
922
|
+
const permission = permissionService();
|
|
923
|
+
if (permission !== void 0 && permission.names.includes(modeId)) {
|
|
924
|
+
let bundledApproval = record.approvals;
|
|
925
|
+
permission.apply(record.agent.session, modeId, (policy) => {
|
|
926
|
+
bundledApproval = policy;
|
|
927
|
+
});
|
|
928
|
+
setApprovalPolicy(record, bundledApproval);
|
|
929
|
+
record.modeId = permission.resolve(modeId).sandbox;
|
|
930
|
+
notify(sessionId, { sessionUpdate: "current_mode_update", currentModeId: modeId });
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
const mode = SANDBOX_MODES2.find((candidate) => candidate === modeId);
|
|
934
|
+
if (mode === void 0) throw invalidParams(`unknown mode: ${modeId}`);
|
|
935
|
+
setSandboxMode2(record.agent.session, mode);
|
|
936
|
+
setApprovalPolicy(record, mode === "danger-full-access" ? "never" : "ask");
|
|
937
|
+
record.modeId = mode;
|
|
938
|
+
notify(sessionId, { sessionUpdate: "current_mode_update", currentModeId: mode });
|
|
939
|
+
};
|
|
940
|
+
const modelChoices = async (record) => {
|
|
702
941
|
const seen = /* @__PURE__ */ new Set();
|
|
703
942
|
const options = [];
|
|
704
943
|
const push = (value, name3) => {
|
|
@@ -717,17 +956,70 @@ async function apply(ctx, config = {}) {
|
|
|
717
956
|
push(encodeChoice(choice.provider, choice.model), choice.label);
|
|
718
957
|
}
|
|
719
958
|
for (const model of modelCandidates()) push(encodeChoice(void 0, model), model);
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
959
|
+
return options;
|
|
960
|
+
};
|
|
961
|
+
const configOptions = async (record) => {
|
|
962
|
+
const result = [];
|
|
963
|
+
const models = await modelChoices(record);
|
|
964
|
+
if (models.length >= 2) {
|
|
965
|
+
result.push({
|
|
723
966
|
type: "select",
|
|
724
967
|
id: "model",
|
|
725
968
|
name: "Model",
|
|
726
969
|
category: "model",
|
|
727
|
-
currentValue:
|
|
728
|
-
options
|
|
970
|
+
currentValue: models[0].value,
|
|
971
|
+
options: models
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
const route = routeOf(record);
|
|
975
|
+
const efforts = await effortCatalog(route.provider, route.model);
|
|
976
|
+
if (efforts !== void 0 && efforts.efforts.length >= 2) {
|
|
977
|
+
const known = new Set(efforts.efforts.map((effort) => effort.id));
|
|
978
|
+
const preferred = [
|
|
979
|
+
record.effort,
|
|
980
|
+
loggedConfig(record)?.reasoningEffort,
|
|
981
|
+
// The user's saved product default (Web UI → settings.yaml),
|
|
982
|
+
// e.g. reasoningEffort: max, outranks the adapter's default.
|
|
983
|
+
defaultSelection().reasoningEffort,
|
|
984
|
+
efforts.defaultEffort
|
|
985
|
+
].find(
|
|
986
|
+
(candidate) => candidate !== void 0 && known.has(candidate)
|
|
987
|
+
);
|
|
988
|
+
result.push({
|
|
989
|
+
type: "select",
|
|
990
|
+
id: "effort",
|
|
991
|
+
name: "Reasoning",
|
|
992
|
+
category: "thought_level",
|
|
993
|
+
currentValue: preferred ?? efforts.efforts[0].id,
|
|
994
|
+
options: efforts.efforts.map((effort) => ({
|
|
995
|
+
value: effort.id,
|
|
996
|
+
name: effort.name,
|
|
997
|
+
...effort.description !== void 0 ? { description: effort.description } : {}
|
|
998
|
+
}))
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
const presets = presetsService();
|
|
1002
|
+
if (presets !== void 0 && record.preset !== void 0) {
|
|
1003
|
+
try {
|
|
1004
|
+
const roster = await presets.list();
|
|
1005
|
+
if (roster.length >= 2) {
|
|
1006
|
+
result.push({
|
|
1007
|
+
type: "select",
|
|
1008
|
+
id: "preset",
|
|
1009
|
+
name: "Preset",
|
|
1010
|
+
currentValue: record.preset,
|
|
1011
|
+
options: roster.map((preset) => ({
|
|
1012
|
+
value: preset.id,
|
|
1013
|
+
name: presetLabel(preset.id),
|
|
1014
|
+
description: `Agent preset \u201C${preset.id}\u201D`
|
|
1015
|
+
}))
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
} catch (error) {
|
|
1019
|
+
logWarn(`preset roster unavailable: ${String(error)}`);
|
|
729
1020
|
}
|
|
730
|
-
|
|
1021
|
+
}
|
|
1022
|
+
return result;
|
|
731
1023
|
};
|
|
732
1024
|
const publishCommands = (sessionId) => {
|
|
733
1025
|
notify(sessionId, {
|
|
@@ -809,15 +1101,20 @@ async function apply(ctx, config = {}) {
|
|
|
809
1101
|
};
|
|
810
1102
|
const statusText = async (record) => {
|
|
811
1103
|
const used = record.projection.contextWindow;
|
|
1104
|
+
const route = routeOf(record);
|
|
1105
|
+
const effort = record.effort ?? loggedConfig(record)?.reasoningEffort;
|
|
812
1106
|
const lines = [
|
|
813
1107
|
`**dsh-acp** ${VERSION} \u2014 DeepSeek Harness ACP bridge`,
|
|
814
1108
|
"",
|
|
815
1109
|
`| | |`,
|
|
816
1110
|
`|---|---|`,
|
|
817
|
-
`| Provider | ${
|
|
818
|
-
`| Model | ${
|
|
1111
|
+
`| Provider | ${route.provider ?? "(composition default)"} |`,
|
|
1112
|
+
`| Model | ${route.model ?? "(composition default)"} |`,
|
|
1113
|
+
`| Reasoning | ${effort ?? "(adapter default)"} |`,
|
|
819
1114
|
`| Credential | ${await describeCredential()} |`,
|
|
1115
|
+
...record.preset !== void 0 ? [`| Preset | ${record.preset} |`] : [],
|
|
820
1116
|
`| Permission mode | ${record.modeId} |`,
|
|
1117
|
+
`| Approvals | ${record.approvals} |`,
|
|
821
1118
|
`| Workspace | ${record.agent.session.header.cwd ?? process.cwd()} |`,
|
|
822
1119
|
`| Session | ${String(record.agent.session.id)} |`,
|
|
823
1120
|
...used !== void 0 ? [`| Context window | ${used.toLocaleString()} tokens |`] : []
|
|
@@ -831,6 +1128,7 @@ async function apply(ctx, config = {}) {
|
|
|
831
1128
|
projection: projection ?? new SessionProjection(),
|
|
832
1129
|
modeId,
|
|
833
1130
|
model,
|
|
1131
|
+
approvals: modeId === "danger-full-access" ? "never" : "ask",
|
|
834
1132
|
cancelled: false,
|
|
835
1133
|
inflight: void 0
|
|
836
1134
|
};
|
|
@@ -923,10 +1221,13 @@ async function apply(ctx, config = {}) {
|
|
|
923
1221
|
validateCwd(params.cwd);
|
|
924
1222
|
syncMcpServers(params.mcpServers, params.cwd);
|
|
925
1223
|
const sessionId = SessionId2(randomUUID());
|
|
1224
|
+
const presets = presetsService();
|
|
1225
|
+
const presetId = presets === void 0 ? void 0 : (await presets.resolve(void 0)).id;
|
|
926
1226
|
const handle = await agents.create({
|
|
927
1227
|
sessionId,
|
|
928
|
-
meta: { cwd: params.cwd },
|
|
929
|
-
agentOptions: agentOptionsFor(config.model)
|
|
1228
|
+
meta: { cwd: params.cwd, ...presetId !== void 0 ? { agentPreset: presetId } : {} },
|
|
1229
|
+
agentOptions: agentOptionsFor(config.model),
|
|
1230
|
+
...presetSetup(presets, presetId) !== void 0 ? { setup: presetSetup(presets, presetId) } : {}
|
|
930
1231
|
});
|
|
931
1232
|
if (closed) {
|
|
932
1233
|
await handle.dispose();
|
|
@@ -939,6 +1240,8 @@ async function apply(ctx, config = {}) {
|
|
|
939
1240
|
config.model,
|
|
940
1241
|
config.permissionMode ?? "workspace-write"
|
|
941
1242
|
);
|
|
1243
|
+
if (presetId !== void 0) record.preset = presetId;
|
|
1244
|
+
ensureSelection(record);
|
|
942
1245
|
queueMicrotask(() => publishCommands(String(sessionId)));
|
|
943
1246
|
const options = await configOptions(record);
|
|
944
1247
|
return {
|
|
@@ -963,8 +1266,11 @@ async function apply(ctx, config = {}) {
|
|
|
963
1266
|
});
|
|
964
1267
|
}
|
|
965
1268
|
let events;
|
|
1269
|
+
let storedHeader;
|
|
966
1270
|
try {
|
|
967
|
-
|
|
1271
|
+
const inspected = await persistence.inspect(SessionId2(sessionId));
|
|
1272
|
+
events = inspected.events;
|
|
1273
|
+
storedHeader = inspected.header;
|
|
968
1274
|
} catch (error) {
|
|
969
1275
|
throw invalidParams(`session not found: ${sessionId} (${errorChain2(error)})`);
|
|
970
1276
|
}
|
|
@@ -976,9 +1282,13 @@ async function apply(ctx, config = {}) {
|
|
|
976
1282
|
title: replay.title
|
|
977
1283
|
});
|
|
978
1284
|
}
|
|
1285
|
+
const presets = presetsService();
|
|
1286
|
+
const storedPreset = presetFromLog(storedHeader, events);
|
|
1287
|
+
const presetId = presets === void 0 ? void 0 : (await presets.resolve(storedPreset)).id;
|
|
979
1288
|
const handle = await agents.resume({
|
|
980
1289
|
resumeSessionId: SessionId2(sessionId),
|
|
981
|
-
agentOptions: agentOptionsFor(config.model)
|
|
1290
|
+
agentOptions: agentOptionsFor(config.model),
|
|
1291
|
+
...presetSetup(presets, presetId) !== void 0 ? { setup: presetSetup(presets, presetId) } : {}
|
|
982
1292
|
});
|
|
983
1293
|
const projection = new SessionProjection(replay.contextWindow);
|
|
984
1294
|
projection.title = replay.title;
|
|
@@ -990,6 +1300,21 @@ async function apply(ctx, config = {}) {
|
|
|
990
1300
|
config.permissionMode ?? "workspace-write",
|
|
991
1301
|
projection
|
|
992
1302
|
);
|
|
1303
|
+
if (presetId !== void 0) record.preset = presetId;
|
|
1304
|
+
{
|
|
1305
|
+
const permission = permissionService();
|
|
1306
|
+
const storedMode = permission?.current(events);
|
|
1307
|
+
if (storedMode !== void 0) record.modeId = storedMode;
|
|
1308
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
1309
|
+
const event = events[index];
|
|
1310
|
+
if (event?.type === "approval/policy") {
|
|
1311
|
+
const policy = event.data?.policy;
|
|
1312
|
+
if (policy === "ask" || policy === "never") record.approvals = policy;
|
|
1313
|
+
break;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
ensureSelection(record);
|
|
993
1318
|
queueMicrotask(() => publishCommands(sessionId));
|
|
994
1319
|
const options = await configOptions(record);
|
|
995
1320
|
return {
|
|
@@ -1095,58 +1420,48 @@ async function apply(ctx, config = {}) {
|
|
|
1095
1420
|
const usage = record.projection.promptUsage();
|
|
1096
1421
|
return { stopReason, ...usage !== void 0 ? { usage } : {} };
|
|
1097
1422
|
},
|
|
1098
|
-
setSessionMode(params) {
|
|
1423
|
+
async setSessionMode(params) {
|
|
1099
1424
|
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({});
|
|
1425
|
+
applyMode(record, params.sessionId, params.modeId);
|
|
1426
|
+
notify(params.sessionId, {
|
|
1427
|
+
sessionUpdate: "config_option_update",
|
|
1428
|
+
configOptions: await configOptions(record)
|
|
1429
|
+
});
|
|
1430
|
+
return {};
|
|
1111
1431
|
},
|
|
1112
1432
|
async setSessionConfigOption(params) {
|
|
1113
1433
|
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
1434
|
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
|
-
|
|
1435
|
+
if (value === void 0) throw invalidParams(`invalid value: ${String(params.value)}`);
|
|
1436
|
+
switch (params.configId) {
|
|
1437
|
+
case "preset": {
|
|
1438
|
+
await switchPreset(record, params.sessionId, value);
|
|
1439
|
+
break;
|
|
1440
|
+
}
|
|
1441
|
+
case "effort": {
|
|
1442
|
+
const route = routeOf(record);
|
|
1443
|
+
const catalog = await effortCatalog(route.provider, route.model);
|
|
1444
|
+
if (catalog === void 0 || !catalog.efforts.some((effort) => effort.id === value)) {
|
|
1445
|
+
throw invalidParams(`unknown effort: ${value}`);
|
|
1446
|
+
}
|
|
1447
|
+
const selection = ensureSelection(record);
|
|
1448
|
+
if (selection === void 0 || route.provider === void 0 || route.model === void 0) {
|
|
1449
|
+
throw invalidParams("reasoning effort switching is unavailable on this host");
|
|
1450
|
+
}
|
|
1451
|
+
selection.current = {
|
|
1452
|
+
provider: route.provider,
|
|
1453
|
+
model: route.model,
|
|
1454
|
+
reasoningEffort: value
|
|
1455
|
+
};
|
|
1456
|
+
record.effort = value;
|
|
1457
|
+
break;
|
|
1141
1458
|
}
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
if (choice.provider !== void 0 && choice.provider !== defaultProvider()) {
|
|
1146
|
-
record.provider = choice.provider;
|
|
1147
|
-
} else {
|
|
1148
|
-
delete record.provider;
|
|
1459
|
+
case "model": {
|
|
1460
|
+
await switchModel(record, params.sessionId, value);
|
|
1461
|
+
break;
|
|
1149
1462
|
}
|
|
1463
|
+
default:
|
|
1464
|
+
throw invalidParams(`unknown config option: ${params.configId}`);
|
|
1150
1465
|
}
|
|
1151
1466
|
return { configOptions: await configOptions(record) };
|
|
1152
1467
|
},
|
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
|
}
|
|
@@ -68,7 +72,8 @@ import {
|
|
|
68
72
|
} from "@agentclientprotocol/sdk";
|
|
69
73
|
|
|
70
74
|
// src/version.ts
|
|
71
|
-
|
|
75
|
+
import { createRequire } from "node:module";
|
|
76
|
+
var VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
72
77
|
|
|
73
78
|
// src/log.ts
|
|
74
79
|
var PREFIX = "[dsh-acp]";
|
|
@@ -628,6 +633,7 @@ async function apply(ctx, config = {}) {
|
|
|
628
633
|
return { provider: value.slice(0, i), model: value.slice(i + 2) };
|
|
629
634
|
};
|
|
630
635
|
let liveCatalog;
|
|
636
|
+
const effortCache = /* @__PURE__ */ new Map();
|
|
631
637
|
const discoverModels = async () => {
|
|
632
638
|
if (liveCatalog !== void 0) return liveCatalog;
|
|
633
639
|
const llm = ctx.get("llm");
|
|
@@ -658,7 +664,141 @@ async function apply(ctx, config = {}) {
|
|
|
658
664
|
};
|
|
659
665
|
ctx.on("llm/adapters-updated", () => {
|
|
660
666
|
liveCatalog = void 0;
|
|
667
|
+
effortCache.clear();
|
|
661
668
|
});
|
|
669
|
+
const routeOf = (record) => {
|
|
670
|
+
const logged = loggedConfig(record);
|
|
671
|
+
const provider = record.provider ?? defaultProvider() ?? logged?.provider;
|
|
672
|
+
const model = record.model ?? config.model ?? defaultSelection().model ?? logged?.model ?? record.agent.options.model;
|
|
673
|
+
return {
|
|
674
|
+
...provider !== void 0 ? { provider } : {},
|
|
675
|
+
...model !== void 0 ? { model } : {}
|
|
676
|
+
};
|
|
677
|
+
};
|
|
678
|
+
const loggedConfig = (record) => {
|
|
679
|
+
try {
|
|
680
|
+
const header = record.agent.session.requestHeader?.();
|
|
681
|
+
const cfg = header?.config;
|
|
682
|
+
if (cfg === void 0) return void 0;
|
|
683
|
+
return {
|
|
684
|
+
provider: cfg.provider,
|
|
685
|
+
model: cfg.model,
|
|
686
|
+
...cfg.reasoningEffort !== void 0 ? { reasoningEffort: String(cfg.reasoningEffort) } : {}
|
|
687
|
+
};
|
|
688
|
+
} catch {
|
|
689
|
+
return void 0;
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
const effortCatalog = async (provider, model) => {
|
|
693
|
+
if (provider === void 0 || model === void 0) return void 0;
|
|
694
|
+
const key = `${provider}::${model}`;
|
|
695
|
+
if (effortCache.has(key)) return effortCache.get(key);
|
|
696
|
+
const llm = ctx.get("llm");
|
|
697
|
+
let catalog;
|
|
698
|
+
try {
|
|
699
|
+
const reasoning = (await llm?.resolveModelInfo?.(provider, model))?.reasoning;
|
|
700
|
+
if (reasoning !== void 0 && reasoning.efforts.length > 0) {
|
|
701
|
+
catalog = {
|
|
702
|
+
efforts: reasoning.efforts.map((effort) => ({
|
|
703
|
+
id: String(effort.id),
|
|
704
|
+
name: effort.name,
|
|
705
|
+
...effort.description !== void 0 ? { description: effort.description } : {}
|
|
706
|
+
})),
|
|
707
|
+
...reasoning.defaultEffort !== void 0 ? { defaultEffort: String(reasoning.defaultEffort) } : {}
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
} catch (error) {
|
|
711
|
+
logDebug(`resolveModelInfo(${key}) failed: ${String(error)}`);
|
|
712
|
+
}
|
|
713
|
+
effortCache.set(key, catalog);
|
|
714
|
+
return catalog;
|
|
715
|
+
};
|
|
716
|
+
const ensureSelection = (record) => {
|
|
717
|
+
const install = harness.installModelSelection;
|
|
718
|
+
if (install === void 0) return void 0;
|
|
719
|
+
if (record.selection !== void 0) return record.selection;
|
|
720
|
+
let picked;
|
|
721
|
+
const selection = {
|
|
722
|
+
get current() {
|
|
723
|
+
if (picked !== void 0) return picked;
|
|
724
|
+
const logged = loggedConfig(record);
|
|
725
|
+
if (logged !== void 0) return logged;
|
|
726
|
+
const { provider, model } = routeOf(record);
|
|
727
|
+
if (provider === void 0 || model === void 0) return void 0;
|
|
728
|
+
const defaults = defaultSelection();
|
|
729
|
+
const effort = defaults.model === void 0 || defaults.model === model ? defaults.reasoningEffort : void 0;
|
|
730
|
+
return { provider, model, ...effort !== void 0 ? { reasoningEffort: effort } : {} };
|
|
731
|
+
},
|
|
732
|
+
set current(next) {
|
|
733
|
+
picked = next;
|
|
734
|
+
},
|
|
735
|
+
assembled: void 0
|
|
736
|
+
};
|
|
737
|
+
try {
|
|
738
|
+
install(record.agent.ctx, selection);
|
|
739
|
+
} catch (error) {
|
|
740
|
+
logWarn(`installModelSelection failed: ${String(error)}`);
|
|
741
|
+
return void 0;
|
|
742
|
+
}
|
|
743
|
+
record.selection = selection;
|
|
744
|
+
return selection;
|
|
745
|
+
};
|
|
746
|
+
const switchModel = async (record, acpSessionId, value) => {
|
|
747
|
+
if (record.inflight !== void 0) {
|
|
748
|
+
throw invalidParams("cannot switch models while a prompt is running");
|
|
749
|
+
}
|
|
750
|
+
const choice = decodeChoice(value);
|
|
751
|
+
const known = /* @__PURE__ */ new Set([
|
|
752
|
+
...modelCandidates().map((model) => encodeChoice(void 0, model)),
|
|
753
|
+
...(await discoverModels()).map((entry) => encodeChoice(entry.provider, entry.model)),
|
|
754
|
+
encodeChoice(record.provider, record.model ?? "")
|
|
755
|
+
]);
|
|
756
|
+
if (!known.has(value)) throw invalidParams(`unknown model: ${value}`);
|
|
757
|
+
if (choice.model === record.model && choice.provider === (record.provider ?? defaultProvider())) return;
|
|
758
|
+
const sessionId = record.agent.session.id;
|
|
759
|
+
await record.dispose().catch((error) => {
|
|
760
|
+
logWarn(`dispose during model switch failed: ${String(error)}`);
|
|
761
|
+
});
|
|
762
|
+
let handle;
|
|
763
|
+
try {
|
|
764
|
+
const presets = presetsService();
|
|
765
|
+
handle = await agents.resume({
|
|
766
|
+
resumeSessionId: sessionId,
|
|
767
|
+
agentOptions: agentOptionsFor(choice.model, choice.provider),
|
|
768
|
+
...presetSetup(presets, record.preset) !== void 0 ? { setup: presetSetup(presets, record.preset) } : {}
|
|
769
|
+
});
|
|
770
|
+
} catch (error) {
|
|
771
|
+
sessions.delete(acpSessionId);
|
|
772
|
+
throw internalError(`model switch failed: ${errorChain2(error)}`);
|
|
773
|
+
}
|
|
774
|
+
record.agent = handle.agent;
|
|
775
|
+
record.dispose = () => handle.dispose();
|
|
776
|
+
record.model = choice.model;
|
|
777
|
+
if (choice.provider !== void 0 && choice.provider !== defaultProvider()) {
|
|
778
|
+
record.provider = choice.provider;
|
|
779
|
+
} else {
|
|
780
|
+
delete record.provider;
|
|
781
|
+
}
|
|
782
|
+
delete record.selection;
|
|
783
|
+
ensureSelection(record);
|
|
784
|
+
if (record.effort !== void 0) {
|
|
785
|
+
const route = routeOf(record);
|
|
786
|
+
const catalog = await effortCatalog(route.provider, route.model);
|
|
787
|
+
if (catalog?.efforts.some((effort) => effort.id === record.effort) === true && route.provider !== void 0 && route.model !== void 0) {
|
|
788
|
+
const selection = ensureSelection(record);
|
|
789
|
+
if (selection !== void 0) {
|
|
790
|
+
selection.current = {
|
|
791
|
+
provider: route.provider,
|
|
792
|
+
model: route.model,
|
|
793
|
+
reasoningEffort: record.effort
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
} else {
|
|
797
|
+
delete record.effort;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
setApprovalPolicy(record, record.approvals);
|
|
801
|
+
};
|
|
662
802
|
const credentialPresent = async () => {
|
|
663
803
|
if (envCredentialPresent()) return true;
|
|
664
804
|
const brand = harness.credentialRef;
|
|
@@ -709,14 +849,113 @@ async function apply(ctx, config = {}) {
|
|
|
709
849
|
record.inflight = void 0;
|
|
710
850
|
inflight.resolve(reason);
|
|
711
851
|
};
|
|
712
|
-
const
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
852
|
+
const presetsService = () => ctx.get("agentPresets");
|
|
853
|
+
const presetSetup = (presets, presetId) => {
|
|
854
|
+
if (presets === void 0 || presetId === void 0) return void 0;
|
|
855
|
+
return async (agentCtx) => {
|
|
856
|
+
logDebug(`preset setup: mounting "${presetId}"`);
|
|
857
|
+
await presets.mount(agentCtx, presetId);
|
|
858
|
+
logDebug(`preset setup: mounted "${presetId}"`);
|
|
859
|
+
};
|
|
860
|
+
};
|
|
861
|
+
const presetFromLog = (header, events) => {
|
|
862
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
863
|
+
const event = events[index];
|
|
864
|
+
if (event?.type === "agent-preset/selected") {
|
|
865
|
+
return event.data?.agentPreset;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return header?.agentPreset;
|
|
869
|
+
};
|
|
870
|
+
const presetLabel = (id) => id.charAt(0).toUpperCase() + id.slice(1);
|
|
871
|
+
const modeState = (record) => {
|
|
872
|
+
const permission = permissionService();
|
|
873
|
+
if (permission !== void 0 && permission.names.length > 0) {
|
|
874
|
+
return {
|
|
875
|
+
currentModeId: record.modeId,
|
|
876
|
+
availableModes: permission.names.map((name3) => {
|
|
877
|
+
const spec = permission.resolve(name3);
|
|
878
|
+
return { id: name3, name: spec.name, description: spec.description };
|
|
879
|
+
})
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
return {
|
|
883
|
+
currentModeId: record.modeId,
|
|
884
|
+
availableModes: SANDBOX_MODES2.map((mode) => {
|
|
885
|
+
const label = MODE_LABELS[mode] ?? { name: mode, description: "" };
|
|
886
|
+
return { id: mode, name: label.name, description: label.description };
|
|
887
|
+
})
|
|
888
|
+
};
|
|
889
|
+
};
|
|
890
|
+
const switchPreset = async (record, acpSessionId, presetId) => {
|
|
891
|
+
if (record.inflight !== void 0) {
|
|
892
|
+
throw invalidParams("cannot switch presets while a prompt is running");
|
|
893
|
+
}
|
|
894
|
+
const presets = presetsService();
|
|
895
|
+
if (presets === void 0) throw invalidParams(`unknown mode: ${presetId}`);
|
|
896
|
+
let resolved;
|
|
897
|
+
try {
|
|
898
|
+
resolved = (await presets.resolve(presetId)).id;
|
|
899
|
+
} catch (error) {
|
|
900
|
+
throw invalidParams(`unknown preset: ${presetId} (${errorChain2(error)})`);
|
|
901
|
+
}
|
|
902
|
+
if (resolved === record.preset) return;
|
|
903
|
+
try {
|
|
904
|
+
record.agent.session.append("agent-preset/selected", { agentPreset: resolved });
|
|
905
|
+
} catch (error) {
|
|
906
|
+
logWarn(`recording preset selection failed: ${String(error)}`);
|
|
907
|
+
}
|
|
908
|
+
const sessionId = record.agent.session.id;
|
|
909
|
+
await record.dispose().catch((error) => {
|
|
910
|
+
logWarn(`dispose during preset switch failed: ${String(error)}`);
|
|
911
|
+
});
|
|
912
|
+
let handle;
|
|
913
|
+
try {
|
|
914
|
+
handle = await agents.resume({
|
|
915
|
+
resumeSessionId: sessionId,
|
|
916
|
+
agentOptions: agentOptionsFor(record.model ?? config.model, record.provider),
|
|
917
|
+
setup: presetSetup(presets, resolved)
|
|
918
|
+
});
|
|
919
|
+
} catch (error) {
|
|
920
|
+
sessions.delete(acpSessionId);
|
|
921
|
+
throw internalError(`preset switch failed: ${errorChain2(error)}`);
|
|
922
|
+
}
|
|
923
|
+
record.agent = handle.agent;
|
|
924
|
+
record.dispose = () => handle.dispose();
|
|
925
|
+
record.preset = resolved;
|
|
926
|
+
delete record.selection;
|
|
927
|
+
ensureSelection(record);
|
|
928
|
+
setApprovalPolicy(record, record.approvals);
|
|
929
|
+
};
|
|
930
|
+
const setApprovalPolicy = (record, policy) => {
|
|
931
|
+
try {
|
|
932
|
+
ctx.get("approval")?.setPolicy?.(record.agent, policy);
|
|
933
|
+
} catch (error) {
|
|
934
|
+
logWarn(`approval policy switch failed: ${String(error)}`);
|
|
935
|
+
}
|
|
936
|
+
record.approvals = policy;
|
|
937
|
+
};
|
|
938
|
+
const permissionService = () => ctx.get("permission");
|
|
939
|
+
const applyMode = (record, sessionId, modeId) => {
|
|
940
|
+
const permission = permissionService();
|
|
941
|
+
if (permission !== void 0 && permission.names.includes(modeId)) {
|
|
942
|
+
let bundledApproval = record.approvals;
|
|
943
|
+
permission.apply(record.agent.session, modeId, (policy) => {
|
|
944
|
+
bundledApproval = policy;
|
|
945
|
+
});
|
|
946
|
+
setApprovalPolicy(record, bundledApproval);
|
|
947
|
+
record.modeId = permission.resolve(modeId).sandbox;
|
|
948
|
+
notify(sessionId, { sessionUpdate: "current_mode_update", currentModeId: modeId });
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
const mode = SANDBOX_MODES2.find((candidate) => candidate === modeId);
|
|
952
|
+
if (mode === void 0) throw invalidParams(`unknown mode: ${modeId}`);
|
|
953
|
+
setSandboxMode2(record.agent.session, mode);
|
|
954
|
+
setApprovalPolicy(record, mode === "danger-full-access" ? "never" : "ask");
|
|
955
|
+
record.modeId = mode;
|
|
956
|
+
notify(sessionId, { sessionUpdate: "current_mode_update", currentModeId: mode });
|
|
957
|
+
};
|
|
958
|
+
const modelChoices = async (record) => {
|
|
720
959
|
const seen = /* @__PURE__ */ new Set();
|
|
721
960
|
const options = [];
|
|
722
961
|
const push = (value, name3) => {
|
|
@@ -735,17 +974,70 @@ async function apply(ctx, config = {}) {
|
|
|
735
974
|
push(encodeChoice(choice.provider, choice.model), choice.label);
|
|
736
975
|
}
|
|
737
976
|
for (const model of modelCandidates()) push(encodeChoice(void 0, model), model);
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
977
|
+
return options;
|
|
978
|
+
};
|
|
979
|
+
const configOptions = async (record) => {
|
|
980
|
+
const result = [];
|
|
981
|
+
const models = await modelChoices(record);
|
|
982
|
+
if (models.length >= 2) {
|
|
983
|
+
result.push({
|
|
741
984
|
type: "select",
|
|
742
985
|
id: "model",
|
|
743
986
|
name: "Model",
|
|
744
987
|
category: "model",
|
|
745
|
-
currentValue:
|
|
746
|
-
options
|
|
988
|
+
currentValue: models[0].value,
|
|
989
|
+
options: models
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
const route = routeOf(record);
|
|
993
|
+
const efforts = await effortCatalog(route.provider, route.model);
|
|
994
|
+
if (efforts !== void 0 && efforts.efforts.length >= 2) {
|
|
995
|
+
const known = new Set(efforts.efforts.map((effort) => effort.id));
|
|
996
|
+
const preferred = [
|
|
997
|
+
record.effort,
|
|
998
|
+
loggedConfig(record)?.reasoningEffort,
|
|
999
|
+
// The user's saved product default (Web UI → settings.yaml),
|
|
1000
|
+
// e.g. reasoningEffort: max, outranks the adapter's default.
|
|
1001
|
+
defaultSelection().reasoningEffort,
|
|
1002
|
+
efforts.defaultEffort
|
|
1003
|
+
].find(
|
|
1004
|
+
(candidate) => candidate !== void 0 && known.has(candidate)
|
|
1005
|
+
);
|
|
1006
|
+
result.push({
|
|
1007
|
+
type: "select",
|
|
1008
|
+
id: "effort",
|
|
1009
|
+
name: "Reasoning",
|
|
1010
|
+
category: "thought_level",
|
|
1011
|
+
currentValue: preferred ?? efforts.efforts[0].id,
|
|
1012
|
+
options: efforts.efforts.map((effort) => ({
|
|
1013
|
+
value: effort.id,
|
|
1014
|
+
name: effort.name,
|
|
1015
|
+
...effort.description !== void 0 ? { description: effort.description } : {}
|
|
1016
|
+
}))
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
const presets = presetsService();
|
|
1020
|
+
if (presets !== void 0 && record.preset !== void 0) {
|
|
1021
|
+
try {
|
|
1022
|
+
const roster = await presets.list();
|
|
1023
|
+
if (roster.length >= 2) {
|
|
1024
|
+
result.push({
|
|
1025
|
+
type: "select",
|
|
1026
|
+
id: "preset",
|
|
1027
|
+
name: "Preset",
|
|
1028
|
+
currentValue: record.preset,
|
|
1029
|
+
options: roster.map((preset) => ({
|
|
1030
|
+
value: preset.id,
|
|
1031
|
+
name: presetLabel(preset.id),
|
|
1032
|
+
description: `Agent preset \u201C${preset.id}\u201D`
|
|
1033
|
+
}))
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
} catch (error) {
|
|
1037
|
+
logWarn(`preset roster unavailable: ${String(error)}`);
|
|
747
1038
|
}
|
|
748
|
-
|
|
1039
|
+
}
|
|
1040
|
+
return result;
|
|
749
1041
|
};
|
|
750
1042
|
const publishCommands = (sessionId) => {
|
|
751
1043
|
notify(sessionId, {
|
|
@@ -827,15 +1119,20 @@ async function apply(ctx, config = {}) {
|
|
|
827
1119
|
};
|
|
828
1120
|
const statusText = async (record) => {
|
|
829
1121
|
const used = record.projection.contextWindow;
|
|
1122
|
+
const route = routeOf(record);
|
|
1123
|
+
const effort = record.effort ?? loggedConfig(record)?.reasoningEffort;
|
|
830
1124
|
const lines = [
|
|
831
1125
|
`**dsh-acp** ${VERSION} \u2014 DeepSeek Harness ACP bridge`,
|
|
832
1126
|
"",
|
|
833
1127
|
`| | |`,
|
|
834
1128
|
`|---|---|`,
|
|
835
|
-
`| Provider | ${
|
|
836
|
-
`| Model | ${
|
|
1129
|
+
`| Provider | ${route.provider ?? "(composition default)"} |`,
|
|
1130
|
+
`| Model | ${route.model ?? "(composition default)"} |`,
|
|
1131
|
+
`| Reasoning | ${effort ?? "(adapter default)"} |`,
|
|
837
1132
|
`| Credential | ${await describeCredential()} |`,
|
|
1133
|
+
...record.preset !== void 0 ? [`| Preset | ${record.preset} |`] : [],
|
|
838
1134
|
`| Permission mode | ${record.modeId} |`,
|
|
1135
|
+
`| Approvals | ${record.approvals} |`,
|
|
839
1136
|
`| Workspace | ${record.agent.session.header.cwd ?? process.cwd()} |`,
|
|
840
1137
|
`| Session | ${String(record.agent.session.id)} |`,
|
|
841
1138
|
...used !== void 0 ? [`| Context window | ${used.toLocaleString()} tokens |`] : []
|
|
@@ -849,6 +1146,7 @@ async function apply(ctx, config = {}) {
|
|
|
849
1146
|
projection: projection ?? new SessionProjection(),
|
|
850
1147
|
modeId,
|
|
851
1148
|
model,
|
|
1149
|
+
approvals: modeId === "danger-full-access" ? "never" : "ask",
|
|
852
1150
|
cancelled: false,
|
|
853
1151
|
inflight: void 0
|
|
854
1152
|
};
|
|
@@ -941,10 +1239,13 @@ async function apply(ctx, config = {}) {
|
|
|
941
1239
|
validateCwd(params.cwd);
|
|
942
1240
|
syncMcpServers(params.mcpServers, params.cwd);
|
|
943
1241
|
const sessionId = SessionId2(randomUUID());
|
|
1242
|
+
const presets = presetsService();
|
|
1243
|
+
const presetId = presets === void 0 ? void 0 : (await presets.resolve(void 0)).id;
|
|
944
1244
|
const handle = await agents.create({
|
|
945
1245
|
sessionId,
|
|
946
|
-
meta: { cwd: params.cwd },
|
|
947
|
-
agentOptions: agentOptionsFor(config.model)
|
|
1246
|
+
meta: { cwd: params.cwd, ...presetId !== void 0 ? { agentPreset: presetId } : {} },
|
|
1247
|
+
agentOptions: agentOptionsFor(config.model),
|
|
1248
|
+
...presetSetup(presets, presetId) !== void 0 ? { setup: presetSetup(presets, presetId) } : {}
|
|
948
1249
|
});
|
|
949
1250
|
if (closed) {
|
|
950
1251
|
await handle.dispose();
|
|
@@ -957,6 +1258,8 @@ async function apply(ctx, config = {}) {
|
|
|
957
1258
|
config.model,
|
|
958
1259
|
config.permissionMode ?? "workspace-write"
|
|
959
1260
|
);
|
|
1261
|
+
if (presetId !== void 0) record.preset = presetId;
|
|
1262
|
+
ensureSelection(record);
|
|
960
1263
|
queueMicrotask(() => publishCommands(String(sessionId)));
|
|
961
1264
|
const options = await configOptions(record);
|
|
962
1265
|
return {
|
|
@@ -981,8 +1284,11 @@ async function apply(ctx, config = {}) {
|
|
|
981
1284
|
});
|
|
982
1285
|
}
|
|
983
1286
|
let events;
|
|
1287
|
+
let storedHeader;
|
|
984
1288
|
try {
|
|
985
|
-
|
|
1289
|
+
const inspected = await persistence.inspect(SessionId2(sessionId));
|
|
1290
|
+
events = inspected.events;
|
|
1291
|
+
storedHeader = inspected.header;
|
|
986
1292
|
} catch (error) {
|
|
987
1293
|
throw invalidParams(`session not found: ${sessionId} (${errorChain2(error)})`);
|
|
988
1294
|
}
|
|
@@ -994,9 +1300,13 @@ async function apply(ctx, config = {}) {
|
|
|
994
1300
|
title: replay.title
|
|
995
1301
|
});
|
|
996
1302
|
}
|
|
1303
|
+
const presets = presetsService();
|
|
1304
|
+
const storedPreset = presetFromLog(storedHeader, events);
|
|
1305
|
+
const presetId = presets === void 0 ? void 0 : (await presets.resolve(storedPreset)).id;
|
|
997
1306
|
const handle = await agents.resume({
|
|
998
1307
|
resumeSessionId: SessionId2(sessionId),
|
|
999
|
-
agentOptions: agentOptionsFor(config.model)
|
|
1308
|
+
agentOptions: agentOptionsFor(config.model),
|
|
1309
|
+
...presetSetup(presets, presetId) !== void 0 ? { setup: presetSetup(presets, presetId) } : {}
|
|
1000
1310
|
});
|
|
1001
1311
|
const projection = new SessionProjection(replay.contextWindow);
|
|
1002
1312
|
projection.title = replay.title;
|
|
@@ -1008,6 +1318,21 @@ async function apply(ctx, config = {}) {
|
|
|
1008
1318
|
config.permissionMode ?? "workspace-write",
|
|
1009
1319
|
projection
|
|
1010
1320
|
);
|
|
1321
|
+
if (presetId !== void 0) record.preset = presetId;
|
|
1322
|
+
{
|
|
1323
|
+
const permission = permissionService();
|
|
1324
|
+
const storedMode = permission?.current(events);
|
|
1325
|
+
if (storedMode !== void 0) record.modeId = storedMode;
|
|
1326
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
1327
|
+
const event = events[index];
|
|
1328
|
+
if (event?.type === "approval/policy") {
|
|
1329
|
+
const policy = event.data?.policy;
|
|
1330
|
+
if (policy === "ask" || policy === "never") record.approvals = policy;
|
|
1331
|
+
break;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
ensureSelection(record);
|
|
1011
1336
|
queueMicrotask(() => publishCommands(sessionId));
|
|
1012
1337
|
const options = await configOptions(record);
|
|
1013
1338
|
return {
|
|
@@ -1113,58 +1438,48 @@ async function apply(ctx, config = {}) {
|
|
|
1113
1438
|
const usage = record.projection.promptUsage();
|
|
1114
1439
|
return { stopReason, ...usage !== void 0 ? { usage } : {} };
|
|
1115
1440
|
},
|
|
1116
|
-
setSessionMode(params) {
|
|
1441
|
+
async setSessionMode(params) {
|
|
1117
1442
|
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({});
|
|
1443
|
+
applyMode(record, params.sessionId, params.modeId);
|
|
1444
|
+
notify(params.sessionId, {
|
|
1445
|
+
sessionUpdate: "config_option_update",
|
|
1446
|
+
configOptions: await configOptions(record)
|
|
1447
|
+
});
|
|
1448
|
+
return {};
|
|
1129
1449
|
},
|
|
1130
1450
|
async setSessionConfigOption(params) {
|
|
1131
1451
|
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
1452
|
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
|
-
|
|
1453
|
+
if (value === void 0) throw invalidParams(`invalid value: ${String(params.value)}`);
|
|
1454
|
+
switch (params.configId) {
|
|
1455
|
+
case "preset": {
|
|
1456
|
+
await switchPreset(record, params.sessionId, value);
|
|
1457
|
+
break;
|
|
1458
|
+
}
|
|
1459
|
+
case "effort": {
|
|
1460
|
+
const route = routeOf(record);
|
|
1461
|
+
const catalog = await effortCatalog(route.provider, route.model);
|
|
1462
|
+
if (catalog === void 0 || !catalog.efforts.some((effort) => effort.id === value)) {
|
|
1463
|
+
throw invalidParams(`unknown effort: ${value}`);
|
|
1464
|
+
}
|
|
1465
|
+
const selection = ensureSelection(record);
|
|
1466
|
+
if (selection === void 0 || route.provider === void 0 || route.model === void 0) {
|
|
1467
|
+
throw invalidParams("reasoning effort switching is unavailable on this host");
|
|
1468
|
+
}
|
|
1469
|
+
selection.current = {
|
|
1470
|
+
provider: route.provider,
|
|
1471
|
+
model: route.model,
|
|
1472
|
+
reasoningEffort: value
|
|
1473
|
+
};
|
|
1474
|
+
record.effort = value;
|
|
1475
|
+
break;
|
|
1159
1476
|
}
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
if (choice.provider !== void 0 && choice.provider !== defaultProvider()) {
|
|
1164
|
-
record.provider = choice.provider;
|
|
1165
|
-
} else {
|
|
1166
|
-
delete record.provider;
|
|
1477
|
+
case "model": {
|
|
1478
|
+
await switchModel(record, params.sessionId, value);
|
|
1479
|
+
break;
|
|
1167
1480
|
}
|
|
1481
|
+
default:
|
|
1482
|
+
throw invalidParams(`unknown config option: ${params.configId}`);
|
|
1168
1483
|
}
|
|
1169
1484
|
return { configOptions: await configOptions(record) };
|
|
1170
1485
|
},
|
|
@@ -1600,6 +1915,8 @@ async function apply2(ctx, config) {
|
|
|
1600
1915
|
const p = kit.plugins;
|
|
1601
1916
|
const anyCtx = ctx;
|
|
1602
1917
|
const credentialsSeam = p["credentialsSeam"];
|
|
1918
|
+
const agentsModule = p["agents"];
|
|
1919
|
+
const installModelSelection = agentsModule.installModelSelection ?? agentsModule.ln;
|
|
1603
1920
|
const harness = {
|
|
1604
1921
|
createUserMessage: kit.llm.createUserMessage,
|
|
1605
1922
|
errorChain: kit.llm.errorChain,
|
|
@@ -1607,6 +1924,7 @@ async function apply2(ctx, config) {
|
|
|
1607
1924
|
foldSessionTitle: kit.sessionTitle.foldSessionTitle,
|
|
1608
1925
|
setSandboxMode: kit.sandboxPolicy.setSandboxMode,
|
|
1609
1926
|
sandboxModes: kit.sandboxPolicy.SANDBOX_MODES,
|
|
1927
|
+
...installModelSelection !== void 0 ? { installModelSelection } : {},
|
|
1610
1928
|
...credentialsSeam?.credentialRef !== void 0 ? { credentialRef: credentialsSeam.credentialRef } : {},
|
|
1611
1929
|
...p["mcpClient"] !== void 0 ? { mcpClient: p["mcpClient"] } : {}
|
|
1612
1930
|
};
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.3.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/openma-ai/deepseek-harness-acp.git"
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"bugs": {
|
|
13
13
|
"url": "https://github.com/openma-ai/deepseek-harness-acp/issues"
|
|
14
14
|
},
|
|
15
|
-
"description": "Agent Client Protocol (ACP) adapter for DeepSeek Harness
|
|
15
|
+
"description": "Agent Client Protocol (ACP) adapter for DeepSeek Harness \u2014 use DeepSeek Harness from ACP clients such as Zed.",
|
|
16
16
|
"main": "dist/index.js",
|
|
17
17
|
"bin": {
|
|
18
18
|
"dsh-acp": "dist/index.js"
|
|
@@ -123,4 +123,4 @@
|
|
|
123
123
|
"patch": "./cordis.patch.yml"
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
-
}
|
|
126
|
+
}
|