@monotykamary/dsh-acp 0.1.0-rc.10

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/acp/acp/README.md
5
+ README.md: 7da6bfee41fec98329753d7b6eb6825f66499c14
6
+ README.zh.md: 64026389d67351b2ad444306d9dc5008c2e5543a
package/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # @monotykamary/dsh-acp
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ Automation-only [Agent Client Protocol](https://agentclientprotocol.com) server over JSON-RPC stdio. Programmatic clients create fresh harness agents, send text/image prompts, collect committed assistant text/images, resolve one-shot permission requests by policy, and cancel work. The primary in-repository client is [`dsh-subagent-acp`](../../subagent/subagent-acp/README.md).
6
+
7
+ This package is a transport adapter, not a UI integration or a capability seam. It does not expose editor navigation, transcript replay, commands, modes, configuration pickers, elicitation, reasoning, plans, titles, or tool presentation. Interactive rendering and human questions belong to the Web host and client modules.
8
+
9
+ ## Plugin
10
+
11
+ `apply(ctx, config)` opens an `AgentSideConnection` on stdin/stdout and drives `ctx.agents`. Stdout is reserved for protocol frames.
12
+
13
+ | Config | Default | Meaning |
14
+ |---|---|---|
15
+ | `provider` | — | Initial provider route for every created agent. |
16
+ | `model` | — | Initial model for every created agent. |
17
+
18
+ Both fields are optional so another agent/request listener may supply the target. The runnable ACP composition requires both.
19
+
20
+ ## Protocol contract
21
+
22
+ | Method | Behavior |
23
+ |---|---|
24
+ | `initialize` | Negotiates the supported version. Image prompts are advertised only when a durable attachment store is mounted and the configured exact provider/model resolves with explicit image input; audio and embedded context stay false. No session, editor, terminal, filesystem, or MCP capability is advertised. |
25
+ | `authenticate` | No-op because the server advertises no authentication methods. |
26
+ | `session/new` | Creates a fresh agent with an absolute primary `cwd`; empty `additionalDirectories` and `mcpServers` are accepted, non-empty values reject. |
27
+ | `session/prompt` | Preserves ordered text and supported inline image blocks, renders resource links as bracketed textual references, and rejects audio, embedded resources, malformed/empty input, or an image when capability was not advertised. It validates the whole image batch and rechecks the session's latest exact route before any save, commits every image before the user event, permits one in-flight request per session, and waits for admission plus, once queued, whole-Agent idle and ordered output delivery. Normal quiescence reports `end_turn`; explicit ACP cancellation, disposal, or a prompt whose admission was discarded (a turnless slot) reports `cancelled`. |
28
+ | `session/cancel` | Marks and aborts any in-progress admission without cancelling or waiting for unrelated Agent work; once this prompt has entered the Agent inbox, it cancels the addressed Agent and waits for the owned interval to quiesce. No late user message is published and the prompt settles as `cancelled`. With no in-flight prompt it cancels autonomous work; unknown ids are no-ops. |
29
+ | `session/update` | Emits one `agent_message_chunk` per non-empty text or image block in a committed `assistant/message`, preserving order. Images are re-read and integrity-verified before inline base64 delivery. Raw deltas and non-message events are omitted. |
30
+ | `session/request_permission` | Offers one-shot allow/reject choices for bridge-owned approval requests carrying a tool call id. Clients may answer automatically. |
31
+
32
+ One connection may own several sessions. The bridge keys records by branded session id and checks exact agent identity before routing events or permission requests. Each session has an independent prompt slot, workspace, cancellation path, and disposer.
33
+
34
+ Committed-message output intentionally trades token-by-token latency for a clean automation result. Uncommitted provider chunks and retry attempts cannot leak partial text or images; reasoning and tool activity remain in the session log for observability through other interfaces. Per-session delivery is serialized because attachment reads are asynchronous, and a missing or corrupt committed image fails the prompt response instead of emitting a placeholder.
35
+
36
+ ## Lifecycle
37
+
38
+ Client disconnect and Cordis disposal share one memoized teardown. The bridge first rejects new sessions and prompts, cancels and quiesces prompt admission, agent activity, and ordered output delivery, then drains continuable descendants only below this connection's exact owned Agents before disposing those handles in parallel and awaiting every result before reporting any failure. Other frontends sharing the Context retain their continuable forests and admission. An ACP-only plugin reload therefore leaves no orphan agent.
39
+
40
+ ACP requires each prompt response to carry a `stopReason`, but the bridge does not claim a prompt-specific turn outcome. The operation interval starts when the prompt enters the Agent inbox and ends after admission, whole-Agent idle, and ordered output delivery all quiesce; failures from unrelated Agent work before that inbox receipt are not attributed to the prompt. Committed assistant messages stream across the owned interval, and steering or injected work may contribute before idle. Settlement precedence is explicit cancellation, output-delivery failure, interval-wide Agent failure, then the correlated turn ending. Token-limit endings settle as `end_turn`; a correlated model error rejects only at the same quiescence boundary.
41
+
42
+ ## Running
43
+
44
+ `pnpm --dir /path/to/deepseek-harness run demo:acp` boots the repository's automation server composition. A parent harness can spawn it through [`@monotykamary/dsh-subagent-acp`](../../subagent/subagent-acp/README.md); other ACP clients need only the core methods above.
45
+
46
+ ## Model Experience
47
+
48
+ ### Prompt text and images
49
+
50
+ #### What the model sees
51
+
52
+ `session/prompt` preserves text/image order in one user message; adjacent text is concatenated, and a resource link appears as a bracketed `[resource_link name=… uri=…]` reference the model may open with its own tools. Inline image base64 is discarded after batch admission, so the durable message contains only verified attachment references. Protocol metadata, client capabilities, permission choices, and session ids never enter the model request.
53
+
54
+ #### Token effect
55
+
56
+ Prompt tokens and image charges are data-dependent and remain in that session's history until compaction. Concurrent ACP sessions retain independent contexts.
57
+
58
+ #### KV Cache effect
59
+
60
+ Append-only; the new user message follows the reusable request prefix and does not invalidate prior cache entries.
61
+
62
+ ### Permission decisions
63
+
64
+ #### What the model sees
65
+
66
+ Nothing directly. The owning tool records its allowed, rejected, cancelled, or unavailable outcome through the normal tool-result path.
67
+
68
+ #### Token effect
69
+
70
+ Only the owning tool result contributes tokens.
71
+
72
+ #### KV Cache effect
73
+
74
+ Append-only through the owning tool result.
75
+
76
+ ## Known Limitations and Deferred Work
77
+
78
+ - **Fresh sessions only** — load, list, resume, delete, and fork are unsupported.
79
+ - **Raster images and one workspace only** — image prompts require a durable store plus an exact route that declares image input; only PNG, JPEG, WebP, and GIF are accepted. Audio, embedded resources, non-empty additional directories, and MCP servers reject; resource links flatten to textual references rather than fetched content.
80
+ - **Committed answers only** — live progress, reasoning, tool activity, plans, titles, and usage stay off the wire.
81
+ - **Connection-owned lifetime** — one connection releases all of its sessions; per-session close is not implemented.
package/README.zh.md ADDED
@@ -0,0 +1,81 @@
1
+ # @monotykamary/dsh-acp
2
+
3
+ [English](README.md) | 中文
4
+
5
+ 通过 JSON-RPC stdio 提供的仅面向自动化的 [ACP(Agent Client Protocol)](https://agentclientprotocol.com) 服务器。程序化客户端可以创建新 harness agent(智能体)、发送文本/图片提示词、收集已提交的 assistant 文本/图片、按策略响应一次性权限请求并取消工作。仓库中的主要客户端是 [`dsh-subagent-acp`](../../subagent/subagent-acp/README.md)。
6
+
7
+ 此包是传输适配器,而非 UI 集成或能力 seam。它不公开编辑器导航、transcript(文本记录)回放、命令、模式、配置选择器、信息征集、推理(reasoning)、计划、标题或工具展示。交互式渲染与向用户提问属于 Web 宿主和客户端模块。
8
+
9
+ ## 插件
10
+
11
+ `apply(ctx, config)` 在 stdin/stdout 上打开 `AgentSideConnection` 并驱动 `ctx.agents`。Stdout 专用于协议帧。
12
+
13
+ | 配置 | 默认值 | 含义 |
14
+ |---|---|---|
15
+ | `provider` | 无 | 每个已创建 agent 的初始提供方路由。 |
16
+ | `model` | 无 | 每个已创建 agent 的初始模型。 |
17
+
18
+ 两个字段都是可选的,以便由另一个 agent/request 监听器提供目标。可运行的 ACP 组合同时要求两者。
19
+
20
+ ## 协议约定
21
+
22
+ | 方法 | 行为 |
23
+ |---|---|
24
+ | `initialize` | 协商受支持的版本。只有挂载持久附件存储,且配置的确切提供方/模型解析后明确支持图片输入时,才公布图片提示词能力;音频与嵌入上下文保持 false。不公布会话、编辑器、终端、文件系统或 MCP 能力。 |
25
+ | `authenticate` | 空操作,因为服务器不公布身份验证方法。 |
26
+ | `session/new` | 以绝对路径作为主 `cwd` 创建新 agent;接受空的 `additionalDirectories` 和 `mcpServers`,拒绝非空值。 |
27
+ | `session/prompt` | 保留文本与受支持内联图片块的顺序,将资源链接渲染为带方括号的文本引用,并拒绝音频、嵌入资源、格式错误/空输入,或在未公布能力时提交图片。它会先校验完整图片批次并重新检查会话的最新确切路由,再保存任一成员;在用户事件前提交全部图片;每个会话只允许一个正在处理的请求,并等待准入,以及消息入队后的整个 Agent 空闲和有序输出交付全部停稳。正常完全停稳时报告 `end_turn`;显式 ACP 取消、资源释放,或准入被丢弃的提示词(无轮次槽位)时报告 `cancelled`。 |
28
+ | `session/cancel` | 标记并中止正在进行的准入,但不会取消或等待同一 Agent 上无关的既有工作;该提示词进入 Agent inbox 后,才会取消指定的 Agent 并等待自有区间停稳。不发布迟到的用户消息,提示词以 `cancelled` 结算。没有进行中的提示词时会取消自主工作;未知 id 为空操作。 |
29
+ | `session/update` | 为已提交 `assistant/message` 中的每个非空文本或图片块发出一个 `agent_message_chunk`,并保留顺序。图片在以内联 base64 交付前会重新读取并校验完整性。省略原始增量和非消息事件。 |
30
+ | `session/request_permission` | 为携带工具调用 id、由桥接层拥有的批准请求提供一次性允许/拒绝选项。客户端可以自动回答。 |
31
+
32
+ 一个连接可以拥有多个会话。桥接层以带品牌的会话 id 作为记录键,并在路由事件或权限请求前检查 agent 是否为同一对象。每个会话都有独立的提示词槽位、工作区、取消路径和资源释放器。
33
+
34
+ 已提交消息输出有意牺牲逐 token 输出的低延迟,以换取干净的自动化结果。未提交的提供方分片和重试尝试无法泄漏部分文本或图片;推理与工具活动仍保留在会话日志中,以便其他界面观测。由于附件读取是异步的,每个会话会串行交付内容;已提交图片缺失或损坏时,提示词响应会失败,而不会发出占位符。
35
+
36
+ ## 生命周期
37
+
38
+ 客户端断开与 Cordis 释放共用同一个记忆化清理流程。桥接层先拒绝新会话和提示词,取消并等待提示词准入、agent 活动和有序输出交付全部停稳,然后只 drain 此连接确切拥有的 Agent 之下的可继续后代,再并行释放这些 handle,并等待全部结果结算后才报告失败。其他共享该上下文的前端会保留其可继续森林和准入。因此,仅 ACP 的插件重载不会遗留 agent。
39
+
40
+ ACP 要求每个提示词响应都携带 `stopReason`,但桥接层不声称它表示提示词专属的轮次结果。操作区间从提示词进入 Agent inbox 开始,在准入、整个 Agent 空闲和有序输出交付全部停稳后结束;inbox 接收前无关 Agent 工作的失败不会归因给该提示词。已提交的 assistant 消息会在自有区间内流式输出,Agent 进入空闲状态前发生的 steering(中途引导)或注入工作也可能参与其中。结算优先级依次为显式取消、输出交付失败、区间内 Agent 失败、关联轮次结束。因 token 上限而结束时以 `end_turn` 结算;关联模型错误也只会在同一个完全停稳边界拒绝提示词。
41
+
42
+ ## 运行
43
+
44
+ `pnpm --dir /path/to/deepseek-harness run demo:acp` 启动仓库的自动化服务器组合。父 harness 可以通过 [`@monotykamary/dsh-subagent-acp`](../../subagent/subagent-acp/README.md) spawn 它;其他 ACP 客户端只需上述核心方法。
45
+
46
+ ## 模型体验
47
+
48
+ ### 提示词文本与图片
49
+
50
+ #### 模型看到的内容
51
+
52
+ `session/prompt` 会在一条用户消息中保留文本/图片顺序;相邻文本会拼接,资源链接则表示为带方括号的 `[resource_link name=… uri=…]` 引用,模型可以使用自身工具打开它。内联图片 base64 在批量准入后即被丢弃,因此持久消息只包含经过校验的附件引用。协议元数据、客户端能力、权限选择和会话 id 绝不进入模型请求。
53
+
54
+ #### Token 影响
55
+
56
+ 提示词 token 与图片费用取决于数据,并保留在该会话的历史中直到上下文压缩(context compaction)。并发 ACP 会话保留独立上下文。
57
+
58
+ #### KV Cache 影响
59
+
60
+ 仅追加;新用户消息位于可复用请求前缀之后,不会使先前缓存条目失效。
61
+
62
+ ### 权限决策
63
+
64
+ #### 模型看到的内容
65
+
66
+ 不会直接看到任何内容。所属工具通过常规工具结果路径记录其结果:允许、拒绝、取消或不可用。
67
+
68
+ #### Token 影响
69
+
70
+ 只有所属工具的结果会贡献 token。
71
+
72
+ #### KV Cache 影响
73
+
74
+ 仅通过所属工具的结果追加。
75
+
76
+ ## 已知限制与暂缓事项
77
+
78
+ - **仅新会话**:不支持加载、列出、恢复、删除和 fork。
79
+ - **仅光栅图片和一个 workspace**:图片提示词要求持久存储以及明确声明支持图片输入的确切路由;只接受 PNG、JPEG、WebP 和 GIF。音频、嵌入资源、非空附加目录和 MCP 服务器都会被拒绝;资源链接只会展平为文本引用,不会获取其内容。
80
+ - **仅已提交答案**:实时进度、推理、工具活动、计划、标题和用量不会通过协议传输。
81
+ - **由连接管理的生命周期**:一个连接会释放其所有会话;尚未实现单个会话关闭功能。
package/lib/index.js ADDED
@@ -0,0 +1,566 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { isAbsolute } from "node:path";
3
+ import { Readable, Writable } from "node:stream";
4
+ import Schema from "@monotykamary/schemastery";
5
+ import { createUserMessage, errorChain } from "@monotykamary/dsh-llm";
6
+ import { AgentSideConnection, PROTOCOL_VERSION, RequestError, ndJsonStream } from "@agentclientprotocol/sdk";
7
+ import { SessionId } from "@monotykamary/dsh-session";
8
+ import { isImageAdmissionError } from "@monotykamary/dsh-attachment";
9
+ //#region lib/types/content.js
10
+ /** ACP wire-content admission and projection owned by the ACP adapter. @module */
11
+ /** Raster formats shared by ACP image blocks and the core attachment vocabulary. */
12
+ const IMAGE_MEDIA_TYPES = [
13
+ "image/png",
14
+ "image/jpeg",
15
+ "image/webp",
16
+ "image/gif"
17
+ ];
18
+ /** Canonical RFC 4648 base64, excluding whitespace and URL-safe aliases. */
19
+ const CANONICAL_BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
20
+ /** Error with a stable ACP request-failure category and no raw binary payload. */
21
+ var AcpContentError = class extends Error {
22
+ /** Whether the bridge should report invalid params or an internal failure. */
23
+ kind;
24
+ /**
25
+ * @param message - safe protocol-facing detail without inline binary data.
26
+ * @param kind - request-failure category.
27
+ * @param options - optional causal chain for diagnostics.
28
+ */
29
+ constructor(message, kind, options) {
30
+ super(message, options);
31
+ this.name = "AcpContentError";
32
+ this.kind = kind;
33
+ }
34
+ };
35
+ /** Narrow a wire MIME string to the durable raster vocabulary. */
36
+ function imageMediaType(value) {
37
+ return IMAGE_MEDIA_TYPES.includes(value) ? value : void 0;
38
+ }
39
+ /** Strictly decode one ACP inline image without accepting base64 aliases. */
40
+ function decodeImage(block) {
41
+ const mediaType = imageMediaType(block.mimeType);
42
+ if (mediaType === void 0) throw new AcpContentError("image mimeType must be image/png, image/jpeg, image/webp, or image/gif", "invalid");
43
+ if (!CANONICAL_BASE64.test(block.data)) throw new AcpContentError("image data must be canonical base64", "invalid");
44
+ const data = Buffer.from(block.data, "base64");
45
+ if (data.toString("base64") !== block.data) throw new AcpContentError("image data must be canonical base64", "invalid");
46
+ return {
47
+ data,
48
+ mediaType
49
+ };
50
+ }
51
+ /** Resolve the exact current route and require explicit image input support. */
52
+ async function assertImageRoute(ctx, agent, signal) {
53
+ const routed = agent.session.requestHeader()?.config;
54
+ const provider = routed?.provider ?? agent.options.provider;
55
+ const model = routed?.model ?? agent.options.model;
56
+ const llm = ctx.get("llm");
57
+ if (provider === void 0 || model === void 0 || llm === void 0) throw new AcpContentError("the current model route could not be resolved for image input", "invalid");
58
+ let info;
59
+ try {
60
+ info = await llm.resolveModelInfo(provider, model, signal);
61
+ } catch (error) {
62
+ throw new AcpContentError("the current model route could not be verified for image input", "internal", { cause: error });
63
+ }
64
+ if (info.inputModalities === void 0 || !info.inputModalities.includes("image")) throw new AcpContentError(`model "${model}" does not declare image input`, "invalid");
65
+ }
66
+ /**
67
+ * Determine whether initialization may truthfully advertise inline image prompts.
68
+ * Unknown service, route, capability, or deployment media support is negative.
69
+ * @param ctx - bridge context carrying optional attachment and model services.
70
+ * @param provider - configured provider route used for newly created sessions.
71
+ * @param model - configured exact model id used for newly created sessions.
72
+ * @returns whether this bridge can admit images at initialization time.
73
+ */
74
+ async function supportsAcpImagePrompts(ctx, provider, model) {
75
+ const attachments = ctx.get("attachments");
76
+ const llm = ctx.get("llm");
77
+ if (attachments === void 0 || llm === void 0 || provider === void 0 || model === void 0) return false;
78
+ if (!attachments.imageLimits.mediaTypes.some((mediaType) => IMAGE_MEDIA_TYPES.includes(mediaType))) return false;
79
+ try {
80
+ return (await llm.resolveModelInfo(provider, model)).inputModalities?.includes("image") === true;
81
+ } catch {
82
+ return false;
83
+ }
84
+ }
85
+ /** Render one baseline resource link into the core's current text vocabulary. */
86
+ function resourceLinkText(block) {
87
+ return `\n[resource_link name=${JSON.stringify(block.name)} uri=${JSON.stringify(block.uri)}]\n`;
88
+ }
89
+ /**
90
+ * Admit one ACP prompt into ordered durable core content.
91
+ * Every wire block and image is validated before the ordered image batch starts
92
+ * writing; cancellation after a successful content-addressed write may leave an
93
+ * unreachable object but never queues a late user message.
94
+ * @param ctx - bridge context carrying attachment and model services.
95
+ * @param agent - destination agent whose latest exact route controls admission.
96
+ * @param prompt - untrusted ACP prompt blocks in wire order.
97
+ * @param imageEnabled - capability result advertised during initialization.
98
+ * @param signal - admission cancellation signal.
99
+ * @returns core content with durable image references in wire order.
100
+ */
101
+ async function admitAcpPrompt(ctx, agent, prompt, imageEnabled, signal) {
102
+ const images = [];
103
+ for (const block of prompt) switch (block.type) {
104
+ case "text":
105
+ case "resource_link": break;
106
+ case "image":
107
+ if (!imageEnabled) throw new AcpContentError("inline image prompts were not advertised by this connection", "invalid");
108
+ images.push(decodeImage(block));
109
+ break;
110
+ case "audio": throw new AcpContentError("audio prompt content is not supported", "invalid");
111
+ case "resource": throw new AcpContentError("embedded resource prompt content is not supported", "invalid");
112
+ /* v8 ignore next 2 -- ACP ContentBlock is a closed generated union. */
113
+ default: throw new AcpContentError("unsupported ACP prompt content", "invalid");
114
+ }
115
+ let refs = [];
116
+ if (images.length > 0) {
117
+ const attachments = ctx.get("attachments");
118
+ if (attachments === void 0) throw new AcpContentError("no attachment store is mounted", "invalid");
119
+ await assertImageRoute(ctx, agent, signal);
120
+ signal.throwIfAborted();
121
+ try {
122
+ refs = await attachments.saveImages(images);
123
+ } catch (error) {
124
+ if (isImageAdmissionError(error)) throw new AcpContentError(error.message, "invalid", { cause: error });
125
+ throw new AcpContentError("unable to persist the prompt image batch", "internal", { cause: error });
126
+ }
127
+ signal.throwIfAborted();
128
+ }
129
+ const content = [];
130
+ let pendingText = "";
131
+ let imageIndex = 0;
132
+ const flushText = () => {
133
+ if (pendingText.length === 0) return;
134
+ content.push({
135
+ type: "text",
136
+ text: pendingText
137
+ });
138
+ pendingText = "";
139
+ };
140
+ for (const block of prompt) switch (block.type) {
141
+ case "text":
142
+ pendingText += block.text;
143
+ break;
144
+ case "resource_link":
145
+ pendingText += resourceLinkText(block);
146
+ break;
147
+ case "image": {
148
+ flushText();
149
+ const ref = refs[imageIndex++];
150
+ content.push({
151
+ type: "image",
152
+ attachment: ref
153
+ });
154
+ break;
155
+ }
156
+ /* v8 ignore start -- the validation pass above rejects both tags before reconstruction. */
157
+ case "audio":
158
+ case "resource": break;
159
+ /* v8 ignore stop */
160
+ /* v8 ignore next 2 -- validated by the first closed-union switch. */
161
+ default: break;
162
+ }
163
+ flushText();
164
+ if (!content.some((block) => block.type === "image" || block.type === "text" && block.text.trim().length > 0)) throw new AcpContentError("empty prompt", "invalid");
165
+ return content;
166
+ }
167
+ /**
168
+ * Translate one committed assistant block to ACP wire content.
169
+ * Images are re-read and integrity-verified before inline base64 delivery;
170
+ * unsupported core output blocks stay off the automation wire.
171
+ * @param ctx - bridge context carrying the authoritative attachment store.
172
+ * @param block - committed core assistant block.
173
+ * @returns ACP text/image content, or undefined for non-output blocks.
174
+ */
175
+ async function assistantBlockToAcp(ctx, block) {
176
+ if (block.type === "text") return block.text.length === 0 ? void 0 : {
177
+ type: "text",
178
+ text: block.text
179
+ };
180
+ if (block.type !== "image") return void 0;
181
+ const attachments = ctx.get("attachments");
182
+ if (attachments === void 0) throw new AcpContentError("cannot deliver assistant image: no attachment store is mounted", "internal");
183
+ let stored;
184
+ try {
185
+ stored = await attachments.readImage(block.attachment);
186
+ } catch (error) {
187
+ throw new AcpContentError("cannot deliver assistant image: the attachment is unavailable or corrupt", "internal", { cause: error });
188
+ }
189
+ return {
190
+ type: "image",
191
+ data: Buffer.from(stored.data).toString("base64"),
192
+ mimeType: stored.ref.mediaType
193
+ };
194
+ }
195
+ //#endregion
196
+ //#region lib/types/codec.js
197
+ /**
198
+ * Pure translation between the harness lifecycle and the automation-only ACP wire.
199
+ * @module @monotykamary/dsh-acp/codec
200
+ */
201
+ /**
202
+ * Map a harness turn ending to ACP's terminal reason vocabulary.
203
+ * @param reason - harness turn outcome.
204
+ * @returns the closest legal ACP stop reason.
205
+ */
206
+ function turnEndToStopReason(reason) {
207
+ switch (reason.kind) {
208
+ case "completed": return "end_turn";
209
+ case "max-tokens": return "max_tokens";
210
+ case "aborted": return "end_turn";
211
+ case "interrupted": return "cancelled";
212
+ case "blocked":
213
+ case "error": return "end_turn";
214
+ /* v8 ignore next 2 -- TurnEndReason is closed and every member is handled above */
215
+ default: return "end_turn";
216
+ }
217
+ }
218
+ //#endregion
219
+ //#region lib/types/index.js
220
+ /**
221
+ * Automation-only Agent Client Protocol server over JSON-RPC stdio.
222
+ *
223
+ * The bridge exposes fresh harness sessions to trusted programmatic clients. It
224
+ * carries prompt text/images, committed assistant text/images, cancellation,
225
+ * and one-shot permission decisions; presentation and human-interaction
226
+ * features stay with the harness's UI modules.
227
+ *
228
+ * @module @monotykamary/dsh-acp
229
+ */
230
+ const name = "acp";
231
+ /** The bridge creates and owns agents; every other concern is carried by the agent composition. */
232
+ const inject = ["agents"];
233
+ /** Preserve invalid-parameter detail in the SDK wire error message. */
234
+ function invalidParams(detail) {
235
+ return RequestError.invalidParams(void 0, detail);
236
+ }
237
+ /** Preserve failed-turn detail; plain handler errors become a generic wire internal error. */
238
+ function internalError(detail) {
239
+ return RequestError.internalError(void 0, detail);
240
+ }
241
+ const Config = Schema.object({
242
+ provider: Schema.string(),
243
+ model: Schema.string()
244
+ });
245
+ /**
246
+ * Mount the automation-only ACP server.
247
+ * @param ctx - Cordis context carrying the agent factory and session events.
248
+ * @param config - Initial provider/model selection and optional test transport.
249
+ */
250
+ function apply(ctx, config) {
251
+ const agents = ctx.agents;
252
+ const logger = ctx.logger;
253
+ const sessions = /* @__PURE__ */ new Map();
254
+ let closed = false;
255
+ let conn;
256
+ let imagePromptEnabled = false;
257
+ /** Return the bridge-owned record for an agent, rejecting same-id impostors. */
258
+ const ownedRecord = (agent) => {
259
+ const record = sessions.get(agent.session.id);
260
+ return record?.agent === agent ? record : void 0;
261
+ };
262
+ const assertOpen = () => {
263
+ if (closed) throw internalError("the ACP bridge has been disposed");
264
+ };
265
+ const requireSession = (sessionId) => {
266
+ const record = sessions.get(sessionId);
267
+ if (record === void 0) throw invalidParams(`unknown session: ${sessionId}`);
268
+ return record;
269
+ };
270
+ /** Send one ordered protocol update while containing transport-only failure. */
271
+ const notify = async (notification) => {
272
+ try {
273
+ await conn.sessionUpdate(notification);
274
+ } catch (error) {
275
+ logger.warn(`acp: session/update failed: ${String(error)}`);
276
+ }
277
+ /* v8 ignore stop */
278
+ };
279
+ const rejectFromError = (inflight, reason) => {
280
+ inflight.reject(internalError(`turn failed: ${reason.error.message}`));
281
+ };
282
+ /**
283
+ * Settle one exact prompt only after admission, agent activity, and ordered
284
+ * assistant delivery have all reached quiescence.
285
+ */
286
+ const settleAfterQuiescence = (record, inflight) => {
287
+ if (inflight.settlementStarted) return;
288
+ inflight.settlementStarted = true;
289
+ (async () => {
290
+ await inflight.admissionDone;
291
+ if (inflight.messageQueued) {
292
+ await record.agent.whenIdle();
293
+ await record.outputTail;
294
+ }
295
+ /* v8 ignore next -- this prompt owns the slot until this exact settlement clears it. */
296
+ if (record.inflight !== inflight) return;
297
+ record.inflight = void 0;
298
+ if (inflight.cancelRequested) {
299
+ inflight.resolve("cancelled");
300
+ return;
301
+ }
302
+ if (inflight.outputError !== void 0) {
303
+ inflight.reject(internalError(`assistant output delivery failed: ${inflight.outputError.message}`));
304
+ return;
305
+ }
306
+ if (inflight.agentError !== void 0) {
307
+ inflight.reject(internalError(`turn failed: ${inflight.agentError.message}`));
308
+ return;
309
+ }
310
+ const end = inflight.endReason;
311
+ if (end === void 0) inflight.resolve("cancelled");
312
+ else if (end.kind === "error") rejectFromError(inflight, end);
313
+ else inflight.resolve(end.kind === "max-tokens" ? "end_turn" : turnEndToStopReason(end));
314
+ })().catch((error) => {
315
+ if (record.inflight !== inflight) return;
316
+ record.inflight = void 0;
317
+ inflight.reject(internalError(`prompt settlement failed: ${errorChain(error)}`));
318
+ });
319
+ /* v8 ignore stop */
320
+ };
321
+ ctx.on("session/event", (session, event) => {
322
+ const record = sessions.get(session.header.id);
323
+ if (record === void 0 || record.agent.session !== session) return;
324
+ try {
325
+ if (event.type === "assistant/message") {
326
+ const inflight = record.inflight?.turn === event.data.turn ? record.inflight : void 0;
327
+ record.outputTail = record.outputTail.then(async () => {
328
+ for (const block of event.data.message.content) {
329
+ const content = await assistantBlockToAcp(ctx, block);
330
+ if (content === void 0) continue;
331
+ await notify({
332
+ sessionId: record.agent.session.id,
333
+ update: {
334
+ sessionUpdate: "agent_message_chunk",
335
+ content
336
+ }
337
+ });
338
+ }
339
+ }).catch((error) => {
340
+ const failure = error;
341
+ if (inflight !== void 0) inflight.outputError ??= failure;
342
+ logger.warn(`acp: assistant output conversion failed: ${errorChain(error)}`);
343
+ });
344
+ }
345
+ } finally {
346
+ const inflight = record.inflight;
347
+ if (inflight !== void 0 && event.type === "turn/end" && inflight.turn === event.data.turn) inflight.endReason = event.data.reason;
348
+ }
349
+ });
350
+ ctx.on("agent/inbox/claimed", ({ agent, message, turn }) => {
351
+ const inflight = ownedRecord(agent)?.inflight;
352
+ if (inflight !== void 0 && inflight.messageId === message.id) inflight.turn = turn;
353
+ });
354
+ ctx.on("agent/error", ({ agent, turn, error }) => {
355
+ const record = ownedRecord(agent);
356
+ const inflight = record?.inflight;
357
+ if (record === void 0 || inflight === void 0 || !inflight.messageQueued || inflight.turn === turn) return;
358
+ inflight.agentError = new Error(errorChain(error));
359
+ settleAfterQuiescence(record, inflight);
360
+ });
361
+ ctx.on("approval/request", (request, next) => {
362
+ const record = ownedRecord(request.agent);
363
+ if (record === void 0 || request.callId === void 0) return next();
364
+ return conn.requestPermission({
365
+ sessionId: record.agent.session.id,
366
+ toolCall: { toolCallId: request.callId },
367
+ options: [{
368
+ optionId: "allow-once",
369
+ name: "Allow once",
370
+ kind: "allow_once"
371
+ }, {
372
+ optionId: "reject-once",
373
+ name: "Reject",
374
+ kind: "reject_once"
375
+ }]
376
+ }).then(({ outcome }) => {
377
+ if (outcome.outcome === "cancelled") return "cancelled";
378
+ return outcome.optionId === "allow-once" ? "allowed-once" : "rejected";
379
+ });
380
+ });
381
+ const makeAgent = (connection) => {
382
+ conn = connection;
383
+ return {
384
+ async initialize(_params) {
385
+ imagePromptEnabled = await supportsAcpImagePrompts(ctx, config.provider, config.model);
386
+ return {
387
+ protocolVersion: PROTOCOL_VERSION,
388
+ agentInfo: {
389
+ name: "deepseek-harness-acp",
390
+ version: "0.0.1"
391
+ },
392
+ agentCapabilities: { promptCapabilities: {
393
+ image: imagePromptEnabled,
394
+ audio: false,
395
+ embeddedContext: false
396
+ } },
397
+ authMethods: []
398
+ };
399
+ },
400
+ authenticate(_params) {
401
+ return Promise.resolve();
402
+ },
403
+ async newSession(params) {
404
+ assertOpen();
405
+ validateSessionParams(params);
406
+ const sessionId = SessionId(randomUUID());
407
+ const handle = await agents.create({
408
+ sessionId,
409
+ meta: { cwd: params.cwd },
410
+ agentOptions: agentOptions(config)
411
+ });
412
+ /* v8 ignore next 4 -- a real stdio close can race an in-flight create. */
413
+ if (closed) {
414
+ await handle.dispose();
415
+ throw internalError("connection closed during session/new");
416
+ }
417
+ sessions.set(sessionId, {
418
+ agent: handle.agent,
419
+ dispose: () => handle.dispose(),
420
+ outputTail: Promise.resolve(),
421
+ inflight: void 0
422
+ });
423
+ return { sessionId };
424
+ },
425
+ async prompt(params) {
426
+ assertOpen();
427
+ const record = requireSession(SessionId(params.sessionId));
428
+ if (record.inflight !== void 0) throw invalidParams("a prompt is already in flight for this session");
429
+ const completion = Promise.withResolvers();
430
+ const admission = Promise.withResolvers();
431
+ const admissionController = new AbortController();
432
+ const inflight = {
433
+ resolve: completion.resolve,
434
+ reject: completion.reject,
435
+ messageId: void 0,
436
+ messageQueued: false,
437
+ turn: void 0,
438
+ endReason: void 0,
439
+ admissionDone: admission.promise,
440
+ finishAdmission: admission.resolve,
441
+ admissionController,
442
+ cancelRequested: false,
443
+ settlementStarted: false,
444
+ outputError: void 0,
445
+ agentError: void 0
446
+ };
447
+ record.inflight = inflight;
448
+ let admissionFailed = false;
449
+ let admissionFailure;
450
+ try {
451
+ if (ctx.agents.get(record.agent.id) !== record.agent) throw internalError("prompt was not queued: the agent was disposed outside the bridge");
452
+ const content = await admitAcpPrompt(ctx, record.agent, params.prompt, imagePromptEnabled, admissionController.signal);
453
+ admissionController.signal.throwIfAborted();
454
+ if (ctx.agents.get(record.agent.id) !== record.agent) throw internalError("prompt was not queued: the agent was disposed outside the bridge");
455
+ const message = createUserMessage({
456
+ content,
457
+ source: { kind: "user" }
458
+ });
459
+ inflight.messageId = message.id;
460
+ inflight.messageQueued = true;
461
+ try {
462
+ record.agent.followup(message);
463
+ } catch (error) {
464
+ inflight.messageQueued = false;
465
+ throw error;
466
+ }
467
+ } catch (error) {
468
+ admissionFailed = true;
469
+ admissionFailure = error;
470
+ } finally {
471
+ inflight.finishAdmission();
472
+ }
473
+ if (inflight.cancelRequested) {
474
+ settleAfterQuiescence(record, inflight);
475
+ return { stopReason: await completion.promise };
476
+ }
477
+ if (admissionFailed) {
478
+ record.inflight = void 0;
479
+ if (admissionFailure instanceof AcpContentError) throw admissionFailure.kind === "invalid" ? invalidParams(admissionFailure.message) : internalError(admissionFailure.message);
480
+ if (admissionFailure instanceof RequestError) throw admissionFailure;
481
+ const detail = admissionFailure.message;
482
+ throw internalError(`prompt was not queued: ${detail}`);
483
+ }
484
+ settleAfterQuiescence(record, inflight);
485
+ return { stopReason: await completion.promise };
486
+ },
487
+ cancel(params) {
488
+ const record = sessions.get(SessionId(params.sessionId));
489
+ if (record === void 0) return Promise.resolve();
490
+ const inflight = record.inflight;
491
+ if (inflight !== void 0) {
492
+ inflight.cancelRequested = true;
493
+ inflight.admissionController.abort(/* @__PURE__ */ new Error("ACP prompt cancelled"));
494
+ settleAfterQuiescence(record, inflight);
495
+ }
496
+ if (inflight === void 0 || inflight.messageQueued) record.agent.cancel({ kind: "user" });
497
+ return Promise.resolve();
498
+ }
499
+ };
500
+ };
501
+ conn = new AgentSideConnection(makeAgent, config.stream ?? ndJsonStream(Writable.toWeb(process.stdout), Readable.toWeb(process.stdin)));
502
+ let quiescing;
503
+ const quiesce = () => {
504
+ if (quiescing !== void 0) return quiescing;
505
+ closed = true;
506
+ const records = [...sessions.values()];
507
+ sessions.clear();
508
+ for (const record of records) {
509
+ const inflight = record.inflight;
510
+ if (inflight !== void 0) {
511
+ inflight.cancelRequested = true;
512
+ inflight.admissionController.abort(/* @__PURE__ */ new Error("ACP bridge disposed"));
513
+ settleAfterQuiescence(record, inflight);
514
+ }
515
+ record.agent.cancel({ kind: "user" });
516
+ }
517
+ quiescing = (async () => {
518
+ await Promise.all(records.map(async (record) => {
519
+ await record.inflight?.admissionDone;
520
+ await record.agent.whenIdle();
521
+ await record.outputTail;
522
+ }));
523
+ const subagents = ctx.get("subagents");
524
+ if (subagents !== void 0) try {
525
+ await subagents.drainContinuableDescendants(records.map((record) => record.agent));
526
+ } catch (error) {
527
+ logger.warn(`acp: continuable subagent teardown failed: ${String(error)}`);
528
+ }
529
+ const disposals = await Promise.allSettled(records.map((record) => record.dispose()));
530
+ const failures = [];
531
+ for (const result of disposals) if (result.status === "rejected") failures.push(result.reason);
532
+ if (failures.length > 0) {
533
+ const detail = failures.map((failure) => errorChain(failure)).join("; ");
534
+ throw new AggregateError(failures, `ACP agent teardown failed for ${failures.length} session(s): ${detail}`);
535
+ }
536
+ })();
537
+ return quiescing;
538
+ };
539
+ /* v8 ignore start -- production transport rejection and teardown failure. */
540
+ conn.closed.catch((error) => {
541
+ logger.warn(`acp: connection closed with an error: ${String(error)}`);
542
+ }).then(quiesce).catch((error) => {
543
+ logger.warn(`acp: connection-close teardown failed: ${String(error)}`);
544
+ });
545
+ /* v8 ignore stop */
546
+ ctx.effect(() => quiesce, "acp.connection");
547
+ }
548
+ /**
549
+ * Build per-agent options from plugin config without assigning absent optional fields.
550
+ * @param config - ACP provider/model configuration.
551
+ * @returns the configured fields only.
552
+ */
553
+ function agentOptions(config) {
554
+ return {
555
+ ...config.provider !== void 0 ? { provider: config.provider } : {},
556
+ ...config.model !== void 0 ? { model: config.model } : {}
557
+ };
558
+ }
559
+ /** Reject session features outside the automation contract. */
560
+ function validateSessionParams(params) {
561
+ if (!isAbsolute(params.cwd)) throw invalidParams(`cwd must be an absolute path: ${params.cwd}`);
562
+ if (params.additionalDirectories !== void 0 && params.additionalDirectories.length > 0) throw invalidParams("additionalDirectories is not supported");
563
+ if (params.mcpServers.length > 0) throw invalidParams("mcpServers is not supported");
564
+ }
565
+ //#endregion
566
+ export { Config, apply, inject, name };
@@ -0,0 +1,23 @@
1
+ //#region lib/types/invariant.js
2
+ /**
3
+ * Package-owned invariant companion for `@monotykamary/dsh-acp`.
4
+ * @module @monotykamary/dsh-acp/invariant
5
+ */
6
+ const PACKAGE_NAME = "@monotykamary/dsh-acp";
7
+ /** Cordis companion plugin name. */
8
+ const name = "acp-invariant";
9
+ /** Service required before the companion can reserve package ownership. */
10
+ const inject = ["invariants"];
11
+ /**
12
+ * No runtime invariant: this transport owns no durable package-local event stream;
13
+ * protocol and lifecycle tests cover its mapping.
14
+ */
15
+ const install = () => {};
16
+ /**
17
+ * Register this package's invariant companion.
18
+ * @param ctx - Cordis context carrying the invariant service.
19
+ * @returns the installed registration's disposer after setup succeeds.
20
+ */
21
+ const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
22
+ //#endregion
23
+ export { apply, inject, name };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Pure translation between the harness lifecycle and the automation-only ACP wire.
3
+ * @module @monotykamary/dsh-acp/codec
4
+ */
5
+ import type { StopReason } from '@agentclientprotocol/sdk';
6
+ import type { TurnEndReason } from '@monotykamary/dsh-session';
7
+ /**
8
+ * Map a harness turn ending to ACP's terminal reason vocabulary.
9
+ * @param reason - harness turn outcome.
10
+ * @returns the closest legal ACP stop reason.
11
+ */
12
+ export declare function turnEndToStopReason(reason: TurnEndReason): StopReason;
13
+ //# sourceMappingURL=codec.d.ts.map
@@ -0,0 +1,50 @@
1
+ /** ACP wire-content admission and projection owned by the ACP adapter. @module */
2
+ import type { ContentBlock as AcpContentBlock } from '@agentclientprotocol/sdk';
3
+ import type { Context } from '@monotykamary/cordis';
4
+ import type { Agent } from '@monotykamary/dsh-agent';
5
+ import type { ContentBlock } from '@monotykamary/dsh-llm';
6
+ /** Content-admission failure category used by the protocol handler. */
7
+ export type AcpContentFailureKind = 'invalid' | 'internal';
8
+ /** Error with a stable ACP request-failure category and no raw binary payload. */
9
+ export declare class AcpContentError extends Error {
10
+ /** Whether the bridge should report invalid params or an internal failure. */
11
+ readonly kind: AcpContentFailureKind;
12
+ /**
13
+ * @param message - safe protocol-facing detail without inline binary data.
14
+ * @param kind - request-failure category.
15
+ * @param options - optional causal chain for diagnostics.
16
+ */
17
+ constructor(message: string, kind: AcpContentFailureKind, options?: ErrorOptions);
18
+ }
19
+ /**
20
+ * Determine whether initialization may truthfully advertise inline image prompts.
21
+ * Unknown service, route, capability, or deployment media support is negative.
22
+ * @param ctx - bridge context carrying optional attachment and model services.
23
+ * @param provider - configured provider route used for newly created sessions.
24
+ * @param model - configured exact model id used for newly created sessions.
25
+ * @returns whether this bridge can admit images at initialization time.
26
+ */
27
+ export declare function supportsAcpImagePrompts(ctx: Context, provider: string | undefined, model: string | undefined): Promise<boolean>;
28
+ /**
29
+ * Admit one ACP prompt into ordered durable core content.
30
+ * Every wire block and image is validated before the ordered image batch starts
31
+ * writing; cancellation after a successful content-addressed write may leave an
32
+ * unreachable object but never queues a late user message.
33
+ * @param ctx - bridge context carrying attachment and model services.
34
+ * @param agent - destination agent whose latest exact route controls admission.
35
+ * @param prompt - untrusted ACP prompt blocks in wire order.
36
+ * @param imageEnabled - capability result advertised during initialization.
37
+ * @param signal - admission cancellation signal.
38
+ * @returns core content with durable image references in wire order.
39
+ */
40
+ export declare function admitAcpPrompt(ctx: Context, agent: Agent, prompt: readonly AcpContentBlock[], imageEnabled: boolean, signal: AbortSignal): Promise<ContentBlock[]>;
41
+ /**
42
+ * Translate one committed assistant block to ACP wire content.
43
+ * Images are re-read and integrity-verified before inline base64 delivery;
44
+ * unsupported core output blocks stay off the automation wire.
45
+ * @param ctx - bridge context carrying the authoritative attachment store.
46
+ * @param block - committed core assistant block.
47
+ * @returns ACP text/image content, or undefined for non-output blocks.
48
+ */
49
+ export declare function assistantBlockToAcp(ctx: Context, block: ContentBlock): Promise<AcpContentBlock | undefined>;
50
+ //# sourceMappingURL=content.d.ts.map
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Automation-only Agent Client Protocol server over JSON-RPC stdio.
3
+ *
4
+ * The bridge exposes fresh harness sessions to trusted programmatic clients. It
5
+ * carries prompt text/images, committed assistant text/images, cancellation,
6
+ * and one-shot permission decisions; presentation and human-interaction
7
+ * features stay with the harness's UI modules.
8
+ *
9
+ * @module @monotykamary/dsh-acp
10
+ */
11
+ import type { Context } from '@monotykamary/cordis';
12
+ import Schema from '@monotykamary/schemastery';
13
+ import { type Stream } from '@agentclientprotocol/sdk';
14
+ export declare const name = "acp";
15
+ /** The bridge creates and owns agents; every other concern is carried by the agent composition. */
16
+ export declare const inject: string[];
17
+ /** Plugin config: the provider/model selection used for each ACP-created agent. */
18
+ export interface AcpConfig {
19
+ /** Provider route for created agents. */
20
+ provider?: string;
21
+ /** Model name for created agents. */
22
+ model?: string;
23
+ /** Runtime-only transport override; production uses stdio. */
24
+ stream?: Stream;
25
+ }
26
+ export declare const Config: Schema<AcpConfig>;
27
+ /**
28
+ * Mount the automation-only ACP server.
29
+ * @param ctx - Cordis context carrying the agent factory and session events.
30
+ * @param config - Initial provider/model selection and optional test transport.
31
+ */
32
+ export declare function apply(ctx: Context, config: AcpConfig): void;
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Package-owned invariant companion for `@monotykamary/dsh-acp`.
3
+ * @module @monotykamary/dsh-acp/invariant
4
+ */
5
+ import type { Context } from '@monotykamary/cordis';
6
+ /** Cordis companion plugin name. */
7
+ export declare const name = "acp-invariant";
8
+ /** Service required before the companion can reserve package ownership. */
9
+ export declare const inject: string[];
10
+ /**
11
+ * Register this package's invariant companion.
12
+ * @param ctx - Cordis context carrying the invariant service.
13
+ * @returns the installed registration's disposer after setup succeeds.
14
+ */
15
+ export declare const apply: (ctx: Context) => Promise<() => void>;
16
+ //# sourceMappingURL=invariant.d.ts.map
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@monotykamary/dsh-acp",
3
+ "description": "Automation-only Agent Client Protocol server for driving DeepSeek Harness agents over JSON-RPC stdio",
4
+ "version": "0.1.0-rc.10",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
11
+ "directory": "packages/acp/acp"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./src/*": "./src/*",
26
+ "./package.json": "./package.json"
27
+ },
28
+ "files": [
29
+ "lib/index.js",
30
+ "lib/invariant.js",
31
+ "lib/types/**/*.d.ts"
32
+ ],
33
+ "license": "MIT",
34
+ "dependencies": {
35
+ "@agentclientprotocol/sdk": "0.25.1",
36
+ "@monotykamary/schemastery": "^3.18.1"
37
+ },
38
+ "peerDependencies": {
39
+ "@monotykamary/dsh-attachment": "^0.1.0-rc.10",
40
+ "@monotykamary/dsh-agent": "^0.1.0-rc.10",
41
+ "@monotykamary/dsh-invariants": "^0.1.0-rc.10",
42
+ "@monotykamary/dsh-llm": "^0.1.0-rc.10",
43
+ "@monotykamary/dsh-session": "^0.1.0-rc.10",
44
+ "@monotykamary/dsh-user-approval": "^0.1.0-rc.10",
45
+ "@monotykamary/cordis": "^4.0.1"
46
+ },
47
+ "devDependencies": {
48
+ "@monotykamary/dsh-agent": "^0.1.0-rc.10",
49
+ "@monotykamary/dsh-invariants": "^0.1.0-rc.10",
50
+ "@monotykamary/dsh-llm": "^0.1.0-rc.10",
51
+ "@monotykamary/dsh-session": "^0.1.0-rc.10",
52
+ "@monotykamary/dsh-tools": "^0.1.0-rc.10",
53
+ "@monotykamary/dsh-agent-loop-testkit": "^0.1.0-rc.10",
54
+ "@monotykamary/cordis": "^4.0.1",
55
+ "@monotykamary/dsh-user-approval": "^0.1.0-rc.10",
56
+ "@monotykamary/dsh-agent-loop": "^0.1.0-rc.10",
57
+ "@monotykamary/dsh-attachment": "^0.1.0-rc.10"
58
+ }
59
+ }