@openma/deepseek-harness-acp 0.2.0 → 0.4.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 +162 -47
- package/dist/index.js +326 -60
- package/package.json +7 -5
- package/profile-root.cordis.yml +3 -0
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
|
@@ -58,7 +58,8 @@ import {
|
|
|
58
58
|
} from "@agentclientprotocol/sdk";
|
|
59
59
|
|
|
60
60
|
// src/version.ts
|
|
61
|
-
|
|
61
|
+
import { createRequire } from "node:module";
|
|
62
|
+
var VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
62
63
|
|
|
63
64
|
// src/log.ts
|
|
64
65
|
var PREFIX = "[dsh-acp]";
|
|
@@ -706,7 +707,9 @@ async function apply(ctx, config = {}) {
|
|
|
706
707
|
if (logged !== void 0) return logged;
|
|
707
708
|
const { provider, model } = routeOf(record);
|
|
708
709
|
if (provider === void 0 || model === void 0) return void 0;
|
|
709
|
-
|
|
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 } : {} };
|
|
710
713
|
},
|
|
711
714
|
set current(next) {
|
|
712
715
|
picked = next;
|
|
@@ -740,9 +743,11 @@ async function apply(ctx, config = {}) {
|
|
|
740
743
|
});
|
|
741
744
|
let handle;
|
|
742
745
|
try {
|
|
746
|
+
const presets = presetsService();
|
|
743
747
|
handle = await agents.resume({
|
|
744
748
|
resumeSessionId: sessionId,
|
|
745
|
-
agentOptions: agentOptionsFor(choice.model, choice.provider)
|
|
749
|
+
agentOptions: agentOptionsFor(choice.model, choice.provider),
|
|
750
|
+
...presetSetup(presets, record.preset) !== void 0 ? { setup: presetSetup(presets, record.preset) } : {}
|
|
746
751
|
});
|
|
747
752
|
} catch (error) {
|
|
748
753
|
sessions.delete(acpSessionId);
|
|
@@ -757,6 +762,7 @@ async function apply(ctx, config = {}) {
|
|
|
757
762
|
delete record.provider;
|
|
758
763
|
}
|
|
759
764
|
delete record.selection;
|
|
765
|
+
ensureSelection(record);
|
|
760
766
|
if (record.effort !== void 0) {
|
|
761
767
|
const route = routeOf(record);
|
|
762
768
|
const catalog = await effortCatalog(route.provider, route.model);
|
|
@@ -825,13 +831,84 @@ async function apply(ctx, config = {}) {
|
|
|
825
831
|
record.inflight = void 0;
|
|
826
832
|
inflight.resolve(reason);
|
|
827
833
|
};
|
|
828
|
-
const
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
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
|
+
};
|
|
835
912
|
const setApprovalPolicy = (record, policy) => {
|
|
836
913
|
try {
|
|
837
914
|
ctx.get("approval")?.setPolicy?.(record.agent, policy);
|
|
@@ -840,7 +917,19 @@ async function apply(ctx, config = {}) {
|
|
|
840
917
|
}
|
|
841
918
|
record.approvals = policy;
|
|
842
919
|
};
|
|
920
|
+
const permissionService = () => ctx.get("permission");
|
|
843
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
|
+
}
|
|
844
933
|
const mode = SANDBOX_MODES2.find((candidate) => candidate === modeId);
|
|
845
934
|
if (mode === void 0) throw invalidParams(`unknown mode: ${modeId}`);
|
|
846
935
|
setSandboxMode2(record.agent.session, mode);
|
|
@@ -871,21 +960,6 @@ async function apply(ctx, config = {}) {
|
|
|
871
960
|
};
|
|
872
961
|
const configOptions = async (record) => {
|
|
873
962
|
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
963
|
const models = await modelChoices(record);
|
|
890
964
|
if (models.length >= 2) {
|
|
891
965
|
result.push({
|
|
@@ -901,7 +975,14 @@ async function apply(ctx, config = {}) {
|
|
|
901
975
|
const efforts = await effortCatalog(route.provider, route.model);
|
|
902
976
|
if (efforts !== void 0 && efforts.efforts.length >= 2) {
|
|
903
977
|
const known = new Set(efforts.efforts.map((effort) => effort.id));
|
|
904
|
-
const preferred = [
|
|
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(
|
|
905
986
|
(candidate) => candidate !== void 0 && known.has(candidate)
|
|
906
987
|
);
|
|
907
988
|
result.push({
|
|
@@ -917,16 +998,27 @@ async function apply(ctx, config = {}) {
|
|
|
917
998
|
}))
|
|
918
999
|
});
|
|
919
1000
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
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)}`);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
930
1022
|
return result;
|
|
931
1023
|
};
|
|
932
1024
|
const publishCommands = (sessionId) => {
|
|
@@ -1020,6 +1112,7 @@ async function apply(ctx, config = {}) {
|
|
|
1020
1112
|
`| Model | ${route.model ?? "(composition default)"} |`,
|
|
1021
1113
|
`| Reasoning | ${effort ?? "(adapter default)"} |`,
|
|
1022
1114
|
`| Credential | ${await describeCredential()} |`,
|
|
1115
|
+
...record.preset !== void 0 ? [`| Preset | ${record.preset} |`] : [],
|
|
1023
1116
|
`| Permission mode | ${record.modeId} |`,
|
|
1024
1117
|
`| Approvals | ${record.approvals} |`,
|
|
1025
1118
|
`| Workspace | ${record.agent.session.header.cwd ?? process.cwd()} |`,
|
|
@@ -1128,10 +1221,13 @@ async function apply(ctx, config = {}) {
|
|
|
1128
1221
|
validateCwd(params.cwd);
|
|
1129
1222
|
syncMcpServers(params.mcpServers, params.cwd);
|
|
1130
1223
|
const sessionId = SessionId2(randomUUID());
|
|
1224
|
+
const presets = presetsService();
|
|
1225
|
+
const presetId = presets === void 0 ? void 0 : (await presets.resolve(void 0)).id;
|
|
1131
1226
|
const handle = await agents.create({
|
|
1132
1227
|
sessionId,
|
|
1133
|
-
meta: { cwd: params.cwd },
|
|
1134
|
-
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) } : {}
|
|
1135
1231
|
});
|
|
1136
1232
|
if (closed) {
|
|
1137
1233
|
await handle.dispose();
|
|
@@ -1144,6 +1240,8 @@ async function apply(ctx, config = {}) {
|
|
|
1144
1240
|
config.model,
|
|
1145
1241
|
config.permissionMode ?? "workspace-write"
|
|
1146
1242
|
);
|
|
1243
|
+
if (presetId !== void 0) record.preset = presetId;
|
|
1244
|
+
ensureSelection(record);
|
|
1147
1245
|
queueMicrotask(() => publishCommands(String(sessionId)));
|
|
1148
1246
|
const options = await configOptions(record);
|
|
1149
1247
|
return {
|
|
@@ -1168,8 +1266,11 @@ async function apply(ctx, config = {}) {
|
|
|
1168
1266
|
});
|
|
1169
1267
|
}
|
|
1170
1268
|
let events;
|
|
1269
|
+
let storedHeader;
|
|
1171
1270
|
try {
|
|
1172
|
-
|
|
1271
|
+
const inspected = await persistence.inspect(SessionId2(sessionId));
|
|
1272
|
+
events = inspected.events;
|
|
1273
|
+
storedHeader = inspected.header;
|
|
1173
1274
|
} catch (error) {
|
|
1174
1275
|
throw invalidParams(`session not found: ${sessionId} (${errorChain2(error)})`);
|
|
1175
1276
|
}
|
|
@@ -1181,9 +1282,13 @@ async function apply(ctx, config = {}) {
|
|
|
1181
1282
|
title: replay.title
|
|
1182
1283
|
});
|
|
1183
1284
|
}
|
|
1285
|
+
const presets = presetsService();
|
|
1286
|
+
const storedPreset = presetFromLog(storedHeader, events);
|
|
1287
|
+
const presetId = presets === void 0 ? void 0 : (await presets.resolve(storedPreset)).id;
|
|
1184
1288
|
const handle = await agents.resume({
|
|
1185
1289
|
resumeSessionId: SessionId2(sessionId),
|
|
1186
|
-
agentOptions: agentOptionsFor(config.model)
|
|
1290
|
+
agentOptions: agentOptionsFor(config.model),
|
|
1291
|
+
...presetSetup(presets, presetId) !== void 0 ? { setup: presetSetup(presets, presetId) } : {}
|
|
1187
1292
|
});
|
|
1188
1293
|
const projection = new SessionProjection(replay.contextWindow);
|
|
1189
1294
|
projection.title = replay.title;
|
|
@@ -1195,6 +1300,21 @@ async function apply(ctx, config = {}) {
|
|
|
1195
1300
|
config.permissionMode ?? "workspace-write",
|
|
1196
1301
|
projection
|
|
1197
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);
|
|
1198
1318
|
queueMicrotask(() => publishCommands(sessionId));
|
|
1199
1319
|
const options = await configOptions(record);
|
|
1200
1320
|
return {
|
|
@@ -1314,13 +1434,8 @@ async function apply(ctx, config = {}) {
|
|
|
1314
1434
|
const value = typeof params.value === "string" ? params.value : void 0;
|
|
1315
1435
|
if (value === void 0) throw invalidParams(`invalid value: ${String(params.value)}`);
|
|
1316
1436
|
switch (params.configId) {
|
|
1317
|
-
case "
|
|
1318
|
-
|
|
1319
|
-
break;
|
|
1320
|
-
}
|
|
1321
|
-
case "approvals": {
|
|
1322
|
-
if (value !== "ask" && value !== "never") throw invalidParams(`unknown approvals: ${value}`);
|
|
1323
|
-
setApprovalPolicy(record, value);
|
|
1437
|
+
case "preset": {
|
|
1438
|
+
await switchPreset(record, params.sessionId, value);
|
|
1324
1439
|
break;
|
|
1325
1440
|
}
|
|
1326
1441
|
case "effort": {
|
package/dist/index.js
CHANGED
|
@@ -47,6 +47,9 @@ var init_self_harness = __esm({
|
|
|
47
47
|
}
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
+
// src/index.ts
|
|
51
|
+
import { spawnSync } from "node:child_process";
|
|
52
|
+
|
|
50
53
|
// src/app.ts
|
|
51
54
|
var app_exports = {};
|
|
52
55
|
__export(app_exports, {
|
|
@@ -72,7 +75,8 @@ import {
|
|
|
72
75
|
} from "@agentclientprotocol/sdk";
|
|
73
76
|
|
|
74
77
|
// src/version.ts
|
|
75
|
-
|
|
78
|
+
import { createRequire } from "node:module";
|
|
79
|
+
var VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
76
80
|
|
|
77
81
|
// src/log.ts
|
|
78
82
|
var PREFIX = "[dsh-acp]";
|
|
@@ -88,6 +92,10 @@ function logDebug(message) {
|
|
|
88
92
|
if (process.env["DSH_ACP_DEBUG"]) process.stderr.write(`${PREFIX} debug: ${message}
|
|
89
93
|
`);
|
|
90
94
|
}
|
|
95
|
+
function logInfo(message) {
|
|
96
|
+
process.stderr.write(`${PREFIX} ${message}
|
|
97
|
+
`);
|
|
98
|
+
}
|
|
91
99
|
|
|
92
100
|
// src/bridge/translate.ts
|
|
93
101
|
var MAX_TITLE_LENGTH = 80;
|
|
@@ -724,7 +732,9 @@ async function apply(ctx, config = {}) {
|
|
|
724
732
|
if (logged !== void 0) return logged;
|
|
725
733
|
const { provider, model } = routeOf(record);
|
|
726
734
|
if (provider === void 0 || model === void 0) return void 0;
|
|
727
|
-
|
|
735
|
+
const defaults = defaultSelection();
|
|
736
|
+
const effort = defaults.model === void 0 || defaults.model === model ? defaults.reasoningEffort : void 0;
|
|
737
|
+
return { provider, model, ...effort !== void 0 ? { reasoningEffort: effort } : {} };
|
|
728
738
|
},
|
|
729
739
|
set current(next) {
|
|
730
740
|
picked = next;
|
|
@@ -758,9 +768,11 @@ async function apply(ctx, config = {}) {
|
|
|
758
768
|
});
|
|
759
769
|
let handle;
|
|
760
770
|
try {
|
|
771
|
+
const presets = presetsService();
|
|
761
772
|
handle = await agents.resume({
|
|
762
773
|
resumeSessionId: sessionId,
|
|
763
|
-
agentOptions: agentOptionsFor(choice.model, choice.provider)
|
|
774
|
+
agentOptions: agentOptionsFor(choice.model, choice.provider),
|
|
775
|
+
...presetSetup(presets, record.preset) !== void 0 ? { setup: presetSetup(presets, record.preset) } : {}
|
|
764
776
|
});
|
|
765
777
|
} catch (error) {
|
|
766
778
|
sessions.delete(acpSessionId);
|
|
@@ -775,6 +787,7 @@ async function apply(ctx, config = {}) {
|
|
|
775
787
|
delete record.provider;
|
|
776
788
|
}
|
|
777
789
|
delete record.selection;
|
|
790
|
+
ensureSelection(record);
|
|
778
791
|
if (record.effort !== void 0) {
|
|
779
792
|
const route = routeOf(record);
|
|
780
793
|
const catalog = await effortCatalog(route.provider, route.model);
|
|
@@ -843,13 +856,84 @@ async function apply(ctx, config = {}) {
|
|
|
843
856
|
record.inflight = void 0;
|
|
844
857
|
inflight.resolve(reason);
|
|
845
858
|
};
|
|
846
|
-
const
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
859
|
+
const presetsService = () => ctx.get("agentPresets");
|
|
860
|
+
const presetSetup = (presets, presetId) => {
|
|
861
|
+
if (presets === void 0 || presetId === void 0) return void 0;
|
|
862
|
+
return async (agentCtx) => {
|
|
863
|
+
logDebug(`preset setup: mounting "${presetId}"`);
|
|
864
|
+
await presets.mount(agentCtx, presetId);
|
|
865
|
+
logDebug(`preset setup: mounted "${presetId}"`);
|
|
866
|
+
};
|
|
867
|
+
};
|
|
868
|
+
const presetFromLog = (header, events) => {
|
|
869
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
870
|
+
const event = events[index];
|
|
871
|
+
if (event?.type === "agent-preset/selected") {
|
|
872
|
+
return event.data?.agentPreset;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
return header?.agentPreset;
|
|
876
|
+
};
|
|
877
|
+
const presetLabel = (id) => id.charAt(0).toUpperCase() + id.slice(1);
|
|
878
|
+
const modeState = (record) => {
|
|
879
|
+
const permission = permissionService();
|
|
880
|
+
if (permission !== void 0 && permission.names.length > 0) {
|
|
881
|
+
return {
|
|
882
|
+
currentModeId: record.modeId,
|
|
883
|
+
availableModes: permission.names.map((name3) => {
|
|
884
|
+
const spec = permission.resolve(name3);
|
|
885
|
+
return { id: name3, name: spec.name, description: spec.description };
|
|
886
|
+
})
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
return {
|
|
890
|
+
currentModeId: record.modeId,
|
|
891
|
+
availableModes: SANDBOX_MODES2.map((mode) => {
|
|
892
|
+
const label = MODE_LABELS[mode] ?? { name: mode, description: "" };
|
|
893
|
+
return { id: mode, name: label.name, description: label.description };
|
|
894
|
+
})
|
|
895
|
+
};
|
|
896
|
+
};
|
|
897
|
+
const switchPreset = async (record, acpSessionId, presetId) => {
|
|
898
|
+
if (record.inflight !== void 0) {
|
|
899
|
+
throw invalidParams("cannot switch presets while a prompt is running");
|
|
900
|
+
}
|
|
901
|
+
const presets = presetsService();
|
|
902
|
+
if (presets === void 0) throw invalidParams(`unknown mode: ${presetId}`);
|
|
903
|
+
let resolved;
|
|
904
|
+
try {
|
|
905
|
+
resolved = (await presets.resolve(presetId)).id;
|
|
906
|
+
} catch (error) {
|
|
907
|
+
throw invalidParams(`unknown preset: ${presetId} (${errorChain2(error)})`);
|
|
908
|
+
}
|
|
909
|
+
if (resolved === record.preset) return;
|
|
910
|
+
try {
|
|
911
|
+
record.agent.session.append("agent-preset/selected", { agentPreset: resolved });
|
|
912
|
+
} catch (error) {
|
|
913
|
+
logWarn(`recording preset selection failed: ${String(error)}`);
|
|
914
|
+
}
|
|
915
|
+
const sessionId = record.agent.session.id;
|
|
916
|
+
await record.dispose().catch((error) => {
|
|
917
|
+
logWarn(`dispose during preset switch failed: ${String(error)}`);
|
|
918
|
+
});
|
|
919
|
+
let handle;
|
|
920
|
+
try {
|
|
921
|
+
handle = await agents.resume({
|
|
922
|
+
resumeSessionId: sessionId,
|
|
923
|
+
agentOptions: agentOptionsFor(record.model ?? config.model, record.provider),
|
|
924
|
+
setup: presetSetup(presets, resolved)
|
|
925
|
+
});
|
|
926
|
+
} catch (error) {
|
|
927
|
+
sessions.delete(acpSessionId);
|
|
928
|
+
throw internalError(`preset switch failed: ${errorChain2(error)}`);
|
|
929
|
+
}
|
|
930
|
+
record.agent = handle.agent;
|
|
931
|
+
record.dispose = () => handle.dispose();
|
|
932
|
+
record.preset = resolved;
|
|
933
|
+
delete record.selection;
|
|
934
|
+
ensureSelection(record);
|
|
935
|
+
setApprovalPolicy(record, record.approvals);
|
|
936
|
+
};
|
|
853
937
|
const setApprovalPolicy = (record, policy) => {
|
|
854
938
|
try {
|
|
855
939
|
ctx.get("approval")?.setPolicy?.(record.agent, policy);
|
|
@@ -858,7 +942,19 @@ async function apply(ctx, config = {}) {
|
|
|
858
942
|
}
|
|
859
943
|
record.approvals = policy;
|
|
860
944
|
};
|
|
945
|
+
const permissionService = () => ctx.get("permission");
|
|
861
946
|
const applyMode = (record, sessionId, modeId) => {
|
|
947
|
+
const permission = permissionService();
|
|
948
|
+
if (permission !== void 0 && permission.names.includes(modeId)) {
|
|
949
|
+
let bundledApproval = record.approvals;
|
|
950
|
+
permission.apply(record.agent.session, modeId, (policy) => {
|
|
951
|
+
bundledApproval = policy;
|
|
952
|
+
});
|
|
953
|
+
setApprovalPolicy(record, bundledApproval);
|
|
954
|
+
record.modeId = permission.resolve(modeId).sandbox;
|
|
955
|
+
notify(sessionId, { sessionUpdate: "current_mode_update", currentModeId: modeId });
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
862
958
|
const mode = SANDBOX_MODES2.find((candidate) => candidate === modeId);
|
|
863
959
|
if (mode === void 0) throw invalidParams(`unknown mode: ${modeId}`);
|
|
864
960
|
setSandboxMode2(record.agent.session, mode);
|
|
@@ -889,21 +985,6 @@ async function apply(ctx, config = {}) {
|
|
|
889
985
|
};
|
|
890
986
|
const configOptions = async (record) => {
|
|
891
987
|
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
988
|
const models = await modelChoices(record);
|
|
908
989
|
if (models.length >= 2) {
|
|
909
990
|
result.push({
|
|
@@ -919,7 +1000,14 @@ async function apply(ctx, config = {}) {
|
|
|
919
1000
|
const efforts = await effortCatalog(route.provider, route.model);
|
|
920
1001
|
if (efforts !== void 0 && efforts.efforts.length >= 2) {
|
|
921
1002
|
const known = new Set(efforts.efforts.map((effort) => effort.id));
|
|
922
|
-
const preferred = [
|
|
1003
|
+
const preferred = [
|
|
1004
|
+
record.effort,
|
|
1005
|
+
loggedConfig(record)?.reasoningEffort,
|
|
1006
|
+
// The user's saved product default (Web UI → settings.yaml),
|
|
1007
|
+
// e.g. reasoningEffort: max, outranks the adapter's default.
|
|
1008
|
+
defaultSelection().reasoningEffort,
|
|
1009
|
+
efforts.defaultEffort
|
|
1010
|
+
].find(
|
|
923
1011
|
(candidate) => candidate !== void 0 && known.has(candidate)
|
|
924
1012
|
);
|
|
925
1013
|
result.push({
|
|
@@ -935,16 +1023,27 @@ async function apply(ctx, config = {}) {
|
|
|
935
1023
|
}))
|
|
936
1024
|
});
|
|
937
1025
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
1026
|
+
const presets = presetsService();
|
|
1027
|
+
if (presets !== void 0 && record.preset !== void 0) {
|
|
1028
|
+
try {
|
|
1029
|
+
const roster = await presets.list();
|
|
1030
|
+
if (roster.length >= 2) {
|
|
1031
|
+
result.push({
|
|
1032
|
+
type: "select",
|
|
1033
|
+
id: "preset",
|
|
1034
|
+
name: "Preset",
|
|
1035
|
+
currentValue: record.preset,
|
|
1036
|
+
options: roster.map((preset) => ({
|
|
1037
|
+
value: preset.id,
|
|
1038
|
+
name: presetLabel(preset.id),
|
|
1039
|
+
description: `Agent preset \u201C${preset.id}\u201D`
|
|
1040
|
+
}))
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
} catch (error) {
|
|
1044
|
+
logWarn(`preset roster unavailable: ${String(error)}`);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
948
1047
|
return result;
|
|
949
1048
|
};
|
|
950
1049
|
const publishCommands = (sessionId) => {
|
|
@@ -1038,6 +1137,7 @@ async function apply(ctx, config = {}) {
|
|
|
1038
1137
|
`| Model | ${route.model ?? "(composition default)"} |`,
|
|
1039
1138
|
`| Reasoning | ${effort ?? "(adapter default)"} |`,
|
|
1040
1139
|
`| Credential | ${await describeCredential()} |`,
|
|
1140
|
+
...record.preset !== void 0 ? [`| Preset | ${record.preset} |`] : [],
|
|
1041
1141
|
`| Permission mode | ${record.modeId} |`,
|
|
1042
1142
|
`| Approvals | ${record.approvals} |`,
|
|
1043
1143
|
`| Workspace | ${record.agent.session.header.cwd ?? process.cwd()} |`,
|
|
@@ -1146,10 +1246,13 @@ async function apply(ctx, config = {}) {
|
|
|
1146
1246
|
validateCwd(params.cwd);
|
|
1147
1247
|
syncMcpServers(params.mcpServers, params.cwd);
|
|
1148
1248
|
const sessionId = SessionId2(randomUUID());
|
|
1249
|
+
const presets = presetsService();
|
|
1250
|
+
const presetId = presets === void 0 ? void 0 : (await presets.resolve(void 0)).id;
|
|
1149
1251
|
const handle = await agents.create({
|
|
1150
1252
|
sessionId,
|
|
1151
|
-
meta: { cwd: params.cwd },
|
|
1152
|
-
agentOptions: agentOptionsFor(config.model)
|
|
1253
|
+
meta: { cwd: params.cwd, ...presetId !== void 0 ? { agentPreset: presetId } : {} },
|
|
1254
|
+
agentOptions: agentOptionsFor(config.model),
|
|
1255
|
+
...presetSetup(presets, presetId) !== void 0 ? { setup: presetSetup(presets, presetId) } : {}
|
|
1153
1256
|
});
|
|
1154
1257
|
if (closed) {
|
|
1155
1258
|
await handle.dispose();
|
|
@@ -1162,6 +1265,8 @@ async function apply(ctx, config = {}) {
|
|
|
1162
1265
|
config.model,
|
|
1163
1266
|
config.permissionMode ?? "workspace-write"
|
|
1164
1267
|
);
|
|
1268
|
+
if (presetId !== void 0) record.preset = presetId;
|
|
1269
|
+
ensureSelection(record);
|
|
1165
1270
|
queueMicrotask(() => publishCommands(String(sessionId)));
|
|
1166
1271
|
const options = await configOptions(record);
|
|
1167
1272
|
return {
|
|
@@ -1186,8 +1291,11 @@ async function apply(ctx, config = {}) {
|
|
|
1186
1291
|
});
|
|
1187
1292
|
}
|
|
1188
1293
|
let events;
|
|
1294
|
+
let storedHeader;
|
|
1189
1295
|
try {
|
|
1190
|
-
|
|
1296
|
+
const inspected = await persistence.inspect(SessionId2(sessionId));
|
|
1297
|
+
events = inspected.events;
|
|
1298
|
+
storedHeader = inspected.header;
|
|
1191
1299
|
} catch (error) {
|
|
1192
1300
|
throw invalidParams(`session not found: ${sessionId} (${errorChain2(error)})`);
|
|
1193
1301
|
}
|
|
@@ -1199,9 +1307,13 @@ async function apply(ctx, config = {}) {
|
|
|
1199
1307
|
title: replay.title
|
|
1200
1308
|
});
|
|
1201
1309
|
}
|
|
1310
|
+
const presets = presetsService();
|
|
1311
|
+
const storedPreset = presetFromLog(storedHeader, events);
|
|
1312
|
+
const presetId = presets === void 0 ? void 0 : (await presets.resolve(storedPreset)).id;
|
|
1202
1313
|
const handle = await agents.resume({
|
|
1203
1314
|
resumeSessionId: SessionId2(sessionId),
|
|
1204
|
-
agentOptions: agentOptionsFor(config.model)
|
|
1315
|
+
agentOptions: agentOptionsFor(config.model),
|
|
1316
|
+
...presetSetup(presets, presetId) !== void 0 ? { setup: presetSetup(presets, presetId) } : {}
|
|
1205
1317
|
});
|
|
1206
1318
|
const projection = new SessionProjection(replay.contextWindow);
|
|
1207
1319
|
projection.title = replay.title;
|
|
@@ -1213,6 +1325,21 @@ async function apply(ctx, config = {}) {
|
|
|
1213
1325
|
config.permissionMode ?? "workspace-write",
|
|
1214
1326
|
projection
|
|
1215
1327
|
);
|
|
1328
|
+
if (presetId !== void 0) record.preset = presetId;
|
|
1329
|
+
{
|
|
1330
|
+
const permission = permissionService();
|
|
1331
|
+
const storedMode = permission?.current(events);
|
|
1332
|
+
if (storedMode !== void 0) record.modeId = storedMode;
|
|
1333
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
1334
|
+
const event = events[index];
|
|
1335
|
+
if (event?.type === "approval/policy") {
|
|
1336
|
+
const policy = event.data?.policy;
|
|
1337
|
+
if (policy === "ask" || policy === "never") record.approvals = policy;
|
|
1338
|
+
break;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
ensureSelection(record);
|
|
1216
1343
|
queueMicrotask(() => publishCommands(sessionId));
|
|
1217
1344
|
const options = await configOptions(record);
|
|
1218
1345
|
return {
|
|
@@ -1332,13 +1459,8 @@ async function apply(ctx, config = {}) {
|
|
|
1332
1459
|
const value = typeof params.value === "string" ? params.value : void 0;
|
|
1333
1460
|
if (value === void 0) throw invalidParams(`invalid value: ${String(params.value)}`);
|
|
1334
1461
|
switch (params.configId) {
|
|
1335
|
-
case "
|
|
1336
|
-
|
|
1337
|
-
break;
|
|
1338
|
-
}
|
|
1339
|
-
case "approvals": {
|
|
1340
|
-
if (value !== "ask" && value !== "never") throw invalidParams(`unknown approvals: ${value}`);
|
|
1341
|
-
setApprovalPolicy(record, value);
|
|
1462
|
+
case "preset": {
|
|
1463
|
+
await switchPreset(record, params.sessionId, value);
|
|
1342
1464
|
break;
|
|
1343
1465
|
}
|
|
1344
1466
|
case "effort": {
|
|
@@ -1637,9 +1759,6 @@ function resolveHost(explicit) {
|
|
|
1637
1759
|
probed.push(`--dsh-path/DSH_PATH: ${explicit}`);
|
|
1638
1760
|
throw new HarnessNotFoundError(probed);
|
|
1639
1761
|
}
|
|
1640
|
-
const own = hostAt(dirname(fileURLToPath(import.meta.url)), "dsh-acp package tree");
|
|
1641
|
-
if (own !== void 0) return own;
|
|
1642
|
-
probed.push("dsh-acp package tree");
|
|
1643
1762
|
const local = hostAt(process.cwd(), `invoking directory (${process.cwd()})`);
|
|
1644
1763
|
if (local !== void 0) return local;
|
|
1645
1764
|
probed.push(`invoking directory: ${process.cwd()}`);
|
|
@@ -1649,6 +1768,9 @@ function resolveHost(explicit) {
|
|
|
1649
1768
|
const globalNpm = hostFromGlobalNpm();
|
|
1650
1769
|
if (globalNpm !== void 0) return globalNpm;
|
|
1651
1770
|
probed.push("npm root -g");
|
|
1771
|
+
const own = hostAt(dirname(fileURLToPath(import.meta.url)), "dsh-acp package tree (vendored)");
|
|
1772
|
+
if (own !== void 0) return own;
|
|
1773
|
+
probed.push("dsh-acp package tree (vendored)");
|
|
1652
1774
|
throw new HarnessNotFoundError(probed);
|
|
1653
1775
|
}
|
|
1654
1776
|
function entryFile(packageDir, subpath) {
|
|
@@ -1885,8 +2007,8 @@ async function apply2(ctx, config) {
|
|
|
1885
2007
|
await compaction;
|
|
1886
2008
|
yield compaction.dispose;
|
|
1887
2009
|
const bridge = anyCtx.plugin(bridge_exports, {
|
|
1888
|
-
provider: settings.provider,
|
|
1889
|
-
model: settings.model,
|
|
2010
|
+
provider: settings.provider ?? "deepseek-official",
|
|
2011
|
+
model: settings.model ?? "deepseek-v4-flash",
|
|
1890
2012
|
models: settings.models,
|
|
1891
2013
|
...settings.maxTokens !== void 0 ? { maxTokens: settings.maxTokens } : {},
|
|
1892
2014
|
permissionMode: settings.permissionMode,
|
|
@@ -1897,9 +2019,130 @@ async function apply2(ctx, config) {
|
|
|
1897
2019
|
}, "dsh-acp-app.composition");
|
|
1898
2020
|
}
|
|
1899
2021
|
|
|
2022
|
+
// src/profile-boot.ts
|
|
2023
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
2024
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
2025
|
+
import { dirname as dirname2, join as join2, sep as sep2 } from "node:path";
|
|
2026
|
+
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
2027
|
+
var BIN = "dsh-acp";
|
|
2028
|
+
function ownRoot() {
|
|
2029
|
+
return dirname2(createRequire2(import.meta.url).resolve("../package.json"));
|
|
2030
|
+
}
|
|
2031
|
+
var OWN_BRIDGE_SPECIFIER = "@openma/deepseek-harness-acp/bridge";
|
|
2032
|
+
function rewriteBridgeRows(row, absoluteEntry) {
|
|
2033
|
+
if (row["name"] === OWN_BRIDGE_SPECIFIER) row["name"] = absoluteEntry;
|
|
2034
|
+
const insert = row["insert"];
|
|
2035
|
+
if (Array.isArray(insert)) {
|
|
2036
|
+
for (const nested of insert) {
|
|
2037
|
+
if (nested !== null && typeof nested === "object") {
|
|
2038
|
+
rewriteBridgeRows(nested, absoluteEntry);
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
function bundlePatchPath(packageJsonPath) {
|
|
2044
|
+
const pkg = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
|
|
2045
|
+
const declared = pkg.dsh?.bundle?.patch;
|
|
2046
|
+
if (declared === void 0) {
|
|
2047
|
+
throw new Error(`${BIN}: bundle ${pkg.name ?? packageJsonPath} declares no dsh.bundle in its package.json`);
|
|
2048
|
+
}
|
|
2049
|
+
return join2(dirname2(packageJsonPath), declared);
|
|
2050
|
+
}
|
|
2051
|
+
async function bootAcpProfile(host, overrides) {
|
|
2052
|
+
const req = createRequire2(join2(host.base, "noop.js"));
|
|
2053
|
+
const dshPkgPath = req.resolve("@deepseek-ai/dsh/package.json");
|
|
2054
|
+
const dshDir = dirname2(dshPkgPath);
|
|
2055
|
+
const appBoot = await import(pathToFileURL2(req.resolve("@deepseek-ai/dsh-app-boot")).href);
|
|
2056
|
+
const homePaths = await import(pathToFileURL2(req.resolve("@deepseek-ai/dsh-home-paths")).href);
|
|
2057
|
+
const home = homePaths.resolveDshHome();
|
|
2058
|
+
let bundlePatches;
|
|
2059
|
+
let profilePatches = [];
|
|
2060
|
+
let profilePatchPath;
|
|
2061
|
+
let rootConfigPath = join2(ownRoot(), "profile-root.cordis.yml");
|
|
2062
|
+
let bareModuleBaseUrl = pathToFileURL2(dshDir + sep2).href;
|
|
2063
|
+
const distBridge = join2(ownRoot(), "dist", "bridge.js");
|
|
2064
|
+
const bridgeEntry = existsSync2(distBridge) ? distBridge : join2(ownRoot(), "src", "bundle.ts");
|
|
2065
|
+
const realProfileDir = join2(home, "profiles", "acp");
|
|
2066
|
+
if (existsSync2(join2(realProfileDir, "package.json"))) {
|
|
2067
|
+
appBoot.healProfilesModuleFallback(dshPkgPath);
|
|
2068
|
+
const profile = appBoot.loadProfile(BIN, "acp", dshPkgPath);
|
|
2069
|
+
bundlePatches = profile.layers.flatMap((layer) => layer.patches);
|
|
2070
|
+
profilePatches = profile.patches;
|
|
2071
|
+
profilePatchPath = join2(profile.dir, "cordis.patch.yml");
|
|
2072
|
+
const realRoot = join2(profile.dir, "cordis.yml");
|
|
2073
|
+
if (existsSync2(realRoot)) rootConfigPath = realRoot;
|
|
2074
|
+
bareModuleBaseUrl = void 0;
|
|
2075
|
+
logDebug(`profile: real (${profile.dir})`);
|
|
2076
|
+
} else {
|
|
2077
|
+
const basePatch = bundlePatchPath(req.resolve("@deepseek-ai/dsh-base/package.json"));
|
|
2078
|
+
const ownPatch = bundlePatchPath(join2(ownRoot(), "package.json"));
|
|
2079
|
+
bundlePatches = [
|
|
2080
|
+
...appBoot.loadOverlayPatches(BIN, basePatch),
|
|
2081
|
+
...appBoot.loadOverlayPatches(BIN, ownPatch)
|
|
2082
|
+
];
|
|
2083
|
+
logDebug(`profile: virtual (dsh-base + ${ownRoot()})`);
|
|
2084
|
+
}
|
|
2085
|
+
for (const row of bundlePatches) rewriteBridgeRows(row, bridgeEntry);
|
|
2086
|
+
const homePatchPath = join2(home, "cordis.patch.yml");
|
|
2087
|
+
const homePatches = appBoot.loadOptionalPatches(BIN, homePatchPath) ?? [];
|
|
2088
|
+
const rows = /* @__PURE__ */ new Map();
|
|
2089
|
+
for (const row of appBoot.composeEntries([bundlePatches, profilePatches, homePatches])) {
|
|
2090
|
+
if (typeof row.id === "string") rows.set(row.id, row);
|
|
2091
|
+
}
|
|
2092
|
+
const overlays = [];
|
|
2093
|
+
const shippedPresets = join2(dshDir, "config", "agent-presets") + sep2;
|
|
2094
|
+
if (rows.has("agent-presets") && existsSync2(shippedPresets)) {
|
|
2095
|
+
overlays.push({
|
|
2096
|
+
id: "agent-presets",
|
|
2097
|
+
config: {
|
|
2098
|
+
...rows.get("agent-presets")?.config ?? {},
|
|
2099
|
+
roots: [{ path: shippedPresets, trust: "system" }]
|
|
2100
|
+
}
|
|
2101
|
+
});
|
|
2102
|
+
}
|
|
2103
|
+
if (process.env["DSH_TELEMETRY_DISABLED"] !== void 0 && rows.has("session-telemetry-otel")) {
|
|
2104
|
+
overlays.push({ id: "session-telemetry-otel", disabled: true });
|
|
2105
|
+
}
|
|
2106
|
+
const bridgeRow = rows.get("acp-bridge");
|
|
2107
|
+
if (overrides !== void 0 && bridgeRow !== void 0) {
|
|
2108
|
+
const patch = {};
|
|
2109
|
+
if (overrides.provider !== void 0) patch["provider"] = overrides.provider;
|
|
2110
|
+
if (overrides.model !== void 0) patch["model"] = overrides.model;
|
|
2111
|
+
if (overrides.permissionMode !== void 0) patch["permissionMode"] = overrides.permissionMode;
|
|
2112
|
+
if (overrides.maxTokens !== void 0) patch["maxTokens"] = overrides.maxTokens;
|
|
2113
|
+
if (Object.keys(patch).length > 0) {
|
|
2114
|
+
overlays.push({ id: "acp-bridge", config: { ...bridgeRow.config ?? {}, ...patch } });
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
const allPatches = [...bundlePatches, ...profilePatches, ...homePatches, ...overlays];
|
|
2118
|
+
const launchEnvironment = appBoot.loadLayeredEnv("dsh");
|
|
2119
|
+
const launchEnvKey = (await import(pathToFileURL2(req.resolve("@deepseek-ai/dsh-launch-environment")).href)).DSH_LAUNCH_ENVIRONMENT_KEY;
|
|
2120
|
+
const ctx = await appBoot.boot(
|
|
2121
|
+
BIN,
|
|
2122
|
+
rootConfigPath,
|
|
2123
|
+
structuredClone(allPatches),
|
|
2124
|
+
(hostCtx) => {
|
|
2125
|
+
hostCtx.provide(launchEnvKey, launchEnvironment);
|
|
2126
|
+
},
|
|
2127
|
+
bareModuleBaseUrl
|
|
2128
|
+
);
|
|
2129
|
+
try {
|
|
2130
|
+
appBoot.assertEntriesLoaded(ctx, BIN);
|
|
2131
|
+
await appBoot.assertEntriesActivated(ctx, BIN);
|
|
2132
|
+
} catch (error) {
|
|
2133
|
+
await ctx.fiber.dispose().catch(() => {
|
|
2134
|
+
});
|
|
2135
|
+
throw error;
|
|
2136
|
+
}
|
|
2137
|
+
if (profilePatchPath !== void 0) {
|
|
2138
|
+
logDebug(`user patch layers are read at boot; edit ${profilePatchPath} and restart to apply`);
|
|
2139
|
+
}
|
|
2140
|
+
return ctx;
|
|
2141
|
+
}
|
|
2142
|
+
|
|
1900
2143
|
// src/settings.ts
|
|
1901
2144
|
import { homedir } from "node:os";
|
|
1902
|
-
import { join as
|
|
2145
|
+
import { join as join3 } from "node:path";
|
|
1903
2146
|
var DEFAULT_PERSONA = `You are a coding assistant powered by the {{model}} model, working inside an editor via the Agent Client Protocol. Your working directory is {{cwd}}. Your bash tool runs under a file sandbox \u2014 a \`[sandbox: file access denied \u2026]\` result is policy, not a command bug.
|
|
1904
2147
|
|
|
1905
2148
|
Verify your work by running the code or tests. Keep answers brief and factual.`;
|
|
@@ -1958,7 +2201,7 @@ function resolveSettings(argv) {
|
|
|
1958
2201
|
throw new SettingsError(`unknown flag: --${name3}`);
|
|
1959
2202
|
}
|
|
1960
2203
|
}
|
|
1961
|
-
const model = stringFlag(parsed, "model") ?? envString("DSH_MODEL")
|
|
2204
|
+
const model = stringFlag(parsed, "model") ?? envString("DSH_MODEL");
|
|
1962
2205
|
const modelsRaw = stringFlag(parsed, "models") ?? envString("DSH_ACP_MODELS");
|
|
1963
2206
|
const models = (modelsRaw !== void 0 ? modelsRaw.split(",") : ["deepseek-v4-flash", "deepseek-v4-pro"]).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
1964
2207
|
const permissionModeRaw = stringFlag(parsed, "permission-mode") ?? envString("DSH_PERMISSION_MODE") ?? "workspace-write";
|
|
@@ -1987,12 +2230,12 @@ function resolveSettings(argv) {
|
|
|
1987
2230
|
}
|
|
1988
2231
|
return {
|
|
1989
2232
|
dshPath: stringFlag(parsed, "dsh-path") ?? envString("DSH_PATH"),
|
|
1990
|
-
provider: stringFlag(parsed, "provider") ?? envString("DSH_PROVIDER")
|
|
2233
|
+
provider: stringFlag(parsed, "provider") ?? envString("DSH_PROVIDER"),
|
|
1991
2234
|
model,
|
|
1992
2235
|
models,
|
|
1993
2236
|
maxTokens,
|
|
1994
2237
|
permissionMode,
|
|
1995
|
-
sessionRoot: stringFlag(parsed, "session-root") ?? envString("DSH_SESSION_ROOT") ??
|
|
2238
|
+
sessionRoot: stringFlag(parsed, "session-root") ?? envString("DSH_SESSION_ROOT") ?? join3(homedir(), ".dsh-acp", "sessions"),
|
|
1996
2239
|
persona: stringFlag(parsed, "persona") ?? envString("DSH_SYSTEM_PROMPT") ?? DEFAULT_PERSONA,
|
|
1997
2240
|
thinking: !parsed.flags.has("no-thinking"),
|
|
1998
2241
|
reasoningEffort: reasoningRaw,
|
|
@@ -2043,6 +2286,18 @@ async function main() {
|
|
|
2043
2286
|
console.log(HELP_TEXT.trimEnd());
|
|
2044
2287
|
return;
|
|
2045
2288
|
}
|
|
2289
|
+
if (argv[0] === "update") {
|
|
2290
|
+
logInfo(`updating ${NAME} (npm install -g ${NAME}@latest)\u2026`);
|
|
2291
|
+
const result = spawnSync("npm", ["install", "-g", `${NAME}@latest`], { stdio: ["ignore", 2, 2] });
|
|
2292
|
+
if (result.status !== 0) {
|
|
2293
|
+
logError(`npm install failed with status ${result.status ?? "unknown"}`);
|
|
2294
|
+
process.exitCode = result.status ?? 1;
|
|
2295
|
+
return;
|
|
2296
|
+
}
|
|
2297
|
+
logInfo("updated. If you also keep a global DeepSeek Harness, refresh it with:");
|
|
2298
|
+
logInfo(" npm install -g @deepseek-ai/dsh@latest");
|
|
2299
|
+
return;
|
|
2300
|
+
}
|
|
2046
2301
|
let settings;
|
|
2047
2302
|
try {
|
|
2048
2303
|
settings = resolveSettings(argv);
|
|
@@ -2056,11 +2311,24 @@ ${HELP_TEXT}`);
|
|
|
2056
2311
|
}
|
|
2057
2312
|
throw error;
|
|
2058
2313
|
}
|
|
2059
|
-
let
|
|
2314
|
+
let ctx;
|
|
2060
2315
|
try {
|
|
2061
2316
|
const host = resolveHost(settings.dshPath);
|
|
2062
2317
|
logDebug(`harness host: ${host.base} via ${host.source} (${host.version ?? "unknown version"})`);
|
|
2063
|
-
|
|
2318
|
+
if (process.env["DSH_ACP_COMPOSE"] === "spine") {
|
|
2319
|
+
const kit = await loadKit(host);
|
|
2320
|
+
const spineCtx = new kit.cordis.Context();
|
|
2321
|
+
await spineCtx.plugin(app_exports, { settings, kit });
|
|
2322
|
+
ctx = spineCtx;
|
|
2323
|
+
} else {
|
|
2324
|
+
const booted = await bootAcpProfile(host, {
|
|
2325
|
+
...settings.provider !== void 0 ? { provider: settings.provider } : {},
|
|
2326
|
+
...settings.model !== void 0 ? { model: settings.model } : {},
|
|
2327
|
+
...settings.permissionMode !== void 0 ? { permissionMode: settings.permissionMode } : {},
|
|
2328
|
+
...settings.maxTokens !== void 0 ? { maxTokens: settings.maxTokens } : {}
|
|
2329
|
+
});
|
|
2330
|
+
ctx = booted;
|
|
2331
|
+
}
|
|
2064
2332
|
} catch (error) {
|
|
2065
2333
|
if (error instanceof HarnessNotFoundError) {
|
|
2066
2334
|
logError(error.message);
|
|
@@ -2069,8 +2337,6 @@ ${HELP_TEXT}`);
|
|
|
2069
2337
|
}
|
|
2070
2338
|
throw error;
|
|
2071
2339
|
}
|
|
2072
|
-
const ctx = new kit.cordis.Context();
|
|
2073
|
-
await ctx.plugin(app_exports, { settings, kit });
|
|
2074
2340
|
let disposing = false;
|
|
2075
2341
|
const shutdown = (code) => {
|
|
2076
2342
|
if (disposing) return;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.4.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"
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"cordis.patch.yml",
|
|
24
24
|
"README.md",
|
|
25
25
|
"LICENSE",
|
|
26
|
-
"package.json"
|
|
26
|
+
"package.json",
|
|
27
|
+
"profile-root.cordis.yml"
|
|
27
28
|
],
|
|
28
29
|
"scripts": {
|
|
29
30
|
"prepublishOnly": "npm run build",
|
|
@@ -110,7 +111,8 @@
|
|
|
110
111
|
"@deepseek-ai/dsh-mcp-client": "^0.1.0-rc.6",
|
|
111
112
|
"@deepseek-ai/dsh-sandbox-policy": "^0.1.0-rc.6",
|
|
112
113
|
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
|
|
113
|
-
"@deepseek-ai/dsh-session-title": "^0.1.0-rc.6"
|
|
114
|
+
"@deepseek-ai/dsh-session-title": "^0.1.0-rc.6",
|
|
115
|
+
"@deepseek-ai/dsh": "^0.1.0-rc.6"
|
|
114
116
|
},
|
|
115
117
|
"exports": {
|
|
116
118
|
".": "./dist/index.js",
|
|
@@ -123,4 +125,4 @@
|
|
|
123
125
|
"patch": "./cordis.patch.yml"
|
|
124
126
|
}
|
|
125
127
|
}
|
|
126
|
-
}
|
|
128
|
+
}
|