hyperbrane 0.1.2 → 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 +105 -23
- package/dist/agent/agentClient.d.ts +5 -0
- package/dist/agent/agentClient.js +4 -0
- package/dist/agent/agentService.d.ts +9 -37
- package/dist/agent/agentService.js +64 -323
- package/dist/agent/agentStorage.d.ts +1 -20
- package/dist/agent/agentStorage.js +0 -30
- package/dist/agent/api.d.ts +105 -152
- package/dist/agent/api.js +1 -8
- package/dist/agent/client.d.ts +3 -1
- package/dist/agent/client.js +2 -3
- package/dist/agent/control.d.ts +23 -0
- package/dist/agent/control.js +479 -0
- package/dist/agent/dimensionAgentClient.d.ts +8 -0
- package/dist/agent/dimensionAgentClient.js +99 -0
- package/dist/agent/hostTranscript.d.ts +0 -6
- package/dist/agent/hostTranscript.js +3 -14
- package/dist/agent/session/backend.d.ts +18 -32
- package/dist/agent/session/claude/home.d.ts +0 -1
- package/dist/agent/session/claude/home.js +0 -1
- package/dist/agent/session/claude/provision.d.ts +10 -0
- package/dist/agent/session/claude/provision.js +17 -0
- package/dist/agent/session/claude/transcript.d.ts +3 -7
- package/dist/agent/session/claude/transcript.js +68 -14
- package/dist/agent/session/client.d.ts +1 -10
- package/dist/agent/session/client.js +0 -3
- package/dist/agent/session/codex/provision.d.ts +14 -0
- package/dist/agent/session/codex/provision.js +25 -0
- package/dist/agent/session/fileTranscript.d.ts +6 -49
- package/dist/agent/session/fileTranscript.js +26 -82
- package/dist/agent/session/install.d.ts +5 -0
- package/dist/agent/session/install.js +35 -0
- package/dist/agent/session/sessionStorage.d.ts +4 -35
- package/dist/agent/session/sessionStorage.js +10 -16
- package/dist/agent/session/sharedConnection.d.ts +0 -8
- package/dist/agent/session/sharedConnection.js +0 -7
- package/dist/agent/session/storedTranscripts.d.ts +1 -6
- package/dist/agent/session/storedTranscripts.js +0 -3
- package/dist/agent/session/worker/jsonRpc.d.ts +2 -13
- package/dist/agent/session/worker/jsonRpc.js +2 -11
- package/dist/agent/session/worker/protocol.d.ts +30 -21
- package/dist/agent/session/worker/protocol.js +29 -18
- package/dist/agent/session/worker/workerParams.d.ts +0 -5
- package/dist/agent/session/worker/workerParams.js +24 -17
- package/dist/agent/session/workerConnection.d.ts +2 -13
- package/dist/agent/session/workerConnection.js +7 -12
- package/dist/agent/session/workerGatewayConnection.d.ts +18 -0
- package/dist/agent/session/workerGatewayConnection.js +86 -0
- package/dist/agent/vendor-types/manifest.json +32 -0
- package/dist/agent/workerBundle.d.ts +4 -19
- package/dist/agent/workerBundle.generated.js +1 -1
- package/dist/agent/workerBundle.js +3 -30
- package/dist/agent/workerReplacement.d.ts +3 -0
- package/dist/agent/workerReplacement.js +49 -0
- package/dist/cli/agent.js +31 -16
- package/dist/cli/command.d.ts +10 -11
- package/dist/cli/command.js +0 -4
- package/dist/cli/dimension.js +6 -6
- package/dist/cli/gateway.js +3 -6
- package/dist/cli/main.js +14 -31
- package/dist/cli/streamPrinter.d.ts +2 -10
- package/dist/cli/streamPrinter.js +16 -46
- package/dist/cli/table.d.ts +0 -3
- package/dist/cli/table.js +0 -4
- package/dist/dimension/api.d.ts +42 -27
- package/dist/dimension/api.js +28 -47
- package/dist/dimension/client.d.ts +5 -0
- package/dist/dimension/client.js +4 -0
- package/dist/dimension/control.d.ts +22 -0
- package/dist/dimension/control.js +346 -0
- package/dist/dimension/image.d.ts +0 -5
- package/dist/dimension/image.js +0 -8
- package/dist/dimension/imageFiles.generated.js +1 -1
- package/dist/dimension/paths.d.ts +5 -4
- package/dist/dimension/paths.js +15 -6
- package/dist/dimension/service.d.ts +8 -24
- package/dist/dimension/service.js +85 -227
- package/dist/dimension/transport-worker.d.ts +1 -0
- package/dist/dimension/transport-worker.js +151 -0
- package/dist/dimension/transportBundle.d.ts +2 -0
- package/dist/dimension/transportBundle.generated.d.ts +1 -0
- package/dist/dimension/transportBundle.generated.js +2 -0
- package/dist/dimension/transportBundle.js +4 -0
- package/dist/dimension/transportClient.d.ts +10 -0
- package/dist/dimension/transportClient.js +115 -0
- package/dist/dimension/transportProtocol.d.ts +53 -0
- package/dist/dimension/transportProtocol.js +8 -0
- package/dist/index.d.ts +7 -9
- package/dist/index.js +7 -19
- package/dist/lib/control.d.ts +11 -0
- package/dist/lib/control.js +62 -0
- package/dist/lib/docker.d.ts +0 -22
- package/dist/lib/docker.js +0 -10
- package/dist/lib/image.d.ts +0 -3
- package/dist/lib/image.js +0 -2
- package/dist/lib/jsonDirectory.d.ts +0 -13
- package/dist/lib/jsonDirectory.js +34 -18
- package/dist/lib/scriptBundle.d.ts +6 -0
- package/dist/lib/scriptBundle.js +28 -0
- package/dist/lib/sh.d.ts +2 -0
- package/dist/lib/sh.js +8 -0
- package/dist/lib/sleep.d.ts +0 -1
- package/dist/lib/sleep.js +0 -1
- package/dist/lib/streamFetch.d.ts +15 -0
- package/dist/lib/streamFetch.js +150 -0
- package/dist/lib/tar.d.ts +0 -1
- package/dist/lib/tar.js +5 -11
- package/dist/lib/unixSocket.d.ts +2 -0
- package/dist/lib/unixSocket.js +28 -0
- package/dist/portal-gateway/api.d.ts +14 -44
- package/dist/portal-gateway/api.js +1 -3
- package/dist/portal-gateway/control.d.ts +42 -0
- package/dist/portal-gateway/control.js +657 -0
- package/dist/portal-gateway/gatewayClient.d.ts +5 -0
- package/dist/portal-gateway/gatewayClient.js +4 -0
- package/dist/portal-gateway/gatewayService.d.ts +8 -38
- package/dist/portal-gateway/gatewayService.js +82 -254
- package/dist/portal-gateway/image.d.ts +0 -1
- package/dist/portal-gateway/image.js +0 -2
- package/dist/portal-gateway/imageFiles.generated.js +2 -2
- package/dist/portal-gateway/portalClient.d.ts +5 -0
- package/dist/portal-gateway/portalClient.js +4 -0
- package/dist/portal-gateway/portalService.d.ts +5 -47
- package/dist/portal-gateway/portalService.js +19 -349
- package/dist/portal-gateway/transport-worker.d.ts +1 -0
- package/dist/portal-gateway/transport-worker.js +205 -0
- package/dist/portal-gateway/transportBundle.d.ts +2 -0
- package/dist/portal-gateway/transportBundle.generated.d.ts +1 -0
- package/dist/portal-gateway/transportBundle.generated.js +2 -0
- package/dist/portal-gateway/transportBundle.js +4 -0
- package/dist/portal-gateway/upstream.d.ts +0 -81
- package/dist/portal-gateway/upstream.js +0 -1
- package/dist/restore.d.ts +9 -0
- package/dist/restore.js +10 -0
- package/dist/sandbox/api.d.ts +126 -187
- package/dist/sandbox/api.js +5 -7
- package/dist/sandbox/client.d.ts +3 -0
- package/dist/sandbox/client.js +7 -0
- package/dist/sandbox/data-endpoints/dataEndpoints.d.ts +13 -0
- package/dist/sandbox/data-endpoints/dataEndpoints.js +100 -0
- package/dist/sandbox/data-endpoints/firewalledPorts.d.ts +4 -0
- package/dist/sandbox/data-endpoints/firewalledPorts.js +28 -0
- package/dist/sandbox/lib/httpOverStream.d.ts +3 -0
- package/dist/sandbox/lib/httpOverStream.js +21 -0
- package/dist/sandbox/lib/remoteFileOperations.d.ts +10 -0
- package/dist/sandbox/lib/remoteFileOperations.js +70 -0
- package/dist/sandbox/lib/shellCommand.d.ts +3 -0
- package/dist/sandbox/lib/shellCommand.js +7 -0
- 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 +3 -0
- package/dist/sandbox/providers/boat/boatService.js +373 -0
- package/dist/sandbox/providers/exedev/exeDevService.d.ts +2 -0
- package/dist/sandbox/providers/exedev/exeDevService.js +129 -0
- package/dist/sandbox/providers/exedev/exeDevToken.d.ts +1 -0
- package/dist/sandbox/providers/exedev/exeDevToken.js +50 -0
- package/dist/sandbox/providers/local/localService.d.ts +2 -0
- package/dist/sandbox/providers/local/localService.js +197 -0
- package/dist/sandbox/providers/ssh/sshService.d.ts +2 -0
- package/dist/sandbox/providers/ssh/sshService.js +83 -0
- package/dist/sandbox/providers/ssh/sshTransport.d.ts +12 -0
- package/dist/sandbox/providers/ssh/sshTransport.js +216 -0
- package/dist/sandbox/service.d.ts +3 -0
- package/dist/sandbox/service.js +26 -0
- package/dist/sandbox/storage/descriptors.d.ts +6 -0
- package/dist/sandbox/storage/descriptors.js +25 -0
- package/dist/sandbox/storage/sandboxStorage.d.ts +11 -0
- package/dist/sandbox/storage/sandboxStorage.js +42 -0
- package/dist/upstreams/anthropic.d.ts +0 -5
- package/dist/upstreams/anthropic.js +1 -8
- package/dist/upstreams/index.d.ts +2 -6
- package/dist/upstreams/index.js +0 -4
- package/dist/upstreams/openai.d.ts +0 -5
- package/dist/upstreams/openai.js +1 -13
- 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 +13 -4
- package/dist/agent/gatewaySignIns.d.ts +0 -5
- package/dist/agent/gatewaySignIns.js +0 -11
- package/dist/agent/session/codex/home.d.ts +0 -2
- package/dist/agent/session/codex/home.js +0 -4
- package/dist/agent/session/codex/messages.d.ts +0 -30
- package/dist/agent/session/codex/messages.js +0 -135
- package/dist/agent/session/codex/transcript.d.ts +0 -12
- package/dist/agent/session/codex/transcript.js +0 -22
- package/dist/agent/session/sdkMessages.d.ts +0 -15
- package/dist/agent/session/sdkMessages.js +0 -28
- package/dist/agent/session/workerSseConnection.d.ts +0 -31
- package/dist/agent/session/workerSseConnection.js +0 -76
- package/dist/agent/vendor-types/codex/app-server/AbsolutePathBuf.d.ts +0 -10
- package/dist/agent/vendor-types/codex/app-server/AbsolutePathBuf.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputBody.d.ts +0 -2
- package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputBody.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputContentItem.d.ts +0 -22
- package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputContentItem.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/ImageDetail.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/ImageDetail.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/ImageGenerationFailure.d.ts +0 -5
- package/dist/agent/vendor-types/codex/app-server/ImageGenerationFailure.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/ImageGenerationItem.d.ts +0 -11
- package/dist/agent/vendor-types/codex/app-server/ImageGenerationItem.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/LegacyAppPathString.d.ts +0 -23
- package/dist/agent/vendor-types/codex/app-server/LegacyAppPathString.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/MessagePhase.d.ts +0 -7
- package/dist/agent/vendor-types/codex/app-server/MessagePhase.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/ReasoningEffort.d.ts +0 -4
- package/dist/agent/vendor-types/codex/app-server/ReasoningEffort.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/SleepItem.d.ts +0 -7
- package/dist/agent/vendor-types/codex/app-server/SleepItem.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/WebSearchItem.d.ts +0 -14
- package/dist/agent/vendor-types/codex/app-server/WebSearchItem.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/serde_json/JsonValue.d.ts +0 -3
- package/dist/agent/vendor-types/codex/app-server/serde_json/JsonValue.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/AgentMessageDelivery.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/AgentMessageDelivery.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/AsyncUserInputQuestion.d.ts +0 -4
- package/dist/agent/vendor-types/codex/app-server/v2/AsyncUserInputQuestion.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/ByteRange.d.ts +0 -4
- package/dist/agent/vendor-types/codex/app-server/v2/ByteRange.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentState.d.ts +0 -5
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentState.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentStatus.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentStatus.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentTool.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentTool.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentToolCallStatus.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentToolCallStatus.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/CommandAction.d.ts +0 -19
- package/dist/agent/vendor-types/codex/app-server/v2/CommandAction.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionSource.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionSource.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionStatus.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionStatus.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallOutputContentItem.d.ts +0 -10
- package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallOutputContentItem.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallStatus.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallStatus.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/FileUpdateChange.d.ts +0 -6
- package/dist/agent/vendor-types/codex/app-server/v2/FileUpdateChange.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/HookPromptFragment.d.ts +0 -4
- package/dist/agent/vendor-types/codex/app-server/v2/HookPromptFragment.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/McpAppDisplayMode.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/McpAppDisplayMode.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/McpAppUi.d.ts +0 -8
- package/dist/agent/vendor-types/codex/app-server/v2/McpAppUi.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallAppContext.d.ts +0 -7
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallAppContext.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallError.d.ts +0 -3
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallError.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallResult.d.ts +0 -6
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallResult.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallStatus.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallStatus.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitation.d.ts +0 -5
- package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitation.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitationEntry.d.ts +0 -6
- package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitationEntry.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/PatchApplyStatus.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/PatchApplyStatus.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/PatchChangeKind.d.ts +0 -8
- package/dist/agent/vendor-types/codex/app-server/v2/PatchChangeKind.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/SubAgentActivityKind.d.ts +0 -1
- package/dist/agent/vendor-types/codex/app-server/v2/SubAgentActivityKind.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/TextElement.d.ts +0 -11
- package/dist/agent/vendor-types/codex/app-server/v2/TextElement.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/ThreadItem.d.ts +0 -215
- package/dist/agent/vendor-types/codex/app-server/v2/ThreadItem.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/UserInput.d.ts +0 -35
- package/dist/agent/vendor-types/codex/app-server/v2/UserInput.js +0 -2
- package/dist/agent/vendor-types/codex/app-server/v2/WebSearchAction.d.ts +0 -14
- package/dist/agent/vendor-types/codex/app-server/v2/WebSearchAction.js +0 -2
- package/dist/cli/services.d.ts +0 -19
- package/dist/cli/services.js +0 -34
- package/dist/dimension/http.d.ts +0 -7
- package/dist/dimension/http.js +0 -77
- package/dist/dimension/storage.d.ts +0 -39
- package/dist/dimension/storage.js +0 -79
- package/dist/portal-gateway/fakes.d.ts +0 -3
- package/dist/portal-gateway/fakes.js +0 -11
- package/dist/portal-gateway/gatewayStorage.d.ts +0 -66
- package/dist/portal-gateway/gatewayStorage.js +0 -125
- package/dist/portal-gateway/portalStorage.d.ts +0 -34
- package/dist/portal-gateway/portalStorage.js +0 -44
- package/dist/sandbox/boatService.d.ts +0 -52
- package/dist/sandbox/boatService.js +0 -343
- package/dist/sandbox/containerService.d.ts +0 -15
- package/dist/sandbox/containerService.js +0 -184
- package/dist/sandbox/endpointProxy.d.ts +0 -90
- package/dist/sandbox/endpointProxy.js +0 -716
- package/dist/sandbox/exeDevAuth.d.ts +0 -16
- package/dist/sandbox/exeDevAuth.js +0 -49
- package/dist/sandbox/localService.d.ts +0 -19
- package/dist/sandbox/localService.js +0 -200
- package/dist/sandbox/sseConnection.d.ts +0 -41
- package/dist/sandbox/sseConnection.js +0 -166
- package/dist/sandbox/sshService.d.ts +0 -30
- package/dist/sandbox/sshService.js +0 -680
|
@@ -7,40 +7,25 @@ export function sandboxFiles(sandbox) {
|
|
|
7
7
|
listDirectory: (path) => sandbox.listDirectory(path),
|
|
8
8
|
};
|
|
9
9
|
}
|
|
10
|
-
/** The file of conversation `nativeId` laid out as `layout` under `home`; null when there's none yet. */
|
|
11
10
|
export function locateTranscript(files, home, layout, nativeId) {
|
|
12
11
|
const escape = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13
12
|
const [before = "", after = ""] = layout.name.split("{id}").map((part) => part.split("*").map(escape).join(".*"));
|
|
14
13
|
const name = new RegExp(`^${before}${escape(nativeId)}${after}$`);
|
|
15
14
|
return findFile(files, `${home}/${layout.dir}`, layout.depth, (entry) => name.test(entry));
|
|
16
15
|
}
|
|
17
|
-
/** How much of the file is read at a time. */
|
|
18
16
|
export const CHUNK_BYTES = 64 * 1024;
|
|
19
|
-
/**
|
|
20
|
-
* How far out of time order lines get, nearly always (parallel branches completing; in long claude
|
|
21
|
-
* sessions half are within 2s, 99% within a minute): searches allow for this much.
|
|
22
|
-
*/
|
|
23
17
|
export const TIME_SLACK_MS = 60_000;
|
|
24
|
-
/** How many timed lines a probe of the bisection looks at: the earliest wins, so one odd line can't mislead it. */
|
|
25
18
|
export const PROBE_LINES = 3;
|
|
26
|
-
/** How much a probe reads, at most, before it gives up on finding a timed line (huge tool outputs). */
|
|
27
19
|
export const PROBE_BYTES = 4 * CHUNK_BYTES;
|
|
28
20
|
const NEWLINE = 0x0a;
|
|
29
|
-
/** A line's time (epoch ms) from its `field`, null without a valid one. */
|
|
30
21
|
function lineTime(entry, field) {
|
|
31
22
|
const value = entry?.[field];
|
|
32
23
|
const time = typeof value === "string" ? Date.parse(value) : NaN;
|
|
33
24
|
return Number.isNaN(time) ? null : time;
|
|
34
25
|
}
|
|
35
|
-
/** How much is read at a time when searching the whole file for where stretches were written again. */
|
|
36
26
|
const SCAN_BYTES = 1024 * 1024;
|
|
37
|
-
/**
|
|
38
|
-
* The stretches written again in the files searched last, by path and size: a search reads the whole
|
|
39
|
-
* file, and a file the backend only appends to that hasn't grown has the same ones.
|
|
40
|
-
*/
|
|
41
27
|
const searchedCopies = new Map();
|
|
42
28
|
const SEARCHED_FILES = 16;
|
|
43
|
-
/** `locate` finds the file; null when there's none yet (nothing stored). */
|
|
44
29
|
export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
45
30
|
function parse(bytes, start) {
|
|
46
31
|
try {
|
|
@@ -48,11 +33,9 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
48
33
|
return { start, time: lineTime(entry, layout.time), messages: lineMessages(entry) };
|
|
49
34
|
}
|
|
50
35
|
catch {
|
|
51
|
-
// A line still being written (the last one), or one this reader doesn't know: nothing in it.
|
|
52
36
|
return { start, time: null, messages: [] };
|
|
53
37
|
}
|
|
54
38
|
}
|
|
55
|
-
/** The stretches between `from` and `to` that aren't skipped, in order. */
|
|
56
39
|
function kept(from, to, skip) {
|
|
57
40
|
const parts = [];
|
|
58
41
|
let start = from;
|
|
@@ -67,14 +50,12 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
67
50
|
parts.push({ start, end: to });
|
|
68
51
|
return parts;
|
|
69
52
|
}
|
|
70
|
-
/** The file's lines before byte `end`, last first, read a chunk at a time, passing over `skip`. */
|
|
71
53
|
async function* backward(path, end, skip = []) {
|
|
72
54
|
for (const part of kept(0, end, skip).reverse())
|
|
73
55
|
yield* backwardIn(path, part.start, part.end);
|
|
74
56
|
}
|
|
75
57
|
async function* backwardIn(path, floor, end) {
|
|
76
58
|
let position = end;
|
|
77
|
-
// Bytes of a line that runs on into what was read before (later in the file).
|
|
78
59
|
let carry = new Uint8Array(0);
|
|
79
60
|
while (position > floor) {
|
|
80
61
|
const from = Math.max(floor, position - CHUNK_BYTES);
|
|
@@ -94,7 +75,6 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
94
75
|
if (carry.length > 0)
|
|
95
76
|
yield parse(carry, floor);
|
|
96
77
|
}
|
|
97
|
-
/** The file's lines from byte `start` to byte `end`, in order, read a chunk at a time, passing over `skip`. */
|
|
98
78
|
async function* forward(path, start, end, skip = []) {
|
|
99
79
|
for (const part of kept(start, end, skip))
|
|
100
80
|
yield* forwardIn(path, part.start, part.end);
|
|
@@ -121,15 +101,9 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
121
101
|
if (carry.length > 0)
|
|
122
102
|
yield parse(carry, carryStart);
|
|
123
103
|
}
|
|
124
|
-
/**
|
|
125
|
-
* Where the lines from time `target` on start, about, between `low` and `high`: the start of a line
|
|
126
|
-
* (or `high`) with only lines from before it, allowing for the slack, ahead of it. Bisects, each
|
|
127
|
-
* probe reading the times of the first few lines after its middle, passing over `skip`.
|
|
128
|
-
*/
|
|
129
104
|
async function seek(path, low, high, target, skip) {
|
|
130
105
|
return (await narrow(path, low, high, target, skip)).high;
|
|
131
106
|
}
|
|
132
|
-
/** seek's bisection: where it got, from both sides. */
|
|
133
107
|
async function narrow(path, low, high, target, skip) {
|
|
134
108
|
while (high - low > CHUNK_BYTES) {
|
|
135
109
|
let middle = Math.floor((low + high) / 2);
|
|
@@ -143,7 +117,7 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
143
117
|
}
|
|
144
118
|
const probe = await probeTime(path, middle, high, skip);
|
|
145
119
|
if (!probe)
|
|
146
|
-
break;
|
|
120
|
+
break;
|
|
147
121
|
if (probe.time >= target)
|
|
148
122
|
high = probe.start;
|
|
149
123
|
else
|
|
@@ -151,14 +125,13 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
151
125
|
}
|
|
152
126
|
return { low, high };
|
|
153
127
|
}
|
|
154
|
-
/** The first line starting after `from` (before `limit`), with the earliest time of the next few timed lines. */
|
|
155
128
|
async function probeTime(path, from, limit, skip) {
|
|
156
129
|
let start = -1;
|
|
157
130
|
let time = Infinity;
|
|
158
131
|
let timed = 0;
|
|
159
132
|
for await (const line of forward(path, from, Math.min(limit, from + PROBE_BYTES), skip)) {
|
|
160
133
|
if (line.start === from && from > 0)
|
|
161
|
-
continue;
|
|
134
|
+
continue;
|
|
162
135
|
if (line.start >= limit)
|
|
163
136
|
break;
|
|
164
137
|
if (start < 0)
|
|
@@ -171,25 +144,17 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
171
144
|
}
|
|
172
145
|
return start < 0 || timed === 0 ? null : { start, time };
|
|
173
146
|
}
|
|
174
|
-
/** The time of the first timed line from `from` on (a line start), if there's one in reach. */
|
|
175
147
|
async function timeAt(path, from, size, skip) {
|
|
176
148
|
for await (const line of forward(path, from, Math.min(size, from + PROBE_BYTES), skip))
|
|
177
149
|
if (line.time !== null)
|
|
178
150
|
return line.time;
|
|
179
151
|
return null;
|
|
180
152
|
}
|
|
181
|
-
/** Where the line holding byte `at` starts. */
|
|
182
153
|
async function lineStart(path, at) {
|
|
183
154
|
for await (const line of backward(path, at + 1))
|
|
184
155
|
return line.start;
|
|
185
156
|
return 0;
|
|
186
157
|
}
|
|
187
|
-
/**
|
|
188
|
-
* The stretches the backend wrote again (TranscriptLayout.copies): each starts at a line with the
|
|
189
|
-
* marker whose time goes back from the timed line before it, and runs until a line is newer than
|
|
190
|
-
* that one. Found by searching the whole file for the marker; the rest only
|
|
191
|
-
* reads around what's found.
|
|
192
|
-
*/
|
|
193
158
|
function copiesOf(path, size) {
|
|
194
159
|
if (!layout.copies)
|
|
195
160
|
return Promise.resolve([]);
|
|
@@ -199,7 +164,6 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
199
164
|
copies = findCopies(path, size, layout.copies);
|
|
200
165
|
copies.catch(() => searchedCopies.delete(key));
|
|
201
166
|
searchedCopies.set(key, copies);
|
|
202
|
-
// A few files' worth: a worker serves an agent's sessions.
|
|
203
167
|
if (searchedCopies.size > SEARCHED_FILES)
|
|
204
168
|
searchedCopies.delete(searchedCopies.keys().next().value);
|
|
205
169
|
}
|
|
@@ -209,7 +173,6 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
209
173
|
const needle = Buffer.from(marker);
|
|
210
174
|
const found = [];
|
|
211
175
|
for (let position = 0; position < size; position += SCAN_BYTES) {
|
|
212
|
-
// Overlapping by the marker's length, so one across two reads is found.
|
|
213
176
|
const from = Math.max(0, position - needle.length + 1);
|
|
214
177
|
const bytes = Buffer.from(await files.readFileRange(path, from, Math.min(size, position + SCAN_BYTES)));
|
|
215
178
|
for (let index = bytes.indexOf(needle); index >= 0; index = bytes.indexOf(needle, index + 1)) {
|
|
@@ -219,7 +182,6 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
219
182
|
}
|
|
220
183
|
const copies = [];
|
|
221
184
|
for (const [index, at] of found.entries()) {
|
|
222
|
-
// The marker's line, and the timed line before it.
|
|
223
185
|
const start = await lineStart(path, at);
|
|
224
186
|
let earlier = null;
|
|
225
187
|
for await (const before of backward(path, start)) {
|
|
@@ -235,13 +197,8 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
235
197
|
time = line.time;
|
|
236
198
|
break;
|
|
237
199
|
}
|
|
238
|
-
// A compaction as it happened comes after what's before it (compacting takes a while: 25s and
|
|
239
|
-
// more); a copy starts back at its time.
|
|
240
200
|
if (time === null || time >= earlier)
|
|
241
201
|
continue;
|
|
242
|
-
// It runs up to what was written before it, and ends with the first line newer than that. It
|
|
243
|
-
// holds what was said since a compaction, so it ends by the next marker: the rest of the file
|
|
244
|
-
// (other copies) can't mislead the search.
|
|
245
202
|
const next = found[index + 1];
|
|
246
203
|
const near = await narrow(path, start + 1, next === undefined ? size : await lineStart(path, next), earlier + 1, []);
|
|
247
204
|
let end = near.high;
|
|
@@ -255,17 +212,9 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
255
212
|
}
|
|
256
213
|
return copies;
|
|
257
214
|
}
|
|
258
|
-
/**
|
|
259
|
-
* The file's lines before `end`, last first, each with the time it's paged by: its own, unless it's
|
|
260
|
-
* odd, more than the slack away from both the timed lines around it in the same direction (a
|
|
261
|
-
* background task's result written minutes after it began, a note dated ahead), when it's the one
|
|
262
|
-
* before it's; a line without a time goes with the timed line after it. These times follow the file
|
|
263
|
-
* order to within the slack, so the late result is paged where it is in the conversation.
|
|
264
|
-
* `after`: the time of the first timed line from `end` on.
|
|
265
|
-
*/
|
|
266
215
|
async function* pagedBackward(path, end, after, skip) {
|
|
267
|
-
let next = after;
|
|
268
|
-
let waiting = [];
|
|
216
|
+
let next = after;
|
|
217
|
+
let waiting = [];
|
|
269
218
|
const settle = function* (before) {
|
|
270
219
|
const [timed, ...untimed] = waiting;
|
|
271
220
|
if (!timed)
|
|
@@ -282,7 +231,6 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
282
231
|
};
|
|
283
232
|
for await (const line of backward(path, end, skip)) {
|
|
284
233
|
if (line.time === null) {
|
|
285
|
-
// Before any timed line: it goes with the line after the end, or is the newest.
|
|
286
234
|
if (waiting.length === 0)
|
|
287
235
|
yield { ...line, time: next ?? Infinity };
|
|
288
236
|
else
|
|
@@ -302,11 +250,9 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
302
250
|
const copies = found?.copies ?? (await copiesOf(path, size));
|
|
303
251
|
const from = Math.min(found?.end ?? (before === null ? size : await seek(path, 0, size, before + TIME_SLACK_MS, copies)), size);
|
|
304
252
|
const after = found ? found.after : from < size ? await timeAt(path, from, size, copies) : null;
|
|
305
|
-
// Read back until the page is full and a line is older than its oldest by more than the slack:
|
|
306
|
-
// nothing before it can belong on the page.
|
|
307
253
|
const taken = [];
|
|
308
254
|
let count = 0;
|
|
309
|
-
let oldest = Infinity;
|
|
255
|
+
let oldest = Infinity;
|
|
310
256
|
let older = false;
|
|
311
257
|
for await (const line of pagedBackward(path, from, after, copies)) {
|
|
312
258
|
if (count >= limit && line.time < oldest - TIME_SLACK_MS) {
|
|
@@ -320,8 +266,6 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
320
266
|
if (count >= limit)
|
|
321
267
|
oldest = oldestOnPage(taken, limit);
|
|
322
268
|
}
|
|
323
|
-
// The page: its newest messages by time, ties with the oldest of them included. A line's messages
|
|
324
|
-
// stay together, so a page can hold a message or so more than the limit.
|
|
325
269
|
const kept = count >= limit ? taken.filter((line) => line.time >= oldest) : taken;
|
|
326
270
|
if (kept.length < taken.length)
|
|
327
271
|
older = true;
|
|
@@ -330,7 +274,6 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
330
274
|
nextBefore: older && Number.isFinite(oldest) ? new Date(oldest).toISOString() : null,
|
|
331
275
|
};
|
|
332
276
|
}
|
|
333
|
-
// Passing over the copies too: a resumed session's stretch written again isn't replayed twice.
|
|
334
277
|
async function replay(afterId, emit) {
|
|
335
278
|
const path = await locate();
|
|
336
279
|
if (!path)
|
|
@@ -361,9 +304,20 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
|
|
|
361
304
|
}
|
|
362
305
|
return true;
|
|
363
306
|
}
|
|
364
|
-
|
|
307
|
+
async function find(match) {
|
|
308
|
+
const path = await locate();
|
|
309
|
+
if (!path)
|
|
310
|
+
return null;
|
|
311
|
+
const end = await files.size(path);
|
|
312
|
+
for await (const line of backward(path, end, await copiesOf(path, end))) {
|
|
313
|
+
const found = line.messages.find(match);
|
|
314
|
+
if (found)
|
|
315
|
+
return found;
|
|
316
|
+
}
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
return { page, replay, find };
|
|
365
320
|
}
|
|
366
|
-
/** The time of the `limit`-th newest message of `lines`. */
|
|
367
321
|
function oldestOnPage(lines, limit) {
|
|
368
322
|
const newestFirst = [...lines].sort((a, b) => b.time - a.time);
|
|
369
323
|
let count = 0;
|
|
@@ -386,7 +340,6 @@ function joined(first, second) {
|
|
|
386
340
|
}
|
|
387
341
|
const DEFAULT_PAGE_SIZE = 50;
|
|
388
342
|
const MAX_PAGE_SIZE = 500;
|
|
389
|
-
/** A page of the API's transcript (AgentTranscript.getTranscript); none stored for a session that never ran. */
|
|
390
343
|
export async function transcriptPage(transcript, { limit, before, cursor } = {}, found) {
|
|
391
344
|
const time = transcriptTime(before ?? cursor);
|
|
392
345
|
if (!transcript)
|
|
@@ -395,7 +348,6 @@ export async function transcriptPage(transcript, { limit, before, cursor } = {},
|
|
|
395
348
|
const page = await transcript.page(time, size, found);
|
|
396
349
|
return { ...page, nextCursor: page.nextBefore };
|
|
397
350
|
}
|
|
398
|
-
/** AgentTranscriptRequest.before as epoch ms; null for none. */
|
|
399
351
|
export function transcriptTime(before) {
|
|
400
352
|
if (before === undefined)
|
|
401
353
|
return null;
|
|
@@ -404,35 +356,27 @@ export function transcriptTime(before) {
|
|
|
404
356
|
throw new AgentInvalidError(`invalid time ${before}: give an ISO 8601 time`);
|
|
405
357
|
return time;
|
|
406
358
|
}
|
|
407
|
-
|
|
408
|
-
* The first file `depth` levels under `dir` whose name `matches` (the backends' own layouts, like
|
|
409
|
-
* projects/<project>/<id>.jsonl), each level listed at once; null when there's none, or no `dir`.
|
|
410
|
-
*/
|
|
411
|
-
async function findFile(files, dir, depth, matches) {
|
|
412
|
-
let names;
|
|
359
|
+
export async function directoryNames(files, dir) {
|
|
413
360
|
try {
|
|
414
|
-
|
|
361
|
+
return await files.listDirectory(dir);
|
|
415
362
|
}
|
|
416
363
|
catch (error) {
|
|
417
364
|
if (error instanceof SandboxNotFoundError)
|
|
418
365
|
return null;
|
|
419
366
|
throw error;
|
|
420
367
|
}
|
|
368
|
+
}
|
|
369
|
+
async function findFile(files, dir, depth, matches) {
|
|
370
|
+
const names = await directoryNames(files, dir);
|
|
371
|
+
if (!names)
|
|
372
|
+
return null;
|
|
421
373
|
if (depth <= 1) {
|
|
422
374
|
const name = names.find(matches);
|
|
423
375
|
return name === undefined ? null : `${dir}/${name}`;
|
|
424
376
|
}
|
|
425
|
-
|
|
426
|
-
const found = await Promise.all(names.sort().reverse().map((name) =>
|
|
427
|
-
// One that isn't a directory has nothing below it.
|
|
428
|
-
findFile(files, `${dir}/${name}`, depth - 1, matches).catch(() => null)));
|
|
377
|
+
const found = await Promise.all(names.sort().reverse().map((name) => findFile(files, `${dir}/${name}`, depth - 1, matches).catch(() => null)));
|
|
429
378
|
return found.find((path) => path !== null) ?? null;
|
|
430
379
|
}
|
|
431
|
-
/**
|
|
432
|
-
* `files` with what was already fetched of the file at `path` in the same round trip: its size, and
|
|
433
|
-
* pieces of it (what a page reads, mostly). Reading back beyond them (a page across huge tool
|
|
434
|
-
* outputs) fetches twice as much as the time before, so it takes a few more reads, not one per chunk.
|
|
435
|
-
*/
|
|
436
380
|
export function prefetchedFiles(files, path, size, pieces) {
|
|
437
381
|
let more = Math.max(CHUNK_BYTES, ...pieces.map(({ bytes }) => bytes.length));
|
|
438
382
|
return {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import manifest from "../vendor-types/manifest.json";
|
|
2
|
+
import type { AgentBackendInfo } from "../api.ts";
|
|
3
|
+
export declare function pinnedVersion(vendor: keyof typeof manifest, pkg: string, sourcePackage?: string): string;
|
|
4
|
+
export declare function installedPackage(installDir: string, pkg: string, version: string, executable: () => string): Promise<Omit<AgentBackendInfo, "backend">>;
|
|
5
|
+
export declare function installPackage(installDir: string, pkg: string, version: string, executable: () => string): Promise<void>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import manifest from "../vendor-types/manifest.json" with { type: "json" };
|
|
3
|
+
export function pinnedVersion(vendor, pkg, sourcePackage = pkg) {
|
|
4
|
+
const prefix = `npm:${sourcePackage}@`;
|
|
5
|
+
const source = manifest[vendor].sources.find((entry) => entry.source.startsWith(prefix));
|
|
6
|
+
if (!source)
|
|
7
|
+
throw new Error(`agent/vendor-types/manifest.json has no ${sourcePackage} for ${vendor} (needed for ${pkg})`);
|
|
8
|
+
return source.source.slice(prefix.length);
|
|
9
|
+
}
|
|
10
|
+
export async function installedPackage(installDir, pkg, version, executable) {
|
|
11
|
+
const manifest = Bun.file(`${installDir}/node_modules/${pkg}/package.json`);
|
|
12
|
+
const found = (await manifest.exists()) ? ((await manifest.json()).version ?? null) : null;
|
|
13
|
+
return { version: found, ready: found !== null && existsSync(executable()), pinned: version };
|
|
14
|
+
}
|
|
15
|
+
export async function installPackage(installDir, pkg, version, executable) {
|
|
16
|
+
const installed = await installedPackage(installDir, pkg, version, executable);
|
|
17
|
+
if (installed.ready && installed.version === version)
|
|
18
|
+
return;
|
|
19
|
+
const force = installed.version === version ? ["--force"] : [];
|
|
20
|
+
const proc = Bun.spawn(["flock", `${installDir}/.install.lock`, process.execPath, "add", "--exact", ...force, `${pkg}@${version}`], {
|
|
21
|
+
cwd: installDir,
|
|
22
|
+
stdin: "ignore",
|
|
23
|
+
stdout: "pipe",
|
|
24
|
+
stderr: "pipe",
|
|
25
|
+
});
|
|
26
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
27
|
+
new Response(proc.stdout).text(),
|
|
28
|
+
new Response(proc.stderr).text(),
|
|
29
|
+
proc.exited,
|
|
30
|
+
]);
|
|
31
|
+
if (exitCode !== 0)
|
|
32
|
+
throw new Error(`bun add ${pkg}@${version} failed (exit ${exitCode}): ${(stderr || stdout).trim()}`);
|
|
33
|
+
if (!existsSync(executable()))
|
|
34
|
+
throw new Error(`bun add ${pkg}@${version} left no ${executable()}: its platform package didn't install`);
|
|
35
|
+
}
|
|
@@ -1,69 +1,38 @@
|
|
|
1
1
|
import type { Sandbox } from "../../sandbox/api.ts";
|
|
2
|
-
import type { AgentAuthStatus, AgentBackend, AgentPermissionRequest, AgentQuestionRequest, AgentSession } from "../api.ts";
|
|
3
|
-
/** What the worker keeps of a session: never its messages, which are the backend's to store. */
|
|
2
|
+
import type { AgentAuthStatus, AgentBackend, AgentPermissionMode, AgentPermissionRequest, AgentQuestionRequest, AgentSession, AgentSessionState } from "../api.ts";
|
|
4
3
|
export type SessionRecord = {
|
|
5
4
|
spec: AgentSession;
|
|
6
|
-
/** The backend's id for the conversation, once known: what a new worker resumes. */
|
|
7
5
|
nativeId: string | null;
|
|
8
|
-
/** Chosen through AgentModels; null for the backend's default. */
|
|
9
6
|
model: string | null;
|
|
7
|
+
permissionMode?: AgentPermissionMode;
|
|
10
8
|
};
|
|
11
|
-
/** Where a session's CLI stands with its sign-in, as far as it outlives a request. */
|
|
12
9
|
export type SessionAuthRecord = {
|
|
13
|
-
/** Where the CLI's login in progress asked the user to sign in; null when none is. */
|
|
14
10
|
login: Extract<AgentAuthStatus, {
|
|
15
11
|
state: "pending";
|
|
16
12
|
}> | null;
|
|
17
|
-
/** Why the last login failed, if it did. */
|
|
18
13
|
error: string | null;
|
|
19
14
|
};
|
|
20
|
-
/** Every name must pass checkName; operations throw InvalidNameError otherwise. */
|
|
21
15
|
export interface SessionStorage {
|
|
22
16
|
getSessions(): Promise<SessionRecord[]>;
|
|
23
|
-
/** Stores the record unless one with that name exists; returns whichever record is stored afterwards. */
|
|
24
17
|
insertSession(record: SessionRecord): Promise<SessionRecord>;
|
|
25
|
-
/** Replaces the record. */
|
|
26
18
|
saveSession(record: SessionRecord): Promise<void>;
|
|
27
|
-
/** Removes the record and everything kept for the session. No-op if missing. */
|
|
28
19
|
removeSession(sessionName: string): Promise<void>;
|
|
29
|
-
/** The permission requests the session's CLI is waiting on, oldest first. */
|
|
30
20
|
getPermissionRequests(sessionName: string): Promise<AgentPermissionRequest[]>;
|
|
31
21
|
savePermissionRequest(sessionName: string, request: AgentPermissionRequest): Promise<void>;
|
|
32
|
-
/** No-op if missing. */
|
|
33
22
|
removePermissionRequest(sessionName: string, requestId: string): Promise<void>;
|
|
34
|
-
/** The questions the session's CLI is waiting on, oldest first. */
|
|
35
23
|
getQuestionRequests(sessionName: string): Promise<AgentQuestionRequest[]>;
|
|
36
24
|
saveQuestionRequest(sessionName: string, request: AgentQuestionRequest): Promise<void>;
|
|
37
|
-
/** No-op if missing. */
|
|
38
25
|
removeQuestionRequest(sessionName: string, requestId: string): Promise<void>;
|
|
39
|
-
/** Null until the session's CLI has been asked to sign in. */
|
|
40
26
|
readAuth(sessionName: string): Promise<SessionAuthRecord | null>;
|
|
41
27
|
saveAuth(sessionName: string, record: SessionAuthRecord): Promise<void>;
|
|
42
|
-
|
|
28
|
+
saveStatus(sessionName: string, status: AgentSessionState): Promise<void>;
|
|
43
29
|
socketPath(): Promise<string>;
|
|
44
|
-
/**
|
|
45
|
-
* Sandbox path of the backend CLI's home (claude's config directory, codex's CODEX_HOME): its
|
|
46
|
-
* sign-in, config and conversations, kept with the rest of the dimension's state. Shared by the
|
|
47
|
-
* dimension's agents, like a home would be. Creates it.
|
|
48
|
-
*/
|
|
49
30
|
backendHome(backend: AgentBackend): Promise<string>;
|
|
50
31
|
}
|
|
51
|
-
/**
|
|
52
|
-
* The agent's worker socket in a dimension's run directory: RUN_PATH inside the dimension, or the
|
|
53
|
-
* host side of that mount, so the host reaches the worker through it.
|
|
54
|
-
*/
|
|
55
32
|
export declare function workerSocketPath(runDirectory: string, agentName: string): string;
|
|
56
|
-
|
|
33
|
+
export declare function workerReadyPath(runDirectory: string, agentName: string): string;
|
|
57
34
|
export declare function sessionRecordPath(stateDirectory: string, agentName: string, sessionName: string): string;
|
|
58
|
-
/** Where the backend CLI's home is (SessionStorage.backendHome) in a dimension's state directory. */
|
|
59
35
|
export declare function backendHomePath(stateDirectory: string, backend: AgentBackend): string;
|
|
60
|
-
/**
|
|
61
|
-
* `stateDirectory` is a sandbox path: the dimension's state directory (STATE_PATH, mounted from the
|
|
62
|
-
* host's hyperbrane storage, so everything here lives there with the rest). Layout:
|
|
63
|
-
* agents/<agent>/sessions/<session>.json, and agents/<agent>/sessions/<session>/ holding
|
|
64
|
-
* permission-requests/<id>.json, question-requests/<id>.json and auth.json; backends/<backend>/
|
|
65
|
-
* (the CLIs' homes). The socket goes in `runDirectory`, the dimension's run directory.
|
|
66
|
-
*/
|
|
67
36
|
export declare function createSessionStorage(sandbox: Sandbox, agentName: string, { stateDirectory, runDirectory }?: {
|
|
68
37
|
stateDirectory?: string;
|
|
69
38
|
runDirectory?: string;
|
|
@@ -1,37 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dimensionLayout } from "../../dimension/api.js";
|
|
2
2
|
import { checkName, createJsonDirectory } from "../../lib/jsonDirectory.js";
|
|
3
|
-
/**
|
|
4
|
-
* The agent's worker socket in a dimension's run directory: RUN_PATH inside the dimension, or the
|
|
5
|
-
* host side of that mount, so the host reaches the worker through it.
|
|
6
|
-
*/
|
|
7
3
|
export function workerSocketPath(runDirectory, agentName) {
|
|
8
4
|
return `${runDirectory}/${agentName}.sock`;
|
|
9
5
|
}
|
|
6
|
+
export function workerReadyPath(runDirectory, agentName) {
|
|
7
|
+
return `${runDirectory}/${agentName}.ready`;
|
|
8
|
+
}
|
|
10
9
|
function sessionsDirectory(stateDirectory, agentName) {
|
|
11
10
|
return `${stateDirectory}/agents/${agentName}/sessions`;
|
|
12
11
|
}
|
|
13
|
-
/** Where the session's record is in a dimension's state directory (see createSessionStorage). */
|
|
14
12
|
export function sessionRecordPath(stateDirectory, agentName, sessionName) {
|
|
15
13
|
return `${sessionsDirectory(stateDirectory, agentName)}/${checkName(sessionName)}.json`;
|
|
16
14
|
}
|
|
17
|
-
/** Where the backend CLI's home is (SessionStorage.backendHome) in a dimension's state directory. */
|
|
18
15
|
export function backendHomePath(stateDirectory, backend) {
|
|
19
16
|
return `${stateDirectory}/backends/${backend}`;
|
|
20
17
|
}
|
|
21
|
-
|
|
22
|
-
* `stateDirectory` is a sandbox path: the dimension's state directory (STATE_PATH, mounted from the
|
|
23
|
-
* host's hyperbrane storage, so everything here lives there with the rest). Layout:
|
|
24
|
-
* agents/<agent>/sessions/<session>.json, and agents/<agent>/sessions/<session>/ holding
|
|
25
|
-
* permission-requests/<id>.json, question-requests/<id>.json and auth.json; backends/<backend>/
|
|
26
|
-
* (the CLIs' homes). The socket goes in `runDirectory`, the dimension's run directory.
|
|
27
|
-
*/
|
|
28
|
-
export function createSessionStorage(sandbox, agentName, { stateDirectory = STATE_PATH, runDirectory = RUN_PATH } = {}) {
|
|
18
|
+
export function createSessionStorage(sandbox, agentName, { stateDirectory = dimensionLayout.statePath(), runDirectory = dimensionLayout.runPath() } = {}) {
|
|
29
19
|
const sessionsDir = sessionsDirectory(stateDirectory, agentName);
|
|
30
20
|
const sessions = createJsonDirectory(sandbox, async () => sessionsDir);
|
|
31
21
|
const sessionDir = (sessionName) => `${sessionsDir}/${checkName(sessionName)}`;
|
|
32
22
|
const permissionRequests = (sessionName) => createJsonDirectory(sandbox, async () => `${sessionDir(sessionName)}/permission-requests`);
|
|
33
23
|
const questionRequests = (sessionName) => createJsonDirectory(sandbox, async () => `${sessionDir(sessionName)}/question-requests`);
|
|
34
24
|
const sessionFiles = (sessionName) => createJsonDirectory(sandbox, async () => sessionDir(sessionName));
|
|
25
|
+
const statusFiles = (sessionName) => createJsonDirectory(sandbox, async () => sessionDir(sessionName));
|
|
35
26
|
async function oldestFirst(stored) {
|
|
36
27
|
return (await stored).sort((a, b) => a.at - b.at).map((entry) => entry.request);
|
|
37
28
|
}
|
|
@@ -44,7 +35,6 @@ export function createSessionStorage(sandbox, agentName, { stateDirectory = STAT
|
|
|
44
35
|
async function saveSession(record) {
|
|
45
36
|
await sessions.save(record.spec.name, record);
|
|
46
37
|
}
|
|
47
|
-
// Everything kept for it first: while the record is there, the removal can be retried.
|
|
48
38
|
async function removeSession(sessionName) {
|
|
49
39
|
await sandbox.removeDirectory(sessionDir(sessionName));
|
|
50
40
|
await sessions.remove(sessionName);
|
|
@@ -73,6 +63,9 @@ export function createSessionStorage(sandbox, agentName, { stateDirectory = STAT
|
|
|
73
63
|
async function saveAuth(sessionName, record) {
|
|
74
64
|
await sessionFiles(sessionName).save("auth", record);
|
|
75
65
|
}
|
|
66
|
+
async function saveStatus(sessionName, status) {
|
|
67
|
+
await statusFiles(sessionName).save("status", { status });
|
|
68
|
+
}
|
|
76
69
|
async function socketPath() {
|
|
77
70
|
return workerSocketPath(runDirectory, agentName);
|
|
78
71
|
}
|
|
@@ -94,6 +87,7 @@ export function createSessionStorage(sandbox, agentName, { stateDirectory = STAT
|
|
|
94
87
|
removeQuestionRequest,
|
|
95
88
|
readAuth,
|
|
96
89
|
saveAuth,
|
|
90
|
+
saveStatus,
|
|
97
91
|
socketPath,
|
|
98
92
|
backendHome,
|
|
99
93
|
};
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import type { ConnectWorker } from "./workerConnection.ts";
|
|
2
2
|
/** @sealed */
|
|
3
3
|
export type SharedConnector = ConnectWorker & {
|
|
4
|
-
/** Closes the connection they share (a later connect opens another). */
|
|
5
4
|
close(): void;
|
|
6
5
|
};
|
|
7
|
-
/**
|
|
8
|
-
* One connection to the worker, shared by everyone `connect`s through it: each gets a view of it
|
|
9
|
-
* (its requests go over it, the worker's notifications reach every view, closing a view only
|
|
10
|
-
* detaches it), so calls cost a round trip each rather than a connection each. Over an endpoint
|
|
11
|
-
* that's what matters (workerSseConnector opens an event stream per connection). The connection is
|
|
12
|
-
* opened on first use, and again after it closes.
|
|
13
|
-
*/
|
|
14
6
|
export declare function sharedConnector(connect: ConnectWorker): SharedConnector;
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* One connection to the worker, shared by everyone `connect`s through it: each gets a view of it
|
|
3
|
-
* (its requests go over it, the worker's notifications reach every view, closing a view only
|
|
4
|
-
* detaches it), so calls cost a round trip each rather than a connection each. Over an endpoint
|
|
5
|
-
* that's what matters (workerSseConnector opens an event stream per connection). The connection is
|
|
6
|
-
* opened on first use, and again after it closes.
|
|
7
|
-
*/
|
|
8
1
|
export function sharedConnector(connect) {
|
|
9
2
|
let current = null;
|
|
10
3
|
function open() {
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import type { AgentBackend } from "../api.ts";
|
|
2
2
|
import type { SessionBackend } from "./backend.ts";
|
|
3
3
|
import type { LineMessages, TranscriptLayout } from "./fileTranscript.ts";
|
|
4
|
-
/**
|
|
5
|
-
* Each backend's conversations as it stores them: where (under the home homeEnvironment points it
|
|
6
|
-
* at) and how a line reads, for reading a transcript with nothing running, from the host. None of
|
|
7
|
-
* the backends' other code, which runs only in the dimension.
|
|
8
|
-
*/
|
|
9
4
|
export type StoredTranscripts = Pick<SessionBackend, "homeEnvironment"> & {
|
|
10
5
|
layout: TranscriptLayout;
|
|
11
6
|
lineMessages(nativeId: string): LineMessages;
|
|
12
7
|
};
|
|
13
|
-
export declare const STORED_TRANSCRIPTS: Record<AgentBackend, StoredTranscripts
|
|
8
|
+
export declare const STORED_TRANSCRIPTS: Partial<Record<AgentBackend, StoredTranscripts>>;
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { claudeHomeEnvironment } from "./claude/home.js";
|
|
2
2
|
import { CLAUDE_TRANSCRIPT_LAYOUT, claudeLineMessages } from "./claude/transcript.js";
|
|
3
|
-
import { codexHomeEnvironment } from "./codex/home.js";
|
|
4
|
-
import { CODEX_TRANSCRIPT_LAYOUT, codexLineMessages } from "./codex/transcript.js";
|
|
5
3
|
export const STORED_TRANSCRIPTS = {
|
|
6
4
|
claude: { homeEnvironment: claudeHomeEnvironment, layout: CLAUDE_TRANSCRIPT_LAYOUT, lineMessages: () => claudeLineMessages },
|
|
7
|
-
codex: { homeEnvironment: codexHomeEnvironment, layout: CODEX_TRANSCRIPT_LAYOUT, lineMessages: codexLineMessages },
|
|
8
5
|
};
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JSON-RPC 2.0 over a byte stream, one JSON object per line (the framing codex's app-server uses).
|
|
3
|
-
* Either end can send requests and notifications; the worker protocol (protocol.ts) is built on it.
|
|
4
|
-
*/
|
|
5
1
|
export type JsonRpcId = string | number;
|
|
6
2
|
export type JsonRpcErrorObject = {
|
|
7
3
|
code: number;
|
|
@@ -9,29 +5,22 @@ export type JsonRpcErrorObject = {
|
|
|
9
5
|
data?: unknown;
|
|
10
6
|
};
|
|
11
7
|
export type JsonRpcPeerOptions = {
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
onRequest(method: string, params: unknown, protocol: number | undefined): Promise<unknown>;
|
|
9
|
+
protocol?: number;
|
|
14
10
|
onNotification?(method: string, params: unknown): void;
|
|
15
11
|
encodeError(error: unknown): JsonRpcErrorObject;
|
|
16
12
|
decodeError(error: JsonRpcErrorObject): Error;
|
|
17
13
|
};
|
|
18
14
|
export interface JsonRpcPeer {
|
|
19
|
-
/** Feeds text read from the connection; complete lines are handled as they arrive. */
|
|
20
15
|
receive(text: string): void;
|
|
21
16
|
request(method: string, params: unknown): Promise<unknown>;
|
|
22
17
|
notify(method: string, params: unknown): void;
|
|
23
|
-
/** Fails every request still waiting for its response. */
|
|
24
18
|
close(error: Error): void;
|
|
25
19
|
}
|
|
26
20
|
export declare function createJsonRpcPeer(write: (line: string) => void, options: JsonRpcPeerOptions): JsonRpcPeer;
|
|
27
|
-
/** The part of a Bun socket a writer needs. */
|
|
28
21
|
type WritableSocket = {
|
|
29
22
|
write(data: Uint8Array): number;
|
|
30
23
|
};
|
|
31
|
-
/**
|
|
32
|
-
* Writes to a Bun socket without losing what it can't take yet: the rest is kept and written on
|
|
33
|
-
* `drain`, which the socket's drain handler must call.
|
|
34
|
-
*/
|
|
35
24
|
export declare function createSocketWriter(socket: WritableSocket): {
|
|
36
25
|
write(text: string): void;
|
|
37
26
|
drain(): void;
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JSON-RPC 2.0 over a byte stream, one JSON object per line (the framing codex's app-server uses).
|
|
3
|
-
* Either end can send requests and notifications; the worker protocol (protocol.ts) is built on it.
|
|
4
|
-
*/
|
|
5
|
-
/** Answer for a line that isn't JSON-RPC at all. */
|
|
6
1
|
const PARSE_ERROR = -32700;
|
|
7
2
|
export function createJsonRpcPeer(write, options) {
|
|
8
3
|
const pending = new Map();
|
|
@@ -31,7 +26,7 @@ export function createJsonRpcPeer(write, options) {
|
|
|
31
26
|
return;
|
|
32
27
|
}
|
|
33
28
|
const id = message.id;
|
|
34
|
-
options.onRequest(message.method, message.params).then((result) => send({ id, result: result ?? null }), (error) => send({ id, error: options.encodeError(error) }));
|
|
29
|
+
options.onRequest(message.method, message.params, message.protocol).then((result) => send({ id, result: result ?? null }), (error) => send({ id, error: options.encodeError(error) }));
|
|
35
30
|
}
|
|
36
31
|
function receive(text) {
|
|
37
32
|
buffered += text;
|
|
@@ -58,7 +53,7 @@ export function createJsonRpcPeer(write, options) {
|
|
|
58
53
|
const id = nextId++;
|
|
59
54
|
return new Promise((resolve, reject) => {
|
|
60
55
|
pending.set(id, { resolve, reject });
|
|
61
|
-
send({ id, method, params });
|
|
56
|
+
send({ id, method, params, ...(options.protocol === undefined ? {} : { protocol: options.protocol }) });
|
|
62
57
|
});
|
|
63
58
|
}
|
|
64
59
|
function notify(method, params) {
|
|
@@ -72,10 +67,6 @@ export function createJsonRpcPeer(write, options) {
|
|
|
72
67
|
}
|
|
73
68
|
return { receive, request, notify, close };
|
|
74
69
|
}
|
|
75
|
-
/**
|
|
76
|
-
* Writes to a Bun socket without losing what it can't take yet: the rest is kept and written on
|
|
77
|
-
* `drain`, which the socket's drain handler must call.
|
|
78
|
-
*/
|
|
79
70
|
export function createSocketWriter(socket) {
|
|
80
71
|
const encoder = new TextEncoder();
|
|
81
72
|
let backlog = null;
|