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.
Files changed (50) hide show
  1. package/README.md +43 -3
  2. package/dist/agent/agentService.js +3 -2
  3. package/dist/agent/api.d.ts +26 -2
  4. package/dist/agent/control.js +1 -1
  5. package/dist/agent/dimensionAgentClient.d.ts +8 -0
  6. package/dist/agent/dimensionAgentClient.js +99 -0
  7. package/dist/agent/session/backend.d.ts +2 -1
  8. package/dist/agent/session/claude/transcript.d.ts +1 -0
  9. package/dist/agent/session/claude/transcript.js +1 -1
  10. package/dist/agent/session/worker/protocol.d.ts +6 -3
  11. package/dist/agent/session/worker/protocol.js +1 -1
  12. package/dist/agent/session/worker/workerParams.js +10 -3
  13. package/dist/agent/vendor-types/manifest.json +5 -60
  14. package/dist/agent/workerBundle.generated.js +1 -1
  15. package/dist/dimension/api.d.ts +3 -0
  16. package/dist/dimension/control.d.ts +2 -1
  17. package/dist/dimension/control.js +18 -0
  18. package/dist/dimension/service.js +27 -1
  19. package/dist/dimension/transportBundle.generated.js +1 -1
  20. package/dist/index.d.ts +3 -1
  21. package/dist/index.js +3 -1
  22. package/dist/lib/control.d.ts +4 -1
  23. package/dist/lib/control.js +2 -2
  24. package/dist/lib/jsonDirectory.js +24 -3
  25. package/dist/lib/unixSocket.d.ts +2 -0
  26. package/dist/lib/unixSocket.js +28 -0
  27. package/dist/portal-gateway/api.d.ts +1 -0
  28. package/dist/portal-gateway/gatewayService.js +14 -0
  29. package/dist/portal-gateway/transportBundle.generated.js +1 -1
  30. package/dist/restore.d.ts +9 -0
  31. package/dist/restore.js +10 -0
  32. package/dist/sandbox/api.d.ts +5 -0
  33. package/dist/sandbox/client.d.ts +2 -1
  34. package/dist/sandbox/client.js +4 -1
  35. package/dist/sandbox/lib/sshKey.d.ts +6 -0
  36. package/dist/sandbox/lib/sshKey.js +75 -0
  37. package/dist/sandbox/providers/boat/boatService.d.ts +2 -1
  38. package/dist/sandbox/providers/boat/boatService.js +72 -20
  39. package/dist/sandbox/providers/exedev/exeDevService.js +4 -2
  40. package/dist/sandbox/providers/exedev/exeDevToken.js +2 -6
  41. package/dist/sandbox/providers/local/localService.js +4 -26
  42. package/dist/sandbox/providers/ssh/sshService.js +2 -1
  43. package/dist/sandbox/service.d.ts +2 -1
  44. package/dist/sandbox/service.js +9 -1
  45. package/dist/vendor-types/claude/agent-sdk/bridge.d.ts +20 -4
  46. package/dist/vendor-types/claude/agent-sdk/browser-sdk.d.ts +24 -0
  47. package/dist/vendor-types/claude/agent-sdk/core.d.ts +2 -0
  48. package/dist/vendor-types/claude/agent-sdk/sdk-tools.d.ts +75 -6
  49. package/dist/vendor-types/claude/agent-sdk/sdk.d.ts +569 -100
  50. 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 `restartGateway` |
89
- | A dimension's transport worker (in it) | started again by `restartDimension` | started by `createDimension` | started again by `restartDimension` |
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?.protocol !== WORKER_PROTOCOL)
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, limits) => (await sessionClient(ref)).call("session/update", { session: ref.sessionName, limits }),
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 }),
@@ -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, limits: AgentSessionLimits): Promise<AgentSessionInfo>;
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 AgentSessionCapabilities = AgentStream & AgentTranscript & AgentInterrupt & AgentInput & AgentSessionStatus & AgentMessageQueue & AgentPermissions & AgentQuestions & AgentBackgroundTasks & AgentAuth & AgentModels & AgentPermissionModes;
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;
@@ -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 AgentSessionLimits, type AgentSessionState, type AgentPermissionMode, type AgentStreamingMessage, type AgentTranscriptPage, type UserMessage } from "../../api.ts";
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
- limits: AgentSessionLimits;
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 = 3;
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 = 3;
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), limits: limits(object(params.limits, "limits")) }),
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 limits(fields) {
130
- return { memoryLimit: optional(fields.memoryLimit, memoryLimit), cpuLimit: optional(fields.cpuLimit, cpuLimit) };
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.283",
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.157.1",
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.157.1/codex-rs/app-server-protocol/schema/typescript",
21
+ "source": "github:openai/codex@rust-v0.164.0/codex-rs/app-server-protocol/schema/typescript",
22
22
  "into": "app-server",
23
- "files": 740
23
+ "files": 751
24
24
  },
25
25
  {
26
- "source": "github:openai/codex@rust-v0.157.1/codex-rs/app-server-protocol/schema/json",
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
  }