hyperbrane 0.1.0 → 0.1.2
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 +41 -3
- package/dist/agent/agentService.d.ts +11 -3
- package/dist/agent/agentService.js +63 -19
- package/dist/agent/agentStorage.d.ts +2 -0
- package/dist/agent/agentStorage.js +4 -2
- package/dist/agent/api.d.ts +83 -32
- package/dist/agent/api.js +7 -0
- package/dist/agent/client.d.ts +3 -0
- package/dist/agent/client.js +5 -0
- package/dist/agent/hostTranscript.d.ts +13 -0
- package/dist/agent/hostTranscript.js +190 -0
- package/dist/agent/session/backend.d.ts +82 -0
- package/dist/agent/session/backend.js +1 -0
- package/dist/agent/session/claude/home.d.ts +2 -0
- package/dist/agent/session/claude/home.js +4 -0
- package/dist/agent/session/claude/transcript.d.ts +13 -0
- package/dist/agent/session/claude/transcript.js +29 -0
- package/dist/agent/session/client.d.ts +5 -1
- package/dist/agent/session/codex/home.d.ts +2 -0
- package/dist/agent/session/codex/home.js +4 -0
- package/dist/agent/session/codex/messages.d.ts +30 -0
- package/dist/agent/session/codex/messages.js +135 -0
- package/dist/agent/session/codex/transcript.d.ts +12 -0
- package/dist/agent/session/codex/transcript.js +22 -0
- package/dist/agent/session/fileTranscript.d.ts +92 -0
- package/dist/agent/session/fileTranscript.js +454 -0
- package/dist/agent/session/sdkMessages.d.ts +15 -0
- package/dist/agent/session/sdkMessages.js +28 -0
- package/dist/agent/session/sessionStorage.d.ts +4 -0
- package/dist/agent/session/sessionStorage.js +13 -2
- package/dist/agent/session/sharedConnection.d.ts +1 -0
- package/dist/agent/session/storedTranscripts.d.ts +13 -0
- package/dist/agent/session/storedTranscripts.js +8 -0
- package/dist/agent/session/worker/protocol.d.ts +5 -1
- package/dist/agent/session/worker/protocol.js +6 -1
- package/dist/agent/session/worker/workerParams.d.ts +11 -0
- package/dist/agent/session/worker/workerParams.js +163 -0
- package/dist/agent/session/workerSseConnection.d.ts +20 -4
- package/dist/agent/session/workerSseConnection.js +57 -7
- package/dist/agent/vendor-types/codex/app-server/AbsolutePathBuf.d.ts +10 -0
- package/dist/agent/vendor-types/codex/app-server/AbsolutePathBuf.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputBody.d.ts +2 -0
- package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputBody.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputContentItem.d.ts +22 -0
- package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputContentItem.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/ImageDetail.d.ts +1 -0
- package/dist/agent/vendor-types/codex/app-server/ImageDetail.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/ImageGenerationFailure.d.ts +5 -0
- package/dist/agent/vendor-types/codex/app-server/ImageGenerationFailure.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/ImageGenerationItem.d.ts +11 -0
- package/dist/agent/vendor-types/codex/app-server/ImageGenerationItem.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/LegacyAppPathString.d.ts +23 -0
- package/dist/agent/vendor-types/codex/app-server/LegacyAppPathString.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/MessagePhase.d.ts +7 -0
- package/dist/agent/vendor-types/codex/app-server/MessagePhase.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/ReasoningEffort.d.ts +4 -0
- package/dist/agent/vendor-types/codex/app-server/ReasoningEffort.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/SleepItem.d.ts +7 -0
- package/dist/agent/vendor-types/codex/app-server/SleepItem.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/WebSearchItem.d.ts +14 -0
- package/dist/agent/vendor-types/codex/app-server/WebSearchItem.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/serde_json/JsonValue.d.ts +3 -0
- package/dist/agent/vendor-types/codex/app-server/serde_json/JsonValue.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/AgentMessageDelivery.d.ts +1 -0
- package/dist/agent/vendor-types/codex/app-server/v2/AgentMessageDelivery.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/AsyncUserInputQuestion.d.ts +4 -0
- package/dist/agent/vendor-types/codex/app-server/v2/AsyncUserInputQuestion.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/ByteRange.d.ts +4 -0
- package/dist/agent/vendor-types/codex/app-server/v2/ByteRange.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentState.d.ts +5 -0
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentState.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentStatus.d.ts +1 -0
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentStatus.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentTool.d.ts +1 -0
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentTool.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentToolCallStatus.d.ts +1 -0
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentToolCallStatus.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/CommandAction.d.ts +19 -0
- package/dist/agent/vendor-types/codex/app-server/v2/CommandAction.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionSource.d.ts +1 -0
- package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionSource.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionStatus.d.ts +1 -0
- package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionStatus.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallOutputContentItem.d.ts +10 -0
- package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallOutputContentItem.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallStatus.d.ts +1 -0
- package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallStatus.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/FileUpdateChange.d.ts +6 -0
- package/dist/agent/vendor-types/codex/app-server/v2/FileUpdateChange.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/HookPromptFragment.d.ts +4 -0
- package/dist/agent/vendor-types/codex/app-server/v2/HookPromptFragment.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/McpAppDisplayMode.d.ts +1 -0
- package/dist/agent/vendor-types/codex/app-server/v2/McpAppDisplayMode.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/McpAppUi.d.ts +8 -0
- package/dist/agent/vendor-types/codex/app-server/v2/McpAppUi.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallAppContext.d.ts +7 -0
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallAppContext.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallError.d.ts +3 -0
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallError.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallResult.d.ts +6 -0
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallResult.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallStatus.d.ts +1 -0
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallStatus.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitation.d.ts +5 -0
- package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitation.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitationEntry.d.ts +6 -0
- package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitationEntry.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/PatchApplyStatus.d.ts +1 -0
- package/dist/agent/vendor-types/codex/app-server/v2/PatchApplyStatus.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/PatchChangeKind.d.ts +8 -0
- package/dist/agent/vendor-types/codex/app-server/v2/PatchChangeKind.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/SubAgentActivityKind.d.ts +1 -0
- package/dist/agent/vendor-types/codex/app-server/v2/SubAgentActivityKind.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/TextElement.d.ts +11 -0
- package/dist/agent/vendor-types/codex/app-server/v2/TextElement.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/ThreadItem.d.ts +215 -0
- package/dist/agent/vendor-types/codex/app-server/v2/ThreadItem.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/UserInput.d.ts +35 -0
- package/dist/agent/vendor-types/codex/app-server/v2/UserInput.js +2 -0
- package/dist/agent/vendor-types/codex/app-server/v2/WebSearchAction.d.ts +14 -0
- package/dist/agent/vendor-types/codex/app-server/v2/WebSearchAction.js +2 -0
- package/dist/agent/workerBundle.generated.js +1 -1
- package/dist/cli/agent.js +30 -4
- package/dist/cli/services.d.ts +6 -5
- package/dist/cli/services.js +4 -3
- package/dist/dimension/api.d.ts +5 -4
- package/dist/dimension/paths.d.ts +4 -0
- package/dist/dimension/paths.js +10 -2
- package/dist/dimension/service.d.ts +5 -1
- package/dist/dimension/service.js +5 -1
- package/dist/dimension/storage.js +3 -2
- package/dist/index.d.ts +2 -3
- package/dist/index.js +7 -5
- package/dist/lib/docker.d.ts +1 -0
- package/dist/portal-gateway/api.d.ts +6 -5
- package/dist/portal-gateway/gatewayService.d.ts +5 -1
- package/dist/portal-gateway/gatewayService.js +25 -13
- package/dist/portal-gateway/image.d.ts +1 -1
- package/dist/portal-gateway/image.js +1 -1
- package/dist/portal-gateway/imageFiles.generated.js +1 -1
- package/dist/portal-gateway/portalService.d.ts +2 -0
- package/dist/portal-gateway/portalService.js +61 -29
- package/dist/portal-gateway/upstream.d.ts +22 -11
- package/dist/sandbox/api.d.ts +60 -23
- package/dist/sandbox/boatService.js +11 -3
- package/dist/sandbox/containerService.js +10 -3
- package/dist/sandbox/endpointProxy.js +16 -1
- package/dist/sandbox/localService.js +20 -2
- package/dist/sandbox/sshService.js +37 -4
- package/package.json +25 -8
package/README.md
CHANGED
|
@@ -24,18 +24,56 @@ hyperbrane dimension create --dir ~/work -m 4G --cpus 2 work
|
|
|
24
24
|
import { createServices, createSshSandbox, MemoryLimit, CPULimit } from "hyperbrane";
|
|
25
25
|
|
|
26
26
|
const host = createSshSandbox("server.example.com", { user: "me", privateKey });
|
|
27
|
-
const { dimensions, agents } = createServices(host);
|
|
27
|
+
const { gateways, dimensions, portals, agents } = createServices(host);
|
|
28
|
+
// The gateway keeps the sign-ins, under its name: a gateway by another name starts with none.
|
|
29
|
+
await gateways.createGateway({ name: "main" });
|
|
28
30
|
await dimensions.createDimension({ name: "work", directory: "work", memoryLimit: MemoryLimit("4G"), cpuLimit: CPULimit("2") });
|
|
31
|
+
// A dimension's only way out: before installing anything in it.
|
|
32
|
+
await portals.createPortal({ name: "work-main", dimensionName: "work", gatewayName: "main" });
|
|
29
33
|
await agents.createAgent({ name: "dev", dimensionName: "work" });
|
|
34
|
+
await agents.install("dev", "claude");
|
|
30
35
|
```
|
|
31
36
|
|
|
37
|
+
Each call resolves once what it made is usable: a gateway once it intercepts, a portal once traffic
|
|
38
|
+
flows through it, an install once the backend runs. Re-running any of them converges.
|
|
39
|
+
|
|
40
|
+
hyperbrane runs where the application does and drives the machine from there: over SSH, or for Boat
|
|
41
|
+
through Boat's API, so the application needs nothing from the machine's own network or DNS. A
|
|
42
|
+
browser reaches an agent through `agents.exposeAgent`'s endpoint, and give it the page's origin:
|
|
43
|
+
origins add up, so a call without one removes nothing. The endpoint's URL lasts, but its proxy stops
|
|
44
|
+
with a reboot or Boat archive, and a page connecting then sees a CORS error. `exposeAgent` starts it
|
|
45
|
+
again, at the same URL: give `workerSseConnector` a function that calls it (through the page's server),
|
|
46
|
+
and it does so again whenever a connection fails to open, then throws `EndpointUnreachableError` if
|
|
47
|
+
that didn't help.
|
|
48
|
+
|
|
49
|
+
What lasts:
|
|
50
|
+
|
|
51
|
+
| | Container restart | Container recreated | Machine reboot, Boat resume |
|
|
52
|
+
|---|---|---|---|
|
|
53
|
+
| Workspace, sessions, sign-ins, installed backends | kept | kept | kept |
|
|
54
|
+
| Agent workers | started again by the next call (or `agents.restoreAgents()`) | same | same |
|
|
55
|
+
| Portals | re-wired by `restartDimension` and `restartGateway`; otherwise as on the right | re-wired by the next agent call, `portals.restorePortals()`, or `createPortalWatcher` (running) | same |
|
|
56
|
+
|
|
57
|
+
A Boat sandbox stops itself after a while; `hasExpiration(sandbox)` tells a sandbox that does, and
|
|
58
|
+
`setExpiration` moves it (`createBoatSandbox`'s own type has both).
|
|
59
|
+
|
|
32
60
|
The root module exports the services, the sandboxes (`createLocalSandbox`, `createSshSandbox`,
|
|
33
|
-
`createContainerSandbox`, `createBoatSandbox`), the endpoint auths and the agent client.
|
|
34
|
-
|
|
61
|
+
`createContainerSandbox`, `createBoatSandbox`), the endpoint auths and the agent client. For a
|
|
62
|
+
browser, which can't take the server side along, the package also exports each part's API
|
|
63
|
+
(`hyperbrane/sandbox/api`, `hyperbrane/dimension/api`, `hyperbrane/portal-gateway/api`,
|
|
64
|
+
`hyperbrane/agent/api`) and the agent's worker client (`hyperbrane/agent/client`). Nothing else is
|
|
65
|
+
importable.
|
|
66
|
+
|
|
67
|
+
What a client may rely on, as the types say: a `readonly` field is set by the package, for the client
|
|
68
|
+
to read; a type marked `@sealed` (the services, sandboxes, clients) is made by the package, for the
|
|
69
|
+
client to use, not implement. Both may gain members in any release; anything else gains only optional
|
|
70
|
+
ones, until a breaking release (`bun run api:diff --breaking` shows what would break).
|
|
35
71
|
|
|
36
72
|
## Building
|
|
37
73
|
|
|
38
74
|
```sh
|
|
39
75
|
bun run build # dist/
|
|
40
76
|
bun run check:package # packs it, installs the tarball elsewhere and uses it under Node and Bun
|
|
77
|
+
bun run api:diff [REF] # how the public interface changed since REF (HEAD): git diff of just that
|
|
78
|
+
bun run api:diff --breaking [REF] # only its breaking changes: removed, renamed, newly required, retyped
|
|
41
79
|
```
|
|
@@ -3,12 +3,18 @@ import type { Sandbox, SandboxAccessibleEndpoint } from "../sandbox/api.ts";
|
|
|
3
3
|
import type { AgentStorage } from "./agentStorage.ts";
|
|
4
4
|
import type { GatewaySignIns } from "./gatewaySignIns.ts";
|
|
5
5
|
import { type AgentClient, type AgentEndpoint, type AgentProvision, type AgentSessionCapabilities } from "./api.ts";
|
|
6
|
+
import type { PortalService } from "../portal-gateway/portalService.ts";
|
|
7
|
+
/** @sealed */
|
|
6
8
|
export interface AgentService extends AgentClient, AgentProvision, AgentSessionCapabilities, AgentEndpoint {
|
|
7
9
|
/** Starts the workers that a restart took down (their dimension runs again); returns their agents' names. */
|
|
8
10
|
restoreAgents(): Promise<string[]>;
|
|
9
11
|
}
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* In the dimension: where the backends are installed (a package with its node_modules). Shared by
|
|
14
|
+
* agents. In the state directory, so they last as long as the dimension: a container recreated (or a
|
|
15
|
+
* machine whose docker kept nothing) doesn't take them, and nothing is installed twice.
|
|
16
|
+
*/
|
|
17
|
+
export declare const WORKER_DIR = "/var/lib/hyperbrane/worker";
|
|
12
18
|
/**
|
|
13
19
|
* In the dimension: the worker's script. Outside WORKER_DIR on purpose: bun caches the listing of the
|
|
14
20
|
* directory a script is loaded from, and packages installed there afterwards didn't resolve.
|
|
@@ -28,4 +34,6 @@ export declare const WORKER_SCRIPT = "/opt/hyperbrane/worker.js";
|
|
|
28
34
|
* `host` is the machine docker runs on: workers' sockets and logs are reached through it, and agents
|
|
29
35
|
* are exposed through its endpoints when it declares them.
|
|
30
36
|
*/
|
|
31
|
-
export declare function createAgentService(storage: AgentStorage, docker: DockerClient, host: Sandbox | (Sandbox & SandboxAccessibleEndpoint), gatewaySignIns?: GatewaySignIns
|
|
37
|
+
export declare function createAgentService(storage: AgentStorage, docker: DockerClient, host: Sandbox | (Sandbox & SandboxAccessibleEndpoint), gatewaySignIns?: GatewaySignIns,
|
|
38
|
+
/** Re-wires a dimension's portals: what a restart (its container's, the machine's) took. */
|
|
39
|
+
restoring?: Pick<PortalService, "restorePortals">): AgentService;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { dimensionStateDirectory } from "../dimension/paths.js";
|
|
1
2
|
import { RUN_PATH, STATE_PATH, containerName as dimensionContainerName } from "../dimension/service.js";
|
|
2
3
|
import { createContainerSandbox } from "../sandbox/containerService.js";
|
|
3
4
|
import { InvalidNameError, checkName } from "../lib/jsonDirectory.js";
|
|
@@ -6,10 +7,18 @@ import { OS_CA_BUNDLE } from "../portal-gateway/portalService.js";
|
|
|
6
7
|
import { AgentConflictError, AgentInvalidError, AgentNotFoundError, } from "./api.js";
|
|
7
8
|
import { createWorkerClient } from "./session/client.js";
|
|
8
9
|
import { workerSocketConnector } from "./session/workerConnection.js";
|
|
10
|
+
import { readHostTranscript } from "./hostTranscript.js";
|
|
9
11
|
import { workerSocketPath } from "./session/sessionStorage.js";
|
|
12
|
+
import { WORKER_PARAMS } from "./session/worker/workerParams.js";
|
|
10
13
|
import { workerBundle } from "./workerBundle.js";
|
|
11
|
-
/**
|
|
12
|
-
|
|
14
|
+
/**
|
|
15
|
+
* In the dimension: where the backends are installed (a package with its node_modules). Shared by
|
|
16
|
+
* agents. In the state directory, so they last as long as the dimension: a container recreated (or a
|
|
17
|
+
* machine whose docker kept nothing) doesn't take them, and nothing is installed twice.
|
|
18
|
+
*/
|
|
19
|
+
export const WORKER_DIR = `${STATE_PATH}/worker`;
|
|
20
|
+
/** Where they were before: moved to WORKER_DIR when a worker starts (ensureWorker). */
|
|
21
|
+
const OLD_WORKER_DIR = "/opt/hyperbrane/worker";
|
|
13
22
|
/**
|
|
14
23
|
* In the dimension: the worker's script. Outside WORKER_DIR on purpose: bun caches the listing of the
|
|
15
24
|
* directory a script is loaded from, and packages installed there afterwards didn't resolve.
|
|
@@ -50,7 +59,9 @@ done
|
|
|
50
59
|
* `host` is the machine docker runs on: workers' sockets and logs are reached through it, and agents
|
|
51
60
|
* are exposed through its endpoints when it declares them.
|
|
52
61
|
*/
|
|
53
|
-
export function createAgentService(storage, docker, host, gatewaySignIns
|
|
62
|
+
export function createAgentService(storage, docker, host, gatewaySignIns,
|
|
63
|
+
/** Re-wires a dimension's portals: what a restart (its container's, the machine's) took. */
|
|
64
|
+
restoring) {
|
|
54
65
|
const endpoints = "exposeEndpoint" in host ? host : null;
|
|
55
66
|
async function runningDimension(dimensionName) {
|
|
56
67
|
const container = dimensionContainerName(dimensionName);
|
|
@@ -65,7 +76,7 @@ export function createAgentService(storage, docker, host, gatewaySignIns) {
|
|
|
65
76
|
if (!runDirectory || !hasState) {
|
|
66
77
|
throw new AgentConflictError(`dimension ${dimensionName} lacks the ${RUN_PATH} and ${STATE_PATH} mounts agents need (it predates them); recreate it`);
|
|
67
78
|
}
|
|
68
|
-
return { container, owner: inspected.Config.User || "0:0", runDirectory };
|
|
79
|
+
return { name: dimensionName, container, owner: inspected.Config.User || "0:0", runDirectory };
|
|
69
80
|
}
|
|
70
81
|
/** The worker as it is: for starting, stopping and checking on it. */
|
|
71
82
|
function rawWorkerClient(dimension, agentName) {
|
|
@@ -94,11 +105,16 @@ export function createAgentService(storage, docker, host, gatewaySignIns) {
|
|
|
94
105
|
function reviveWorker(agentName, dimension) {
|
|
95
106
|
let started = reviving.get(agentName);
|
|
96
107
|
if (!started) {
|
|
97
|
-
|
|
108
|
+
// Whatever took the worker down (a restart, a reboot, a Boat resume) took the dimension's
|
|
109
|
+
// portals too: without them the worker, and the backends it installs, can't reach anything.
|
|
110
|
+
const restorePortals = () => restoring?.restorePortals({ dimensionName: dimension.name });
|
|
111
|
+
started = Promise.resolve(restorePortals())
|
|
112
|
+
.then(() => ensureWorker(agentName, dimension))
|
|
98
113
|
.catch(async () => {
|
|
99
114
|
// Unreachable even freshly started: the container's mounts may no longer be the host's
|
|
100
115
|
// directories (it started while they were something else). A restart binds them again.
|
|
101
116
|
await docker.restartContainer(dimension.container);
|
|
117
|
+
await restorePortals();
|
|
102
118
|
await ensureWorker(agentName, dimension);
|
|
103
119
|
})
|
|
104
120
|
.finally(() => reviving.delete(agentName));
|
|
@@ -160,9 +176,13 @@ export function createAgentService(storage, docker, host, gatewaySignIns) {
|
|
|
160
176
|
return;
|
|
161
177
|
const root = createContainerSandbox(docker, container, { user: "root" });
|
|
162
178
|
// The run and state directories too: the host created them, possibly as another user than the dimension's.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
179
|
+
// Backends installed where they used to be move along, once.
|
|
180
|
+
const setup = await root.runCommand([
|
|
181
|
+
"sh",
|
|
182
|
+
"-c",
|
|
183
|
+
'mkdir -p "$W" "$(dirname "$SCRIPT")" && chown "$OWNER" "$(dirname "$SCRIPT")" "$W" "$R" "$S" && ' +
|
|
184
|
+
'{ [ -e "$W/node_modules" ] || [ ! -d "$OLD/node_modules" ] || { cp -a "$OLD/." "$W/" && rm -rf "$OLD"; }; }',
|
|
185
|
+
], { env: { W: WORKER_DIR, SCRIPT: WORKER_SCRIPT, OLD: OLD_WORKER_DIR, R: RUN_PATH, S: STATE_PATH, OWNER: owner } });
|
|
166
186
|
if (setup.exitCode !== 0)
|
|
167
187
|
throw new Error(`preparing ${container} for agent ${agentName} failed: ${setup.stderr.trim()}`);
|
|
168
188
|
const sandbox = createContainerSandbox(docker, container);
|
|
@@ -275,23 +295,34 @@ export function createAgentService(storage, docker, host, gatewaySignIns) {
|
|
|
275
295
|
// Lazy: the stored endpoint is served again if its proxy stopped (a reboot), with the same URL and
|
|
276
296
|
// credentials. The proxy connects to the worker's socket per stream, so a worker restart is fine.
|
|
277
297
|
// Each way the host is reached (its accessible host) has an endpoint of its own: several server
|
|
278
|
-
// records may drive the same machine.
|
|
298
|
+
// records may drive the same machine. Origins only add up: the endpoint is shared, and one caller's
|
|
299
|
+
// origins must not cut off another's pages (a narrower set closes nothing). What can run at once does.
|
|
279
300
|
async function exposeAgent(agentName, scheme, allowedOrigins = []) {
|
|
280
|
-
const agent = await requireAgent(agentName);
|
|
281
301
|
if (!endpoints)
|
|
282
302
|
throw new AgentInvalidError("this host doesn't declare accessible endpoints");
|
|
303
|
+
const key = endpoints.accessibleHost;
|
|
304
|
+
const [agent, stored] = await Promise.all([requireAgent(agentName), storage.readEndpoints(agentName).then((all) => all[key])]);
|
|
283
305
|
const dimension = await runningDimension(agent.dimensionName);
|
|
306
|
+
const [healthy, served] = await Promise.all([
|
|
307
|
+
rawWorkerClient(dimension, agentName).call("worker/health", {}).then(() => true, () => false),
|
|
308
|
+
stored && stored.credentials.scheme === scheme ? endpoints.ensureEndpoint(stored.id) : false,
|
|
309
|
+
]);
|
|
284
310
|
// The client connects to the worker's socket through the endpoint: nothing on its way could start
|
|
285
311
|
// a worker a restart took down, so it's started here.
|
|
286
|
-
if (!
|
|
312
|
+
if (!healthy)
|
|
287
313
|
await reviveWorker(agentName, dimension);
|
|
314
|
+
const origins = [...new Set([...(stored?.allowedOrigins ?? []), ...allowedOrigins])];
|
|
315
|
+
if (stored && served) {
|
|
316
|
+
if (origins.every((origin) => stored.allowedOrigins.includes(origin)))
|
|
317
|
+
return stored;
|
|
318
|
+
// Same URL and credentials, so pages already connected stay connected.
|
|
319
|
+
if (await endpoints.updateEndpoint(stored.id, { allowedOrigins: origins })) {
|
|
320
|
+
const updated = { ...stored, allowedOrigins: origins };
|
|
321
|
+
await storage.saveEndpoint(agentName, key, updated);
|
|
322
|
+
return updated;
|
|
323
|
+
}
|
|
288
324
|
}
|
|
289
|
-
|
|
290
|
-
const stored = (await storage.readEndpoints(agentName))[key];
|
|
291
|
-
const sameOrigins = (origins) => [...(origins ?? [])].sort().join("\n") === [...allowedOrigins].sort().join("\n");
|
|
292
|
-
if (stored && stored.credentials.scheme === scheme && sameOrigins(stored.allowedOrigins) && (await endpoints.ensureEndpoint(stored.id))) {
|
|
293
|
-
return stored;
|
|
294
|
-
}
|
|
325
|
+
// Another scheme, or it can't be served as it was given out (closed, its port taken).
|
|
295
326
|
if (stored) {
|
|
296
327
|
await endpoints.closeEndpoint(stored.id);
|
|
297
328
|
await storage.removeEndpoint(agentName, key);
|
|
@@ -300,7 +331,7 @@ export function createAgentService(storage, docker, host, gatewaySignIns) {
|
|
|
300
331
|
target: { unix: workerSocketPath(dimension.runDirectory, agentName) },
|
|
301
332
|
scheme,
|
|
302
333
|
transport: "sse",
|
|
303
|
-
allowedOrigins,
|
|
334
|
+
allowedOrigins: origins,
|
|
304
335
|
});
|
|
305
336
|
await storage.saveEndpoint(agentName, key, endpoint);
|
|
306
337
|
return endpoint;
|
|
@@ -320,6 +351,18 @@ export function createAgentService(storage, docker, host, gatewaySignIns) {
|
|
|
320
351
|
async function sessionClient({ agentName }) {
|
|
321
352
|
return clientFor(agentName);
|
|
322
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* Read on the host from the dimension's state directory (hostTranscript.ts), so nothing has to run,
|
|
356
|
+
* neither the worker nor the dimension; only a session whose backend's home is elsewhere asks its
|
|
357
|
+
* worker.
|
|
358
|
+
*/
|
|
359
|
+
async function getTranscript(ref, request = {}) {
|
|
360
|
+
const { session, limit, before } = WORKER_PARAMS["session/transcript"]({ session: ref.sessionName, ...request });
|
|
361
|
+
const agent = await requireAgent(ref.agentName);
|
|
362
|
+
const stateDirectory = dimensionStateDirectory(await storage.dataDirectory(), agent.dimensionName);
|
|
363
|
+
const page = await readHostTranscript(host, { stateDirectory, agentName: agent.name, sessionName: session }, { limit, before });
|
|
364
|
+
return page ?? (await sessionClient(ref)).call("session/transcript", { session, limit, before });
|
|
365
|
+
}
|
|
323
366
|
// Agents whose dimension or worker is down have no sessions to show right now.
|
|
324
367
|
async function listSessions() {
|
|
325
368
|
const perAgent = await Promise.all((await storage.listAgents()).map(async (agent) => {
|
|
@@ -356,10 +399,11 @@ export function createAgentService(storage, docker, host, gatewaySignIns) {
|
|
|
356
399
|
exposeAgent,
|
|
357
400
|
restoreAgents,
|
|
358
401
|
install: async (agentName, backend) => void (await (await clientFor(agentName)).call("backend/install", { backend })),
|
|
402
|
+
listBackends: async (agentName) => (await clientFor(agentName)).call("backend/list", {}),
|
|
359
403
|
createSession: async (agentName, session) => (await clientFor(agentName)).call("session/create", { session }),
|
|
360
404
|
updateSession: async (ref, limits) => (await sessionClient(ref)).call("session/update", { session: ref.sessionName, limits }),
|
|
361
405
|
stopSession: async (ref) => void (await (await sessionClient(ref)).call("session/stop", { session: ref.sessionName })),
|
|
362
|
-
getTranscript
|
|
406
|
+
getTranscript,
|
|
363
407
|
cancel: async (ref) => void (await (await sessionClient(ref)).call("session/interrupt", { session: ref.sessionName })),
|
|
364
408
|
sendMessage: async (ref, message) => void (await (await sessionClient(ref)).call("session/send", { session: ref.sessionName, message })),
|
|
365
409
|
getSessionStatus: async (ref) => (await sessionClient(ref)).call("session/status", { session: ref.sessionName }),
|
|
@@ -9,6 +9,8 @@ export interface AgentStorage {
|
|
|
9
9
|
/** No-op if missing. */
|
|
10
10
|
removeAgent(agentName: string): Promise<void>;
|
|
11
11
|
listAgents(): Promise<Agent[]>;
|
|
12
|
+
/** Hyperbrane's data directory these are kept in, with the dimensions' (see dimension/paths.ts). */
|
|
13
|
+
dataDirectory(): Promise<string>;
|
|
12
14
|
/**
|
|
13
15
|
* The agent's accessible endpoints (AgentService.exposeAgent), credentials included, by the
|
|
14
16
|
* accessible host each was exposed for: one per way the machine is reached.
|
|
@@ -4,9 +4,10 @@ import { createJsonDirectory, defaultDataDir } from "../lib/jsonDirectory.js";
|
|
|
4
4
|
* agents/<agent>.json, agent-endpoints/<agent>.json.
|
|
5
5
|
*/
|
|
6
6
|
export function createAgentStorage(sandbox, dir) {
|
|
7
|
-
const
|
|
7
|
+
const dataDirectory = async () => dir ?? (await defaultDataDir(sandbox));
|
|
8
|
+
const agents = createJsonDirectory(sandbox, async () => `${await dataDirectory()}/agents`);
|
|
8
9
|
// A record used to hold one endpoint; it is read as the entry for its URL's host:port.
|
|
9
|
-
const endpoints = createJsonDirectory(sandbox, async () => `${
|
|
10
|
+
const endpoints = createJsonDirectory(sandbox, async () => `${await dataDirectory()}/agent-endpoints`);
|
|
10
11
|
async function readEndpoints(agentName) {
|
|
11
12
|
const record = await endpoints.read(agentName);
|
|
12
13
|
if (!record)
|
|
@@ -44,6 +45,7 @@ export function createAgentStorage(sandbox, dir) {
|
|
|
44
45
|
readAgent,
|
|
45
46
|
removeAgent,
|
|
46
47
|
listAgents,
|
|
48
|
+
dataDirectory,
|
|
47
49
|
readEndpoints,
|
|
48
50
|
saveEndpoint,
|
|
49
51
|
removeEndpoint,
|
package/dist/agent/api.d.ts
CHANGED
|
@@ -30,18 +30,19 @@ export type AgentSessionLimits = Partial<Pick<AgentSession, "memoryLimit" | "cpu
|
|
|
30
30
|
*/
|
|
31
31
|
export type AgentSessionState = "stopped" | "starting" | "idle" | "running" | "waiting" | "error";
|
|
32
32
|
export type AgentSessionInfo = AgentSession & {
|
|
33
|
-
agentName: string;
|
|
34
|
-
status: AgentSessionState;
|
|
33
|
+
readonly agentName: string;
|
|
34
|
+
readonly status: AgentSessionState;
|
|
35
35
|
/** The backend's own id for the conversation (claude's session id, codex's thread id), once known. */
|
|
36
|
-
nativeId: string | null;
|
|
36
|
+
readonly nativeId: string | null;
|
|
37
37
|
/** Chosen through AgentModels; null for the backend's default. */
|
|
38
|
-
model: string | null;
|
|
38
|
+
readonly model: string | null;
|
|
39
39
|
};
|
|
40
40
|
/** Where a session lives: sessions are named per agent. */
|
|
41
41
|
export type SessionRef = {
|
|
42
42
|
agentName: string;
|
|
43
43
|
sessionName: string;
|
|
44
44
|
};
|
|
45
|
+
/** @sealed */
|
|
45
46
|
export interface AgentClient {
|
|
46
47
|
/** PUT /v1/agents/:agentName — starts (or updates) the worker in the dimension. */
|
|
47
48
|
createAgent(agent: Agent): Promise<Agent>;
|
|
@@ -83,13 +84,23 @@ export type AgentTranscriptMessage = Extract<AgentStreamingMessage, {
|
|
|
83
84
|
export type AgentTranscriptRequest = {
|
|
84
85
|
/** How many messages a page holds, about (a tool call and its result share a page); default 50, at most 500. */
|
|
85
86
|
limit?: number;
|
|
86
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* An ISO 8601 time: the page holds the newest messages from before it (a page's `nextBefore`, for
|
|
89
|
+
* the page before that one); omitted for the newest page.
|
|
90
|
+
*/
|
|
91
|
+
before?: string;
|
|
92
|
+
/** @deprecated `before` by its old name: a page's `nextCursor` is its `nextBefore`. */
|
|
87
93
|
cursor?: string;
|
|
88
94
|
};
|
|
89
|
-
/**
|
|
95
|
+
/**
|
|
96
|
+
* Newest pages first; each page's messages oldest first. `nextBefore` is the time of its oldest
|
|
97
|
+
* message, when there are older ones: every message of that time is on this page.
|
|
98
|
+
*/
|
|
90
99
|
export type AgentTranscriptPage = {
|
|
91
|
-
messages: AgentTranscriptMessage[];
|
|
92
|
-
|
|
100
|
+
readonly messages: AgentTranscriptMessage[];
|
|
101
|
+
readonly nextBefore: string | null;
|
|
102
|
+
/** @deprecated `nextBefore` by its old name, for requests that give it as `cursor`. */
|
|
103
|
+
readonly nextCursor: string | null;
|
|
93
104
|
};
|
|
94
105
|
export type UserMessage = SDKUserMessage;
|
|
95
106
|
/**
|
|
@@ -97,24 +108,24 @@ export type UserMessage = SDKUserMessage;
|
|
|
97
108
|
* `can_use_tool` control request) isn't exported, so this mirrors it.
|
|
98
109
|
*/
|
|
99
110
|
export type AgentPermissionRequest = {
|
|
100
|
-
id: string;
|
|
101
|
-
tool_name: string;
|
|
102
|
-
input: Record<string, unknown>;
|
|
103
|
-
tool_use_id: string;
|
|
111
|
+
readonly id: string;
|
|
112
|
+
readonly tool_name: string;
|
|
113
|
+
readonly input: Record<string, unknown>;
|
|
114
|
+
readonly tool_use_id: string;
|
|
104
115
|
/** Why the agent asks, when it says (e.g. codex's reason for wanting network access). */
|
|
105
|
-
decision_reason?: string;
|
|
116
|
+
readonly decision_reason?: string;
|
|
106
117
|
};
|
|
107
118
|
export type AgentPermissionResult = PermissionResult;
|
|
108
119
|
type AskUserQuestion = AskUserQuestionInput["questions"][number];
|
|
109
120
|
/** A question of claude's AskUserQuestion tool; codex's may come with any number of options. */
|
|
110
121
|
export type AgentQuestion = Omit<AskUserQuestion, "options"> & {
|
|
111
|
-
options: AskUserQuestion["options"][number][];
|
|
122
|
+
readonly options: AskUserQuestion["options"][number][];
|
|
112
123
|
};
|
|
113
124
|
/** The agent asking the user something (claude's AskUserQuestion, codex's request_user_input). */
|
|
114
125
|
export type AgentQuestionRequest = {
|
|
115
|
-
id: string;
|
|
116
|
-
tool_use_id: string;
|
|
117
|
-
questions: AgentQuestion[];
|
|
126
|
+
readonly id: string;
|
|
127
|
+
readonly tool_use_id: string;
|
|
128
|
+
readonly questions: AgentQuestion[];
|
|
118
129
|
};
|
|
119
130
|
/**
|
|
120
131
|
* AskUserQuestion's own answer: question text -> the chosen label (labels joined with ", " for a
|
|
@@ -138,31 +149,54 @@ export type AgentBackgroundTask = SDKBackgroundTasksChangedMessage["tasks"][numb
|
|
|
138
149
|
* the code the page shows, when `needsCode`).
|
|
139
150
|
*/
|
|
140
151
|
export type AgentAuthStatus = {
|
|
141
|
-
state: "signedIn";
|
|
152
|
+
readonly state: "signedIn";
|
|
142
153
|
} | {
|
|
143
|
-
state: "signedOut"; /** Why the last sign-in failed, if one did. */
|
|
144
|
-
error?: string;
|
|
154
|
+
readonly state: "signedOut"; /** Why the last sign-in failed, if one did. */
|
|
155
|
+
readonly error?: string;
|
|
145
156
|
} | {
|
|
146
|
-
state: "pending";
|
|
147
|
-
url: string;
|
|
148
|
-
userCode?: string;
|
|
149
|
-
needsCode: boolean;
|
|
157
|
+
readonly state: "pending";
|
|
158
|
+
readonly url: string;
|
|
159
|
+
readonly userCode?: string;
|
|
160
|
+
readonly needsCode: boolean;
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* A backend in an agent's dimension: the version installed (null: none), and whether it's ready to
|
|
164
|
+
* run, i.e. this release's version, complete. Kept in the dimension's state directory, so it lasts as
|
|
165
|
+
* long as the dimension, its container recreated or not.
|
|
166
|
+
*/
|
|
167
|
+
export type AgentBackendInfo = {
|
|
168
|
+
readonly backend: AgentBackend;
|
|
169
|
+
readonly version: string | null;
|
|
170
|
+
readonly ready: boolean;
|
|
150
171
|
};
|
|
172
|
+
/** @sealed */
|
|
151
173
|
export interface AgentProvision {
|
|
152
|
-
/**
|
|
174
|
+
/**
|
|
175
|
+
* POST /v1/agents/:agentName/backends/:backend/install — installs the backend in the dimension, or
|
|
176
|
+
* updates it, or completes it; nothing to do when it's ready. Downloads through the dimension's portals.
|
|
177
|
+
*/
|
|
153
178
|
install(agentName: string, backend: AgentBackend): Promise<void>;
|
|
179
|
+
/** GET /v1/agents/:agentName/backends — each backend, as installed in the agent's dimension. */
|
|
180
|
+
listBackends(agentName: string): Promise<AgentBackendInfo[]>;
|
|
154
181
|
}
|
|
182
|
+
/** @sealed */
|
|
155
183
|
export interface AgentEndpoint {
|
|
156
184
|
/**
|
|
157
185
|
* POST /v1/agents/:agentName/endpoint — the agent's worker, reachable from the client over the
|
|
158
186
|
* network (workerSseConnector speaks to it), authenticated by `scheme`, callable from pages of
|
|
159
|
-
* `allowedOrigins`.
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
187
|
+
* `allowedOrigins`. Its URL lasts, but the proxy behind it doesn't: an archived and resumed Boat
|
|
188
|
+
* sandbox, or a reboot, stops it, and a page connecting then gets what a browser reports as a CORS
|
|
189
|
+
* error. This call (three round trips to the host when nothing changed) starts the agent's worker
|
|
190
|
+
* and the endpoint's proxy again if they stopped, keeping the same URL and credentials: give
|
|
191
|
+
* workerSseConnector a function that calls it, and it calls it again whenever a connection fails
|
|
192
|
+
* to open; an endpoint kept elsewhere goes stale. Origins add up: those given join the endpoint's own, in place, so
|
|
193
|
+
* pages already connected stay connected, and a call with fewer (or none) removes nothing. A new
|
|
194
|
+
* endpoint only for another scheme, or once the old one can't be served (its port taken); the old
|
|
195
|
+
* one's connections end then. Closed with the agent. Needs a host that declares endpoints.
|
|
163
196
|
*/
|
|
164
197
|
exposeAgent(agentName: string, scheme: SandboxEndpointScheme, allowedOrigins?: string[]): Promise<SandboxEndpoint>;
|
|
165
198
|
}
|
|
199
|
+
/** @sealed */
|
|
166
200
|
export interface AgentStream {
|
|
167
201
|
/**
|
|
168
202
|
* GET /v1/agents/:agentName/sessions/:sessionName/stream?after_id= — the complete messages after
|
|
@@ -172,18 +206,21 @@ export interface AgentStream {
|
|
|
172
206
|
*/
|
|
173
207
|
getStream(ref: SessionRef, req: AgentStreamingRequest): AsyncGenerator<AgentStreamingMessage>;
|
|
174
208
|
}
|
|
209
|
+
/** @sealed */
|
|
175
210
|
export interface AgentTranscript {
|
|
176
211
|
/**
|
|
177
|
-
* GET /v1/agents/:agentName/sessions/:sessionName/transcript?limit=&
|
|
212
|
+
* GET /v1/agents/:agentName/sessions/:sessionName/transcript?limit=&before= (or &cursor=, its old name) — a page of the
|
|
178
213
|
* conversation, read from the backend's own storage a piece at a time, so a conversation of any
|
|
179
214
|
* length can be paged through.
|
|
180
215
|
*/
|
|
181
216
|
getTranscript(ref: SessionRef, req?: AgentTranscriptRequest): Promise<AgentTranscriptPage>;
|
|
182
217
|
}
|
|
218
|
+
/** @sealed */
|
|
183
219
|
export interface AgentInterrupt {
|
|
184
220
|
/** POST /v1/agents/:agentName/sessions/:sessionName/interrupt — ends the current turn; pending permission requests are denied and questions dropped. */
|
|
185
221
|
cancel(ref: SessionRef): Promise<void>;
|
|
186
222
|
}
|
|
223
|
+
/** @sealed */
|
|
187
224
|
export interface AgentInput {
|
|
188
225
|
/**
|
|
189
226
|
* POST /v1/agents/:agentName/sessions/:sessionName/messages — resolves once the agent confirms it
|
|
@@ -192,10 +229,12 @@ export interface AgentInput {
|
|
|
192
229
|
*/
|
|
193
230
|
sendMessage(ref: SessionRef, message: UserMessage): Promise<void>;
|
|
194
231
|
}
|
|
232
|
+
/** @sealed */
|
|
195
233
|
export interface AgentSessionStatus {
|
|
196
234
|
/** GET /v1/agents/:agentName/sessions/:sessionName/status */
|
|
197
235
|
getSessionStatus(ref: SessionRef): Promise<AgentSessionState>;
|
|
198
236
|
}
|
|
237
|
+
/** @sealed */
|
|
199
238
|
export interface AgentMessageQueue {
|
|
200
239
|
/**
|
|
201
240
|
* GET /v1/agents/:agentName/sessions/:sessionName/queue — from the agent (claude's command
|
|
@@ -205,24 +244,28 @@ export interface AgentMessageQueue {
|
|
|
205
244
|
*/
|
|
206
245
|
getLastUserMessages(ref: SessionRef): Promise<AgentLastUserMessage[]>;
|
|
207
246
|
}
|
|
247
|
+
/** @sealed */
|
|
208
248
|
export interface AgentPermissions {
|
|
209
249
|
/** GET /v1/agents/:agentName/sessions/:sessionName/permission-requests — the ones still waiting. */
|
|
210
250
|
listPermissionRequests(ref: SessionRef): Promise<AgentPermissionRequest[]>;
|
|
211
251
|
/** POST /v1/agents/:agentName/sessions/:sessionName/permission-requests/:requestId */
|
|
212
252
|
respondPermissionRequest(ref: SessionRef, requestId: string, result: AgentPermissionResult): Promise<void>;
|
|
213
253
|
}
|
|
254
|
+
/** @sealed */
|
|
214
255
|
export interface AgentQuestions {
|
|
215
256
|
/** GET /v1/agents/:agentName/sessions/:sessionName/question-requests — the ones still waiting. */
|
|
216
257
|
listQuestionRequests(ref: SessionRef): Promise<AgentQuestionRequest[]>;
|
|
217
258
|
/** POST /v1/agents/:agentName/sessions/:sessionName/question-requests/:requestId */
|
|
218
259
|
respondQuestionRequest(ref: SessionRef, requestId: string, response: AgentQuestionResponse): Promise<void>;
|
|
219
260
|
}
|
|
261
|
+
/** @sealed */
|
|
220
262
|
export interface AgentBackgroundTasks {
|
|
221
263
|
/** GET /v1/agents/:agentName/sessions/:sessionName/background-tasks — those still running; none while the CLI isn't. */
|
|
222
264
|
listBackgroundTasks(ref: SessionRef): Promise<AgentBackgroundTask[]>;
|
|
223
265
|
/** POST /v1/agents/:agentName/sessions/:sessionName/background-tasks/:taskId/stop — ends the task. */
|
|
224
266
|
stopTask(ref: SessionRef, taskId: string): Promise<void>;
|
|
225
267
|
}
|
|
268
|
+
/** @sealed */
|
|
226
269
|
export interface AgentAuth {
|
|
227
270
|
/** GET /v1/agents/:agentName/sessions/:sessionName/auth */
|
|
228
271
|
getAuthStatus(ref: SessionRef): Promise<AgentAuthStatus>;
|
|
@@ -236,6 +279,7 @@ export interface AgentAuth {
|
|
|
236
279
|
/** POST /v1/agents/:agentName/sessions/:sessionName/auth/code — finishes a pending sign-in that needs the page's code. */
|
|
237
280
|
submitAuthCode(ref: SessionRef, code: string): Promise<AgentAuthStatus>;
|
|
238
281
|
}
|
|
282
|
+
/** @sealed */
|
|
239
283
|
export interface AgentModels {
|
|
240
284
|
/** GET /v1/agents/:agentName/sessions/:sessionName/models — starts the CLI if it isn't running. */
|
|
241
285
|
listModels(ref: SessionRef): Promise<AgentModelInfo[]>;
|
|
@@ -246,8 +290,8 @@ export interface AgentModels {
|
|
|
246
290
|
export type AgentSessionCapabilities = AgentStream & AgentTranscript & AgentInterrupt & AgentInput & AgentSessionStatus & AgentMessageQueue & AgentPermissions & AgentQuestions & AgentBackgroundTasks & AgentAuth & AgentModels;
|
|
247
291
|
/** A message sent to a session, and whether its agent still holds it queued (not yet taken into a turn). */
|
|
248
292
|
export type AgentLastUserMessage = {
|
|
249
|
-
message: UserMessage;
|
|
250
|
-
queued: boolean;
|
|
293
|
+
readonly message: UserMessage;
|
|
294
|
+
readonly queued: boolean;
|
|
251
295
|
};
|
|
252
296
|
/** The request can't be satisfied as sent (bad name, unknown backend, unknown after_id, ...). */
|
|
253
297
|
export declare class AgentInvalidError extends Error {
|
|
@@ -261,4 +305,11 @@ export declare class AgentNotFoundError extends Error {
|
|
|
261
305
|
export declare class AgentConflictError extends Error {
|
|
262
306
|
name: string;
|
|
263
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* The session's backend isn't installed in its dimension, or not completely (AgentBackendInfo.ready):
|
|
310
|
+
* AgentProvision.install puts it there. A conflict with the dimension's state, so caught as one too.
|
|
311
|
+
*/
|
|
312
|
+
export declare class AgentBackendNotInstalledError extends AgentConflictError {
|
|
313
|
+
name: string;
|
|
314
|
+
}
|
|
264
315
|
export {};
|
package/dist/agent/api.js
CHANGED
|
@@ -12,3 +12,10 @@ export class AgentNotFoundError extends Error {
|
|
|
12
12
|
export class AgentConflictError extends Error {
|
|
13
13
|
name = "AgentConflictError";
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* The session's backend isn't installed in its dimension, or not completely (AgentBackendInfo.ready):
|
|
17
|
+
* AgentProvision.install puts it there. A conflict with the dimension's state, so caught as one too.
|
|
18
|
+
*/
|
|
19
|
+
export class AgentBackendNotInstalledError extends AgentConflictError {
|
|
20
|
+
name = "AgentBackendNotInstalledError";
|
|
21
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { createWorkerClient, type WorkerClient } from "./session/client.ts";
|
|
2
|
+
export { sharedConnector, type SharedConnector } from "./session/sharedConnection.ts";
|
|
3
|
+
export { EndpointLoginRequiredError, EndpointUnreachableError, exeDevLoginUrl, workerSseConnector, type WorkerEndpoint } from "./session/workerSseConnection.ts";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// An agent's worker, reached through its endpoint (AgentService.exposeAgent): what a browser can
|
|
2
|
+
// import (hyperbrane/agent/client), since the root module brings the server side with it.
|
|
3
|
+
export { createWorkerClient } from "./session/client.js";
|
|
4
|
+
export { sharedConnector } from "./session/sharedConnection.js";
|
|
5
|
+
export { EndpointLoginRequiredError, EndpointUnreachableError, exeDevLoginUrl, workerSseConnector } from "./session/workerSseConnection.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Sandbox } from "../sandbox/api.ts";
|
|
2
|
+
import { type AgentTranscriptPage, type AgentTranscriptRequest } from "./api.ts";
|
|
3
|
+
/**
|
|
4
|
+
* A page of the session's transcript, read on `host` from the dimension's state directory (its host
|
|
5
|
+
* side) by the backend's own layout and parsing, so nothing has to run: one command, plus a read
|
|
6
|
+
* for each further chunk the rare page that needs more takes. Null for a session whose env moves the backend's home
|
|
7
|
+
* elsewhere (into the container, where only its worker reaches it).
|
|
8
|
+
*/
|
|
9
|
+
export declare function readHostTranscript(host: Sandbox, { stateDirectory, agentName, sessionName }: {
|
|
10
|
+
stateDirectory: string;
|
|
11
|
+
agentName: string;
|
|
12
|
+
sessionName: string;
|
|
13
|
+
}, { limit, before, cursor }: AgentTranscriptRequest): Promise<AgentTranscriptPage | null>;
|