@zvada/agent-server 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/package.json +87 -0
- package/src/client/client.ts +589 -0
- package/src/client/index.ts +18 -0
- package/src/client/transports.ts +84 -0
- package/src/core/agents/acp/acp-agent.ts +322 -0
- package/src/core/agents/acp/adapter.ts +260 -0
- package/src/core/agents/acp/client.ts +212 -0
- package/src/core/agents/acp/known-agents.ts +29 -0
- package/src/core/agents/acp/mappings.ts +136 -0
- package/src/core/agents/base.ts +145 -0
- package/src/core/agents/claude-code/adapter.ts +451 -0
- package/src/core/agents/claude-code/claude-agent.ts +235 -0
- package/src/core/agents/claude-code/generator-session.ts +344 -0
- package/src/core/agents/claude-code/options.ts +161 -0
- package/src/core/agents/claude-code/session-manager.ts +159 -0
- package/src/core/agents/codex-app-server/adapter.ts +214 -0
- package/src/core/agents/codex-app-server/client.ts +221 -0
- package/src/core/agents/codex-app-server/codex-app-server-agent.ts +385 -0
- package/src/core/agents/codex-items.ts +122 -0
- package/src/core/agents/codex-sdk/adapter.ts +204 -0
- package/src/core/agents/codex-sdk/codex-sdk-agent.ts +236 -0
- package/src/core/agents/config-fingerprint.ts +19 -0
- package/src/core/agents/error-classifier.ts +68 -0
- package/src/core/agents/registry.ts +40 -0
- package/src/core/agents/session-store.ts +72 -0
- package/src/core/agents/tool-meta.ts +68 -0
- package/src/core/agents/types.ts +54 -0
- package/src/core/index.ts +114 -0
- package/src/core/presets.ts +78 -0
- package/src/core/provision/extract.ts +31 -0
- package/src/core/provision/index.ts +10 -0
- package/src/core/provision/npm.ts +114 -0
- package/src/core/provision/pins.ts +51 -0
- package/src/core/provision/platform.ts +73 -0
- package/src/core/provision/provisioner.ts +478 -0
- package/src/core/proxy/anthropic-proxy.ts +69 -0
- package/src/core/proxy/api-key-store.ts +34 -0
- package/src/core/proxy/index.ts +7 -0
- package/src/core/runtime/agent-runtime.ts +363 -0
- package/src/core/runtime/event-processor.ts +218 -0
- package/src/core/runtime/event-sink.ts +37 -0
- package/src/core/utils/errors.ts +41 -0
- package/src/index.ts +4 -0
- package/src/protocol/async-queue.ts +68 -0
- package/src/protocol/config.ts +100 -0
- package/src/protocol/factories.ts +125 -0
- package/src/protocol/harness.ts +50 -0
- package/src/protocol/ids.ts +53 -0
- package/src/protocol/index.ts +16 -0
- package/src/protocol/lifecycle.ts +309 -0
- package/src/protocol/models.ts +45 -0
- package/src/protocol/part-input.ts +58 -0
- package/src/protocol/parts.ts +60 -0
- package/src/protocol/thinking.ts +32 -0
- package/src/protocol/tokens.ts +39 -0
- package/src/protocol/tool-state.ts +89 -0
- package/src/protocol/wire.ts +313 -0
- package/src/server/acp/binding.ts +163 -0
- package/src/server/acp/translate.ts +160 -0
- package/src/server/agent-server.ts +357 -0
- package/src/server/bin.ts +174 -0
- package/src/server/index.ts +24 -0
- package/src/server/install.ts +51 -0
- package/src/server/session-log.ts +66 -0
- package/src/server/transports.ts +149 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/// <reference types="bun" />
|
|
2
|
+
import { createInterface } from "node:readline";
|
|
3
|
+
import { type WireTransport, channelTransport, pushNdjsonLines } from "../protocol/index.ts";
|
|
4
|
+
import type { AgentServer } from "./agent-server.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* NDJSON over stdio — for a parent process that spawns the server and owns its
|
|
8
|
+
* pipes (deus-machine's model). Nothing else may write to stdout in this mode;
|
|
9
|
+
* logs belong on stderr.
|
|
10
|
+
*/
|
|
11
|
+
export function stdioTransport(
|
|
12
|
+
stdin: NodeJS.ReadableStream = process.stdin,
|
|
13
|
+
stdout: NodeJS.WritableStream = process.stdout,
|
|
14
|
+
): WireTransport {
|
|
15
|
+
const rl = createInterface({ input: stdin, terminal: false });
|
|
16
|
+
const { transport, push, end } = channelTransport({
|
|
17
|
+
send: (line) => void stdout.write(`${line}\n`),
|
|
18
|
+
close: () => rl.close(),
|
|
19
|
+
});
|
|
20
|
+
rl.on("line", push);
|
|
21
|
+
rl.on("close", () => end("stdin closed"));
|
|
22
|
+
// A dead parent pipe (EPIPE) must route through the close path — an
|
|
23
|
+
// unhandled stream 'error' would crash the process instead.
|
|
24
|
+
stdin.on("error", (err: Error) => end(`stdin error: ${err.message}`));
|
|
25
|
+
stdout.on("error", (err: Error) => end(`stdout error: ${err.message}`));
|
|
26
|
+
return transport;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ListenOptions {
|
|
30
|
+
/** Port to bind (0 = ephemeral). */
|
|
31
|
+
port?: number;
|
|
32
|
+
hostname?: string;
|
|
33
|
+
/** Restrict upgrades to this path (e.g. "/wire"); any path when omitted. */
|
|
34
|
+
path?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Accept WebSocket clients (agnt's model: the consumer connects to us). Each
|
|
39
|
+
* accepted socket becomes an attached transport; all of them receive the
|
|
40
|
+
* event broadcast.
|
|
41
|
+
*/
|
|
42
|
+
export function listen(
|
|
43
|
+
server: AgentServer,
|
|
44
|
+
opts: ListenOptions = {},
|
|
45
|
+
): { url: string; port: number; stop(): void } {
|
|
46
|
+
type Channel = ReturnType<typeof channelTransport>;
|
|
47
|
+
const bun = Bun.serve<{ channel?: Channel }>({
|
|
48
|
+
port: opts.port ?? 0,
|
|
49
|
+
hostname: opts.hostname ?? "127.0.0.1",
|
|
50
|
+
fetch(req, srv) {
|
|
51
|
+
if (opts.path && new URL(req.url).pathname !== opts.path) {
|
|
52
|
+
return new Response("not found", { status: 404 });
|
|
53
|
+
}
|
|
54
|
+
return srv.upgrade(req, { data: {} })
|
|
55
|
+
? undefined
|
|
56
|
+
: new Response("websocket upgrade required", { status: 426 });
|
|
57
|
+
},
|
|
58
|
+
websocket: {
|
|
59
|
+
open(ws) {
|
|
60
|
+
const channel = channelTransport({
|
|
61
|
+
send: (line) => void ws.send(line),
|
|
62
|
+
close: () => ws.close(),
|
|
63
|
+
});
|
|
64
|
+
ws.data.channel = channel;
|
|
65
|
+
server.attach(channel.transport);
|
|
66
|
+
},
|
|
67
|
+
message(ws, message) {
|
|
68
|
+
const channel = ws.data.channel;
|
|
69
|
+
if (channel) pushNdjsonLines(channel.push, message);
|
|
70
|
+
},
|
|
71
|
+
close(ws) {
|
|
72
|
+
ws.data.channel?.end("socket closed");
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
const port = bun.port ?? opts.port ?? 0;
|
|
77
|
+
return {
|
|
78
|
+
url: `ws://${bun.hostname ?? opts.hostname ?? "127.0.0.1"}:${port}${opts.path ?? ""}`,
|
|
79
|
+
port,
|
|
80
|
+
stop: () => void bun.stop(true),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface DialOptions {
|
|
85
|
+
/** Base reconnect delay; grows exponentially up to `maxRetryDelayMs`. */
|
|
86
|
+
retryDelayMs?: number;
|
|
87
|
+
maxRetryDelayMs?: number;
|
|
88
|
+
onStateChange?: (state: "connecting" | "connected" | "closed") => void;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Dial out to a consumer and keep redialing on drop (echo's model: the
|
|
93
|
+
* sandboxed server opens a reverse connection to the backend). The remote end
|
|
94
|
+
* replays missed events via `events/replay` after each reconnect.
|
|
95
|
+
*/
|
|
96
|
+
export function dial(server: AgentServer, url: string, opts: DialOptions = {}): { stop(): void } {
|
|
97
|
+
const base = opts.retryDelayMs ?? 500;
|
|
98
|
+
const cap = opts.maxRetryDelayMs ?? 30_000;
|
|
99
|
+
let stopped = false;
|
|
100
|
+
let attempt = 0;
|
|
101
|
+
let current: WireTransport | null = null;
|
|
102
|
+
let socket: WebSocket | null = null;
|
|
103
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
104
|
+
|
|
105
|
+
const connect = () => {
|
|
106
|
+
if (stopped) return;
|
|
107
|
+
opts.onStateChange?.("connecting");
|
|
108
|
+
const ws = new WebSocket(url);
|
|
109
|
+
socket = ws;
|
|
110
|
+
const channel = channelTransport({
|
|
111
|
+
send: (line) => void ws.send(line),
|
|
112
|
+
close: () => ws.close(),
|
|
113
|
+
});
|
|
114
|
+
ws.addEventListener("open", () => {
|
|
115
|
+
if (stopped) {
|
|
116
|
+
ws.close();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
attempt = 0;
|
|
120
|
+
current = channel.transport;
|
|
121
|
+
server.attach(channel.transport);
|
|
122
|
+
opts.onStateChange?.("connected");
|
|
123
|
+
});
|
|
124
|
+
ws.addEventListener("message", (ev) => pushNdjsonLines(channel.push, ev.data));
|
|
125
|
+
ws.addEventListener("close", () => {
|
|
126
|
+
channel.end("socket closed");
|
|
127
|
+
if (socket === ws) socket = null;
|
|
128
|
+
if (current === channel.transport) current = null;
|
|
129
|
+
if (stopped) return;
|
|
130
|
+
opts.onStateChange?.("closed");
|
|
131
|
+
attempt += 1;
|
|
132
|
+
timer = setTimeout(connect, Math.min(cap, base * 2 ** Math.min(attempt - 1, 6)));
|
|
133
|
+
});
|
|
134
|
+
// A close event always follows; nothing to do here.
|
|
135
|
+
ws.addEventListener("error", () => {});
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
connect();
|
|
139
|
+
return {
|
|
140
|
+
stop() {
|
|
141
|
+
stopped = true;
|
|
142
|
+
if (timer) clearTimeout(timer);
|
|
143
|
+
if (current) current.close();
|
|
144
|
+
else socket?.close();
|
|
145
|
+
socket = null;
|
|
146
|
+
current = null;
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|