@openma/deepseek-harness-acp 0.4.14 → 0.4.15

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
@@ -104,6 +104,28 @@ owns the composition.
104
104
 
105
105
  There are two independent ways to extend an ACP-backed surface.
106
106
 
107
+ To use portable Agent Plugins, Codex plugins, Claude Code plugins, or Pi
108
+ packages through an ACP client, add the
109
+ [Agent Plugins Bridge](https://github.com/openma-ai/dsh-agents-plugins-bridge) to the
110
+ same profile:
111
+
112
+ ```sh
113
+ dsh plugin --profile acp add @openma/dsh-agents-plugins-bridge@latest
114
+ ```
115
+
116
+ The Bridge contributes ordinary Host rows. Imported commands, skills, tools,
117
+ hooks, MCP connections, agents, and Pi extensions therefore reach ACP through
118
+ this adapter's existing projections; there is no ACP-specific plugin import
119
+ runtime. The Bridge's Web management panel and MCP Apps HTML renderer remain
120
+ Web surfaces and are not sent over ACP.
121
+
122
+ For extensions that need a session-owned background lifecycle without an open
123
+ terminal UI, use
124
+ [Martty owner](https://github.com/openma-ai/deepseek-harness-tui/blob/main/README.en.md#martty-owner-long-lived-headless-acp--pi-rpc).
125
+ It is a generic ACP `rpc` client: this package remains the server/transport,
126
+ while Martty owns the long-lived Session and explicit startup/shutdown slash
127
+ commands.
128
+
107
129
  ### Extend the Host composition
108
130
 
109
131
  The ACP adapter rides the Cordis tree that the profile already owns. Add dsh
package/dist/bridge.js CHANGED
@@ -961,6 +961,35 @@ function buildReplay(events) {
961
961
  return { updates, title, contextWindow: projection.contextWindow };
962
962
  }
963
963
 
964
+ // src/bridge/latest-publication.ts
965
+ var LatestPublication = class {
966
+ revisions = /* @__PURE__ */ new Map();
967
+ async run(key, produce, publish) {
968
+ const revision = (this.revisions.get(key) ?? 0) + 1;
969
+ this.revisions.set(key, revision);
970
+ const value = await produce();
971
+ if (this.revisions.get(key) !== revision) return;
972
+ publish(value);
973
+ }
974
+ invalidate(key) {
975
+ this.revisions.set(key, (this.revisions.get(key) ?? 0) + 1);
976
+ }
977
+ };
978
+
979
+ // src/bridge/interaction-mode.ts
980
+ function interactionModeFromClientMeta(meta) {
981
+ if (meta === null || typeof meta !== "object") return void 0;
982
+ const dsh = meta["dsh"];
983
+ if (dsh === null || typeof dsh !== "object") return void 0;
984
+ const interaction = dsh["interaction"];
985
+ if (interaction === null || typeof interaction !== "object") return void 0;
986
+ const mode = interaction["mode"];
987
+ return mode === "interactive" || mode === "rpc" ? mode : void 0;
988
+ }
989
+ function withInteractionMode(options, mode) {
990
+ return mode === void 0 ? options : { ...options, interactionMode: mode };
991
+ }
992
+
964
993
  // src/bridge/prompt.ts
965
994
  function deliverPrompt(target, message, running) {
966
995
  if (running) {
@@ -1767,6 +1796,7 @@ async function apply3(ctx, config = {}) {
1767
1796
  const { createUserMessage: createUserMessage2, errorChain: errorChain2, sessionId: SessionId2, foldSessionTitle: foldSessionTitle2, setSandboxMode: setSandboxMode2 } = harness;
1768
1797
  const SANDBOX_MODES2 = harness.sandboxModes;
1769
1798
  const sessions = /* @__PURE__ */ new Map();
1799
+ const commandPublications = new LatestPublication();
1770
1800
  const subagentByChild = /* @__PURE__ */ new Map();
1771
1801
  const subagentByRun = /* @__PURE__ */ new Map();
1772
1802
  const watchedSubagentParents = /* @__PURE__ */ new WeakSet();
@@ -1775,6 +1805,7 @@ async function apply3(ctx, config = {}) {
1775
1805
  let clientTerminalOutput = false;
1776
1806
  let clientSubagentTranscript = false;
1777
1807
  let clientElicitationForm = false;
1808
+ let clientInteractionMode;
1778
1809
  const tuiClient = { advertised: false };
1779
1810
  const rpc = new AcpRpc();
1780
1811
  const findAgent = (agentId) => {
@@ -2003,11 +2034,11 @@ async function apply3(ctx, config = {}) {
2003
2034
  'no credential found for this provider: call authenticate with `_meta["api-key"].apiKey` or `_meta.gateway`, use the browser method, run `dsh-acp login`, or save a key in the dsh Web UI (Settings \u2192 Models)'
2004
2035
  );
2005
2036
  };
2006
- const agentOptionsFor = (model, provider) => ({
2037
+ const agentOptionsFor = (model, provider) => withInteractionMode({
2007
2038
  ...provider !== void 0 ? { provider } : config.provider !== void 0 ? { provider: config.provider } : {},
2008
2039
  ...model !== void 0 ? { model } : {},
2009
2040
  ...config.maxTokens !== void 0 ? { maxTokens: config.maxTokens } : {}
2010
- });
2041
+ }, clientInteractionMode);
2011
2042
  const ownedRecord = (agent) => {
2012
2043
  const record = sessions.get(String(agent.session.id));
2013
2044
  return record?.agent === agent ? record : void 0;
@@ -2481,12 +2512,20 @@ async function apply3(ctx, config = {}) {
2481
2512
  const publishCommands = (sessionId, record) => {
2482
2513
  const target = record ?? sessions.get(sessionId);
2483
2514
  if (target === void 0) return;
2484
- void availableCommandsFor(target).then((availableCommands) => {
2515
+ void commandPublications.run(sessionId, () => availableCommandsFor(target), (availableCommands) => {
2516
+ if (sessions.get(sessionId) !== target) return;
2517
+ logDebug(
2518
+ `available commands ${sessionId}: ${availableCommands.map((command) => command.name).join(", ")}`
2519
+ );
2485
2520
  notify(sessionId, { sessionUpdate: "available_commands_update", availableCommands });
2486
2521
  }).catch((error) => {
2487
2522
  logWarn(`publishing commands failed: ${String(error)}`);
2488
2523
  });
2489
2524
  };
2525
+ ctx.on("commands/change", () => {
2526
+ logDebug(`commands/change: ${sessions.size} live ACP session(s)`);
2527
+ for (const [sessionId, record] of sessions) publishCommands(sessionId, record);
2528
+ });
2490
2529
  const publishConfigOptions = (sessionId, record) => {
2491
2530
  void configOptions(record).then((options) => {
2492
2531
  notify(sessionId, {
@@ -2699,6 +2738,7 @@ async function apply3(ctx, config = {}) {
2699
2738
  const capsMeta = params.clientCapabilities?.["_meta"];
2700
2739
  clientTerminalOutput = capsMeta !== null && typeof capsMeta === "object" ? capsMeta["terminal_output"] === true : false;
2701
2740
  clientSubagentTranscript = capsMeta !== null && typeof capsMeta === "object" ? capsMeta["subagent-transcript"] === true : false;
2741
+ clientInteractionMode = interactionModeFromClientMeta(capsMeta);
2702
2742
  clientElicitationForm = params.clientCapabilities?.elicitation?.form !== void 0 && params.clientCapabilities.elicitation.form !== null;
2703
2743
  tuiClient.advertised = advertisesCordis(capsMeta);
2704
2744
  const providers = await listProviderRoutes();
package/dist/index.js CHANGED
@@ -44,6 +44,7 @@ ${probed.map((p) => ` ${p}`).join("\n")}` : ""
44
44
  }
45
45
  };
46
46
  var ANCHOR = "@deepseek-ai/dsh-agent";
47
+ var PROFILE_LAUNCHER = "@deepseek-ai/dsh";
47
48
  function packageDirFrom(base, name) {
48
49
  let dir = resolve(base);
49
50
  for (; ; ) {
@@ -66,7 +67,8 @@ function versionOf(packageDir) {
66
67
  }
67
68
  function hostAt(base, source) {
68
69
  const anchor = packageDirFrom(base, ANCHOR);
69
- if (anchor === void 0) return void 0;
70
+ const launcher = packageDirFrom(base, PROFILE_LAUNCHER);
71
+ if (anchor === void 0 || launcher === void 0) return void 0;
70
72
  return { base: resolve(base), version: versionOf(anchor), source };
71
73
  }
72
74
  function hostFromExplicit(explicit) {
package/dist/plugin.js CHANGED
@@ -962,6 +962,35 @@ function buildReplay(events) {
962
962
  return { updates, title, contextWindow: projection.contextWindow };
963
963
  }
964
964
 
965
+ // src/bridge/latest-publication.ts
966
+ var LatestPublication = class {
967
+ revisions = /* @__PURE__ */ new Map();
968
+ async run(key, produce, publish) {
969
+ const revision = (this.revisions.get(key) ?? 0) + 1;
970
+ this.revisions.set(key, revision);
971
+ const value = await produce();
972
+ if (this.revisions.get(key) !== revision) return;
973
+ publish(value);
974
+ }
975
+ invalidate(key) {
976
+ this.revisions.set(key, (this.revisions.get(key) ?? 0) + 1);
977
+ }
978
+ };
979
+
980
+ // src/bridge/interaction-mode.ts
981
+ function interactionModeFromClientMeta(meta) {
982
+ if (meta === null || typeof meta !== "object") return void 0;
983
+ const dsh = meta["dsh"];
984
+ if (dsh === null || typeof dsh !== "object") return void 0;
985
+ const interaction = dsh["interaction"];
986
+ if (interaction === null || typeof interaction !== "object") return void 0;
987
+ const mode = interaction["mode"];
988
+ return mode === "interactive" || mode === "rpc" ? mode : void 0;
989
+ }
990
+ function withInteractionMode(options, mode) {
991
+ return mode === void 0 ? options : { ...options, interactionMode: mode };
992
+ }
993
+
965
994
  // src/bridge/prompt.ts
966
995
  function deliverPrompt(target, message, running) {
967
996
  if (running) {
@@ -1768,6 +1797,7 @@ async function apply3(ctx, config = {}) {
1768
1797
  const { createUserMessage: createUserMessage2, errorChain: errorChain2, sessionId: SessionId2, foldSessionTitle: foldSessionTitle2, setSandboxMode: setSandboxMode2 } = harness;
1769
1798
  const SANDBOX_MODES2 = harness.sandboxModes;
1770
1799
  const sessions = /* @__PURE__ */ new Map();
1800
+ const commandPublications = new LatestPublication();
1771
1801
  const subagentByChild = /* @__PURE__ */ new Map();
1772
1802
  const subagentByRun = /* @__PURE__ */ new Map();
1773
1803
  const watchedSubagentParents = /* @__PURE__ */ new WeakSet();
@@ -1776,6 +1806,7 @@ async function apply3(ctx, config = {}) {
1776
1806
  let clientTerminalOutput = false;
1777
1807
  let clientSubagentTranscript = false;
1778
1808
  let clientElicitationForm = false;
1809
+ let clientInteractionMode;
1779
1810
  const tuiClient = { advertised: false };
1780
1811
  const rpc = new AcpRpc();
1781
1812
  const findAgent = (agentId) => {
@@ -2004,11 +2035,11 @@ async function apply3(ctx, config = {}) {
2004
2035
  'no credential found for this provider: call authenticate with `_meta["api-key"].apiKey` or `_meta.gateway`, use the browser method, run `dsh-acp login`, or save a key in the dsh Web UI (Settings \u2192 Models)'
2005
2036
  );
2006
2037
  };
2007
- const agentOptionsFor = (model, provider) => ({
2038
+ const agentOptionsFor = (model, provider) => withInteractionMode({
2008
2039
  ...provider !== void 0 ? { provider } : config.provider !== void 0 ? { provider: config.provider } : {},
2009
2040
  ...model !== void 0 ? { model } : {},
2010
2041
  ...config.maxTokens !== void 0 ? { maxTokens: config.maxTokens } : {}
2011
- });
2042
+ }, clientInteractionMode);
2012
2043
  const ownedRecord = (agent) => {
2013
2044
  const record = sessions.get(String(agent.session.id));
2014
2045
  return record?.agent === agent ? record : void 0;
@@ -2482,12 +2513,20 @@ async function apply3(ctx, config = {}) {
2482
2513
  const publishCommands = (sessionId, record) => {
2483
2514
  const target = record ?? sessions.get(sessionId);
2484
2515
  if (target === void 0) return;
2485
- void availableCommandsFor(target).then((availableCommands) => {
2516
+ void commandPublications.run(sessionId, () => availableCommandsFor(target), (availableCommands) => {
2517
+ if (sessions.get(sessionId) !== target) return;
2518
+ logDebug(
2519
+ `available commands ${sessionId}: ${availableCommands.map((command) => command.name).join(", ")}`
2520
+ );
2486
2521
  notify(sessionId, { sessionUpdate: "available_commands_update", availableCommands });
2487
2522
  }).catch((error) => {
2488
2523
  logWarn(`publishing commands failed: ${String(error)}`);
2489
2524
  });
2490
2525
  };
2526
+ ctx.on("commands/change", () => {
2527
+ logDebug(`commands/change: ${sessions.size} live ACP session(s)`);
2528
+ for (const [sessionId, record] of sessions) publishCommands(sessionId, record);
2529
+ });
2491
2530
  const publishConfigOptions = (sessionId, record) => {
2492
2531
  void configOptions(record).then((options) => {
2493
2532
  notify(sessionId, {
@@ -2700,6 +2739,7 @@ async function apply3(ctx, config = {}) {
2700
2739
  const capsMeta = params.clientCapabilities?.["_meta"];
2701
2740
  clientTerminalOutput = capsMeta !== null && typeof capsMeta === "object" ? capsMeta["terminal_output"] === true : false;
2702
2741
  clientSubagentTranscript = capsMeta !== null && typeof capsMeta === "object" ? capsMeta["subagent-transcript"] === true : false;
2742
+ clientInteractionMode = interactionModeFromClientMeta(capsMeta);
2703
2743
  clientElicitationForm = params.clientCapabilities?.elicitation?.form !== void 0 && params.clientCapabilities.elicitation.form !== null;
2704
2744
  tuiClient.advertised = advertisesCordis(capsMeta);
2705
2745
  const providers = await listProviderRoutes();
package/dist/server.js CHANGED
@@ -957,6 +957,35 @@ function buildReplay(events) {
957
957
  return { updates, title, contextWindow: projection.contextWindow };
958
958
  }
959
959
 
960
+ // src/bridge/latest-publication.ts
961
+ var LatestPublication = class {
962
+ revisions = /* @__PURE__ */ new Map();
963
+ async run(key, produce, publish) {
964
+ const revision = (this.revisions.get(key) ?? 0) + 1;
965
+ this.revisions.set(key, revision);
966
+ const value = await produce();
967
+ if (this.revisions.get(key) !== revision) return;
968
+ publish(value);
969
+ }
970
+ invalidate(key) {
971
+ this.revisions.set(key, (this.revisions.get(key) ?? 0) + 1);
972
+ }
973
+ };
974
+
975
+ // src/bridge/interaction-mode.ts
976
+ function interactionModeFromClientMeta(meta) {
977
+ if (meta === null || typeof meta !== "object") return void 0;
978
+ const dsh = meta["dsh"];
979
+ if (dsh === null || typeof dsh !== "object") return void 0;
980
+ const interaction = dsh["interaction"];
981
+ if (interaction === null || typeof interaction !== "object") return void 0;
982
+ const mode = interaction["mode"];
983
+ return mode === "interactive" || mode === "rpc" ? mode : void 0;
984
+ }
985
+ function withInteractionMode(options, mode) {
986
+ return mode === void 0 ? options : { ...options, interactionMode: mode };
987
+ }
988
+
960
989
  // src/bridge/prompt.ts
961
990
  function deliverPrompt(target, message, running) {
962
991
  if (running) {
@@ -1763,6 +1792,7 @@ async function apply3(ctx, config = {}) {
1763
1792
  const { createUserMessage: createUserMessage2, errorChain: errorChain2, sessionId: SessionId2, foldSessionTitle: foldSessionTitle2, setSandboxMode: setSandboxMode2 } = harness;
1764
1793
  const SANDBOX_MODES2 = harness.sandboxModes;
1765
1794
  const sessions = /* @__PURE__ */ new Map();
1795
+ const commandPublications = new LatestPublication();
1766
1796
  const subagentByChild = /* @__PURE__ */ new Map();
1767
1797
  const subagentByRun = /* @__PURE__ */ new Map();
1768
1798
  const watchedSubagentParents = /* @__PURE__ */ new WeakSet();
@@ -1771,6 +1801,7 @@ async function apply3(ctx, config = {}) {
1771
1801
  let clientTerminalOutput = false;
1772
1802
  let clientSubagentTranscript = false;
1773
1803
  let clientElicitationForm = false;
1804
+ let clientInteractionMode;
1774
1805
  const tuiClient = { advertised: false };
1775
1806
  const rpc = new AcpRpc();
1776
1807
  const findAgent = (agentId) => {
@@ -1999,11 +2030,11 @@ async function apply3(ctx, config = {}) {
1999
2030
  'no credential found for this provider: call authenticate with `_meta["api-key"].apiKey` or `_meta.gateway`, use the browser method, run `dsh-acp login`, or save a key in the dsh Web UI (Settings \u2192 Models)'
2000
2031
  );
2001
2032
  };
2002
- const agentOptionsFor = (model, provider) => ({
2033
+ const agentOptionsFor = (model, provider) => withInteractionMode({
2003
2034
  ...provider !== void 0 ? { provider } : config.provider !== void 0 ? { provider: config.provider } : {},
2004
2035
  ...model !== void 0 ? { model } : {},
2005
2036
  ...config.maxTokens !== void 0 ? { maxTokens: config.maxTokens } : {}
2006
- });
2037
+ }, clientInteractionMode);
2007
2038
  const ownedRecord = (agent) => {
2008
2039
  const record = sessions.get(String(agent.session.id));
2009
2040
  return record?.agent === agent ? record : void 0;
@@ -2477,12 +2508,20 @@ async function apply3(ctx, config = {}) {
2477
2508
  const publishCommands = (sessionId, record) => {
2478
2509
  const target = record ?? sessions.get(sessionId);
2479
2510
  if (target === void 0) return;
2480
- void availableCommandsFor(target).then((availableCommands) => {
2511
+ void commandPublications.run(sessionId, () => availableCommandsFor(target), (availableCommands) => {
2512
+ if (sessions.get(sessionId) !== target) return;
2513
+ logDebug(
2514
+ `available commands ${sessionId}: ${availableCommands.map((command) => command.name).join(", ")}`
2515
+ );
2481
2516
  notify(sessionId, { sessionUpdate: "available_commands_update", availableCommands });
2482
2517
  }).catch((error) => {
2483
2518
  logWarn(`publishing commands failed: ${String(error)}`);
2484
2519
  });
2485
2520
  };
2521
+ ctx.on("commands/change", () => {
2522
+ logDebug(`commands/change: ${sessions.size} live ACP session(s)`);
2523
+ for (const [sessionId, record] of sessions) publishCommands(sessionId, record);
2524
+ });
2486
2525
  const publishConfigOptions = (sessionId, record) => {
2487
2526
  void configOptions(record).then((options) => {
2488
2527
  notify(sessionId, {
@@ -2695,6 +2734,7 @@ async function apply3(ctx, config = {}) {
2695
2734
  const capsMeta = params.clientCapabilities?.["_meta"];
2696
2735
  clientTerminalOutput = capsMeta !== null && typeof capsMeta === "object" ? capsMeta["terminal_output"] === true : false;
2697
2736
  clientSubagentTranscript = capsMeta !== null && typeof capsMeta === "object" ? capsMeta["subagent-transcript"] === true : false;
2737
+ clientInteractionMode = interactionModeFromClientMeta(capsMeta);
2698
2738
  clientElicitationForm = params.clientCapabilities?.elicitation?.form !== void 0 && params.clientCapabilities.elicitation.form !== null;
2699
2739
  tuiClient.advertised = advertisesCordis(capsMeta);
2700
2740
  const providers = await listProviderRoutes();
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.4.14",
6
+ "version": "0.4.15",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/openma-ai/deepseek-harness-acp.git"
@@ -110,7 +110,8 @@
110
110
  "pnpm": "11.22.0",
111
111
  "tsx": "^4.23.1",
112
112
  "typescript": "^7.0.2",
113
- "vitest": "^4.1.10"
113
+ "vitest": "^4.1.10",
114
+ "yaml": "^2.9.0"
114
115
  },
115
116
  "dependencies": {
116
117
  "@agentclientprotocol/sdk": "^1.3.0",