@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 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
- var VERSION = "0.1.0";
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
- return { provider, model };
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 modeState = (record) => ({
829
- currentModeId: record.modeId,
830
- availableModes: SANDBOX_MODES2.map((mode) => {
831
- const label = MODE_LABELS[mode] ?? { name: mode, description: "" };
832
- return { id: mode, name: label.name, description: label.description };
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 = [record.effort, loggedConfig(record)?.reasoningEffort, efforts.defaultEffort].find(
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
- result.push({
921
- type: "select",
922
- id: "approvals",
923
- name: "Approvals",
924
- currentValue: record.approvals,
925
- options: [
926
- { value: "ask", name: "Ask", description: "Ask before actions beyond the sandbox mode" },
927
- { value: "never", name: "Never ask", description: "Auto-approve every permission request" }
928
- ]
929
- });
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
- ({ events } = await persistence.inspect(SessionId2(sessionId)));
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 "mode": {
1318
- applyMode(record, params.sessionId, value);
1319
- break;
1320
- }
1321
- case "approvals": {
1322
- if (value !== "ask" && value !== "never") throw invalidParams(`unknown approvals: ${value}`);
1323
- setApprovalPolicy(record, value);
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
- var VERSION = "0.1.0";
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
- return { provider, model };
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 modeState = (record) => ({
847
- currentModeId: record.modeId,
848
- availableModes: SANDBOX_MODES2.map((mode) => {
849
- const label = MODE_LABELS[mode] ?? { name: mode, description: "" };
850
- return { id: mode, name: label.name, description: label.description };
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 = [record.effort, loggedConfig(record)?.reasoningEffort, efforts.defaultEffort].find(
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
- result.push({
939
- type: "select",
940
- id: "approvals",
941
- name: "Approvals",
942
- currentValue: record.approvals,
943
- options: [
944
- { value: "ask", name: "Ask", description: "Ask before actions beyond the sandbox mode" },
945
- { value: "never", name: "Never ask", description: "Auto-approve every permission request" }
946
- ]
947
- });
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
- ({ events } = await persistence.inspect(SessionId2(sessionId)));
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 "mode": {
1336
- applyMode(record, params.sessionId, value);
1337
- break;
1338
- }
1339
- case "approvals": {
1340
- if (value !== "ask" && value !== "never") throw invalidParams(`unknown approvals: ${value}`);
1341
- setApprovalPolicy(record, value);
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 join2 } from "node:path";
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") ?? "deepseek-v4-flash";
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") ?? "deepseek-official",
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") ?? join2(homedir(), ".dsh-acp", "sessions"),
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 kit;
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
- kit = await loadKit(host);
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.2.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 — use DeepSeek Harness from ACP clients such as Zed.",
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
+ }
@@ -0,0 +1,3 @@
1
+ # dsh-acp self-hosted profile root — an empty entry list; the tree is
2
+ # composed as bundle patches (dsh-base, this package) plus the home layer.
3
+ []