mosse-agent-bridge 0.1.4 → 0.1.5
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/acp/client.d.ts +47 -0
- package/dist/acp/client.js +94 -0
- package/dist/acp/client.js.map +1 -0
- package/dist/acp/index.d.ts +5 -0
- package/dist/acp/index.js +6 -0
- package/dist/acp/index.js.map +1 -0
- package/dist/acp/jsonrpc.d.ts +37 -0
- package/dist/acp/jsonrpc.js +120 -0
- package/dist/acp/jsonrpc.js.map +1 -0
- package/dist/acp/mock-server.d.ts +15 -0
- package/dist/acp/mock-server.js +100 -0
- package/dist/acp/mock-server.js.map +1 -0
- package/dist/acp/types.d.ts +138 -0
- package/dist/acp/types.js +20 -0
- package/dist/acp/types.js.map +1 -0
- package/dist/drivers/hermes-map.d.ts +34 -0
- package/dist/drivers/hermes-map.js +126 -0
- package/dist/drivers/hermes-map.js.map +1 -0
- package/dist/drivers/hermes.d.ts +66 -0
- package/dist/drivers/hermes.js +232 -0
- package/dist/drivers/hermes.js.map +1 -0
- package/dist/drivers/index.js +2 -0
- package/dist/drivers/index.js.map +1 -1
- package/dist/drivers/resolve.d.ts +3 -0
- package/dist/drivers/resolve.js +8 -0
- package/dist/drivers/resolve.js.map +1 -1
- package/dist/drivers/types.d.ts +13 -3
- package/dist/hermes-config.d.ts +33 -0
- package/dist/hermes-config.js +164 -0
- package/dist/hermes-config.js.map +1 -0
- package/dist/hermes-detect.d.ts +41 -0
- package/dist/hermes-detect.js +165 -0
- package/dist/hermes-detect.js.map +1 -0
- package/dist/index.js +84 -11
- package/dist/index.js.map +1 -1
- package/dist/memory.d.ts +9 -0
- package/dist/memory.js +12 -1
- package/dist/memory.js.map +1 -1
- package/package.json +20 -14
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Readable, Writable } from "node:stream";
|
|
2
|
+
import { type ContentBlock, type InitializeResult, type LoadSessionParams, type McpServer, type NewSessionResult, type PromptResult, type SessionUpdateNotification } from "./types.js";
|
|
3
|
+
/** initialize 阶段协议版本/capability 校验失败时抛出(受控,不静默继续)。见 §9.4。 */
|
|
4
|
+
export declare class AcpProtocolError extends Error {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
export type SessionUpdateHandler = (n: SessionUpdateNotification) => void;
|
|
8
|
+
export interface InitializeOptions {
|
|
9
|
+
/** 客户端期望的协议大版本(默认 ACP_PROTOCOL_VERSION=1)。agent 回的版本须 === 此值,否则受控报错。 */
|
|
10
|
+
expectedVersion?: number;
|
|
11
|
+
/** 上报给 agent 的 clientCapabilities(默认空对象)。 */
|
|
12
|
+
clientCapabilities?: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
export interface NewSessionArgs {
|
|
15
|
+
cwd: string;
|
|
16
|
+
mcpServers?: McpServer[];
|
|
17
|
+
}
|
|
18
|
+
export interface PromptArgs {
|
|
19
|
+
sessionId: string;
|
|
20
|
+
content: ContentBlock[];
|
|
21
|
+
}
|
|
22
|
+
export declare class AcpClient {
|
|
23
|
+
private readonly conn;
|
|
24
|
+
private readonly updateHandlers;
|
|
25
|
+
private initialized;
|
|
26
|
+
constructor(readable: Readable, writable: Writable);
|
|
27
|
+
/** 订阅服务端推送的 session/update(原样透传;F2.2 driver 负责映射成 CcEvent)。 */
|
|
28
|
+
onSessionUpdate(cb: SessionUpdateHandler): void;
|
|
29
|
+
/** initialize:协商协议版本 + 交换 capabilities。版本不匹配 → AcpProtocolError(§9.4)。 */
|
|
30
|
+
initialize(opts?: InitializeOptions): Promise<InitializeResult>;
|
|
31
|
+
/** session/new:开新会话,返回 sessionId。 */
|
|
32
|
+
newSession(args: NewSessionArgs): Promise<NewSessionResult>;
|
|
33
|
+
/** session/load:恢复已有会话(agent 先经 session/update 重放历史,再 resolve)。 */
|
|
34
|
+
loadSession(args: LoadSessionParams): Promise<void>;
|
|
35
|
+
/** session/load 的语义别名(Hermes 文档里有 resume_session;映射到官方 session/load)。 */
|
|
36
|
+
resumeSession(args: LoadSessionParams): Promise<void>;
|
|
37
|
+
/** session/prompt:发一轮 prompt content blocks,等回合结束的 PromptResponse{stopReason}。
|
|
38
|
+
* 回合中服务端经 onSessionUpdate 推流。 */
|
|
39
|
+
prompt(args: PromptArgs): Promise<PromptResult>;
|
|
40
|
+
/** session/set_model:覆盖该会话的模型(D6;Mosse agent 指定 model 时用,否则用 Hermes 配置默认)。
|
|
41
|
+
* Hermes acp_adapter 文档作 set_session_model;映射到 ACP 的 session/set_model(modelId)。 */
|
|
42
|
+
setSessionModel(sessionId: string, modelId: string): Promise<void>;
|
|
43
|
+
/** session/cancel:通知 agent 取消进行中的回合(无响应;进行中的 prompt 将回 stopReason:"cancelled")。 */
|
|
44
|
+
cancel(sessionId: string): void;
|
|
45
|
+
/** 是否已成功 initialize(供消费方防御性检查)。 */
|
|
46
|
+
get isInitialized(): boolean;
|
|
47
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// 最小 ACP 客户端 —— Hermes 适配 M2 F2.1。
|
|
2
|
+
// 通用(不含 Hermes 具体 spawn / 事件映射,那是 F2.2)。对一对 readable/writable 流工作,
|
|
3
|
+
// 将来接子进程 stdout/stdin。只封装我们要用的方法 + session/update 订阅。
|
|
4
|
+
//
|
|
5
|
+
// 方法集与 schema 来源见 ./types.ts 顶部注释(官方 ACP 规范 + Hermes acp_adapter)。
|
|
6
|
+
// 分帧 = newline-delimited JSON-RPC(见 ./jsonrpc.ts)。
|
|
7
|
+
import { JsonRpcConnection } from "./jsonrpc.js";
|
|
8
|
+
import { ACP_PROTOCOL_VERSION, } from "./types.js";
|
|
9
|
+
/** initialize 阶段协议版本/capability 校验失败时抛出(受控,不静默继续)。见 §9.4。 */
|
|
10
|
+
export class AcpProtocolError extends Error {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "AcpProtocolError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class AcpClient {
|
|
17
|
+
conn;
|
|
18
|
+
updateHandlers = [];
|
|
19
|
+
initialized = false;
|
|
20
|
+
constructor(readable, writable) {
|
|
21
|
+
this.conn = new JsonRpcConnection(readable, writable);
|
|
22
|
+
// 把 ACP 的 session/update 通知分发给订阅者;其它通知忽略(本通用客户端不需要)。
|
|
23
|
+
this.conn.onNotification((m) => {
|
|
24
|
+
if (m.method === "session/update") {
|
|
25
|
+
const n = m.params;
|
|
26
|
+
for (const cb of this.updateHandlers)
|
|
27
|
+
cb(n);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/** 订阅服务端推送的 session/update(原样透传;F2.2 driver 负责映射成 CcEvent)。 */
|
|
32
|
+
onSessionUpdate(cb) {
|
|
33
|
+
this.updateHandlers.push(cb);
|
|
34
|
+
}
|
|
35
|
+
/** initialize:协商协议版本 + 交换 capabilities。版本不匹配 → AcpProtocolError(§9.4)。 */
|
|
36
|
+
async initialize(opts = {}) {
|
|
37
|
+
const expected = opts.expectedVersion ?? ACP_PROTOCOL_VERSION;
|
|
38
|
+
const params = {
|
|
39
|
+
protocolVersion: expected,
|
|
40
|
+
clientCapabilities: opts.clientCapabilities ?? {},
|
|
41
|
+
};
|
|
42
|
+
const res = await this.conn.request("initialize", params);
|
|
43
|
+
// 协议版本是单整数大版本号;不匹配则受控报错(防协议漂移静默破坏)。
|
|
44
|
+
if (typeof res?.protocolVersion !== "number") {
|
|
45
|
+
throw new AcpProtocolError(`ACP initialize: agent did not return a numeric protocolVersion (got ${JSON.stringify(res?.protocolVersion)})`);
|
|
46
|
+
}
|
|
47
|
+
if (res.protocolVersion !== expected) {
|
|
48
|
+
throw new AcpProtocolError(`ACP protocol version mismatch: client expected ${expected}, agent reported ${res.protocolVersion}`);
|
|
49
|
+
}
|
|
50
|
+
this.initialized = true;
|
|
51
|
+
return res;
|
|
52
|
+
}
|
|
53
|
+
/** session/new:开新会话,返回 sessionId。 */
|
|
54
|
+
newSession(args) {
|
|
55
|
+
return this.conn.request("session/new", {
|
|
56
|
+
cwd: args.cwd,
|
|
57
|
+
...(args.mcpServers ? { mcpServers: args.mcpServers } : {}),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/** session/load:恢复已有会话(agent 先经 session/update 重放历史,再 resolve)。 */
|
|
61
|
+
async loadSession(args) {
|
|
62
|
+
await this.conn.request("session/load", {
|
|
63
|
+
sessionId: args.sessionId,
|
|
64
|
+
cwd: args.cwd,
|
|
65
|
+
...(args.mcpServers ? { mcpServers: args.mcpServers } : {}),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** session/load 的语义别名(Hermes 文档里有 resume_session;映射到官方 session/load)。 */
|
|
69
|
+
resumeSession(args) {
|
|
70
|
+
return this.loadSession(args);
|
|
71
|
+
}
|
|
72
|
+
/** session/prompt:发一轮 prompt content blocks,等回合结束的 PromptResponse{stopReason}。
|
|
73
|
+
* 回合中服务端经 onSessionUpdate 推流。 */
|
|
74
|
+
prompt(args) {
|
|
75
|
+
return this.conn.request("session/prompt", {
|
|
76
|
+
sessionId: args.sessionId,
|
|
77
|
+
prompt: args.content,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/** session/set_model:覆盖该会话的模型(D6;Mosse agent 指定 model 时用,否则用 Hermes 配置默认)。
|
|
81
|
+
* Hermes acp_adapter 文档作 set_session_model;映射到 ACP 的 session/set_model(modelId)。 */
|
|
82
|
+
async setSessionModel(sessionId, modelId) {
|
|
83
|
+
await this.conn.request("session/set_model", { sessionId, modelId });
|
|
84
|
+
}
|
|
85
|
+
/** session/cancel:通知 agent 取消进行中的回合(无响应;进行中的 prompt 将回 stopReason:"cancelled")。 */
|
|
86
|
+
cancel(sessionId) {
|
|
87
|
+
this.conn.notify("session/cancel", { sessionId });
|
|
88
|
+
}
|
|
89
|
+
/** 是否已成功 initialize(供消费方防御性检查)。 */
|
|
90
|
+
get isInitialized() {
|
|
91
|
+
return this.initialized;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/acp/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AcpClient, AcpProtocolError } from "./client.js";
|
|
2
|
+
export type { InitializeOptions, NewSessionArgs, PromptArgs, SessionUpdateHandler, } from "./client.js";
|
|
3
|
+
export { JsonRpcConnection, LineFramer } from "./jsonrpc.js";
|
|
4
|
+
export type { NotificationHandler } from "./jsonrpc.js";
|
|
5
|
+
export * from "./types.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// ACP(Agent Client Protocol)最小客户端 —— Hermes 适配 M2 F2.1。
|
|
2
|
+
// 通用 JSON-RPC-over-stdio 客户端;Hermes 具体 spawn / 事件映射在 F2.2。
|
|
3
|
+
export { AcpClient, AcpProtocolError } from "./client.js";
|
|
4
|
+
export { JsonRpcConnection, LineFramer } from "./jsonrpc.js";
|
|
5
|
+
export * from "./types.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/acp/index.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,2DAA2D;AAC3D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAO1D,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE7D,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Readable, Writable } from "node:stream";
|
|
2
|
+
/** 把任意切分的字节流(stdout chunk)重组成完整文本行(按 \n 切,跳过空行 / \r)。
|
|
3
|
+
* 纯状态机,易单测半包组装。 */
|
|
4
|
+
export declare class LineFramer {
|
|
5
|
+
private buf;
|
|
6
|
+
/** 喂入一段文本,返回本次可完整产出的行(已去掉换行 / \r,跳过空行)。 */
|
|
7
|
+
push(chunk: string): string[];
|
|
8
|
+
}
|
|
9
|
+
export type NotificationHandler = (msg: {
|
|
10
|
+
method: string;
|
|
11
|
+
params: unknown;
|
|
12
|
+
}) => void;
|
|
13
|
+
/** 一对 readable(agent stdout)/writable(agent stdin)上的 JSON-RPC 连接:
|
|
14
|
+
* - request(method, params):写带 id 的请求,按 id 配对响应(result→resolve / error→reject)。
|
|
15
|
+
* - notify(method, params):写无 id 的通知。
|
|
16
|
+
* - onNotification(cb):订阅服务端推来的通知(如 session/update)。
|
|
17
|
+
* 坏 JSON 行被跳过(不崩);流关闭时挂起请求全部 reject。 */
|
|
18
|
+
export declare class JsonRpcConnection {
|
|
19
|
+
private readonly readable;
|
|
20
|
+
private readonly writable;
|
|
21
|
+
private readonly framer;
|
|
22
|
+
private readonly pending;
|
|
23
|
+
private readonly notifyHandlers;
|
|
24
|
+
private nextId;
|
|
25
|
+
private closed;
|
|
26
|
+
constructor(readable: Readable, writable: Writable);
|
|
27
|
+
/** 发请求,返回响应 result(或在 error / 流关闭时 reject)。 */
|
|
28
|
+
request<T = unknown>(method: string, params?: unknown): Promise<T>;
|
|
29
|
+
/** 发通知(无响应)。 */
|
|
30
|
+
notify(method: string, params?: unknown): void;
|
|
31
|
+
/** 订阅服务端通知。 */
|
|
32
|
+
onNotification(cb: NotificationHandler): void;
|
|
33
|
+
private write;
|
|
34
|
+
private onData;
|
|
35
|
+
private dispatch;
|
|
36
|
+
private onClosed;
|
|
37
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// 最小 JSON-RPC 2.0 over stdio —— newline-delimited 分帧(ACP §分帧:一行一帧,无内嵌换行)。
|
|
2
|
+
// D4 终裁:手写,不引第三方 ACP 库。stdout=协议帧、stderr=日志(由消费方保证,客户端不混入)。
|
|
3
|
+
/** 把任意切分的字节流(stdout chunk)重组成完整文本行(按 \n 切,跳过空行 / \r)。
|
|
4
|
+
* 纯状态机,易单测半包组装。 */
|
|
5
|
+
export class LineFramer {
|
|
6
|
+
buf = "";
|
|
7
|
+
/** 喂入一段文本,返回本次可完整产出的行(已去掉换行 / \r,跳过空行)。 */
|
|
8
|
+
push(chunk) {
|
|
9
|
+
this.buf += chunk;
|
|
10
|
+
const lines = [];
|
|
11
|
+
let nl = this.buf.indexOf("\n");
|
|
12
|
+
while (nl >= 0) {
|
|
13
|
+
const line = this.buf.slice(0, nl).replace(/\r$/, "");
|
|
14
|
+
this.buf = this.buf.slice(nl + 1);
|
|
15
|
+
if (line.length > 0)
|
|
16
|
+
lines.push(line);
|
|
17
|
+
nl = this.buf.indexOf("\n");
|
|
18
|
+
}
|
|
19
|
+
return lines;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** 一对 readable(agent stdout)/writable(agent stdin)上的 JSON-RPC 连接:
|
|
23
|
+
* - request(method, params):写带 id 的请求,按 id 配对响应(result→resolve / error→reject)。
|
|
24
|
+
* - notify(method, params):写无 id 的通知。
|
|
25
|
+
* - onNotification(cb):订阅服务端推来的通知(如 session/update)。
|
|
26
|
+
* 坏 JSON 行被跳过(不崩);流关闭时挂起请求全部 reject。 */
|
|
27
|
+
export class JsonRpcConnection {
|
|
28
|
+
readable;
|
|
29
|
+
writable;
|
|
30
|
+
framer = new LineFramer();
|
|
31
|
+
pending = new Map();
|
|
32
|
+
notifyHandlers = [];
|
|
33
|
+
nextId = 1;
|
|
34
|
+
closed = false;
|
|
35
|
+
constructor(readable, writable) {
|
|
36
|
+
this.readable = readable;
|
|
37
|
+
this.writable = writable;
|
|
38
|
+
this.readable.setEncoding("utf8");
|
|
39
|
+
this.readable.on("data", (chunk) => this.onData(chunk));
|
|
40
|
+
this.readable.on("close", () => this.onClosed());
|
|
41
|
+
this.readable.on("end", () => this.onClosed());
|
|
42
|
+
this.readable.on("error", (e) => this.onClosed(e));
|
|
43
|
+
}
|
|
44
|
+
/** 发请求,返回响应 result(或在 error / 流关闭时 reject)。 */
|
|
45
|
+
request(method, params) {
|
|
46
|
+
if (this.closed)
|
|
47
|
+
return Promise.reject(new Error("ACP connection closed"));
|
|
48
|
+
const id = this.nextId++;
|
|
49
|
+
const req = { jsonrpc: "2.0", id, method, params };
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
this.pending.set(id, { resolve: resolve, reject });
|
|
52
|
+
this.write(req);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/** 发通知(无响应)。 */
|
|
56
|
+
notify(method, params) {
|
|
57
|
+
if (this.closed)
|
|
58
|
+
return;
|
|
59
|
+
const note = { jsonrpc: "2.0", method, params };
|
|
60
|
+
this.write(note);
|
|
61
|
+
}
|
|
62
|
+
/** 订阅服务端通知。 */
|
|
63
|
+
onNotification(cb) {
|
|
64
|
+
this.notifyHandlers.push(cb);
|
|
65
|
+
}
|
|
66
|
+
write(msg) {
|
|
67
|
+
// 单行一帧:JSON.stringify 不产出内嵌换行(payload 内换行被转义为 \\n),行尾补 \n。
|
|
68
|
+
this.writable.write(`${JSON.stringify(msg)}\n`);
|
|
69
|
+
}
|
|
70
|
+
onData(chunk) {
|
|
71
|
+
for (const line of this.framer.push(chunk)) {
|
|
72
|
+
let msg;
|
|
73
|
+
try {
|
|
74
|
+
msg = JSON.parse(line);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// 坏 JSON / 误入 stdout 的非协议行 → 跳过,不崩(消费方应把日志走 stderr)。
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
this.dispatch(msg);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
dispatch(msg) {
|
|
84
|
+
// 带 id 且有 result/error → 响应;带 method 且无 id → 通知;带 id+method → 服务端请求(本客户端暂不处理,忽略)。
|
|
85
|
+
const m = msg;
|
|
86
|
+
if (m.id !== undefined && m.method === undefined) {
|
|
87
|
+
const p = this.pending.get(m.id);
|
|
88
|
+
if (!p)
|
|
89
|
+
return; // 未知 id → 丢弃
|
|
90
|
+
this.pending.delete(m.id);
|
|
91
|
+
if (m.error)
|
|
92
|
+
p.reject(rpcError(m.error));
|
|
93
|
+
else
|
|
94
|
+
p.resolve(m.result);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (m.method !== undefined && m.id === undefined) {
|
|
98
|
+
for (const cb of this.notifyHandlers)
|
|
99
|
+
cb({ method: m.method, params: m.params });
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
// 服务端→客户端请求(ACP 双向)本通用客户端不需要 → 忽略(F2.x 如需可扩展)。
|
|
103
|
+
}
|
|
104
|
+
onClosed(err) {
|
|
105
|
+
if (this.closed)
|
|
106
|
+
return;
|
|
107
|
+
this.closed = true;
|
|
108
|
+
const reason = err ? `ACP connection closed: ${err.message}` : "ACP connection closed";
|
|
109
|
+
for (const [, p] of this.pending)
|
|
110
|
+
p.reject(new Error(reason));
|
|
111
|
+
this.pending.clear();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function rpcError(e) {
|
|
115
|
+
const err = new Error(`ACP RPC error ${e.code}: ${e.message}`);
|
|
116
|
+
err.code = e.code;
|
|
117
|
+
err.data = e.data;
|
|
118
|
+
return err;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=jsonrpc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonrpc.js","sourceRoot":"","sources":["../../src/acp/jsonrpc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PassThrough } from "node:stream";
|
|
2
|
+
export interface MockServerOptions {
|
|
3
|
+
/** initialize 回报的协议版本(默认 = 客户端期望版本,用于测"版本不符"时传别的)。 */
|
|
4
|
+
protocolVersion?: number;
|
|
5
|
+
/** prompt 期间推送的 session/update 序列(默认给一组有序文本/思考/工具)。 */
|
|
6
|
+
updates?: Record<string, unknown>[];
|
|
7
|
+
/** prompt 最终 stopReason(默认 end_turn)。 */
|
|
8
|
+
stopReason?: string;
|
|
9
|
+
/** session/new 返回的 sessionId。 */
|
|
10
|
+
sessionId?: string;
|
|
11
|
+
}
|
|
12
|
+
/** 接 fromClient(客户端写给 agent 的流 = agent stdin)/ toClient(agent 写给客户端 = agent stdout)。 */
|
|
13
|
+
export declare function startMockAcpServer(fromClient: PassThrough, toClient: PassThrough, opts?: MockServerOptions): {
|
|
14
|
+
cancelled: boolean;
|
|
15
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// 确定性 mock ACP server(stdio 形状)—— F2.1 TDD fixture。
|
|
2
|
+
// 不依赖子进程:对一对 PassThrough(模拟 agent stdin/stdout)回应固定的 JSON-RPC,
|
|
3
|
+
// 行为镜像真 hermes acp 的协议骨架(以官方 ACP schema 为准):
|
|
4
|
+
// - initialize → 回 { protocolVersion, agentCapabilities }
|
|
5
|
+
// - session/new → 回 { sessionId }
|
|
6
|
+
// - session/load → 回 null(先重放历史 session/update 再 resolve,可选)
|
|
7
|
+
// - session/prompt → 先推几条 session/update(agent_message_chunk / agent_thought_chunk /
|
|
8
|
+
// tool_call),再回 { stopReason: "end_turn" }
|
|
9
|
+
// - session/cancel(通知)→ 记录,被取消的 prompt 回 { stopReason: "cancelled" }
|
|
10
|
+
//
|
|
11
|
+
// 用 LineFramer 解析客户端写入,确定性、无定时器(除 prompt 的微任务排序)。
|
|
12
|
+
import { LineFramer } from "./jsonrpc.js";
|
|
13
|
+
import { ACP_PROTOCOL_VERSION } from "./types.js";
|
|
14
|
+
/** 接 fromClient(客户端写给 agent 的流 = agent stdin)/ toClient(agent 写给客户端 = agent stdout)。 */
|
|
15
|
+
export function startMockAcpServer(fromClient, toClient, opts = {}) {
|
|
16
|
+
const state = { cancelled: false };
|
|
17
|
+
const framer = new LineFramer();
|
|
18
|
+
const sessionId = opts.sessionId ?? "sess_mock_1";
|
|
19
|
+
const protocolVersion = opts.protocolVersion ?? ACP_PROTOCOL_VERSION;
|
|
20
|
+
const stopReason = opts.stopReason ?? "end_turn";
|
|
21
|
+
const updates = opts.updates ??
|
|
22
|
+
[
|
|
23
|
+
{ sessionUpdate: "agent_thought_chunk", content: { type: "text", text: "thinking…" } },
|
|
24
|
+
{ sessionUpdate: "agent_message_chunk", content: { type: "text", text: "Hello" } },
|
|
25
|
+
{ sessionUpdate: "agent_message_chunk", content: { type: "text", text: " world" } },
|
|
26
|
+
{ sessionUpdate: "tool_call", toolCallId: "t1", title: "Read", status: "pending" },
|
|
27
|
+
];
|
|
28
|
+
function send(obj) {
|
|
29
|
+
toClient.write(`${JSON.stringify(obj)}\n`);
|
|
30
|
+
}
|
|
31
|
+
function reply(id, result) {
|
|
32
|
+
send({ jsonrpc: "2.0", id, result });
|
|
33
|
+
}
|
|
34
|
+
function replyError(id, code, message) {
|
|
35
|
+
send({ jsonrpc: "2.0", id, error: { code, message } });
|
|
36
|
+
}
|
|
37
|
+
function notify(method, params) {
|
|
38
|
+
send({ jsonrpc: "2.0", method, params });
|
|
39
|
+
}
|
|
40
|
+
fromClient.setEncoding("utf8");
|
|
41
|
+
fromClient.on("data", (chunk) => {
|
|
42
|
+
for (const line of framer.push(chunk)) {
|
|
43
|
+
let msg;
|
|
44
|
+
try {
|
|
45
|
+
msg = JSON.parse(line);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
handle(msg);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
function handle(msg) {
|
|
54
|
+
switch (msg.method) {
|
|
55
|
+
case "initialize":
|
|
56
|
+
reply(msg.id, {
|
|
57
|
+
protocolVersion,
|
|
58
|
+
agentCapabilities: { loadSession: true, promptCapabilities: { image: false } },
|
|
59
|
+
});
|
|
60
|
+
return;
|
|
61
|
+
case "session/new":
|
|
62
|
+
reply(msg.id, { sessionId });
|
|
63
|
+
return;
|
|
64
|
+
case "session/set_model":
|
|
65
|
+
// 模型覆盖(D6);ACP 回 null。
|
|
66
|
+
reply(msg.id, null);
|
|
67
|
+
return;
|
|
68
|
+
case "session/load":
|
|
69
|
+
// 先重放一条历史 user_message_chunk,再 resolve null。
|
|
70
|
+
notify("session/update", {
|
|
71
|
+
sessionId: msg.params?.sessionId ?? sessionId,
|
|
72
|
+
update: { sessionUpdate: "user_message_chunk", content: { type: "text", text: "prev" } },
|
|
73
|
+
});
|
|
74
|
+
reply(msg.id, null);
|
|
75
|
+
return;
|
|
76
|
+
case "session/prompt": {
|
|
77
|
+
const sid = msg.params?.sessionId ?? sessionId;
|
|
78
|
+
if (state.cancelled) {
|
|
79
|
+
reply(msg.id, { stopReason: "cancelled" });
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// 有序推送 session/update,然后回 PromptResponse(确定性:同步顺序写入)。
|
|
83
|
+
for (const u of updates)
|
|
84
|
+
notify("session/update", { sessionId: sid, update: u });
|
|
85
|
+
reply(msg.id, { stopReason });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
case "session/cancel":
|
|
89
|
+
// 通知,无响应。标记取消(影响后续 prompt 在某些测试里)。
|
|
90
|
+
state.cancelled = true;
|
|
91
|
+
return;
|
|
92
|
+
default:
|
|
93
|
+
if (msg.id !== undefined)
|
|
94
|
+
replyError(msg.id, -32601, `method not found: ${msg.method}`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return state;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=mock-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-server.js","sourceRoot":"","sources":["../../src/acp/mock-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/** ACP 当前协议大版本(单整数);见 §9.4 协议版本校验。 */
|
|
2
|
+
export declare const ACP_PROTOCOL_VERSION = 1;
|
|
3
|
+
/** 文本内容块(prompt / agent_message_chunk 主用)。 */
|
|
4
|
+
export interface TextContentBlock {
|
|
5
|
+
type: "text";
|
|
6
|
+
text: string;
|
|
7
|
+
annotations?: unknown;
|
|
8
|
+
}
|
|
9
|
+
/** 图片内容块。 */
|
|
10
|
+
export interface ImageContentBlock {
|
|
11
|
+
type: "image";
|
|
12
|
+
data: string;
|
|
13
|
+
mimeType: string;
|
|
14
|
+
uri?: string;
|
|
15
|
+
annotations?: unknown;
|
|
16
|
+
}
|
|
17
|
+
/** 音频内容块。 */
|
|
18
|
+
export interface AudioContentBlock {
|
|
19
|
+
type: "audio";
|
|
20
|
+
data: string;
|
|
21
|
+
mimeType: string;
|
|
22
|
+
annotations?: unknown;
|
|
23
|
+
}
|
|
24
|
+
/** 内嵌资源。 */
|
|
25
|
+
export interface ResourceContentBlock {
|
|
26
|
+
type: "resource";
|
|
27
|
+
resource: {
|
|
28
|
+
uri: string;
|
|
29
|
+
text?: string;
|
|
30
|
+
blob?: string;
|
|
31
|
+
mimeType?: string;
|
|
32
|
+
};
|
|
33
|
+
annotations?: unknown;
|
|
34
|
+
}
|
|
35
|
+
/** 资源链接。 */
|
|
36
|
+
export interface ResourceLinkContentBlock {
|
|
37
|
+
type: "resource_link";
|
|
38
|
+
uri: string;
|
|
39
|
+
name: string;
|
|
40
|
+
mimeType?: string;
|
|
41
|
+
title?: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
size?: number;
|
|
44
|
+
annotations?: unknown;
|
|
45
|
+
}
|
|
46
|
+
export type ContentBlock = TextContentBlock | ImageContentBlock | AudioContentBlock | ResourceContentBlock | ResourceLinkContentBlock;
|
|
47
|
+
/** stdio transport MCP server(name + 绝对 command + args + env)。 */
|
|
48
|
+
export interface McpServerStdio {
|
|
49
|
+
name: string;
|
|
50
|
+
command: string;
|
|
51
|
+
args?: string[];
|
|
52
|
+
env?: {
|
|
53
|
+
name: string;
|
|
54
|
+
value: string;
|
|
55
|
+
}[];
|
|
56
|
+
}
|
|
57
|
+
/** http / sse transport MCP server。 */
|
|
58
|
+
export interface McpServerHttp {
|
|
59
|
+
type: "http" | "sse";
|
|
60
|
+
name: string;
|
|
61
|
+
url: string;
|
|
62
|
+
headers?: {
|
|
63
|
+
name: string;
|
|
64
|
+
value: string;
|
|
65
|
+
}[];
|
|
66
|
+
}
|
|
67
|
+
export type McpServer = McpServerStdio | McpServerHttp;
|
|
68
|
+
/** initialize 请求 params。capabilities 形状由各端自定义,本客户端只声明并校验版本。 */
|
|
69
|
+
export interface InitializeParams {
|
|
70
|
+
protocolVersion: number;
|
|
71
|
+
clientCapabilities?: Record<string, unknown>;
|
|
72
|
+
}
|
|
73
|
+
/** initialize 响应:agent 回报它支持的版本 + capabilities + 可选 authMethods。 */
|
|
74
|
+
export interface InitializeResult {
|
|
75
|
+
protocolVersion: number;
|
|
76
|
+
agentCapabilities?: Record<string, unknown>;
|
|
77
|
+
authMethods?: unknown[];
|
|
78
|
+
}
|
|
79
|
+
export interface NewSessionParams {
|
|
80
|
+
cwd: string;
|
|
81
|
+
mcpServers?: McpServer[];
|
|
82
|
+
}
|
|
83
|
+
export interface NewSessionResult {
|
|
84
|
+
sessionId: string;
|
|
85
|
+
}
|
|
86
|
+
export interface LoadSessionParams {
|
|
87
|
+
sessionId: string;
|
|
88
|
+
cwd: string;
|
|
89
|
+
mcpServers?: McpServer[];
|
|
90
|
+
}
|
|
91
|
+
export interface PromptParams {
|
|
92
|
+
sessionId: string;
|
|
93
|
+
prompt: ContentBlock[];
|
|
94
|
+
}
|
|
95
|
+
/** PromptResponse:本回合为何停。 */
|
|
96
|
+
export type StopReason = "end_turn" | "max_tokens" | "max_turn_requests" | "refusal" | "cancelled";
|
|
97
|
+
export interface PromptResult {
|
|
98
|
+
stopReason: StopReason;
|
|
99
|
+
/** 部分 agent(含 Hermes)附带用量;形状不固定,原样透传。 */
|
|
100
|
+
usage?: unknown;
|
|
101
|
+
}
|
|
102
|
+
export interface CancelParams {
|
|
103
|
+
sessionId: string;
|
|
104
|
+
}
|
|
105
|
+
/** session/update 的 update 体:判别字段 sessionUpdate(snake_case 值);其余字段随变体而定。
|
|
106
|
+
* 本通用客户端不解释内容 —— 原样透传给订阅者(F2.2 driver 负责映射成 CcEvent)。 */
|
|
107
|
+
export interface SessionUpdate {
|
|
108
|
+
sessionUpdate: string;
|
|
109
|
+
[k: string]: unknown;
|
|
110
|
+
}
|
|
111
|
+
export interface SessionUpdateNotification {
|
|
112
|
+
sessionId: string;
|
|
113
|
+
update: SessionUpdate;
|
|
114
|
+
}
|
|
115
|
+
export type JsonRpcId = number | string;
|
|
116
|
+
export interface JsonRpcRequest {
|
|
117
|
+
jsonrpc: "2.0";
|
|
118
|
+
id: JsonRpcId;
|
|
119
|
+
method: string;
|
|
120
|
+
params?: unknown;
|
|
121
|
+
}
|
|
122
|
+
export interface JsonRpcNotification {
|
|
123
|
+
jsonrpc: "2.0";
|
|
124
|
+
method: string;
|
|
125
|
+
params?: unknown;
|
|
126
|
+
}
|
|
127
|
+
export interface JsonRpcError {
|
|
128
|
+
code: number;
|
|
129
|
+
message: string;
|
|
130
|
+
data?: unknown;
|
|
131
|
+
}
|
|
132
|
+
export interface JsonRpcResponse {
|
|
133
|
+
jsonrpc: "2.0";
|
|
134
|
+
id: JsonRpcId;
|
|
135
|
+
result?: unknown;
|
|
136
|
+
error?: JsonRpcError;
|
|
137
|
+
}
|
|
138
|
+
export type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcResponse;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// ACP (Agent Client Protocol) 线协议类型 —— Hermes 适配 M2 F2.1。
|
|
2
|
+
//
|
|
3
|
+
// 来源(以官方 schema 为准,代码据实不臆造):
|
|
4
|
+
// - 规范站 https://agentclientprotocol.com(Zed 开放标准)
|
|
5
|
+
// · 概览/方法集:initialize / authenticate / session/new / session/load /
|
|
6
|
+
// session/prompt / session/cancel + session/update 通知。
|
|
7
|
+
// · 分帧:newline-delimited JSON-RPC 2.0 over stdio(stdout=协议帧、stderr=日志);
|
|
8
|
+
// 一行一个 JSON 对象,**不得含内嵌换行**(LSP 风格 Content-Length **不**使用)。
|
|
9
|
+
// · 字段命名:对象属性 camelCase;唯有判别字段(如 update.sessionUpdate 的值、
|
|
10
|
+
// content.type)用 snake_case。protocolVersion 是**单个整数**(当前 = 1)。
|
|
11
|
+
// - Hermes acp_adapter(github NousResearch/hermes-agent acp_adapter/server.py):
|
|
12
|
+
// 实现 initialize/authenticate/session/new|load|resume|fork/prompt/cancel +
|
|
13
|
+
// set_model/set_mode;session_update 变体含 agent_message_chunk /
|
|
14
|
+
// agent_thought_chunk / tool_start / tool_complete / plan_update / usage_update /
|
|
15
|
+
// user_message_chunk(注:tool/plan 变体名 Hermes 用 tool_start/tool_complete/
|
|
16
|
+
// plan_update,官方 schema 用 tool_call/tool_call_update/plan —— 本通用客户端
|
|
17
|
+
// **不解释 update 内容**,原样透传给 onSessionUpdate,由 F2.2 driver 做收敛映射)。
|
|
18
|
+
/** ACP 当前协议大版本(单整数);见 §9.4 协议版本校验。 */
|
|
19
|
+
export const ACP_PROTOCOL_VERSION = 1;
|
|
20
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/acp/types.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,EAAE;AACF,6BAA6B;AAC7B,oDAAoD;AACpD,wEAAwE;AACxE,6DAA6D;AAC7D,4EAA4E;AAC5E,iEAAiE;AACjE,8DAA8D;AAC9D,qEAAqE;AACrE,kFAAkF;AAClF,8EAA8E;AAC9E,kEAAkE;AAClE,sFAAsF;AACtF,4EAA4E;AAC5E,wEAAwE;AACxE,oEAAoE;AAEpE,sCAAsC;AACtC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { McpServer } from "../acp/types.js";
|
|
2
|
+
import type { CcEvent } from "./types.js";
|
|
3
|
+
/** session/update 的 update 体(判别字段 sessionUpdate,其余随变体)。 */
|
|
4
|
+
export interface SessionUpdate {
|
|
5
|
+
sessionUpdate: string;
|
|
6
|
+
[k: string]: unknown;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* ACP `session_update` → CcEvent(或 null=忽略)。映射表(§9.2):
|
|
10
|
+
* - agent_message_chunk → {type:"text", delta} (driver 累积成最终文本)
|
|
11
|
+
* - agent_thought_chunk → {type:"activity", tool:"thinking"} (思考摘要)
|
|
12
|
+
* - tool_call / tool_call_update / tool_start / tool_complete
|
|
13
|
+
* → {type:"activity", tool, summary} (工具摘要)
|
|
14
|
+
* - plan / plan_update → {type:"activity", tool:"plan"} (计划摘要)
|
|
15
|
+
* - usage_update → null(忽略;用量并入 result 由 driver 处理)
|
|
16
|
+
* - user_message_chunk → null(resume 时历史重放,非本轮 agent 产出)
|
|
17
|
+
* - 其它/未知 → null
|
|
18
|
+
*/
|
|
19
|
+
export declare function mapSessionUpdate(update: SessionUpdate): CcEvent | null;
|
|
20
|
+
/**
|
|
21
|
+
* daemon 的 `mcpConfig`(claude `--mcp-config` 形状:`{mcpServers:{name:{type,url,headers}|{command,args,env}}}`)
|
|
22
|
+
* → ACP `session/new` 的 `mcpServers` 数组(F2.6)。
|
|
23
|
+
*
|
|
24
|
+
* **knowledge token 透传与 claude 对齐**:daemon 给 claude 和 hermes 两个 driver 的是**同一个** mcpConfig JSON
|
|
25
|
+
* (见 agent-bridge/src/index.ts cc_turn:知识 MCP = `{type:"http", url, headers:{Authorization:"Bearer <turnToken>"}}`)。
|
|
26
|
+
* claude 经 `--mcp-config` 注入;hermes 在此转成 ACP `mcpServers`,token 原样落到 ACP http server 的 `headers`
|
|
27
|
+
* (或 stdio server 的 `env`)。Mosse 不在此处生成/改写 token —— 只搬运 daemon 已组好的形状。
|
|
28
|
+
*
|
|
29
|
+
* 形状判别:
|
|
30
|
+
* - http / sse transport:有 `type:"http"|"sse"` 或仅有 `url`(claude http 配置必带 url);headers → ACP `{name,value}[]`。
|
|
31
|
+
* - stdio transport:有 `command`;`args` 透传、`env`(可携带 token,如 `*_API_KEY`/`MOSSE_KNOWLEDGE_TOKEN`)→ `{name,value}[]`。
|
|
32
|
+
* - 既无 url 也无 command 的坏项跳过(不注入坏 server)。
|
|
33
|
+
*/
|
|
34
|
+
export declare function mcpConfigToAcpServers(mcpConfig?: string): McpServer[] | undefined;
|