@openma/deepseek-harness-acp 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bridge.js CHANGED
@@ -960,6 +960,24 @@ async function apply(ctx, config = {}) {
960
960
  };
961
961
  const configOptions = async (record) => {
962
962
  const result = [];
963
+ {
964
+ const permission = permissionService();
965
+ const levels = permission !== void 0 && permission.names.length > 0 ? permission.names.map((name3) => {
966
+ const spec = permission.resolve(name3);
967
+ return { value: name3, name: spec.name, description: spec.description };
968
+ }) : SANDBOX_MODES2.map((mode) => {
969
+ const label = MODE_LABELS[mode] ?? { name: mode, description: "" };
970
+ return { value: mode, name: label.name, description: label.description };
971
+ });
972
+ result.push({
973
+ type: "select",
974
+ id: "mode",
975
+ name: "Permissions",
976
+ category: "mode",
977
+ currentValue: record.modeId,
978
+ options: levels
979
+ });
980
+ }
963
981
  const models = await modelChoices(record);
964
982
  if (models.length >= 2) {
965
983
  result.push({
@@ -1434,6 +1452,10 @@ async function apply(ctx, config = {}) {
1434
1452
  const value = typeof params.value === "string" ? params.value : void 0;
1435
1453
  if (value === void 0) throw invalidParams(`invalid value: ${String(params.value)}`);
1436
1454
  switch (params.configId) {
1455
+ case "mode": {
1456
+ applyMode(record, params.sessionId, value);
1457
+ break;
1458
+ }
1437
1459
  case "preset": {
1438
1460
  await switchPreset(record, params.sessionId, value);
1439
1461
  break;
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, {
@@ -89,6 +92,10 @@ function logDebug(message) {
89
92
  if (process.env["DSH_ACP_DEBUG"]) process.stderr.write(`${PREFIX} debug: ${message}
90
93
  `);
91
94
  }
95
+ function logInfo(message) {
96
+ process.stderr.write(`${PREFIX} ${message}
97
+ `);
98
+ }
92
99
 
93
100
  // src/bridge/translate.ts
94
101
  var MAX_TITLE_LENGTH = 80;
@@ -978,6 +985,24 @@ async function apply(ctx, config = {}) {
978
985
  };
979
986
  const configOptions = async (record) => {
980
987
  const result = [];
988
+ {
989
+ const permission = permissionService();
990
+ const levels = permission !== void 0 && permission.names.length > 0 ? permission.names.map((name3) => {
991
+ const spec = permission.resolve(name3);
992
+ return { value: name3, name: spec.name, description: spec.description };
993
+ }) : SANDBOX_MODES2.map((mode) => {
994
+ const label = MODE_LABELS[mode] ?? { name: mode, description: "" };
995
+ return { value: mode, name: label.name, description: label.description };
996
+ });
997
+ result.push({
998
+ type: "select",
999
+ id: "mode",
1000
+ name: "Permissions",
1001
+ category: "mode",
1002
+ currentValue: record.modeId,
1003
+ options: levels
1004
+ });
1005
+ }
981
1006
  const models = await modelChoices(record);
982
1007
  if (models.length >= 2) {
983
1008
  result.push({
@@ -1452,6 +1477,10 @@ async function apply(ctx, config = {}) {
1452
1477
  const value = typeof params.value === "string" ? params.value : void 0;
1453
1478
  if (value === void 0) throw invalidParams(`invalid value: ${String(params.value)}`);
1454
1479
  switch (params.configId) {
1480
+ case "mode": {
1481
+ applyMode(record, params.sessionId, value);
1482
+ break;
1483
+ }
1455
1484
  case "preset": {
1456
1485
  await switchPreset(record, params.sessionId, value);
1457
1486
  break;
@@ -1752,9 +1781,6 @@ function resolveHost(explicit) {
1752
1781
  probed.push(`--dsh-path/DSH_PATH: ${explicit}`);
1753
1782
  throw new HarnessNotFoundError(probed);
1754
1783
  }
1755
- const own = hostAt(dirname(fileURLToPath(import.meta.url)), "dsh-acp package tree");
1756
- if (own !== void 0) return own;
1757
- probed.push("dsh-acp package tree");
1758
1784
  const local = hostAt(process.cwd(), `invoking directory (${process.cwd()})`);
1759
1785
  if (local !== void 0) return local;
1760
1786
  probed.push(`invoking directory: ${process.cwd()}`);
@@ -1764,6 +1790,9 @@ function resolveHost(explicit) {
1764
1790
  const globalNpm = hostFromGlobalNpm();
1765
1791
  if (globalNpm !== void 0) return globalNpm;
1766
1792
  probed.push("npm root -g");
1793
+ const own = hostAt(dirname(fileURLToPath(import.meta.url)), "dsh-acp package tree (vendored)");
1794
+ if (own !== void 0) return own;
1795
+ probed.push("dsh-acp package tree (vendored)");
1767
1796
  throw new HarnessNotFoundError(probed);
1768
1797
  }
1769
1798
  function entryFile(packageDir, subpath) {
@@ -2000,8 +2029,8 @@ async function apply2(ctx, config) {
2000
2029
  await compaction;
2001
2030
  yield compaction.dispose;
2002
2031
  const bridge = anyCtx.plugin(bridge_exports, {
2003
- provider: settings.provider,
2004
- model: settings.model,
2032
+ provider: settings.provider ?? "deepseek-official",
2033
+ model: settings.model ?? "deepseek-v4-flash",
2005
2034
  models: settings.models,
2006
2035
  ...settings.maxTokens !== void 0 ? { maxTokens: settings.maxTokens } : {},
2007
2036
  permissionMode: settings.permissionMode,
@@ -2012,9 +2041,130 @@ async function apply2(ctx, config) {
2012
2041
  }, "dsh-acp-app.composition");
2013
2042
  }
2014
2043
 
2044
+ // src/profile-boot.ts
2045
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
2046
+ import { createRequire as createRequire2 } from "node:module";
2047
+ import { dirname as dirname2, join as join2, sep as sep2 } from "node:path";
2048
+ import { pathToFileURL as pathToFileURL2 } from "node:url";
2049
+ var BIN = "dsh-acp";
2050
+ function ownRoot() {
2051
+ return dirname2(createRequire2(import.meta.url).resolve("../package.json"));
2052
+ }
2053
+ var OWN_BRIDGE_SPECIFIER = "@openma/deepseek-harness-acp/bridge";
2054
+ function rewriteBridgeRows(row, absoluteEntry) {
2055
+ if (row["name"] === OWN_BRIDGE_SPECIFIER) row["name"] = absoluteEntry;
2056
+ const insert = row["insert"];
2057
+ if (Array.isArray(insert)) {
2058
+ for (const nested of insert) {
2059
+ if (nested !== null && typeof nested === "object") {
2060
+ rewriteBridgeRows(nested, absoluteEntry);
2061
+ }
2062
+ }
2063
+ }
2064
+ }
2065
+ function bundlePatchPath(packageJsonPath) {
2066
+ const pkg = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
2067
+ const declared = pkg.dsh?.bundle?.patch;
2068
+ if (declared === void 0) {
2069
+ throw new Error(`${BIN}: bundle ${pkg.name ?? packageJsonPath} declares no dsh.bundle in its package.json`);
2070
+ }
2071
+ return join2(dirname2(packageJsonPath), declared);
2072
+ }
2073
+ async function bootAcpProfile(host, overrides) {
2074
+ const req = createRequire2(join2(host.base, "noop.js"));
2075
+ const dshPkgPath = req.resolve("@deepseek-ai/dsh/package.json");
2076
+ const dshDir = dirname2(dshPkgPath);
2077
+ const appBoot = await import(pathToFileURL2(req.resolve("@deepseek-ai/dsh-app-boot")).href);
2078
+ const homePaths = await import(pathToFileURL2(req.resolve("@deepseek-ai/dsh-home-paths")).href);
2079
+ const home = homePaths.resolveDshHome();
2080
+ let bundlePatches;
2081
+ let profilePatches = [];
2082
+ let profilePatchPath;
2083
+ let rootConfigPath = join2(ownRoot(), "profile-root.cordis.yml");
2084
+ let bareModuleBaseUrl = pathToFileURL2(dshDir + sep2).href;
2085
+ const distBridge = join2(ownRoot(), "dist", "bridge.js");
2086
+ const bridgeEntry = import.meta.url.endsWith(".ts") || !existsSync2(distBridge) ? join2(ownRoot(), "src", "bundle.ts") : distBridge;
2087
+ const realProfileDir = join2(home, "profiles", "acp");
2088
+ if (existsSync2(join2(realProfileDir, "package.json"))) {
2089
+ appBoot.healProfilesModuleFallback(dshPkgPath);
2090
+ const profile = appBoot.loadProfile(BIN, "acp", dshPkgPath);
2091
+ bundlePatches = profile.layers.flatMap((layer) => layer.patches);
2092
+ profilePatches = profile.patches;
2093
+ profilePatchPath = join2(profile.dir, "cordis.patch.yml");
2094
+ const realRoot = join2(profile.dir, "cordis.yml");
2095
+ if (existsSync2(realRoot)) rootConfigPath = realRoot;
2096
+ bareModuleBaseUrl = void 0;
2097
+ logDebug(`profile: real (${profile.dir})`);
2098
+ } else {
2099
+ const basePatch = bundlePatchPath(req.resolve("@deepseek-ai/dsh-base/package.json"));
2100
+ const ownPatch = bundlePatchPath(join2(ownRoot(), "package.json"));
2101
+ bundlePatches = [
2102
+ ...appBoot.loadOverlayPatches(BIN, basePatch),
2103
+ ...appBoot.loadOverlayPatches(BIN, ownPatch)
2104
+ ];
2105
+ logDebug(`profile: virtual (dsh-base + ${ownRoot()})`);
2106
+ }
2107
+ for (const row of bundlePatches) rewriteBridgeRows(row, bridgeEntry);
2108
+ const homePatchPath = join2(home, "cordis.patch.yml");
2109
+ const homePatches = appBoot.loadOptionalPatches(BIN, homePatchPath) ?? [];
2110
+ const rows = /* @__PURE__ */ new Map();
2111
+ for (const row of appBoot.composeEntries([bundlePatches, profilePatches, homePatches])) {
2112
+ if (typeof row.id === "string") rows.set(row.id, row);
2113
+ }
2114
+ const overlays = [];
2115
+ const shippedPresets = join2(dshDir, "config", "agent-presets") + sep2;
2116
+ if (rows.has("agent-presets") && existsSync2(shippedPresets)) {
2117
+ overlays.push({
2118
+ id: "agent-presets",
2119
+ config: {
2120
+ ...rows.get("agent-presets")?.config ?? {},
2121
+ roots: [{ path: shippedPresets, trust: "system" }]
2122
+ }
2123
+ });
2124
+ }
2125
+ if (process.env["DSH_TELEMETRY_DISABLED"] !== void 0 && rows.has("session-telemetry-otel")) {
2126
+ overlays.push({ id: "session-telemetry-otel", disabled: true });
2127
+ }
2128
+ const bridgeRow = rows.get("acp-bridge");
2129
+ if (overrides !== void 0 && bridgeRow !== void 0) {
2130
+ const patch = {};
2131
+ if (overrides.provider !== void 0) patch["provider"] = overrides.provider;
2132
+ if (overrides.model !== void 0) patch["model"] = overrides.model;
2133
+ if (overrides.permissionMode !== void 0) patch["permissionMode"] = overrides.permissionMode;
2134
+ if (overrides.maxTokens !== void 0) patch["maxTokens"] = overrides.maxTokens;
2135
+ if (Object.keys(patch).length > 0) {
2136
+ overlays.push({ id: "acp-bridge", config: { ...bridgeRow.config ?? {}, ...patch } });
2137
+ }
2138
+ }
2139
+ const allPatches = [...bundlePatches, ...profilePatches, ...homePatches, ...overlays];
2140
+ const launchEnvironment = appBoot.loadLayeredEnv("dsh");
2141
+ const launchEnvKey = (await import(pathToFileURL2(req.resolve("@deepseek-ai/dsh-launch-environment")).href)).DSH_LAUNCH_ENVIRONMENT_KEY;
2142
+ const ctx = await appBoot.boot(
2143
+ BIN,
2144
+ rootConfigPath,
2145
+ structuredClone(allPatches),
2146
+ (hostCtx) => {
2147
+ hostCtx.provide(launchEnvKey, launchEnvironment);
2148
+ },
2149
+ bareModuleBaseUrl
2150
+ );
2151
+ try {
2152
+ appBoot.assertEntriesLoaded(ctx, BIN);
2153
+ await appBoot.assertEntriesActivated(ctx, BIN);
2154
+ } catch (error) {
2155
+ await ctx.fiber.dispose().catch(() => {
2156
+ });
2157
+ throw error;
2158
+ }
2159
+ if (profilePatchPath !== void 0) {
2160
+ logDebug(`user patch layers are read at boot; edit ${profilePatchPath} and restart to apply`);
2161
+ }
2162
+ return ctx;
2163
+ }
2164
+
2015
2165
  // src/settings.ts
2016
2166
  import { homedir } from "node:os";
2017
- import { join as join2 } from "node:path";
2167
+ import { join as join3 } from "node:path";
2018
2168
  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.
2019
2169
 
2020
2170
  Verify your work by running the code or tests. Keep answers brief and factual.`;
@@ -2073,7 +2223,7 @@ function resolveSettings(argv) {
2073
2223
  throw new SettingsError(`unknown flag: --${name3}`);
2074
2224
  }
2075
2225
  }
2076
- const model = stringFlag(parsed, "model") ?? envString("DSH_MODEL") ?? "deepseek-v4-flash";
2226
+ const model = stringFlag(parsed, "model") ?? envString("DSH_MODEL");
2077
2227
  const modelsRaw = stringFlag(parsed, "models") ?? envString("DSH_ACP_MODELS");
2078
2228
  const models = (modelsRaw !== void 0 ? modelsRaw.split(",") : ["deepseek-v4-flash", "deepseek-v4-pro"]).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
2079
2229
  const permissionModeRaw = stringFlag(parsed, "permission-mode") ?? envString("DSH_PERMISSION_MODE") ?? "workspace-write";
@@ -2102,12 +2252,12 @@ function resolveSettings(argv) {
2102
2252
  }
2103
2253
  return {
2104
2254
  dshPath: stringFlag(parsed, "dsh-path") ?? envString("DSH_PATH"),
2105
- provider: stringFlag(parsed, "provider") ?? envString("DSH_PROVIDER") ?? "deepseek-official",
2255
+ provider: stringFlag(parsed, "provider") ?? envString("DSH_PROVIDER"),
2106
2256
  model,
2107
2257
  models,
2108
2258
  maxTokens,
2109
2259
  permissionMode,
2110
- sessionRoot: stringFlag(parsed, "session-root") ?? envString("DSH_SESSION_ROOT") ?? join2(homedir(), ".dsh-acp", "sessions"),
2260
+ sessionRoot: stringFlag(parsed, "session-root") ?? envString("DSH_SESSION_ROOT") ?? join3(homedir(), ".dsh-acp", "sessions"),
2111
2261
  persona: stringFlag(parsed, "persona") ?? envString("DSH_SYSTEM_PROMPT") ?? DEFAULT_PERSONA,
2112
2262
  thinking: !parsed.flags.has("no-thinking"),
2113
2263
  reasoningEffort: reasoningRaw,
@@ -2158,6 +2308,18 @@ async function main() {
2158
2308
  console.log(HELP_TEXT.trimEnd());
2159
2309
  return;
2160
2310
  }
2311
+ if (argv[0] === "update") {
2312
+ logInfo(`updating ${NAME} (npm install -g ${NAME}@latest)\u2026`);
2313
+ const result = spawnSync("npm", ["install", "-g", `${NAME}@latest`], { stdio: ["ignore", 2, 2] });
2314
+ if (result.status !== 0) {
2315
+ logError(`npm install failed with status ${result.status ?? "unknown"}`);
2316
+ process.exitCode = result.status ?? 1;
2317
+ return;
2318
+ }
2319
+ logInfo("updated. If you also keep a global DeepSeek Harness, refresh it with:");
2320
+ logInfo(" npm install -g @deepseek-ai/dsh@latest");
2321
+ return;
2322
+ }
2161
2323
  let settings;
2162
2324
  try {
2163
2325
  settings = resolveSettings(argv);
@@ -2171,11 +2333,24 @@ ${HELP_TEXT}`);
2171
2333
  }
2172
2334
  throw error;
2173
2335
  }
2174
- let kit;
2336
+ let ctx;
2175
2337
  try {
2176
2338
  const host = resolveHost(settings.dshPath);
2177
2339
  logDebug(`harness host: ${host.base} via ${host.source} (${host.version ?? "unknown version"})`);
2178
- kit = await loadKit(host);
2340
+ if (process.env["DSH_ACP_COMPOSE"] === "spine") {
2341
+ const kit = await loadKit(host);
2342
+ const spineCtx = new kit.cordis.Context();
2343
+ await spineCtx.plugin(app_exports, { settings, kit });
2344
+ ctx = spineCtx;
2345
+ } else {
2346
+ const booted = await bootAcpProfile(host, {
2347
+ ...settings.provider !== void 0 ? { provider: settings.provider } : {},
2348
+ ...settings.model !== void 0 ? { model: settings.model } : {},
2349
+ ...settings.permissionMode !== void 0 ? { permissionMode: settings.permissionMode } : {},
2350
+ ...settings.maxTokens !== void 0 ? { maxTokens: settings.maxTokens } : {}
2351
+ });
2352
+ ctx = booted;
2353
+ }
2179
2354
  } catch (error) {
2180
2355
  if (error instanceof HarnessNotFoundError) {
2181
2356
  logError(error.message);
@@ -2184,8 +2359,6 @@ ${HELP_TEXT}`);
2184
2359
  }
2185
2360
  throw error;
2186
2361
  }
2187
- const ctx = new kit.cordis.Context();
2188
- await ctx.plugin(app_exports, { settings, kit });
2189
2362
  let disposing = false;
2190
2363
  const shutdown = (code) => {
2191
2364
  if (disposing) return;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.3.0",
6
+ "version": "0.4.1",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/openma-ai/deepseek-harness-acp.git"
@@ -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",
@@ -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
+ []