hyperbrane 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -3
- package/dist/agent/agentService.js +3 -2
- package/dist/agent/api.d.ts +26 -2
- package/dist/agent/control.js +1 -1
- package/dist/agent/dimensionAgentClient.d.ts +8 -0
- package/dist/agent/dimensionAgentClient.js +99 -0
- package/dist/agent/session/backend.d.ts +2 -1
- package/dist/agent/session/claude/transcript.d.ts +1 -0
- package/dist/agent/session/claude/transcript.js +1 -1
- package/dist/agent/session/worker/protocol.d.ts +6 -3
- package/dist/agent/session/worker/protocol.js +1 -1
- package/dist/agent/session/worker/workerParams.js +10 -3
- package/dist/agent/vendor-types/manifest.json +5 -60
- package/dist/agent/workerBundle.generated.js +1 -1
- package/dist/dimension/api.d.ts +3 -0
- package/dist/dimension/control.d.ts +2 -1
- package/dist/dimension/control.js +18 -0
- package/dist/dimension/service.js +27 -1
- package/dist/dimension/transportBundle.generated.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/lib/control.d.ts +4 -1
- package/dist/lib/control.js +2 -2
- package/dist/lib/jsonDirectory.js +24 -3
- package/dist/lib/unixSocket.d.ts +2 -0
- package/dist/lib/unixSocket.js +28 -0
- package/dist/portal-gateway/api.d.ts +1 -0
- package/dist/portal-gateway/gatewayService.js +14 -0
- package/dist/portal-gateway/transportBundle.generated.js +1 -1
- package/dist/restore.d.ts +9 -0
- package/dist/restore.js +10 -0
- package/dist/sandbox/api.d.ts +5 -0
- package/dist/sandbox/client.d.ts +2 -1
- package/dist/sandbox/client.js +4 -1
- package/dist/sandbox/lib/sshKey.d.ts +6 -0
- package/dist/sandbox/lib/sshKey.js +75 -0
- package/dist/sandbox/providers/boat/boatService.d.ts +2 -1
- package/dist/sandbox/providers/boat/boatService.js +72 -20
- package/dist/sandbox/providers/exedev/exeDevService.js +4 -2
- package/dist/sandbox/providers/exedev/exeDevToken.js +2 -6
- package/dist/sandbox/providers/local/localService.js +4 -26
- package/dist/sandbox/providers/ssh/sshService.js +2 -1
- package/dist/sandbox/service.d.ts +2 -1
- package/dist/sandbox/service.js +9 -1
- package/dist/vendor-types/claude/agent-sdk/bridge.d.ts +20 -4
- package/dist/vendor-types/claude/agent-sdk/browser-sdk.d.ts +24 -0
- package/dist/vendor-types/claude/agent-sdk/core.d.ts +2 -0
- package/dist/vendor-types/claude/agent-sdk/sdk-tools.d.ts +75 -6
- package/dist/vendor-types/claude/agent-sdk/sdk.d.ts +569 -100
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ hyperbrane dimension create --dir ~/work -m 4G --cpus 2 work
|
|
|
22
22
|
## Library
|
|
23
23
|
|
|
24
24
|
```ts
|
|
25
|
-
import { createAgentClient, createDimensionClient, createGatewayClient, createPortalClient, createSandboxClient, MemoryLimit, CPULimit } from "hyperbrane";
|
|
25
|
+
import { createAgentClient, createDimensionClient, createGatewayClient, createPortalClient, createSandboxClient, listSandboxes, MemoryLimit, CPULimit } from "hyperbrane";
|
|
26
26
|
|
|
27
27
|
const host = createSandboxClient({ kind: "ssh", sshHost: "server.example.com", sshUser: "me", sshKey: privateKey });
|
|
28
28
|
// A client per part, over the sandbox: making one does nothing.
|
|
@@ -37,11 +37,16 @@ await dimensions.createDimension({ name: "work", directory: "work", memoryLimit:
|
|
|
37
37
|
await portals.createPortal({ name: "work-main", dimensionName: "work", gatewayName: "main" });
|
|
38
38
|
await agents.createAgent({ name: "dev", dimensionName: "work" });
|
|
39
39
|
await agents.install("dev", "claude");
|
|
40
|
+
// A command in the dimension, as its user, in its workspace unless given a cwd.
|
|
41
|
+
const { exitCode, stdout } = await dimensions.runCommand("work", ["git", "status"], { cwd: "repo" });
|
|
40
42
|
```
|
|
41
43
|
|
|
42
44
|
Each call resolves once what it made is usable: a gateway once it intercepts, a portal once traffic
|
|
43
45
|
flows through it, an install once the backend runs. Re-running any of them converges.
|
|
44
46
|
|
|
47
|
+
`sshKey` is an unencrypted private key, in OpenSSH format (as `ssh-keygen` writes it), PKCS#8 (as
|
|
48
|
+
Node's `crypto` exports Ed25519, RSA and ECDSA keys), PKCS#1 (RSA) or SEC1 (ECDSA).
|
|
49
|
+
|
|
45
50
|
hyperbrane runs where the application does and drives the machine from there, each call over a
|
|
46
51
|
connection of its own (SSH, Boat's API): nothing is kept between calls, so it runs in serverless
|
|
47
52
|
functions too. A browser reaches the machine through data endpoints instead: ports the sandbox makes
|
|
@@ -58,6 +63,8 @@ const boat = createSandboxClient({ kind: "boat", sshKey }); // BOAT_API_KEY; rea
|
|
|
58
63
|
const [ctx] = await boat.create({ name: "box" }); // ctx, now with the sandbox's id, is what's kept
|
|
59
64
|
// Elsewhere, with ctx alone: its operations read its state; open() resumes it first.
|
|
60
65
|
const again = createSandboxClient(ctx);
|
|
66
|
+
// Or found by its name, with the API key alone: every sandbox the key sees, and names can repeat.
|
|
67
|
+
const [found] = (await listSandboxes({ kind: "boat" })).filter((sandbox) => sandbox.name === "box");
|
|
61
68
|
```
|
|
62
69
|
|
|
63
70
|
Under a `url` with a root path, every endpoint is at `<url>/<name>` (with
|
|
@@ -79,19 +86,52 @@ can be told `EndpointUnreachableError` or `EndpointCredentialsError` (get new on
|
|
|
79
86
|
agent can share one connection: `sharedConnector(workerGatewayConnector(...))` opens it on first use
|
|
80
87
|
and keeps it until `close()`.
|
|
81
88
|
|
|
89
|
+
Code running in a dimension (a script a session runs, a service in the workspace) drives the
|
|
90
|
+
dimension's agents over their workers' sockets, under `/run/hyperbrane`:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { createDimensionAgentClient, currentSession } from "hyperbrane";
|
|
94
|
+
|
|
95
|
+
const agents = createDimensionAgentClient();
|
|
96
|
+
const me = currentSession(); // the session this code runs under ({ agentName, sessionName }), or null
|
|
97
|
+
await agents.createSession(me!.agentName, { name: "helper", backend: "claude", memoryLimit: MemoryLimit("2G"), cpuLimit: CPULimit("1") });
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
It does what the agents' workers do: sessions, their streams, transcripts, permissions and the
|
|
101
|
+
rest, and installing backends. Making or stopping agents, and starting their workers, take the
|
|
102
|
+
machine: `listAgents()` names the agents whose workers run, and a call to an agent whose worker
|
|
103
|
+
doesn't gets `AgentConflictError` until a call through the machine's `agents` starts it. Its
|
|
104
|
+
`login` doesn't know whether the gateway already holds a sign-in, so it signs in as the backend
|
|
105
|
+
does. Anything in the dimension can drive its agents this way, a session itself included.
|
|
106
|
+
|
|
82
107
|
What lasts:
|
|
83
108
|
|
|
84
109
|
| | Container restart | Container recreated | Machine reboot, Boat resume |
|
|
85
110
|
|---|---|---|---|
|
|
86
111
|
| Workspace, sessions, sign-ins, installed backends | kept | kept | kept |
|
|
87
112
|
| Agent workers | started again by the next call (or `agents.restoreAgents()`) | same | same |
|
|
88
|
-
| A gateway's transport worker (on the machine) | kept | kept | started again by `
|
|
89
|
-
| A dimension's transport worker (in it) | started again by `restartDimension` | started by `createDimension` | started again by `
|
|
113
|
+
| A gateway's transport worker (on the machine) | kept | kept | started again by `restore` (or `gateways.restoreGateways()`) |
|
|
114
|
+
| A dimension's transport worker (in it) | started again by `restartDimension` or `restore` | started by `createDimension` | started again by `restore` (or `dimensions.restoreDimensions()`) |
|
|
90
115
|
| Portals | re-wired by `restartDimension` and `restartGateway`; otherwise as on the right | re-wired by the next agent call, or `portals.restorePortals()` | same |
|
|
91
116
|
|
|
117
|
+
After a reboot or a Boat resume, one call starts again whatever didn't survive, in the order it
|
|
118
|
+
needs: the gateways' transport workers and the dimensions' (without restarting their containers),
|
|
119
|
+
then the portals, then the agents' workers. It's safe to call any time, and does nothing more where
|
|
120
|
+
everything runs:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { restore } from "hyperbrane";
|
|
124
|
+
|
|
125
|
+
await sandbox.open(); // resumes a stopped Boat sandbox
|
|
126
|
+
const { gateways, dimensions, agents } = await restore(sandbox); // what it started again
|
|
127
|
+
```
|
|
128
|
+
|
|
92
129
|
A Boat sandbox stops itself after a while; `sandbox.hasExpiration()` tells a sandbox that does, and
|
|
93
130
|
`setExpiration` moves it.
|
|
94
131
|
|
|
132
|
+
On Boat, `readFile`, `readFileRange`, `writeFile` and `writeFileExclusive` reach what Boat's file API
|
|
133
|
+
does: files under `/home/user` and `/tmp`. Another path is a `SandboxPathError`, which says why.
|
|
134
|
+
|
|
95
135
|
The root module exports the clients (`createDimensionClient`, `createGatewayClient`,
|
|
96
136
|
`createPortalClient`, `createAgentClient`), the sandbox of any kind (`createSandboxClient`) and the
|
|
97
137
|
agent's worker client. An upstream of your own is a `defineUpstream({ name, modes })`, given to a
|
|
@@ -94,7 +94,7 @@ export function createAgentService(host, { dataDirectory } = {}) {
|
|
|
94
94
|
const agent = await requireAgent(agentName);
|
|
95
95
|
const dimension = await runningDimension(agent.dimensionName);
|
|
96
96
|
const health = await rawWorkerClient(dimension, agentName).call("worker/health", {}).catch(() => null);
|
|
97
|
-
if (health
|
|
97
|
+
if (!health || health.protocol < WORKER_PROTOCOL)
|
|
98
98
|
await reviveWorker(agentName, dimension.name);
|
|
99
99
|
return workerSocketPath(dimension.runDirectory, agentName);
|
|
100
100
|
}
|
|
@@ -140,9 +140,10 @@ export function createAgentService(host, { dataDirectory } = {}) {
|
|
|
140
140
|
install: (agentName, backend) => installScript(host, agentName, backend, controlOptions),
|
|
141
141
|
listBackends: (agentName) => listBackendsScript(host, agentName, controlOptions),
|
|
142
142
|
createSession: async (agentName, session) => (await clientFor(agentName)).call("session/create", { session }),
|
|
143
|
-
updateSession: async (ref,
|
|
143
|
+
updateSession: async (ref, update) => (await sessionClient(ref)).call("session/update", { session: ref.sessionName, update }),
|
|
144
144
|
stopSession: async (ref) => void (await (await sessionClient(ref)).call("session/stop", { session: ref.sessionName })),
|
|
145
145
|
getTranscript,
|
|
146
|
+
getUsage: async (ref) => (await sessionClient(ref)).call("session/usage", { session: ref.sessionName }),
|
|
146
147
|
cancel: async (ref) => void (await (await sessionClient(ref)).call("session/interrupt", { session: ref.sessionName })),
|
|
147
148
|
sendMessage: async (ref, message) => void (await (await sessionClient(ref)).call("session/send", { session: ref.sessionName, message })),
|
|
148
149
|
getSessionStatus: async (ref) => (await sessionClient(ref)).call("session/status", { session: ref.sessionName }),
|
package/dist/agent/api.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export type AgentSession = {
|
|
|
18
18
|
env?: Record<string, string>;
|
|
19
19
|
};
|
|
20
20
|
export type AgentSessionLimits = Partial<Pick<AgentSession, "memoryLimit" | "cpuLimit">>;
|
|
21
|
+
export type AgentSessionUpdate = Partial<Omit<AgentSession, "name">>;
|
|
21
22
|
export type AgentSessionState = ({
|
|
22
23
|
readonly state: "exited";
|
|
23
24
|
readonly code: number | null;
|
|
@@ -97,7 +98,7 @@ export interface AgentClient {
|
|
|
97
98
|
stopAgent(agentName: string): Promise<void>;
|
|
98
99
|
listAgents(): Promise<Agent[]>;
|
|
99
100
|
createSession(agentName: string, session: AgentSession): Promise<AgentSessionInfo>;
|
|
100
|
-
updateSession(ref: SessionRef,
|
|
101
|
+
updateSession(ref: SessionRef, update: AgentSessionUpdate): Promise<AgentSessionInfo>;
|
|
101
102
|
listSessions(): Promise<AgentSessionInfo[]>;
|
|
102
103
|
stopSession(ref: SessionRef): Promise<void>;
|
|
103
104
|
restoreAgents(): Promise<string[]>;
|
|
@@ -224,7 +225,30 @@ export interface AgentModels {
|
|
|
224
225
|
export interface AgentPermissionModes {
|
|
225
226
|
setPermissionMode(ref: SessionRef, mode: AgentPermissionMode): Promise<void>;
|
|
226
227
|
}
|
|
227
|
-
export type
|
|
228
|
+
export type AgentModelUsage = {
|
|
229
|
+
readonly model: string;
|
|
230
|
+
readonly inputTokens: number;
|
|
231
|
+
readonly outputTokens: number;
|
|
232
|
+
readonly cacheReadTokens: number;
|
|
233
|
+
readonly cacheWriteTokens: number;
|
|
234
|
+
readonly costUsd: number | null;
|
|
235
|
+
};
|
|
236
|
+
export type AgentTurnUsage = {
|
|
237
|
+
readonly id: string;
|
|
238
|
+
readonly startedAt: string;
|
|
239
|
+
readonly endedAt: string;
|
|
240
|
+
readonly models: AgentModelUsage[];
|
|
241
|
+
readonly costUsd: number | null;
|
|
242
|
+
};
|
|
243
|
+
/** @sealed */
|
|
244
|
+
export interface AgentUsage {
|
|
245
|
+
getUsage(ref: SessionRef): Promise<AgentTurnUsage[]>;
|
|
246
|
+
}
|
|
247
|
+
export type AgentSessionCapabilities = AgentStream & AgentTranscript & AgentInterrupt & AgentInput & AgentSessionStatus & AgentMessageQueue & AgentPermissions & AgentQuestions & AgentBackgroundTasks & AgentAuth & AgentModels & AgentPermissionModes & AgentUsage;
|
|
248
|
+
/** @sealed */
|
|
249
|
+
export interface DimensionAgentClient extends Pick<AgentClient, "createSession" | "updateSession" | "listSessions" | "stopSession">, AgentProvision, AgentSessionCapabilities {
|
|
250
|
+
listAgents(): Promise<string[]>;
|
|
251
|
+
}
|
|
228
252
|
export type AgentLastUserMessage = {
|
|
229
253
|
readonly message: UserMessage;
|
|
230
254
|
readonly queued: boolean;
|
package/dist/agent/control.js
CHANGED
|
@@ -295,7 +295,7 @@ mkdir -p "$RUN"
|
|
|
295
295
|
exec 8> "$RUN/$AGENT.start.lock"
|
|
296
296
|
flock -w "$((START_TIMEOUT + STOP_TIMEOUT + 30))" 8
|
|
297
297
|
|
|
298
|
-
if worker_running "$AGENT" && [ "$WORKER_PROTOCOL" = "$PROTOCOL" ] && { [ -z "\${REPLACE_BUILD:-}" ] || [ "$WORKER_BUILD" = "$BUILD" ]; }; then exit 0; fi
|
|
298
|
+
if worker_running "$AGENT" && { [ "$WORKER_PROTOCOL" -gt "$PROTOCOL" ] 2> /dev/null || { [ "$WORKER_PROTOCOL" = "$PROTOCOL" ] && { [ -z "\${REPLACE_BUILD:-}" ] || [ "$WORKER_BUILD" = "$BUILD" ]; }; }; }; then exit 0; fi
|
|
299
299
|
|
|
300
300
|
runs_as="\${DIMENSION_USER%%:*}"
|
|
301
301
|
SOCKET_OWNER=""
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type DimensionAgentClient, type SessionRef } from "./api.ts";
|
|
2
|
+
export type DimensionDirectories = {
|
|
3
|
+
runDirectory: string;
|
|
4
|
+
stateDirectory: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function createDimensionAgentClient(): DimensionAgentClient;
|
|
7
|
+
export declare function currentSession(): SessionRef | null;
|
|
8
|
+
export declare function dimensionAgentClient({ runDirectory, stateDirectory }: DimensionDirectories): DimensionAgentClient;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { dimensionLayout } from "../dimension/api.js";
|
|
3
|
+
import { InvalidNameError, checkName } from "../lib/jsonDirectory.js";
|
|
4
|
+
import { SandboxConnectionRefusedError } from "../sandbox/api.js";
|
|
5
|
+
import { connectUnixSocket } from "../lib/unixSocket.js";
|
|
6
|
+
import { AgentConflictError, AgentInvalidError, AgentNotFoundError, } from "./api.js";
|
|
7
|
+
import { createWorkerClient } from "./session/client.js";
|
|
8
|
+
import { workerSocketPath } from "./session/sessionStorage.js";
|
|
9
|
+
import { workerStreamConnector } from "./session/workerConnection.js";
|
|
10
|
+
export function createDimensionAgentClient() {
|
|
11
|
+
return dimensionAgentClient({ runDirectory: dimensionLayout.runPath(), stateDirectory: dimensionLayout.statePath() });
|
|
12
|
+
}
|
|
13
|
+
export function currentSession() {
|
|
14
|
+
const { HYB_AGENT: agentName, HYB_SESSION: sessionName } = process.env;
|
|
15
|
+
return agentName && sessionName ? { agentName, sessionName } : null;
|
|
16
|
+
}
|
|
17
|
+
export function dimensionAgentClient({ runDirectory, stateDirectory }) {
|
|
18
|
+
function connector(agentName) {
|
|
19
|
+
const socket = workerSocketPath(runDirectory, agentName);
|
|
20
|
+
const connect = workerStreamConnector(() => connectUnixSocket(socket), socket);
|
|
21
|
+
return async (onNotification) => {
|
|
22
|
+
assertValidName("agent", agentName);
|
|
23
|
+
try {
|
|
24
|
+
return await connect(onNotification);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
if (!(error instanceof SandboxConnectionRefusedError))
|
|
28
|
+
throw error;
|
|
29
|
+
const known = await stat(`${stateDirectory}/agents/${agentName}`).then(() => true, () => false);
|
|
30
|
+
if (!known)
|
|
31
|
+
throw new AgentNotFoundError(`agent ${agentName} does not exist in this dimension`);
|
|
32
|
+
throw new AgentConflictError(`agent ${agentName}'s worker isn't running; a call through the machine's agents client starts it`);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function clientFor(agentName) {
|
|
37
|
+
return createWorkerClient(connector(agentName));
|
|
38
|
+
}
|
|
39
|
+
const sessionClient = ({ agentName }) => clientFor(agentName);
|
|
40
|
+
async function listAgents() {
|
|
41
|
+
const entries = await readdir(runDirectory).catch(() => []);
|
|
42
|
+
const names = entries.filter((entry) => entry.endsWith(".ready") && !entry.startsWith(".")).map((entry) => entry.slice(0, -".ready".length));
|
|
43
|
+
const running = await Promise.all(names.map((name) => stat(workerSocketPath(runDirectory, name)).then(() => name, () => null)));
|
|
44
|
+
return running.filter((name) => name !== null).sort();
|
|
45
|
+
}
|
|
46
|
+
async function* getStream(ref, request) {
|
|
47
|
+
yield* sessionClient(ref).stream(ref.sessionName, request.after_id);
|
|
48
|
+
}
|
|
49
|
+
async function listSessions() {
|
|
50
|
+
const listed = await Promise.all((await listAgents()).map((agentName) => clientFor(agentName)
|
|
51
|
+
.call("session/list", {})
|
|
52
|
+
.catch((error) => {
|
|
53
|
+
if (error instanceof AgentConflictError || error instanceof AgentNotFoundError)
|
|
54
|
+
return [];
|
|
55
|
+
throw error;
|
|
56
|
+
})));
|
|
57
|
+
return listed.flat();
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
listAgents,
|
|
61
|
+
listSessions,
|
|
62
|
+
install: async (agentName, backend) => void (await clientFor(agentName).call("backend/install", { backend })),
|
|
63
|
+
listBackends: (agentName) => clientFor(agentName).call("backend/list", {}),
|
|
64
|
+
createSession: (agentName, session) => clientFor(agentName).call("session/create", { session }),
|
|
65
|
+
updateSession: (ref, update) => sessionClient(ref).call("session/update", { session: ref.sessionName, update }),
|
|
66
|
+
stopSession: async (ref) => void (await sessionClient(ref).call("session/stop", { session: ref.sessionName })),
|
|
67
|
+
getStream,
|
|
68
|
+
getTranscript: (ref, { toolUseId, limit, before } = {}) => toolUseId === undefined
|
|
69
|
+
? sessionClient(ref).call("session/transcript", { session: ref.sessionName, limit, before })
|
|
70
|
+
: sessionClient(ref).call("session/subagentTranscript", { session: ref.sessionName, toolUseId, limit, before }),
|
|
71
|
+
getUsage: (ref) => sessionClient(ref).call("session/usage", { session: ref.sessionName }),
|
|
72
|
+
cancel: async (ref) => void (await sessionClient(ref).call("session/interrupt", { session: ref.sessionName })),
|
|
73
|
+
sendMessage: async (ref, message) => void (await sessionClient(ref).call("session/send", { session: ref.sessionName, message })),
|
|
74
|
+
getSessionStatus: (ref) => sessionClient(ref).call("session/status", { session: ref.sessionName }),
|
|
75
|
+
getLastUserMessages: (ref) => sessionClient(ref).call("session/lastUserMessages", { session: ref.sessionName }),
|
|
76
|
+
listPermissionRequests: (ref) => sessionClient(ref).call("permission/list", { session: ref.sessionName }),
|
|
77
|
+
respondPermissionRequest: async (ref, request, result) => void (await sessionClient(ref).call("permission/respond", { session: ref.sessionName, request, result })),
|
|
78
|
+
listQuestionRequests: (ref) => sessionClient(ref).call("question/list", { session: ref.sessionName }),
|
|
79
|
+
respondQuestionRequest: async (ref, request, response) => void (await sessionClient(ref).call("question/respond", { session: ref.sessionName, request, response })),
|
|
80
|
+
listModels: (ref) => sessionClient(ref).call("model/list", { session: ref.sessionName }),
|
|
81
|
+
setModel: async (ref, model) => void (await sessionClient(ref).call("model/set", { session: ref.sessionName, model })),
|
|
82
|
+
setPermissionMode: async (ref, mode) => void (await sessionClient(ref).call("permission/mode", { session: ref.sessionName, mode })),
|
|
83
|
+
listBackgroundTasks: (ref) => sessionClient(ref).call("task/list", { session: ref.sessionName }),
|
|
84
|
+
stopTask: async (ref, task) => void (await sessionClient(ref).call("task/stop", { session: ref.sessionName, task })),
|
|
85
|
+
getAuthStatus: (ref) => sessionClient(ref).call("auth/status", { session: ref.sessionName }),
|
|
86
|
+
login: (ref) => sessionClient(ref).call("auth/login", { session: ref.sessionName, gatewaySignedIn: false }),
|
|
87
|
+
submitAuthCode: (ref, code) => sessionClient(ref).call("auth/code", { session: ref.sessionName, code }),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function assertValidName(kind, name) {
|
|
91
|
+
try {
|
|
92
|
+
checkName(name);
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
if (error instanceof InvalidNameError)
|
|
96
|
+
throw new AgentInvalidError(`invalid ${kind} name: ${error.message}`);
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentBackendInfo, AgentBackgroundTasks, AgentInput, AgentInterrupt, AgentMessageQueue, AgentModels, AgentPermissionMode, AgentPermissionModes, AgentPermissionRequest, AgentPermissionResult, AgentQuestionRequest, AgentQuestionResponse, AgentSession, AgentStreamingMessage, SessionRef } from "../api.ts";
|
|
1
|
+
import type { AgentBackendInfo, AgentBackgroundTasks, AgentInput, AgentInterrupt, AgentMessageQueue, AgentModels, AgentPermissionMode, AgentPermissionModes, AgentPermissionRequest, AgentPermissionResult, AgentQuestionRequest, AgentQuestionResponse, AgentSession, AgentStreamingMessage, AgentTurnUsage, SessionRef } from "../api.ts";
|
|
2
2
|
import type { Transcript, TranscriptFiles } from "./fileTranscript.ts";
|
|
3
3
|
export type ForSession<Capability> = {
|
|
4
4
|
[K in keyof Capability]: Capability[K] extends (ref: SessionRef, ...args: infer Args) => infer Result ? (...args: Args) => Result : never;
|
|
@@ -63,5 +63,6 @@ export type SessionBackend = {
|
|
|
63
63
|
transcript(source: TranscriptSource): Transcript;
|
|
64
64
|
findSubagent(source: TranscriptSource, toolUseId: string): Promise<string | null>;
|
|
65
65
|
subagentTranscript(source: TranscriptSource, subagentId: string, toolUseId: string): Transcript;
|
|
66
|
+
usage(source: TranscriptSource): Promise<AgentTurnUsage[]>;
|
|
66
67
|
homeEnvironment(home: string): Record<string, string>;
|
|
67
68
|
};
|
|
@@ -4,5 +4,6 @@ import { type FileTranscript, type TranscriptLayout } from "../fileTranscript.ts
|
|
|
4
4
|
export declare function claudeTranscript({ nativeId, env, files }: TranscriptSource): FileTranscript;
|
|
5
5
|
export declare function claudeSubagentTranscript(source: TranscriptSource, agentId: string, toolUseId: string): FileTranscript;
|
|
6
6
|
export declare function claudeFindSubagent(source: TranscriptSource, toolUseId: string): Promise<string | null>;
|
|
7
|
+
export declare function claudeHome(env: Record<string, string>): string;
|
|
7
8
|
export declare const CLAUDE_TRANSCRIPT_LAYOUT: TranscriptLayout;
|
|
8
9
|
export declare function claudeLineMessages(line: unknown): AgentTranscriptMessage[];
|
|
@@ -31,7 +31,7 @@ async function subagentsDirectory({ nativeId, env, files }) {
|
|
|
31
31
|
const main = await locateTranscript(files, claudeHome(env), CLAUDE_TRANSCRIPT_LAYOUT, nativeId);
|
|
32
32
|
return main ? `${main.slice(0, -".jsonl".length)}/subagents` : null;
|
|
33
33
|
}
|
|
34
|
-
function claudeHome(env) {
|
|
34
|
+
export function claudeHome(env) {
|
|
35
35
|
return env.CLAUDE_CONFIG_DIR ?? `${env.HOME}/.claude`;
|
|
36
36
|
}
|
|
37
37
|
const SUBAGENT_FILE = /^agent-.+\.jsonl$/;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentAuthStatus, type AgentBackend, type AgentBackendInfo, type AgentBackgroundTask, type AgentLastUserMessage, type AgentModelInfo, type AgentPermissionRequest, type AgentPermissionResult, type AgentQuestionRequest, type AgentQuestionResponse, type AgentSession, type AgentSessionInfo, type
|
|
1
|
+
import { type AgentAuthStatus, type AgentBackend, type AgentBackendInfo, type AgentBackgroundTask, type AgentLastUserMessage, type AgentModelInfo, type AgentPermissionRequest, type AgentPermissionResult, type AgentQuestionRequest, type AgentQuestionResponse, type AgentSession, type AgentSessionInfo, type AgentSessionUpdate, type AgentSessionState, type AgentPermissionMode, type AgentStreamingMessage, type AgentTranscriptPage, type AgentTurnUsage, type UserMessage } from "../../api.ts";
|
|
2
2
|
import type { JsonRpcErrorObject } from "./jsonRpc.ts";
|
|
3
3
|
/** @sealed */
|
|
4
4
|
export type WorkerRequests = {
|
|
@@ -14,7 +14,7 @@ export type WorkerRequests = {
|
|
|
14
14
|
"session/list": [Record<string, never>, AgentSessionInfo[]];
|
|
15
15
|
"session/update": [{
|
|
16
16
|
session: string;
|
|
17
|
-
|
|
17
|
+
update: AgentSessionUpdate;
|
|
18
18
|
}, AgentSessionInfo];
|
|
19
19
|
"session/stop": [{
|
|
20
20
|
session: string;
|
|
@@ -52,6 +52,9 @@ export type WorkerRequests = {
|
|
|
52
52
|
limit?: number;
|
|
53
53
|
before?: string;
|
|
54
54
|
}, AgentTranscriptPage];
|
|
55
|
+
"session/usage": [{
|
|
56
|
+
session: string;
|
|
57
|
+
}, AgentTurnUsage[]];
|
|
55
58
|
"permission/list": [{
|
|
56
59
|
session: string;
|
|
57
60
|
}, AgentPermissionRequest[]];
|
|
@@ -105,7 +108,7 @@ export type WorkerNotifications = {
|
|
|
105
108
|
message: AgentStreamingMessage;
|
|
106
109
|
};
|
|
107
110
|
};
|
|
108
|
-
export declare const WORKER_PROTOCOL =
|
|
111
|
+
export declare const WORKER_PROTOCOL = 6;
|
|
109
112
|
export type WorkerHealth = {
|
|
110
113
|
agentName: string;
|
|
111
114
|
build: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AgentBackendNotInstalledError, AgentConflictError, AgentInvalidError, AgentNotFoundError, } from "../../api.js";
|
|
2
|
-
export const WORKER_PROTOCOL =
|
|
2
|
+
export const WORKER_PROTOCOL = 6;
|
|
3
3
|
export const WorkerErrorCode = {
|
|
4
4
|
methodNotFound: -32601,
|
|
5
5
|
invalidParams: -32602,
|
|
@@ -6,7 +6,7 @@ export const WORKER_PARAMS = {
|
|
|
6
6
|
"backend/list": () => ({}),
|
|
7
7
|
"session/create": (params) => ({ session: session(object(params.session, "session")) }),
|
|
8
8
|
"session/list": () => ({}),
|
|
9
|
-
"session/update": (params) => ({ session: name(params),
|
|
9
|
+
"session/update": (params) => ({ session: name(params), update: sessionUpdate(object(params.update, "update")) }),
|
|
10
10
|
"session/stop": (params) => ({ session: name(params) }),
|
|
11
11
|
"session/send": (params) => ({ session: name(params), message: userMessage(params.message) }),
|
|
12
12
|
"session/interrupt": (params) => ({ session: name(params) }),
|
|
@@ -29,6 +29,7 @@ export const WORKER_PARAMS = {
|
|
|
29
29
|
limit: optional(params.limit, (value) => number(value, "limit")),
|
|
30
30
|
before: optional(params.before, (value) => text(value, "before")),
|
|
31
31
|
}),
|
|
32
|
+
"session/usage": (params) => ({ session: name(params) }),
|
|
32
33
|
"permission/list": (params) => ({ session: name(params) }),
|
|
33
34
|
"permission/respond": (params) => ({
|
|
34
35
|
session: name(params),
|
|
@@ -126,8 +127,14 @@ function session(fields) {
|
|
|
126
127
|
env: optional(fields.env, environment),
|
|
127
128
|
};
|
|
128
129
|
}
|
|
129
|
-
function
|
|
130
|
-
return {
|
|
130
|
+
function sessionUpdate(fields) {
|
|
131
|
+
return {
|
|
132
|
+
backend: optional(fields.backend, backend),
|
|
133
|
+
memoryLimit: optional(fields.memoryLimit, memoryLimit),
|
|
134
|
+
cpuLimit: optional(fields.cpuLimit, cpuLimit),
|
|
135
|
+
cwd: optional(fields.cwd, (value) => text(value, "cwd")),
|
|
136
|
+
env: optional(fields.env, environment),
|
|
137
|
+
};
|
|
131
138
|
}
|
|
132
139
|
function userMessage(value) {
|
|
133
140
|
const fields = object(value, "a message (an SDK user message: {type: \"user\", message})");
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"docs": "https://code.claude.com/docs/en/agent-sdk/typescript",
|
|
4
4
|
"sources": [
|
|
5
5
|
{
|
|
6
|
-
"source": "npm:@anthropic-ai/claude-agent-sdk@0.3.
|
|
6
|
+
"source": "npm:@anthropic-ai/claude-agent-sdk@0.3.310",
|
|
7
7
|
"into": "agent-sdk",
|
|
8
8
|
"files": 7
|
|
9
9
|
}
|
|
@@ -13,75 +13,20 @@
|
|
|
13
13
|
"docs": "https://developers.openai.com/codex/app-server",
|
|
14
14
|
"sources": [
|
|
15
15
|
{
|
|
16
|
-
"source": "npm:@openai/codex-sdk@0.
|
|
16
|
+
"source": "npm:@openai/codex-sdk@0.164.0",
|
|
17
17
|
"into": "sdk",
|
|
18
18
|
"files": 1
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
|
-
"source": "github:openai/codex@rust-v0.
|
|
21
|
+
"source": "github:openai/codex@rust-v0.164.0/codex-rs/app-server-protocol/schema/typescript",
|
|
22
22
|
"into": "app-server",
|
|
23
|
-
"files":
|
|
23
|
+
"files": 751
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
|
-
"source": "github:openai/codex@rust-v0.
|
|
26
|
+
"source": "github:openai/codex@rust-v0.164.0/codex-rs/app-server-protocol/schema/json",
|
|
27
27
|
"into": "app-server-json",
|
|
28
28
|
"files": 2
|
|
29
29
|
}
|
|
30
30
|
]
|
|
31
|
-
},
|
|
32
|
-
"pi": {
|
|
33
|
-
"docs": "https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/rpc.md",
|
|
34
|
-
"sources": [
|
|
35
|
-
{
|
|
36
|
-
"source": "npm:@mariozechner/pi-coding-agent@0.73.1",
|
|
37
|
-
"into": "coding-agent",
|
|
38
|
-
"files": 137
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"source": "npm:@mariozechner/pi-agent-core@0.73.1",
|
|
42
|
-
"into": "agent-core",
|
|
43
|
-
"files": 5
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"source": "npm:@mariozechner/pi-ai@0.73.1",
|
|
47
|
-
"into": "ai",
|
|
48
|
-
"files": 44
|
|
49
|
-
}
|
|
50
|
-
]
|
|
51
|
-
},
|
|
52
|
-
"opencode": {
|
|
53
|
-
"docs": "https://opencode.ai/docs/server",
|
|
54
|
-
"sources": [
|
|
55
|
-
{
|
|
56
|
-
"source": "npm:@opencode-ai/sdk@1.18.32",
|
|
57
|
-
"into": "sdk",
|
|
58
|
-
"files": 39
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
"source": "github:anomalyco/opencode@v1.18.32/packages/sdk",
|
|
62
|
-
"into": "",
|
|
63
|
-
"files": 1
|
|
64
|
-
}
|
|
65
|
-
]
|
|
66
|
-
},
|
|
67
|
-
"openclaw": {
|
|
68
|
-
"docs": "https://docs.openclaw.ai/gateway/protocol",
|
|
69
|
-
"sources": [
|
|
70
|
-
{
|
|
71
|
-
"source": "npm:@openclaw/gateway-protocol@2026.9.6",
|
|
72
|
-
"into": "gateway-protocol",
|
|
73
|
-
"files": 17
|
|
74
|
-
}
|
|
75
|
-
]
|
|
76
|
-
},
|
|
77
|
-
"hermes": {
|
|
78
|
-
"docs": "https://hermes-agent.nousresearch.com/docs",
|
|
79
|
-
"sources": [
|
|
80
|
-
{
|
|
81
|
-
"source": "github:NousResearch/hermes-agent@v2026.9.24/apps/shared/src",
|
|
82
|
-
"into": "gateway",
|
|
83
|
-
"files": 3
|
|
84
|
-
}
|
|
85
|
-
]
|
|
86
31
|
}
|
|
87
32
|
}
|