@getpaseo/server 0.1.63 → 0.1.66
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/dist/server/client/daemon-client-transport-types.d.ts +2 -0
- package/dist/server/client/daemon-client-transport-types.d.ts.map +1 -1
- package/dist/server/client/daemon-client-websocket-transport.d.ts +2 -1
- package/dist/server/client/daemon-client-websocket-transport.d.ts.map +1 -1
- package/dist/server/client/daemon-client-websocket-transport.js +4 -4
- package/dist/server/client/daemon-client-websocket-transport.js.map +1 -1
- package/dist/server/client/daemon-client.d.ts +30 -20
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +206 -98
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/client/terminal-stream-router.d.ts +24 -0
- package/dist/server/client/terminal-stream-router.d.ts.map +1 -0
- package/dist/server/client/terminal-stream-router.js +100 -0
- package/dist/server/client/terminal-stream-router.js.map +1 -0
- package/dist/server/server/agent/activity-curator.d.ts +6 -3
- package/dist/server/server/agent/activity-curator.d.ts.map +1 -1
- package/dist/server/server/agent/activity-curator.js +45 -138
- package/dist/server/server/agent/activity-curator.js.map +1 -1
- package/dist/server/server/agent/agent-manager.d.ts +3 -14
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +75 -140
- package/dist/server/server/agent/agent-manager.js.map +1 -1
- package/dist/server/server/agent/agent-metadata-generator.d.ts +0 -5
- package/dist/server/server/agent/agent-metadata-generator.d.ts.map +1 -1
- package/dist/server/server/agent/agent-metadata-generator.js +3 -93
- package/dist/server/server/agent/agent-metadata-generator.js.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +15 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
- package/dist/server/server/agent/agent-stream-coalescer.d.ts +1 -1
- package/dist/server/server/agent/agent-stream-coalescer.d.ts.map +1 -1
- package/dist/server/server/agent/agent-stream-coalescer.js +1 -1
- package/dist/server/server/agent/agent-stream-coalescer.js.map +1 -1
- package/dist/server/server/agent/agent-timeline-store.d.ts.map +1 -1
- package/dist/server/server/agent/agent-timeline-store.js +29 -32
- package/dist/server/server/agent/agent-timeline-store.js.map +1 -1
- package/dist/server/server/agent/foreground-run-state.d.ts +50 -0
- package/dist/server/server/agent/foreground-run-state.d.ts.map +1 -0
- package/dist/server/server/agent/foreground-run-state.js +162 -0
- package/dist/server/server/agent/foreground-run-state.js.map +1 -0
- package/dist/server/server/agent/mcp-server.d.ts +5 -3
- package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
- package/dist/server/server/agent/mcp-server.js +110 -99
- package/dist/server/server/agent/mcp-server.js.map +1 -1
- package/dist/server/server/agent/mcp-shared.d.ts.map +1 -1
- package/dist/server/server/agent/mcp-shared.js +7 -1
- package/dist/server/server/agent/mcp-shared.js.map +1 -1
- package/dist/server/server/agent/prompt-attachments.d.ts +4 -3
- package/dist/server/server/agent/prompt-attachments.d.ts.map +1 -1
- package/dist/server/server/agent/prompt-attachments.js +43 -4
- package/dist/server/server/agent/prompt-attachments.js.map +1 -1
- package/dist/server/server/agent/provider-manifest.d.ts.map +1 -1
- package/dist/server/server/agent/provider-manifest.js +7 -0
- package/dist/server/server/agent/provider-manifest.js.map +1 -1
- package/dist/server/server/agent/providers/acp-agent.d.ts +38 -1
- package/dist/server/server/agent/providers/acp-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/acp-agent.js +257 -140
- package/dist/server/server/agent/providers/acp-agent.js.map +1 -1
- package/dist/server/server/agent/providers/claude/sidechain-tracker.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude/sidechain-tracker.js +1 -1
- package/dist/server/server/agent/providers/claude/sidechain-tracker.js.map +1 -1
- package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude/tool-call-mapper.js +68 -198
- package/dist/server/server/agent/providers/claude/tool-call-mapper.js.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.js +52 -102
- package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
- package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex/tool-call-mapper.js +125 -141
- package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +36 -6
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +374 -219
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +6 -2
- package/dist/server/server/agent/providers/mock-load-test-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/mock-load-test-agent.js +294 -113
- package/dist/server/server/agent/providers/mock-load-test-agent.js.map +1 -1
- package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts +1 -1
- package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts.map +1 -1
- package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js +94 -2
- package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js.map +1 -1
- package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts.map +1 -1
- package/dist/server/server/agent/providers/opencode/tool-call-mapper.js +24 -115
- package/dist/server/server/agent/providers/opencode/tool-call-mapper.js.map +1 -1
- package/dist/server/server/agent/providers/opencode-agent.d.ts +102 -1
- package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/opencode-agent.js +416 -158
- package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
- package/dist/server/server/agent/providers/provider-runner.d.ts +27 -0
- package/dist/server/server/agent/providers/provider-runner.d.ts.map +1 -0
- package/dist/server/server/agent/providers/provider-runner.js +80 -0
- package/dist/server/server/agent/providers/provider-runner.js.map +1 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +6 -3
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
- package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts +2 -0
- package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts.map +1 -1
- package/dist/server/server/agent/providers/tool-call-mapper-utils.js +31 -0
- package/dist/server/server/agent/providers/tool-call-mapper-utils.js.map +1 -1
- package/dist/server/server/agent/timeline-projection.d.ts +21 -5
- package/dist/server/server/agent/timeline-projection.d.ts.map +1 -1
- package/dist/server/server/agent/timeline-projection.js +59 -9
- package/dist/server/server/agent/timeline-projection.js.map +1 -1
- package/dist/server/server/auth.d.ts +25 -0
- package/dist/server/server/auth.d.ts.map +1 -0
- package/dist/server/server/auth.js +93 -0
- package/dist/server/server/auth.js.map +1 -0
- package/dist/server/server/bootstrap.d.ts +3 -1
- package/dist/server/server/bootstrap.d.ts.map +1 -1
- package/dist/server/server/bootstrap.js +87 -30
- package/dist/server/server/bootstrap.js.map +1 -1
- package/dist/server/server/config.d.ts.map +1 -1
- package/dist/server/server/config.js +11 -0
- package/dist/server/server/config.js.map +1 -1
- package/dist/server/server/exports.d.ts +7 -1
- package/dist/server/server/exports.d.ts.map +1 -1
- package/dist/server/server/exports.js +6 -1
- package/dist/server/server/exports.js.map +1 -1
- package/dist/server/server/file-explorer/service.d.ts +10 -0
- package/dist/server/server/file-explorer/service.d.ts.map +1 -1
- package/dist/server/server/file-explorer/service.js +38 -4
- package/dist/server/server/file-explorer/service.js.map +1 -1
- package/dist/server/server/index.js +9 -6
- package/dist/server/server/index.js.map +1 -1
- package/dist/server/server/logger.d.ts.map +1 -1
- package/dist/server/server/logger.js +15 -1
- package/dist/server/server/logger.js.map +1 -1
- package/dist/server/server/pagination/cursor.d.ts +16 -0
- package/dist/server/server/pagination/cursor.d.ts.map +1 -0
- package/dist/server/server/pagination/cursor.js +62 -0
- package/dist/server/server/pagination/cursor.js.map +1 -0
- package/dist/server/server/pagination/sortable-pager.d.ts +24 -0
- package/dist/server/server/pagination/sortable-pager.d.ts.map +1 -0
- package/dist/server/server/pagination/sortable-pager.js +68 -0
- package/dist/server/server/pagination/sortable-pager.js.map +1 -0
- package/dist/server/server/paseo-worktree-archive-service.d.ts +3 -1
- package/dist/server/server/paseo-worktree-archive-service.d.ts.map +1 -1
- package/dist/server/server/paseo-worktree-archive-service.js +61 -53
- package/dist/server/server/paseo-worktree-archive-service.js.map +1 -1
- package/dist/server/server/paseo-worktree-service.d.ts +13 -0
- package/dist/server/server/paseo-worktree-service.d.ts.map +1 -1
- package/dist/server/server/paseo-worktree-service.js +72 -3
- package/dist/server/server/paseo-worktree-service.js.map +1 -1
- package/dist/server/server/persisted-config.d.ts +25 -0
- package/dist/server/server/persisted-config.d.ts.map +1 -1
- package/dist/server/server/persisted-config.js +9 -0
- package/dist/server/server/persisted-config.js.map +1 -1
- package/dist/server/server/relay-transport.d.ts.map +1 -1
- package/dist/server/server/relay-transport.js +16 -4
- package/dist/server/server/relay-transport.js.map +1 -1
- package/dist/server/server/resolve-worktree-creation-intent.d.ts +0 -10
- package/dist/server/server/resolve-worktree-creation-intent.d.ts.map +1 -1
- package/dist/server/server/resolve-worktree-creation-intent.js +1 -45
- package/dist/server/server/resolve-worktree-creation-intent.js.map +1 -1
- package/dist/server/server/script-status-projection.d.ts +6 -1
- package/dist/server/server/script-status-projection.d.ts.map +1 -1
- package/dist/server/server/script-status-projection.js +12 -3
- package/dist/server/server/script-status-projection.js.map +1 -1
- package/dist/server/server/session.d.ts +19 -51
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +354 -1069
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/server/websocket-server.d.ts +4 -2
- package/dist/server/server/websocket-server.d.ts.map +1 -1
- package/dist/server/server/websocket-server.js +65 -12
- package/dist/server/server/websocket-server.js.map +1 -1
- package/dist/server/server/workspace-directory.d.ts +69 -0
- package/dist/server/server/workspace-directory.d.ts.map +1 -0
- package/dist/server/server/workspace-directory.js +229 -0
- package/dist/server/server/workspace-directory.js.map +1 -0
- package/dist/server/server/worktree-bootstrap.d.ts.map +1 -1
- package/dist/server/server/worktree-bootstrap.js +6 -2
- package/dist/server/server/worktree-bootstrap.js.map +1 -1
- package/dist/server/server/worktree-core.d.ts +2 -0
- package/dist/server/server/worktree-core.d.ts.map +1 -1
- package/dist/server/server/worktree-core.js.map +1 -1
- package/dist/server/server/worktree-errors.d.ts +1 -1
- package/dist/server/server/worktree-errors.d.ts.map +1 -1
- package/dist/server/server/worktree-errors.js +1 -4
- package/dist/server/server/worktree-errors.js.map +1 -1
- package/dist/server/server/worktree-session.d.ts +47 -20
- package/dist/server/server/worktree-session.d.ts.map +1 -1
- package/dist/server/server/worktree-session.js +68 -25
- package/dist/server/server/worktree-session.js.map +1 -1
- package/dist/server/shared/binary-frames/file-transfer.d.ts +56 -0
- package/dist/server/shared/binary-frames/file-transfer.d.ts.map +1 -0
- package/dist/server/shared/binary-frames/file-transfer.js +108 -0
- package/dist/server/shared/binary-frames/file-transfer.js.map +1 -0
- package/dist/server/shared/binary-frames/index.d.ts +3 -0
- package/dist/server/shared/binary-frames/index.d.ts.map +1 -0
- package/dist/server/shared/binary-frames/index.js +3 -0
- package/dist/server/shared/binary-frames/index.js.map +1 -0
- package/dist/server/shared/{terminal-stream-protocol.d.ts → binary-frames/terminal.d.ts} +2 -2
- package/dist/server/shared/binary-frames/terminal.d.ts.map +1 -0
- package/dist/server/shared/{terminal-stream-protocol.js → binary-frames/terminal.js} +2 -2
- package/dist/server/shared/binary-frames/terminal.js.map +1 -0
- package/dist/server/shared/client-capabilities.d.ts +5 -0
- package/dist/server/shared/client-capabilities.d.ts.map +1 -0
- package/dist/server/shared/client-capabilities.js +4 -0
- package/dist/server/shared/client-capabilities.js.map +1 -0
- package/dist/server/shared/connection-offer.d.ts +8 -0
- package/dist/server/shared/connection-offer.d.ts.map +1 -1
- package/dist/server/shared/connection-offer.js +35 -0
- package/dist/server/shared/connection-offer.js.map +1 -1
- package/dist/server/shared/daemon-endpoints.d.ts +16 -1
- package/dist/server/shared/daemon-endpoints.d.ts.map +1 -1
- package/dist/server/shared/daemon-endpoints.js +65 -6
- package/dist/server/shared/daemon-endpoints.js.map +1 -1
- package/dist/server/shared/host-connection-schema.d.ts +23 -0
- package/dist/server/shared/host-connection-schema.d.ts.map +1 -0
- package/dist/server/shared/host-connection-schema.js +9 -0
- package/dist/server/shared/host-connection-schema.js.map +1 -0
- package/dist/server/shared/messages.d.ts +3242 -535
- package/dist/server/shared/messages.d.ts.map +1 -1
- package/dist/server/shared/messages.js +73 -34
- package/dist/server/shared/messages.js.map +1 -1
- package/dist/server/terminal/terminal-manager-factory.d.ts +7 -0
- package/dist/server/terminal/terminal-manager-factory.d.ts.map +1 -0
- package/dist/server/terminal/terminal-manager-factory.js +13 -0
- package/dist/server/terminal/terminal-manager-factory.js.map +1 -0
- package/dist/server/terminal/terminal-manager.d.ts +7 -1
- package/dist/server/terminal/terminal-manager.d.ts.map +1 -1
- package/dist/server/terminal/terminal-manager.js +14 -1
- package/dist/server/terminal/terminal-manager.js.map +1 -1
- package/dist/server/terminal/terminal-session-controller.d.ts +63 -0
- package/dist/server/terminal/terminal-session-controller.d.ts.map +1 -0
- package/dist/server/terminal/terminal-session-controller.js +615 -0
- package/dist/server/terminal/terminal-session-controller.js.map +1 -0
- package/dist/server/terminal/terminal-ts-loader.mjs +20 -0
- package/dist/server/terminal/terminal-worker-process.d.ts +2 -0
- package/dist/server/terminal/terminal-worker-process.d.ts.map +1 -0
- package/dist/server/terminal/terminal-worker-process.js +221 -0
- package/dist/server/terminal/terminal-worker-process.js.map +1 -0
- package/dist/server/terminal/terminal-worker-protocol.d.ts +113 -0
- package/dist/server/terminal/terminal-worker-protocol.d.ts.map +1 -0
- package/dist/server/terminal/terminal-worker-protocol.js +2 -0
- package/dist/server/terminal/terminal-worker-protocol.js.map +1 -0
- package/dist/server/terminal/terminal.d.ts +7 -0
- package/dist/server/terminal/terminal.d.ts.map +1 -1
- package/dist/server/terminal/terminal.js +45 -9
- package/dist/server/terminal/terminal.js.map +1 -1
- package/dist/server/terminal/worker-terminal-manager.d.ts +19 -0
- package/dist/server/terminal/worker-terminal-manager.d.ts.map +1 -0
- package/dist/server/terminal/worker-terminal-manager.js +466 -0
- package/dist/server/terminal/worker-terminal-manager.js.map +1 -0
- package/dist/server/utils/directory-suggestions.d.ts.map +1 -1
- package/dist/server/utils/directory-suggestions.js +10 -1
- package/dist/server/utils/directory-suggestions.js.map +1 -1
- package/dist/server/utils/process-tree.d.ts +25 -0
- package/dist/server/utils/process-tree.d.ts.map +1 -0
- package/dist/server/utils/process-tree.js +96 -0
- package/dist/server/utils/process-tree.js.map +1 -0
- package/dist/server/utils/windows-command.d.ts.map +1 -1
- package/dist/server/utils/windows-command.js +5 -1
- package/dist/server/utils/windows-command.js.map +1 -1
- package/dist/server/utils/worktree-metadata.d.ts +44 -0
- package/dist/server/utils/worktree-metadata.d.ts.map +1 -1
- package/dist/server/utils/worktree-metadata.js +58 -0
- package/dist/server/utils/worktree-metadata.js.map +1 -1
- package/dist/server/utils/worktree.d.ts +14 -2
- package/dist/server/utils/worktree.d.ts.map +1 -1
- package/dist/server/utils/worktree.js +22 -13
- package/dist/server/utils/worktree.js.map +1 -1
- package/package.json +5 -4
- package/dist/server/shared/terminal-stream-protocol.d.ts.map +0 -1
- package/dist/server/shared/terminal-stream-protocol.js.map +0 -1
|
@@ -6,10 +6,12 @@ import { realpathSync } from "node:fs";
|
|
|
6
6
|
import { basename, resolve, sep } from "path";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import { z } from "zod";
|
|
9
|
+
import { CLIENT_CAPS } from "../shared/client-capabilities.js";
|
|
9
10
|
import { isLegacyEditorTargetId, serializeAgentStreamEvent, } from "./messages.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { TerminalSessionController } from "../terminal/terminal-session-controller.js";
|
|
12
|
+
import { encodeFileTransferFrame, FileTransferOpcode, } from "../shared/binary-frames/index.js";
|
|
13
|
+
import { CursorError } from "./pagination/cursor.js";
|
|
14
|
+
import { SortablePager } from "./pagination/sortable-pager.js";
|
|
13
15
|
import { TTSManager } from "./agent/tts-manager.js";
|
|
14
16
|
import { STTManager } from "./agent/stt-manager.js";
|
|
15
17
|
import { maybePersistTtsDebugAudio } from "./agent/tts-debug.js";
|
|
@@ -22,7 +24,7 @@ import { ensureAgentLoaded } from "./agent/agent-loading.js";
|
|
|
22
24
|
import { sendPromptToAgent, unarchiveAgentState } from "./agent/mcp-shared.js";
|
|
23
25
|
import { experimental_createMCPClient } from "ai";
|
|
24
26
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
25
|
-
import { buildWorkspaceScriptPayloads } from "./script-status-projection.js";
|
|
27
|
+
import { buildWorkspaceScriptPayloads, readPaseoConfigForProjection, } from "./script-status-projection.js";
|
|
26
28
|
import { deriveProjectSlug } from "./workspace-git-metadata.js";
|
|
27
29
|
import { spawnWorkspaceScript } from "./worktree-bootstrap.js";
|
|
28
30
|
import { applyMutableProviderConfigToOverrides } from "./daemon-config-store.js";
|
|
@@ -38,9 +40,8 @@ import { checkoutLiteFromGitSnapshot, normalizeWorkspaceId as normalizePersisted
|
|
|
38
40
|
import { createPersistedProjectRecord, createPersistedWorkspaceRecord, } from "./workspace-registry.js";
|
|
39
41
|
import { buildVoiceModeSystemPrompt, stripVoiceModeSystemPrompt, wrapSpokenInput, } from "./voice-config.js";
|
|
40
42
|
import { isVoicePermissionAllowed } from "./voice-permission-policy.js";
|
|
41
|
-
import { listDirectoryEntries, readExplorerFile, getDownloadableFileInfo, } from "./file-explorer/service.js";
|
|
43
|
+
import { listDirectoryEntries, readExplorerFile, readExplorerFileBytes, getDownloadableFileInfo, } from "./file-explorer/service.js";
|
|
42
44
|
import { readPaseoConfigForEdit, writePaseoConfigForEdit, } from "../utils/paseo-config-file.js";
|
|
43
|
-
import { runAsyncWorktreeBootstrap } from "./worktree-bootstrap.js";
|
|
44
45
|
import { archivePersistedWorkspaceRecord } from "./workspace-archive-service.js";
|
|
45
46
|
import { WorkspaceReconciliationService } from "./workspace-reconciliation-service.js";
|
|
46
47
|
import { checkoutResolvedBranch, commitChanges, mergeToBase, mergeFromBase, pullCurrentBranch, pushCurrentBranch, createPullRequest, } from "../utils/checkout-git.js";
|
|
@@ -54,10 +55,10 @@ import { ChatServiceError } from "./chat/chat-service.js";
|
|
|
54
55
|
import { notifyChatMentions } from "./chat/chat-mentions.js";
|
|
55
56
|
import { execCommand } from "../utils/spawn.js";
|
|
56
57
|
import { createGitHubService, } from "../services/github-service.js";
|
|
57
|
-
import {
|
|
58
|
+
import { summarizeFetchWorkspacesEntries, WorkspaceDirectory, } from "./workspace-directory.js";
|
|
59
|
+
import { attemptFirstAgentBranchAutoName, createPaseoWorktree, } from "./paseo-worktree-service.js";
|
|
58
60
|
import { createWorktreeCoreDeps } from "./worktree-core.js";
|
|
59
|
-
import { assertSafeGitRef as assertWorktreeSafeGitRef, buildAgentSessionConfig as buildWorktreeAgentSessionConfig,
|
|
60
|
-
import { killTerminalsUnderPath as killWorktreeTerminalsUnderPath } from "./paseo-worktree-archive-service.js";
|
|
61
|
+
import { assertSafeGitRef as assertWorktreeSafeGitRef, buildAgentSessionConfig as buildWorktreeAgentSessionConfig, createPaseoWorktreeWorkflow as createWorktreeWorkflow, handleCreatePaseoWorktreeRequest as handleCreateWorktreeRequest, handlePaseoWorktreeArchiveRequest as handleWorktreeArchiveRequest, handlePaseoWorktreeListRequest as handleWorktreeListRequest, handleWorkspaceSetupStatusRequest as handleWorkspaceSetupStatusRequestMessage, } from "./worktree-session.js";
|
|
61
62
|
import { toWorktreeWireError } from "./worktree-errors.js";
|
|
62
63
|
const MAX_INITIAL_AGENT_TITLE_CHARS = Math.min(60, MAX_EXPLICIT_AGENT_TITLE_CHARS);
|
|
63
64
|
const WORKSPACE_GIT_WATCH_REMOVED_STATE_KEY = "__removed__";
|
|
@@ -175,7 +176,6 @@ function clientSupportsAllProviders(appVersion) {
|
|
|
175
176
|
function clientSupportsFlexibleEditorIds(appVersion) {
|
|
176
177
|
return isAppVersionAtLeast(appVersion, MIN_VERSION_FLEXIBLE_EDITOR_IDS);
|
|
177
178
|
}
|
|
178
|
-
const MAX_TERMINAL_STREAM_SLOTS = 256;
|
|
179
179
|
function beginAgentDeleteIfSupported(agentStorage, agentId) {
|
|
180
180
|
if ("beginDelete" in agentStorage && typeof agentStorage.beginDelete === "function") {
|
|
181
181
|
agentStorage.beginDelete(agentId);
|
|
@@ -207,50 +207,19 @@ export function resolveCreateAgentTitles(options) {
|
|
|
207
207
|
provisionalTitle,
|
|
208
208
|
};
|
|
209
209
|
}
|
|
210
|
-
function
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if ((key !== "status_priority" &&
|
|
222
|
-
key !== "activity_at" &&
|
|
223
|
-
key !== "name" &&
|
|
224
|
-
key !== "project_id") ||
|
|
225
|
-
(direction !== "asc" && direction !== "desc")) {
|
|
226
|
-
throw new SessionRequestError("invalid_cursor", "Invalid fetch_workspaces cursor");
|
|
227
|
-
}
|
|
228
|
-
cursorSort.push({ key, direction });
|
|
229
|
-
}
|
|
230
|
-
return cursorSort;
|
|
231
|
-
}
|
|
232
|
-
function parseFetchAgentsCursorSort(raw) {
|
|
233
|
-
const cursorSort = [];
|
|
234
|
-
for (const item of raw) {
|
|
235
|
-
if (!item ||
|
|
236
|
-
typeof item !== "object" ||
|
|
237
|
-
typeof item.key !== "string" ||
|
|
238
|
-
typeof item.direction !== "string") {
|
|
239
|
-
throw new SessionRequestError("invalid_cursor", "Invalid fetch_agents cursor");
|
|
240
|
-
}
|
|
241
|
-
const key = item.key;
|
|
242
|
-
const direction = item.direction;
|
|
243
|
-
if ((key !== "status_priority" &&
|
|
244
|
-
key !== "created_at" &&
|
|
245
|
-
key !== "updated_at" &&
|
|
246
|
-
key !== "title") ||
|
|
247
|
-
(direction !== "asc" && direction !== "desc")) {
|
|
248
|
-
throw new SessionRequestError("invalid_cursor", "Invalid fetch_agents cursor");
|
|
249
|
-
}
|
|
250
|
-
cursorSort.push({ key, direction });
|
|
251
|
-
}
|
|
252
|
-
return cursorSort;
|
|
210
|
+
function getFirstUserMessageText(timeline) {
|
|
211
|
+
for (const item of timeline) {
|
|
212
|
+
if (item.type !== "user_message") {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const text = item.text.trim();
|
|
216
|
+
if (text) {
|
|
217
|
+
return text;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
253
221
|
}
|
|
222
|
+
const FETCH_AGENTS_SORT_KEYS = ["status_priority", "created_at", "updated_at", "title"];
|
|
254
223
|
export function resolveWaitForFinishError(options) {
|
|
255
224
|
if (options.status !== "error") {
|
|
256
225
|
return null;
|
|
@@ -258,27 +227,6 @@ export function resolveWaitForFinishError(options) {
|
|
|
258
227
|
const message = options.final?.lastError;
|
|
259
228
|
return typeof message === "string" && message.trim().length > 0 ? message : "Agent failed";
|
|
260
229
|
}
|
|
261
|
-
function summarizeFetchWorkspacesEntries(entries) {
|
|
262
|
-
const workspaces = Array.from(entries, (entry) => ({
|
|
263
|
-
id: entry.id,
|
|
264
|
-
projectId: entry.projectId,
|
|
265
|
-
projectDisplayName: entry.projectDisplayName,
|
|
266
|
-
name: entry.name,
|
|
267
|
-
status: entry.status,
|
|
268
|
-
workspaceKind: entry.workspaceKind,
|
|
269
|
-
activityAt: entry.activityAt,
|
|
270
|
-
}));
|
|
271
|
-
const statusCounts = new Map();
|
|
272
|
-
for (const workspace of workspaces) {
|
|
273
|
-
statusCounts.set(workspace.status, (statusCounts.get(workspace.status) ?? 0) + 1);
|
|
274
|
-
}
|
|
275
|
-
return {
|
|
276
|
-
count: workspaces.length,
|
|
277
|
-
projectIds: [...new Set(workspaces.map((workspace) => workspace.projectId))],
|
|
278
|
-
statusCounts: Object.fromEntries(statusCounts),
|
|
279
|
-
workspaces,
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
230
|
class SessionRequestError extends Error {
|
|
283
231
|
constructor(code, message) {
|
|
284
232
|
super(message);
|
|
@@ -305,6 +253,31 @@ class VoiceFeatureUnavailableError extends Error {
|
|
|
305
253
|
this.missingModelIds = [...context.missingModelIds];
|
|
306
254
|
}
|
|
307
255
|
}
|
|
256
|
+
function buildImportPersistenceHandle(input) {
|
|
257
|
+
const cwd = input.cwd ?? process.cwd();
|
|
258
|
+
return {
|
|
259
|
+
provider: input.provider,
|
|
260
|
+
sessionId: input.sessionId,
|
|
261
|
+
nativeHandle: input.sessionId,
|
|
262
|
+
metadata: {
|
|
263
|
+
provider: input.provider,
|
|
264
|
+
cwd,
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function applyImportCwdOverride(handle, cwd) {
|
|
269
|
+
if (!cwd) {
|
|
270
|
+
return handle;
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
...handle,
|
|
274
|
+
metadata: {
|
|
275
|
+
...handle.metadata,
|
|
276
|
+
provider: handle.provider,
|
|
277
|
+
cwd,
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
}
|
|
308
281
|
function convertPCMToWavBuffer(pcmBuffer, sampleRate, channels, bitsPerSample) {
|
|
309
282
|
const headerSize = 44;
|
|
310
283
|
const wavBuffer = Buffer.alloc(headerSize + pcmBuffer.length);
|
|
@@ -326,6 +299,13 @@ function convertPCMToWavBuffer(pcmBuffer, sampleRate, channels, bitsPerSample) {
|
|
|
326
299
|
pcmBuffer.copy(wavBuffer, 44);
|
|
327
300
|
return wavBuffer;
|
|
328
301
|
}
|
|
302
|
+
function parseClientCapabilities(capabilities) {
|
|
303
|
+
if (!capabilities) {
|
|
304
|
+
return new Set();
|
|
305
|
+
}
|
|
306
|
+
const known = new Set(Object.values(CLIENT_CAPS));
|
|
307
|
+
return new Set(Object.entries(capabilities).flatMap(([key, value]) => value === true && known.has(key) ? [key] : []));
|
|
308
|
+
}
|
|
329
309
|
/**
|
|
330
310
|
* Session represents a single connected client session.
|
|
331
311
|
* It owns all state management, orchestration logic, and message processing.
|
|
@@ -358,12 +338,6 @@ export class Session {
|
|
|
358
338
|
this.clientActivity = null;
|
|
359
339
|
this.MOBILE_BACKGROUND_STREAM_GRACE_MS = 60000;
|
|
360
340
|
this.unsubscribeProviderSnapshotEvents = null;
|
|
361
|
-
this.subscribedTerminalDirectories = new Set();
|
|
362
|
-
this.unsubscribeTerminalsChanged = null;
|
|
363
|
-
this.terminalExitSubscriptions = new Map();
|
|
364
|
-
this.activeTerminalStreams = new Map();
|
|
365
|
-
this.terminalIdToSlot = new Map();
|
|
366
|
-
this.nextTerminalSlot = 0;
|
|
367
341
|
this.inflightRequests = 0;
|
|
368
342
|
this.peakInflightRequests = 0;
|
|
369
343
|
this.availableEditorTargetsCache = new TTLCache({
|
|
@@ -381,16 +355,28 @@ export class Session {
|
|
|
381
355
|
this.workspaceGitSubscriptions = new Map();
|
|
382
356
|
this.voiceModeAgentId = null;
|
|
383
357
|
this.voiceModeBaseConfig = null;
|
|
384
|
-
this.
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
358
|
+
this.agentsPager = new SortablePager({
|
|
359
|
+
validKeys: FETCH_AGENTS_SORT_KEYS,
|
|
360
|
+
defaultSort: [{ key: "updated_at", direction: "desc" }],
|
|
361
|
+
label: "fetch_agents",
|
|
362
|
+
getId: (agent) => agent.id,
|
|
363
|
+
getSortValue: (agent, key) => {
|
|
364
|
+
switch (key) {
|
|
365
|
+
case "status_priority":
|
|
366
|
+
return this.getStatusPriority(agent);
|
|
367
|
+
case "created_at":
|
|
368
|
+
return Date.parse(agent.createdAt);
|
|
369
|
+
case "updated_at":
|
|
370
|
+
return Date.parse(agent.updatedAt);
|
|
371
|
+
case "title":
|
|
372
|
+
return agent.title?.toLocaleLowerCase() ?? "";
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
const { clientId, appVersion, clientCapabilities, onMessage, onBinaryMessage, onLifecycleIntent, logger, downloadTokenStore, pushTokenStore, paseoHome, agentManager, agentStorage, projectRegistry, workspaceRegistry, chatService, scheduleService, loopService, checkoutDiffManager, github, workspaceGitService, daemonConfigStore, mcpBaseUrl, stt, tts, terminalManager, providerSnapshotManager, scriptRouteStore, scriptRuntimeStore, workspaceSetupSnapshots, onBranchChanged, getDaemonTcpPort, getDaemonTcpHost, resolveScriptHealth, voice, voiceBridge, dictation, agentProviderRuntimeSettings, providerOverrides, isDev, } = options;
|
|
392
377
|
this.clientId = clientId;
|
|
393
378
|
this.appVersion = appVersion ?? null;
|
|
379
|
+
this.clientCapabilities = parseClientCapabilities(clientCapabilities);
|
|
394
380
|
this.sessionId = uuidv4();
|
|
395
381
|
this.onMessage = onMessage;
|
|
396
382
|
this.onBinaryMessage = onBinaryMessage ?? null;
|
|
@@ -416,6 +402,14 @@ export class Session {
|
|
|
416
402
|
this.daemonConfigStore = daemonConfigStore;
|
|
417
403
|
this.mcpBaseUrl = mcpBaseUrl ?? null;
|
|
418
404
|
this.terminalManager = terminalManager;
|
|
405
|
+
this.terminalController = new TerminalSessionController({
|
|
406
|
+
terminalManager,
|
|
407
|
+
emit: (msg) => this.emit(msg),
|
|
408
|
+
emitBinary: (frame) => this.emitBinary(frame),
|
|
409
|
+
hasBinaryChannel: () => this.onBinaryMessage !== null,
|
|
410
|
+
isPathWithinRoot: (rootPath, candidatePath) => this.isPathWithinRoot(rootPath, candidatePath),
|
|
411
|
+
sessionLogger: this.sessionLogger,
|
|
412
|
+
});
|
|
419
413
|
this.providerSnapshotManager = providerSnapshotManager ?? null;
|
|
420
414
|
this.scriptRouteStore = scriptRouteStore ?? null;
|
|
421
415
|
this.scriptRuntimeStore = scriptRuntimeStore ?? null;
|
|
@@ -430,6 +424,14 @@ export class Session {
|
|
|
430
424
|
this.providerOverrides = providerOverrides;
|
|
431
425
|
this.isDev = isDev === true;
|
|
432
426
|
this.abortController = new AbortController();
|
|
427
|
+
this.workspaceDirectory = new WorkspaceDirectory({
|
|
428
|
+
logger: this.sessionLogger,
|
|
429
|
+
projectRegistry: this.projectRegistry,
|
|
430
|
+
workspaceRegistry: this.workspaceRegistry,
|
|
431
|
+
listAgentPayloads: () => this.listAgentPayloads(),
|
|
432
|
+
isProviderVisibleToClient: (provider) => this.isProviderVisibleToClient(provider),
|
|
433
|
+
buildWorkspaceDescriptor: (input) => this.buildWorkspaceDescriptor(input),
|
|
434
|
+
});
|
|
433
435
|
this.initializePerSessionManagers({ tts, stt, dictation });
|
|
434
436
|
// Initialize agent MCP client asynchronously
|
|
435
437
|
void this.initializeAgentMcp();
|
|
@@ -441,6 +443,12 @@ export class Session {
|
|
|
441
443
|
this.appVersion = appVersion;
|
|
442
444
|
}
|
|
443
445
|
}
|
|
446
|
+
updateClientCapabilities(capabilities) {
|
|
447
|
+
this.clientCapabilities = parseClientCapabilities(capabilities);
|
|
448
|
+
}
|
|
449
|
+
supports(capability) {
|
|
450
|
+
return this.clientCapabilities.has(capability);
|
|
451
|
+
}
|
|
444
452
|
async syncWorkspaceGitObserverForWorkspace(workspace) {
|
|
445
453
|
const descriptor = await this.describeWorkspaceRecordWithGitData(workspace);
|
|
446
454
|
this.syncWorkspaceGitObservers([descriptor]);
|
|
@@ -451,8 +459,17 @@ export class Session {
|
|
|
451
459
|
async archiveWorkspaceRecordForExternalMutation(workspaceId) {
|
|
452
460
|
await this.archiveWorkspaceRecord(workspaceId);
|
|
453
461
|
}
|
|
462
|
+
markWorkspaceArchivingForExternalMutation(workspaceIds, archivingAt) {
|
|
463
|
+
this.markWorkspaceArchiving(workspaceIds, archivingAt);
|
|
464
|
+
}
|
|
465
|
+
clearWorkspaceArchivingForExternalMutation(workspaceIds) {
|
|
466
|
+
this.clearWorkspaceArchiving(workspaceIds);
|
|
467
|
+
}
|
|
468
|
+
async emitWorkspaceUpdatesForExternalWorkspaceIds(workspaceIds) {
|
|
469
|
+
await this.emitWorkspaceUpdatesForWorkspaceIds(workspaceIds);
|
|
470
|
+
}
|
|
454
471
|
async emitWorkspaceUpdatesForExternalCwds(cwds) {
|
|
455
|
-
await
|
|
472
|
+
await Promise.all(Array.from(cwds, (cwd) => this.emitWorkspaceUpdateForCwd(cwd)));
|
|
456
473
|
}
|
|
457
474
|
async warmWorkspaceGitDataForWorkspace(workspace) {
|
|
458
475
|
await this.syncWorkspaceGitObserverForWorkspace(workspace);
|
|
@@ -465,9 +482,10 @@ export class Session {
|
|
|
465
482
|
return this.clientActivity;
|
|
466
483
|
}
|
|
467
484
|
getRuntimeMetrics() {
|
|
485
|
+
const terminalMetrics = this.terminalController.getMetrics();
|
|
468
486
|
return {
|
|
469
|
-
terminalDirectorySubscriptionCount:
|
|
470
|
-
terminalSubscriptionCount:
|
|
487
|
+
terminalDirectorySubscriptionCount: terminalMetrics.directorySubscriptionCount,
|
|
488
|
+
terminalSubscriptionCount: terminalMetrics.streamSubscriptionCount,
|
|
471
489
|
inflightRequests: this.inflightRequests,
|
|
472
490
|
peakInflightRequests: this.peakInflightRequests,
|
|
473
491
|
};
|
|
@@ -605,9 +623,7 @@ export class Session {
|
|
|
605
623
|
* Subscribe to AgentManager events and forward them to the client
|
|
606
624
|
*/
|
|
607
625
|
subscribeToOptionalManagers() {
|
|
608
|
-
|
|
609
|
-
this.unsubscribeTerminalsChanged = this.terminalManager.subscribeTerminalsChanged((event) => this.handleTerminalsChanged(event));
|
|
610
|
-
}
|
|
626
|
+
this.terminalController.start();
|
|
611
627
|
if (this.providerSnapshotManager) {
|
|
612
628
|
const handleProviderSnapshotChange = (entries, cwd) => {
|
|
613
629
|
// COMPAT(providersSnapshot): keep provider visibility gating for older clients.
|
|
@@ -1110,6 +1126,8 @@ export class Session {
|
|
|
1110
1126
|
return this.handleCreateAgentRequest(msg);
|
|
1111
1127
|
case "resume_agent_request":
|
|
1112
1128
|
return this.handleResumeAgentRequest(msg);
|
|
1129
|
+
case "import_agent_request":
|
|
1130
|
+
return this.handleImportAgentRequest(msg);
|
|
1113
1131
|
case "refresh_agent_request":
|
|
1114
1132
|
return this.handleRefreshAgentRequest(msg);
|
|
1115
1133
|
case "cancel_agent_request":
|
|
@@ -1336,34 +1354,10 @@ export class Session {
|
|
|
1336
1354
|
}
|
|
1337
1355
|
}
|
|
1338
1356
|
dispatchTerminalMessage(msg) {
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
this.handleSubscribeTerminalsRequest(msg);
|
|
1342
|
-
return undefined;
|
|
1343
|
-
case "unsubscribe_terminals_request":
|
|
1344
|
-
this.handleUnsubscribeTerminalsRequest(msg);
|
|
1345
|
-
return undefined;
|
|
1346
|
-
case "list_terminals_request":
|
|
1347
|
-
return this.handleListTerminalsRequest(msg);
|
|
1348
|
-
case "create_terminal_request":
|
|
1349
|
-
return this.handleCreateTerminalRequest(msg);
|
|
1350
|
-
case "start_workspace_script_request":
|
|
1351
|
-
return this.handleStartWorkspaceScriptRequest(msg);
|
|
1352
|
-
case "subscribe_terminal_request":
|
|
1353
|
-
return this.handleSubscribeTerminalRequest(msg);
|
|
1354
|
-
case "unsubscribe_terminal_request":
|
|
1355
|
-
this.handleUnsubscribeTerminalRequest(msg);
|
|
1356
|
-
return undefined;
|
|
1357
|
-
case "terminal_input":
|
|
1358
|
-
this.handleTerminalInput(msg);
|
|
1359
|
-
return undefined;
|
|
1360
|
-
case "kill_terminal_request":
|
|
1361
|
-
return this.handleKillTerminalRequest(msg);
|
|
1362
|
-
case "capture_terminal_request":
|
|
1363
|
-
return this.handleCaptureTerminalRequest(msg);
|
|
1364
|
-
default:
|
|
1365
|
-
return undefined;
|
|
1357
|
+
if (msg.type === "start_workspace_script_request") {
|
|
1358
|
+
return this.handleStartWorkspaceScriptRequest(msg);
|
|
1366
1359
|
}
|
|
1360
|
+
return this.terminalController.dispatch(msg);
|
|
1367
1361
|
}
|
|
1368
1362
|
dispatchChatScheduleLoopMessage(msg) {
|
|
1369
1363
|
switch (msg.type) {
|
|
@@ -1423,38 +1417,7 @@ export class Session {
|
|
|
1423
1417
|
this.peakInflightRequests = this.inflightRequests;
|
|
1424
1418
|
}
|
|
1425
1419
|
handleBinaryFrame(frame) {
|
|
1426
|
-
|
|
1427
|
-
if (!activeStream || !this.terminalManager) {
|
|
1428
|
-
return;
|
|
1429
|
-
}
|
|
1430
|
-
const terminal = this.terminalManager.getTerminal(activeStream.terminalId);
|
|
1431
|
-
if (!terminal) {
|
|
1432
|
-
this.detachTerminalStream(activeStream.terminalId, { emitExit: true });
|
|
1433
|
-
return;
|
|
1434
|
-
}
|
|
1435
|
-
switch (frame.opcode) {
|
|
1436
|
-
case TerminalStreamOpcode.Input: {
|
|
1437
|
-
if (frame.payload.byteLength === 0) {
|
|
1438
|
-
return;
|
|
1439
|
-
}
|
|
1440
|
-
const text = Buffer.from(frame.payload).toString("utf8");
|
|
1441
|
-
if (!text) {
|
|
1442
|
-
return;
|
|
1443
|
-
}
|
|
1444
|
-
terminal.send({ type: "input", data: text });
|
|
1445
|
-
return;
|
|
1446
|
-
}
|
|
1447
|
-
case TerminalStreamOpcode.Resize: {
|
|
1448
|
-
const resize = decodeTerminalResizePayload(frame.payload);
|
|
1449
|
-
if (!resize) {
|
|
1450
|
-
return;
|
|
1451
|
-
}
|
|
1452
|
-
terminal.send({ type: "resize", rows: resize.rows, cols: resize.cols });
|
|
1453
|
-
return;
|
|
1454
|
-
}
|
|
1455
|
-
default:
|
|
1456
|
-
return;
|
|
1457
|
-
}
|
|
1420
|
+
this.terminalController.handleBinaryFrame(frame);
|
|
1458
1421
|
}
|
|
1459
1422
|
async handleRestartServerRequest(requestId, reason) {
|
|
1460
1423
|
const payload = {
|
|
@@ -1633,7 +1596,7 @@ export class Session {
|
|
|
1633
1596
|
const terminals = [];
|
|
1634
1597
|
for (const terminalId of msg.terminalIds) {
|
|
1635
1598
|
try {
|
|
1636
|
-
terminals.push(this.killTerminalForClose(terminalId));
|
|
1599
|
+
terminals.push(this.terminalController.killTerminalForClose(terminalId));
|
|
1637
1600
|
}
|
|
1638
1601
|
catch (error) {
|
|
1639
1602
|
this.sessionLogger.warn({ err: error, terminalId, requestId: msg.requestId }, "Failed to kill terminal during close_items batch");
|
|
@@ -2064,14 +2027,22 @@ export class Session {
|
|
|
2064
2027
|
...config,
|
|
2065
2028
|
...(provisionalTitle ? { title: provisionalTitle } : {}),
|
|
2066
2029
|
};
|
|
2067
|
-
const
|
|
2068
|
-
|
|
2030
|
+
const firstAgentContext = {
|
|
2031
|
+
...(trimmedPrompt ? { prompt: trimmedPrompt } : {}),
|
|
2032
|
+
...(attachments && attachments.length > 0 ? { attachments } : {}),
|
|
2033
|
+
};
|
|
2034
|
+
const { sessionConfig, setupContinuation } = await this.buildAgentSessionConfig(resolvedConfig, git, worktreeName, firstAgentContext);
|
|
2035
|
+
let resolvedWorkspace = msg.workspaceId
|
|
2069
2036
|
? await this.workspaceRegistry.get(msg.workspaceId)
|
|
2070
2037
|
: ((await this.findWorkspaceByDirectory(sessionConfig.cwd)) ??
|
|
2071
2038
|
(await this.findOrCreateWorkspaceForDirectory(sessionConfig.cwd)));
|
|
2072
2039
|
if (!resolvedWorkspace) {
|
|
2073
2040
|
throw new Error(`Workspace not found: ${msg.workspaceId}`);
|
|
2074
2041
|
}
|
|
2042
|
+
resolvedWorkspace = await this.maybeAutoNameWorkspaceBranchForFirstAgent({
|
|
2043
|
+
workspace: resolvedWorkspace,
|
|
2044
|
+
firstAgentContext,
|
|
2045
|
+
});
|
|
2075
2046
|
const snapshot = await this.agentManager.createAgent({
|
|
2076
2047
|
...sessionConfig,
|
|
2077
2048
|
cwd: resolvedWorkspace.cwd,
|
|
@@ -2102,25 +2073,9 @@ export class Session {
|
|
|
2102
2073
|
},
|
|
2103
2074
|
});
|
|
2104
2075
|
}
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
worktree: worktreeBootstrap.worktree,
|
|
2109
|
-
shouldBootstrap: worktreeBootstrap.shouldBootstrap,
|
|
2110
|
-
terminalManager: this.terminalManager,
|
|
2111
|
-
appendTimelineItem: (item) => appendTimelineItemIfAgentKnown({
|
|
2112
|
-
agentManager: this.agentManager,
|
|
2113
|
-
agentId: snapshot.id,
|
|
2114
|
-
item,
|
|
2115
|
-
}),
|
|
2116
|
-
emitLiveTimelineItem: (item) => emitLiveTimelineItemIfAgentKnown({
|
|
2117
|
-
agentManager: this.agentManager,
|
|
2118
|
-
agentId: snapshot.id,
|
|
2119
|
-
item,
|
|
2120
|
-
}),
|
|
2121
|
-
logger: this.sessionLogger,
|
|
2122
|
-
});
|
|
2123
|
-
}
|
|
2076
|
+
setupContinuation?.startAfterAgentCreate({
|
|
2077
|
+
agentId: snapshot.id,
|
|
2078
|
+
});
|
|
2124
2079
|
this.sessionLogger.info({ agentId: snapshot.id, provider: snapshot.provider }, `Created agent ${snapshot.id} (${snapshot.provider})`);
|
|
2125
2080
|
}
|
|
2126
2081
|
catch (error) {
|
|
@@ -2164,9 +2119,6 @@ export class Session {
|
|
|
2164
2119
|
explicitTitle: params.explicitTitle,
|
|
2165
2120
|
paseoHome: this.paseoHome,
|
|
2166
2121
|
logger: this.sessionLogger,
|
|
2167
|
-
deps: {
|
|
2168
|
-
workspaceGitService: this.workspaceGitService,
|
|
2169
|
-
},
|
|
2170
2122
|
});
|
|
2171
2123
|
const started = await this.handleSendAgentMessage(snapshot.id, trimmedPrompt || "", resolveClientMessageId(clientMessageId), images, attachments, outputSchema ? { outputSchema } : undefined);
|
|
2172
2124
|
if (!started.ok) {
|
|
@@ -2223,6 +2175,83 @@ export class Session {
|
|
|
2223
2175
|
});
|
|
2224
2176
|
}
|
|
2225
2177
|
}
|
|
2178
|
+
async handleImportAgentRequest(msg) {
|
|
2179
|
+
const { provider, sessionId, cwd, labels, requestId } = msg;
|
|
2180
|
+
this.sessionLogger.info({ sessionId, provider }, `Importing agent ${sessionId} (${provider})`);
|
|
2181
|
+
try {
|
|
2182
|
+
const descriptor = await this.agentManager.findPersistedAgent(provider, sessionId);
|
|
2183
|
+
if (!descriptor && provider === "opencode" && !cwd) {
|
|
2184
|
+
throw new Error("OpenCode sessions require --cwd when the session cannot be found in persisted agents");
|
|
2185
|
+
}
|
|
2186
|
+
const handle = descriptor
|
|
2187
|
+
? applyImportCwdOverride(descriptor.persistence, cwd)
|
|
2188
|
+
: buildImportPersistenceHandle({ provider, sessionId, cwd });
|
|
2189
|
+
const overrides = cwd ? { cwd } : undefined;
|
|
2190
|
+
await this.unarchiveAgentByHandle(handle);
|
|
2191
|
+
const snapshot = await this.agentManager.resumeAgentFromPersistence(handle, overrides, undefined, {
|
|
2192
|
+
labels,
|
|
2193
|
+
});
|
|
2194
|
+
await unarchiveAgentState(this.agentStorage, this.agentManager, snapshot.id);
|
|
2195
|
+
await this.agentManager.hydrateTimelineFromProvider(snapshot.id);
|
|
2196
|
+
await this.applyImportedAgentTitle(snapshot);
|
|
2197
|
+
await this.forwardAgentUpdate(snapshot);
|
|
2198
|
+
const timelineSize = this.agentManager.getTimeline(snapshot.id).length;
|
|
2199
|
+
const agentPayload = await this.buildAgentPayload(snapshot);
|
|
2200
|
+
this.emit({
|
|
2201
|
+
type: "status",
|
|
2202
|
+
payload: {
|
|
2203
|
+
status: "agent_resumed",
|
|
2204
|
+
agentId: snapshot.id,
|
|
2205
|
+
requestId,
|
|
2206
|
+
timelineSize,
|
|
2207
|
+
agent: agentPayload,
|
|
2208
|
+
},
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
2211
|
+
catch (error) {
|
|
2212
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2213
|
+
this.sessionLogger.error({ err: error }, "Failed to import agent");
|
|
2214
|
+
this.emit({
|
|
2215
|
+
type: "status",
|
|
2216
|
+
payload: {
|
|
2217
|
+
status: "agent_create_failed",
|
|
2218
|
+
requestId,
|
|
2219
|
+
error: message,
|
|
2220
|
+
},
|
|
2221
|
+
});
|
|
2222
|
+
this.emit({
|
|
2223
|
+
type: "activity_log",
|
|
2224
|
+
payload: {
|
|
2225
|
+
id: uuidv4(),
|
|
2226
|
+
timestamp: new Date(),
|
|
2227
|
+
type: "error",
|
|
2228
|
+
content: `Failed to import agent: ${message}`,
|
|
2229
|
+
},
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
async applyImportedAgentTitle(snapshot) {
|
|
2234
|
+
const initialPrompt = getFirstUserMessageText(this.agentManager.getTimeline(snapshot.id));
|
|
2235
|
+
if (!initialPrompt) {
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
2238
|
+
const { explicitTitle, provisionalTitle } = resolveCreateAgentTitles({
|
|
2239
|
+
configTitle: snapshot.config.title,
|
|
2240
|
+
initialPrompt,
|
|
2241
|
+
});
|
|
2242
|
+
if (!explicitTitle && provisionalTitle) {
|
|
2243
|
+
await this.agentManager.setTitle(snapshot.id, provisionalTitle);
|
|
2244
|
+
}
|
|
2245
|
+
scheduleAgentMetadataGeneration({
|
|
2246
|
+
agentManager: this.agentManager,
|
|
2247
|
+
agentId: snapshot.id,
|
|
2248
|
+
cwd: snapshot.cwd,
|
|
2249
|
+
initialPrompt,
|
|
2250
|
+
explicitTitle,
|
|
2251
|
+
paseoHome: this.paseoHome,
|
|
2252
|
+
logger: this.sessionLogger,
|
|
2253
|
+
});
|
|
2254
|
+
}
|
|
2226
2255
|
async handleRefreshAgentRequest(msg) {
|
|
2227
2256
|
const { agentId, requestId } = msg;
|
|
2228
2257
|
this.sessionLogger.info({ agentId }, `Refreshing agent ${agentId} from persistence`);
|
|
@@ -2298,16 +2327,53 @@ export class Session {
|
|
|
2298
2327
|
this.handleAgentRunError(agentId, error, "Failed to cancel running agent on request");
|
|
2299
2328
|
}
|
|
2300
2329
|
}
|
|
2301
|
-
async buildAgentSessionConfig(config, gitOptions, legacyWorktreeName,
|
|
2330
|
+
async buildAgentSessionConfig(config, gitOptions, legacyWorktreeName, firstAgentContext) {
|
|
2302
2331
|
return buildWorktreeAgentSessionConfig({
|
|
2303
2332
|
paseoHome: this.paseoHome,
|
|
2304
2333
|
sessionLogger: this.sessionLogger,
|
|
2305
2334
|
workspaceGitService: this.workspaceGitService,
|
|
2306
|
-
createPaseoWorktree: (input, serviceOptions) => this.
|
|
2335
|
+
createPaseoWorktree: (input, serviceOptions) => this.createPaseoWorktreeWorkflow(input, {
|
|
2336
|
+
...serviceOptions,
|
|
2337
|
+
setupContinuation: {
|
|
2338
|
+
kind: "agent",
|
|
2339
|
+
terminalManager: this.terminalManager,
|
|
2340
|
+
appendTimelineItem: ({ agentId, item }) => appendTimelineItemIfAgentKnown({
|
|
2341
|
+
agentManager: this.agentManager,
|
|
2342
|
+
agentId,
|
|
2343
|
+
item,
|
|
2344
|
+
}),
|
|
2345
|
+
emitLiveTimelineItem: ({ agentId, item }) => emitLiveTimelineItemIfAgentKnown({
|
|
2346
|
+
agentManager: this.agentManager,
|
|
2347
|
+
agentId,
|
|
2348
|
+
item,
|
|
2349
|
+
}),
|
|
2350
|
+
logger: this.sessionLogger,
|
|
2351
|
+
},
|
|
2352
|
+
}),
|
|
2307
2353
|
checkoutExistingBranch: (cwd, branch) => this.checkoutExistingBranch(cwd, branch),
|
|
2308
2354
|
createBranchFromBase: (params) => this.createBranchFromBase(params),
|
|
2309
2355
|
github: this.github,
|
|
2310
|
-
}, config, gitOptions, legacyWorktreeName,
|
|
2356
|
+
}, config, gitOptions, legacyWorktreeName, firstAgentContext);
|
|
2357
|
+
}
|
|
2358
|
+
async maybeAutoNameWorkspaceBranchForFirstAgent(input) {
|
|
2359
|
+
const coreDeps = createWorktreeCoreDeps(this.github);
|
|
2360
|
+
const result = await attemptFirstAgentBranchAutoName({
|
|
2361
|
+
cwd: input.workspace.cwd,
|
|
2362
|
+
firstAgentContext: input.firstAgentContext,
|
|
2363
|
+
generateBranchName: coreDeps.generateBranchName,
|
|
2364
|
+
});
|
|
2365
|
+
if (!result.renamed || !result.branchName) {
|
|
2366
|
+
return input.workspace;
|
|
2367
|
+
}
|
|
2368
|
+
const updatedWorkspace = {
|
|
2369
|
+
...input.workspace,
|
|
2370
|
+
displayName: result.branchName,
|
|
2371
|
+
updatedAt: new Date().toISOString(),
|
|
2372
|
+
};
|
|
2373
|
+
await this.workspaceRegistry.upsert(updatedWorkspace);
|
|
2374
|
+
await this.notifyGitMutation(input.workspace.cwd, "rename-branch");
|
|
2375
|
+
await this.emitWorkspaceUpdateForCwd(input.workspace.cwd);
|
|
2376
|
+
return updatedWorkspace;
|
|
2311
2377
|
}
|
|
2312
2378
|
emitProviderDisabledResponse(kind, provider, requestId, fetchedAt) {
|
|
2313
2379
|
const payload = {
|
|
@@ -3970,16 +4036,13 @@ export class Session {
|
|
|
3970
4036
|
workspaceGitService: this.workspaceGitService,
|
|
3971
4037
|
agentManager: this.agentManager,
|
|
3972
4038
|
agentStorage: this.agentStorage,
|
|
3973
|
-
archiveWorkspaceRecord:
|
|
3974
|
-
const workspace = await this.findWorkspaceByDirectory(workspaceDirectory);
|
|
3975
|
-
if (workspace) {
|
|
3976
|
-
await this.archiveWorkspaceRecord(workspace.workspaceId);
|
|
3977
|
-
}
|
|
3978
|
-
},
|
|
4039
|
+
archiveWorkspaceRecord: (workspaceId) => this.archiveWorkspaceRecord(workspaceId),
|
|
3979
4040
|
emit: (message) => this.emit(message),
|
|
3980
|
-
|
|
4041
|
+
emitWorkspaceUpdatesForWorkspaceIds: (workspaceIds) => this.emitWorkspaceUpdatesForWorkspaceIds(workspaceIds),
|
|
4042
|
+
markWorkspaceArchiving: (workspaceIds, archivingAt) => this.markWorkspaceArchiving(workspaceIds, archivingAt),
|
|
4043
|
+
clearWorkspaceArchiving: (workspaceIds) => this.clearWorkspaceArchiving(workspaceIds),
|
|
3981
4044
|
isPathWithinRoot: (rootPath, candidatePath) => this.isPathWithinRoot(rootPath, candidatePath),
|
|
3982
|
-
killTerminalsUnderPath: (rootPath) => this.killTerminalsUnderPath(rootPath),
|
|
4045
|
+
killTerminalsUnderPath: (rootPath) => this.terminalController.killTerminalsUnderPath(rootPath),
|
|
3983
4046
|
sessionLogger: this.sessionLogger,
|
|
3984
4047
|
}, msg);
|
|
3985
4048
|
}
|
|
@@ -4024,22 +4087,49 @@ export class Session {
|
|
|
4024
4087
|
});
|
|
4025
4088
|
}
|
|
4026
4089
|
else {
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
cwd,
|
|
4035
|
-
path: file.path,
|
|
4036
|
-
mode,
|
|
4037
|
-
directory: null,
|
|
4038
|
-
file,
|
|
4039
|
-
error: null,
|
|
4090
|
+
if (request.acceptBinary && this.onBinaryMessage) {
|
|
4091
|
+
const file = await readExplorerFileBytes({
|
|
4092
|
+
root: cwd,
|
|
4093
|
+
relativePath: requestedPath,
|
|
4094
|
+
});
|
|
4095
|
+
this.emitBinary(encodeFileTransferFrame({
|
|
4096
|
+
opcode: FileTransferOpcode.FileBegin,
|
|
4040
4097
|
requestId,
|
|
4041
|
-
|
|
4042
|
-
|
|
4098
|
+
metadata: {
|
|
4099
|
+
mime: file.mimeType,
|
|
4100
|
+
size: file.size,
|
|
4101
|
+
encoding: file.encoding,
|
|
4102
|
+
modifiedAt: file.modifiedAt,
|
|
4103
|
+
},
|
|
4104
|
+
}));
|
|
4105
|
+
this.emitBinary(encodeFileTransferFrame({
|
|
4106
|
+
opcode: FileTransferOpcode.FileChunk,
|
|
4107
|
+
requestId,
|
|
4108
|
+
payload: file.bytes,
|
|
4109
|
+
}));
|
|
4110
|
+
this.emitBinary(encodeFileTransferFrame({
|
|
4111
|
+
opcode: FileTransferOpcode.FileEnd,
|
|
4112
|
+
requestId,
|
|
4113
|
+
}));
|
|
4114
|
+
}
|
|
4115
|
+
else {
|
|
4116
|
+
const file = await readExplorerFile({
|
|
4117
|
+
root: cwd,
|
|
4118
|
+
relativePath: requestedPath,
|
|
4119
|
+
});
|
|
4120
|
+
this.emit({
|
|
4121
|
+
type: "file_explorer_response",
|
|
4122
|
+
payload: {
|
|
4123
|
+
cwd,
|
|
4124
|
+
path: file.path,
|
|
4125
|
+
mode,
|
|
4126
|
+
directory: null,
|
|
4127
|
+
file,
|
|
4128
|
+
error: null,
|
|
4129
|
+
requestId,
|
|
4130
|
+
},
|
|
4131
|
+
});
|
|
4132
|
+
}
|
|
4043
4133
|
}
|
|
4044
4134
|
}
|
|
4045
4135
|
catch (error) {
|
|
@@ -4237,22 +4327,6 @@ export class Session {
|
|
|
4237
4327
|
const payload = this.buildStoredAgentPayload(record);
|
|
4238
4328
|
return this.isProviderVisibleToClient(payload.provider) ? payload : null;
|
|
4239
4329
|
}
|
|
4240
|
-
normalizeFetchAgentsSort(sort) {
|
|
4241
|
-
const fallback = [{ key: "updated_at", direction: "desc" }];
|
|
4242
|
-
if (!sort || sort.length === 0) {
|
|
4243
|
-
return fallback;
|
|
4244
|
-
}
|
|
4245
|
-
const deduped = [];
|
|
4246
|
-
const seen = new Set();
|
|
4247
|
-
for (const entry of sort) {
|
|
4248
|
-
if (seen.has(entry.key)) {
|
|
4249
|
-
continue;
|
|
4250
|
-
}
|
|
4251
|
-
seen.add(entry.key);
|
|
4252
|
-
deduped.push(entry);
|
|
4253
|
-
}
|
|
4254
|
-
return deduped.length > 0 ? deduped : fallback;
|
|
4255
|
-
}
|
|
4256
4330
|
getStatusPriority(agent) {
|
|
4257
4331
|
const attentionReason = agent.attentionReason ?? null;
|
|
4258
4332
|
const hasPendingPermission = (agent.pendingPermissions?.length ?? 0) > 0;
|
|
@@ -4270,109 +4344,6 @@ export class Session {
|
|
|
4270
4344
|
}
|
|
4271
4345
|
return 4;
|
|
4272
4346
|
}
|
|
4273
|
-
getFetchAgentsSortValue(entry, key) {
|
|
4274
|
-
switch (key) {
|
|
4275
|
-
case "status_priority":
|
|
4276
|
-
return this.getStatusPriority(entry.agent);
|
|
4277
|
-
case "created_at":
|
|
4278
|
-
return Date.parse(entry.agent.createdAt);
|
|
4279
|
-
case "updated_at":
|
|
4280
|
-
return Date.parse(entry.agent.updatedAt);
|
|
4281
|
-
case "title":
|
|
4282
|
-
return entry.agent.title?.toLocaleLowerCase() ?? "";
|
|
4283
|
-
}
|
|
4284
|
-
}
|
|
4285
|
-
getFetchAgentsSortValueFromAgent(agent, key) {
|
|
4286
|
-
switch (key) {
|
|
4287
|
-
case "status_priority":
|
|
4288
|
-
return this.getStatusPriority(agent);
|
|
4289
|
-
case "created_at":
|
|
4290
|
-
return Date.parse(agent.createdAt);
|
|
4291
|
-
case "updated_at":
|
|
4292
|
-
return Date.parse(agent.updatedAt);
|
|
4293
|
-
case "title":
|
|
4294
|
-
return agent.title?.toLocaleLowerCase() ?? "";
|
|
4295
|
-
}
|
|
4296
|
-
}
|
|
4297
|
-
compareSortValues(left, right) {
|
|
4298
|
-
if (left === right) {
|
|
4299
|
-
return 0;
|
|
4300
|
-
}
|
|
4301
|
-
if (left === null) {
|
|
4302
|
-
return -1;
|
|
4303
|
-
}
|
|
4304
|
-
if (right === null) {
|
|
4305
|
-
return 1;
|
|
4306
|
-
}
|
|
4307
|
-
if (typeof left === "number" && typeof right === "number") {
|
|
4308
|
-
return left < right ? -1 : 1;
|
|
4309
|
-
}
|
|
4310
|
-
return String(left).localeCompare(String(right));
|
|
4311
|
-
}
|
|
4312
|
-
compareFetchAgentsAgents(left, right, sort) {
|
|
4313
|
-
for (const spec of sort) {
|
|
4314
|
-
const leftValue = this.getFetchAgentsSortValueFromAgent(left, spec.key);
|
|
4315
|
-
const rightValue = this.getFetchAgentsSortValueFromAgent(right, spec.key);
|
|
4316
|
-
const base = this.compareSortValues(leftValue, rightValue);
|
|
4317
|
-
if (base === 0) {
|
|
4318
|
-
continue;
|
|
4319
|
-
}
|
|
4320
|
-
return spec.direction === "asc" ? base : -base;
|
|
4321
|
-
}
|
|
4322
|
-
return left.id.localeCompare(right.id);
|
|
4323
|
-
}
|
|
4324
|
-
encodeFetchAgentsCursor(entry, sort) {
|
|
4325
|
-
const values = {};
|
|
4326
|
-
for (const spec of sort) {
|
|
4327
|
-
values[spec.key] = this.getFetchAgentsSortValue(entry, spec.key);
|
|
4328
|
-
}
|
|
4329
|
-
return Buffer.from(JSON.stringify({
|
|
4330
|
-
sort,
|
|
4331
|
-
values,
|
|
4332
|
-
id: entry.agent.id,
|
|
4333
|
-
}), "utf8").toString("base64url");
|
|
4334
|
-
}
|
|
4335
|
-
decodeFetchAgentsCursor(cursor, sort) {
|
|
4336
|
-
let parsed;
|
|
4337
|
-
try {
|
|
4338
|
-
parsed = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
4339
|
-
}
|
|
4340
|
-
catch {
|
|
4341
|
-
throw new SessionRequestError("invalid_cursor", "Invalid fetch_agents cursor");
|
|
4342
|
-
}
|
|
4343
|
-
if (!parsed || typeof parsed !== "object") {
|
|
4344
|
-
throw new SessionRequestError("invalid_cursor", "Invalid fetch_agents cursor");
|
|
4345
|
-
}
|
|
4346
|
-
const payload = parsed;
|
|
4347
|
-
if (!Array.isArray(payload.sort) || typeof payload.id !== "string") {
|
|
4348
|
-
throw new SessionRequestError("invalid_cursor", "Invalid fetch_agents cursor");
|
|
4349
|
-
}
|
|
4350
|
-
if (!payload.values || typeof payload.values !== "object") {
|
|
4351
|
-
throw new SessionRequestError("invalid_cursor", "Invalid fetch_agents cursor");
|
|
4352
|
-
}
|
|
4353
|
-
const cursorSort = parseFetchAgentsCursorSort(payload.sort);
|
|
4354
|
-
if (cursorSort.length !== sort.length ||
|
|
4355
|
-
cursorSort.some((entry, index) => entry.key !== sort[index]?.key || entry.direction !== sort[index]?.direction)) {
|
|
4356
|
-
throw new SessionRequestError("invalid_cursor", "fetch_agents cursor does not match current sort");
|
|
4357
|
-
}
|
|
4358
|
-
return {
|
|
4359
|
-
sort: cursorSort,
|
|
4360
|
-
values: payload.values,
|
|
4361
|
-
id: payload.id,
|
|
4362
|
-
};
|
|
4363
|
-
}
|
|
4364
|
-
compareAgentWithCursor(agent, cursor, sort) {
|
|
4365
|
-
for (const spec of sort) {
|
|
4366
|
-
const leftValue = this.getFetchAgentsSortValueFromAgent(agent, spec.key);
|
|
4367
|
-
const rightValue = cursor.values[spec.key] !== undefined ? (cursor.values[spec.key] ?? null) : null;
|
|
4368
|
-
const base = this.compareSortValues(leftValue, rightValue);
|
|
4369
|
-
if (base === 0) {
|
|
4370
|
-
continue;
|
|
4371
|
-
}
|
|
4372
|
-
return spec.direction === "asc" ? base : -base;
|
|
4373
|
-
}
|
|
4374
|
-
return agent.id.localeCompare(cursor.id);
|
|
4375
|
-
}
|
|
4376
4347
|
async buildActiveProjectPlacementsByWorkspaceCwd() {
|
|
4377
4348
|
const [persistedWorkspaces, persistedProjects] = await Promise.all([
|
|
4378
4349
|
this.workspaceRegistry.list(),
|
|
@@ -4431,7 +4402,7 @@ export class Session {
|
|
|
4431
4402
|
? { ...request.filter, includeArchived: true }
|
|
4432
4403
|
: request.filter;
|
|
4433
4404
|
const scope = request.type === "fetch_agents_request" ? request.scope : undefined;
|
|
4434
|
-
const sort = this.
|
|
4405
|
+
const sort = this.agentsPager.normalizeSort(request.sort);
|
|
4435
4406
|
let agents = await this.listAgentPayloads({
|
|
4436
4407
|
labels: filter?.labels,
|
|
4437
4408
|
includeUnavailablePersisted: request.type === "fetch_agent_history_request",
|
|
@@ -4454,11 +4425,11 @@ export class Session {
|
|
|
4454
4425
|
return placementPromise;
|
|
4455
4426
|
};
|
|
4456
4427
|
let candidates = [...agents];
|
|
4457
|
-
candidates.sort((left, right) => this.
|
|
4428
|
+
candidates.sort((left, right) => this.agentsPager.compare(left, right, sort));
|
|
4458
4429
|
const cursorToken = request.page?.cursor;
|
|
4459
4430
|
if (cursorToken) {
|
|
4460
|
-
const cursor = this.
|
|
4461
|
-
candidates = candidates.filter((agent) => this.
|
|
4431
|
+
const cursor = this.decodeAgentCursor(cursorToken, sort);
|
|
4432
|
+
candidates = candidates.filter((agent) => this.agentsPager.compareWithCursor(agent, cursor, sort) > 0);
|
|
4462
4433
|
}
|
|
4463
4434
|
const limit = request.page?.limit ?? 200;
|
|
4464
4435
|
const matchedEntries = await this.collectFetchAgentsEntries({
|
|
@@ -4470,7 +4441,7 @@ export class Session {
|
|
|
4470
4441
|
const pagedEntries = matchedEntries.slice(0, limit);
|
|
4471
4442
|
const hasMore = matchedEntries.length > limit;
|
|
4472
4443
|
const nextCursor = hasMore && pagedEntries.length > 0
|
|
4473
|
-
? this.
|
|
4444
|
+
? this.agentsPager.encode(pagedEntries[pagedEntries.length - 1].agent, sort)
|
|
4474
4445
|
: null;
|
|
4475
4446
|
return {
|
|
4476
4447
|
entries: pagedEntries,
|
|
@@ -4481,21 +4452,16 @@ export class Session {
|
|
|
4481
4452
|
},
|
|
4482
4453
|
};
|
|
4483
4454
|
}
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
return "needs_input";
|
|
4488
|
-
}
|
|
4489
|
-
if (agent.status === "error" || agent.attentionReason === "error") {
|
|
4490
|
-
return "failed";
|
|
4491
|
-
}
|
|
4492
|
-
if (agent.status === "running") {
|
|
4493
|
-
return "running";
|
|
4455
|
+
decodeAgentCursor(token, sort) {
|
|
4456
|
+
try {
|
|
4457
|
+
return this.agentsPager.decode(token, sort);
|
|
4494
4458
|
}
|
|
4495
|
-
|
|
4496
|
-
|
|
4459
|
+
catch (error) {
|
|
4460
|
+
if (error instanceof CursorError) {
|
|
4461
|
+
throw new SessionRequestError("invalid_cursor", error.message);
|
|
4462
|
+
}
|
|
4463
|
+
throw error;
|
|
4497
4464
|
}
|
|
4498
|
-
return "done";
|
|
4499
4465
|
}
|
|
4500
4466
|
async describeWorkspaceRecord(workspace, projectRecord) {
|
|
4501
4467
|
const resolvedProjectRecord = projectRecord ?? (await this.projectRegistry.get(workspace.projectId));
|
|
@@ -4513,6 +4479,7 @@ export class Session {
|
|
|
4513
4479
|
projectKind: (resolvedProjectRecord?.kind ?? "directory") === "git" ? "git" : "non_git",
|
|
4514
4480
|
workspaceKind: workspace.kind,
|
|
4515
4481
|
name: workspace.displayName,
|
|
4482
|
+
archivingAt: null,
|
|
4516
4483
|
status: "done",
|
|
4517
4484
|
activityAt: null,
|
|
4518
4485
|
diffStat,
|
|
@@ -4520,6 +4487,7 @@ export class Session {
|
|
|
4520
4487
|
? buildWorkspaceScriptPayloads({
|
|
4521
4488
|
workspaceId: workspace.workspaceId,
|
|
4522
4489
|
workspaceDirectory: workspace.cwd,
|
|
4490
|
+
paseoConfig: readPaseoConfigForProjection(workspace.cwd, this.sessionLogger),
|
|
4523
4491
|
routeStore: this.scriptRouteStore,
|
|
4524
4492
|
runtimeStore: this.scriptRuntimeStore,
|
|
4525
4493
|
daemonPort: this.getDaemonTcpPort?.() ?? null,
|
|
@@ -4582,6 +4550,7 @@ export class Session {
|
|
|
4582
4550
|
projectKind: "git",
|
|
4583
4551
|
workspaceKind: result.workspace.kind,
|
|
4584
4552
|
name: result.worktree.branchName || result.workspace.displayName,
|
|
4553
|
+
archivingAt: null,
|
|
4585
4554
|
status: "done",
|
|
4586
4555
|
activityAt: null,
|
|
4587
4556
|
diffStat: { additions: 0, deletions: 0 },
|
|
@@ -4604,239 +4573,37 @@ export class Session {
|
|
|
4604
4573
|
}
|
|
4605
4574
|
return this.describeWorkspaceRecord(input.workspace, input.projectRecord);
|
|
4606
4575
|
}
|
|
4576
|
+
markWorkspaceArchiving(workspaceIds, archivingAt) {
|
|
4577
|
+
this.workspaceDirectory.markArchiving(workspaceIds, archivingAt);
|
|
4578
|
+
}
|
|
4579
|
+
clearWorkspaceArchiving(workspaceIds) {
|
|
4580
|
+
this.workspaceDirectory.clearArchiving(workspaceIds);
|
|
4581
|
+
}
|
|
4607
4582
|
async buildWorkspaceDescriptorMap(options) {
|
|
4608
|
-
|
|
4609
|
-
this.listAgentPayloads(),
|
|
4610
|
-
this.workspaceRegistry.list(),
|
|
4611
|
-
this.projectRegistry.list(),
|
|
4612
|
-
]);
|
|
4613
|
-
const activeProjects = new Map(persistedProjects
|
|
4614
|
-
.filter((project) => !project.archivedAt)
|
|
4615
|
-
.map((project) => [project.projectId, project]));
|
|
4616
|
-
const archivedProjectIds = new Set(persistedProjects.filter((project) => project.archivedAt).map((project) => project.projectId));
|
|
4617
|
-
const activeRecords = persistedWorkspaces.filter((workspace) => !workspace.archivedAt && !archivedProjectIds.has(workspace.projectId));
|
|
4618
|
-
const descriptorsByWorkspaceId = new Map();
|
|
4619
|
-
const workspaceIds = options.workspaceIds ? new Set(options.workspaceIds) : null;
|
|
4620
|
-
const workspaceIdsByDirectory = new Map(activeRecords.map((workspace) => [normalizePersistedWorkspaceId(workspace.cwd), workspace.workspaceId]));
|
|
4621
|
-
const includedWorkspaces = activeRecords.filter((workspace) => !workspaceIds || workspaceIds.has(workspace.workspaceId));
|
|
4622
|
-
const workspaceDescriptors = await Promise.all(includedWorkspaces.map((workspace) => this.buildWorkspaceDescriptor({
|
|
4623
|
-
workspace,
|
|
4624
|
-
projectRecord: activeProjects.get(workspace.projectId) ?? null,
|
|
4625
|
-
includeGitData: options.includeGitData,
|
|
4626
|
-
})));
|
|
4627
|
-
for (let i = 0; i < includedWorkspaces.length; i += 1) {
|
|
4628
|
-
descriptorsByWorkspaceId.set(includedWorkspaces[i].workspaceId, workspaceDescriptors[i]);
|
|
4629
|
-
}
|
|
4630
|
-
for (const agent of agents) {
|
|
4631
|
-
if (agent.archivedAt) {
|
|
4632
|
-
continue;
|
|
4633
|
-
}
|
|
4634
|
-
if (!this.isProviderVisibleToClient(agent.provider)) {
|
|
4635
|
-
continue;
|
|
4636
|
-
}
|
|
4637
|
-
const workspaceId = workspaceIdsByDirectory.get(normalizePersistedWorkspaceId(agent.cwd));
|
|
4638
|
-
if (workspaceId === undefined) {
|
|
4639
|
-
continue;
|
|
4640
|
-
}
|
|
4641
|
-
const existing = descriptorsByWorkspaceId.get(workspaceId);
|
|
4642
|
-
if (!existing) {
|
|
4643
|
-
continue;
|
|
4644
|
-
}
|
|
4645
|
-
const bucket = this.deriveWorkspaceStateBucket(agent);
|
|
4646
|
-
if (this.workspaceStatePriority[bucket] < this.workspaceStatePriority[existing.status]) {
|
|
4647
|
-
existing.status = bucket;
|
|
4648
|
-
}
|
|
4649
|
-
}
|
|
4650
|
-
return descriptorsByWorkspaceId;
|
|
4583
|
+
return this.workspaceDirectory.buildDescriptorMap(options);
|
|
4651
4584
|
}
|
|
4652
4585
|
resolveRegisteredWorkspaceIdForCwd(cwd, workspaces) {
|
|
4653
|
-
|
|
4654
|
-
const exact = workspaces.find((workspace) => workspace.cwd === normalizedCwd);
|
|
4655
|
-
if (exact) {
|
|
4656
|
-
return exact.workspaceId;
|
|
4657
|
-
}
|
|
4658
|
-
const userHome = homedir();
|
|
4659
|
-
let bestMatch = null;
|
|
4660
|
-
for (const workspace of workspaces) {
|
|
4661
|
-
if (workspace.cwd === userHome)
|
|
4662
|
-
continue;
|
|
4663
|
-
if (workspace.archivedAt)
|
|
4664
|
-
continue;
|
|
4665
|
-
const prefix = workspace.cwd.endsWith(sep) ? workspace.cwd : `${workspace.cwd}${sep}`;
|
|
4666
|
-
if (!normalizedCwd.startsWith(prefix)) {
|
|
4667
|
-
continue;
|
|
4668
|
-
}
|
|
4669
|
-
if (!bestMatch || workspace.cwd.length > bestMatch.cwd.length) {
|
|
4670
|
-
bestMatch = workspace;
|
|
4671
|
-
}
|
|
4672
|
-
}
|
|
4673
|
-
return bestMatch?.workspaceId ?? normalizedCwd;
|
|
4586
|
+
return this.workspaceDirectory.resolveRegisteredWorkspaceIdForCwd(cwd, workspaces);
|
|
4674
4587
|
}
|
|
4675
|
-
|
|
4676
|
-
return
|
|
4677
|
-
includeGitData: true,
|
|
4678
|
-
})).values());
|
|
4588
|
+
matchesWorkspaceFilter(input) {
|
|
4589
|
+
return this.workspaceDirectory.matchesFilter(input);
|
|
4679
4590
|
}
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
return fallback;
|
|
4591
|
+
async listFetchWorkspacesEntries(request) {
|
|
4592
|
+
try {
|
|
4593
|
+
return await this.workspaceDirectory.listFetchEntries(request);
|
|
4684
4594
|
}
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
if (seen.has(entry.key)) {
|
|
4689
|
-
continue;
|
|
4690
|
-
}
|
|
4691
|
-
seen.add(entry.key);
|
|
4692
|
-
deduped.push(entry);
|
|
4693
|
-
}
|
|
4694
|
-
return deduped.length > 0 ? deduped : fallback;
|
|
4695
|
-
}
|
|
4696
|
-
getFetchWorkspacesSortValue(workspace, key) {
|
|
4697
|
-
switch (key) {
|
|
4698
|
-
case "status_priority":
|
|
4699
|
-
return this.workspaceStatePriority[workspace.status];
|
|
4700
|
-
case "activity_at":
|
|
4701
|
-
return workspace.activityAt ? Date.parse(workspace.activityAt) : null;
|
|
4702
|
-
case "name":
|
|
4703
|
-
return workspace.name.toLocaleLowerCase();
|
|
4704
|
-
case "project_id":
|
|
4705
|
-
return workspace.projectId.toLocaleLowerCase();
|
|
4706
|
-
}
|
|
4707
|
-
}
|
|
4708
|
-
compareFetchWorkspacesEntries(left, right, sort) {
|
|
4709
|
-
for (const spec of sort) {
|
|
4710
|
-
const leftValue = this.getFetchWorkspacesSortValue(left, spec.key);
|
|
4711
|
-
const rightValue = this.getFetchWorkspacesSortValue(right, spec.key);
|
|
4712
|
-
const base = this.compareSortValues(leftValue, rightValue);
|
|
4713
|
-
if (base === 0) {
|
|
4714
|
-
continue;
|
|
4595
|
+
catch (error) {
|
|
4596
|
+
if (error instanceof CursorError) {
|
|
4597
|
+
throw new SessionRequestError("invalid_cursor", error.message);
|
|
4715
4598
|
}
|
|
4716
|
-
|
|
4599
|
+
throw error;
|
|
4717
4600
|
}
|
|
4718
|
-
return left.id.localeCompare(right.id);
|
|
4719
4601
|
}
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
return Buffer.from(JSON.stringify({
|
|
4726
|
-
sort,
|
|
4727
|
-
values,
|
|
4728
|
-
id: entry.id,
|
|
4729
|
-
}), "utf8").toString("base64url");
|
|
4730
|
-
}
|
|
4731
|
-
decodeFetchWorkspacesCursor(cursor, sort) {
|
|
4732
|
-
let parsed;
|
|
4733
|
-
try {
|
|
4734
|
-
parsed = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
4735
|
-
}
|
|
4736
|
-
catch {
|
|
4737
|
-
throw new SessionRequestError("invalid_cursor", "Invalid fetch_workspaces cursor");
|
|
4738
|
-
}
|
|
4739
|
-
if (!parsed || typeof parsed !== "object") {
|
|
4740
|
-
throw new SessionRequestError("invalid_cursor", "Invalid fetch_workspaces cursor");
|
|
4741
|
-
}
|
|
4742
|
-
const payload = parsed;
|
|
4743
|
-
if (!Array.isArray(payload.sort) || typeof payload.id !== "string") {
|
|
4744
|
-
throw new SessionRequestError("invalid_cursor", "Invalid fetch_workspaces cursor");
|
|
4745
|
-
}
|
|
4746
|
-
if (!payload.values || typeof payload.values !== "object") {
|
|
4747
|
-
throw new SessionRequestError("invalid_cursor", "Invalid fetch_workspaces cursor");
|
|
4748
|
-
}
|
|
4749
|
-
const cursorSort = parseFetchWorkspacesCursorSort(payload.sort);
|
|
4750
|
-
if (cursorSort.length !== sort.length ||
|
|
4751
|
-
cursorSort.some((entry, index) => entry.key !== sort[index]?.key || entry.direction !== sort[index]?.direction)) {
|
|
4752
|
-
throw new SessionRequestError("invalid_cursor", "fetch_workspaces cursor does not match current sort");
|
|
4753
|
-
}
|
|
4754
|
-
return {
|
|
4755
|
-
sort: cursorSort,
|
|
4756
|
-
values: payload.values,
|
|
4757
|
-
id: String(payload.id),
|
|
4758
|
-
};
|
|
4759
|
-
}
|
|
4760
|
-
compareWorkspaceWithCursor(workspace, cursor, sort) {
|
|
4761
|
-
for (const spec of sort) {
|
|
4762
|
-
const leftValue = this.getFetchWorkspacesSortValue(workspace, spec.key);
|
|
4763
|
-
const rightValue = cursor.values[spec.key] !== undefined ? (cursor.values[spec.key] ?? null) : null;
|
|
4764
|
-
const base = this.compareSortValues(leftValue, rightValue);
|
|
4765
|
-
if (base === 0) {
|
|
4766
|
-
continue;
|
|
4767
|
-
}
|
|
4768
|
-
return spec.direction === "asc" ? base : -base;
|
|
4769
|
-
}
|
|
4770
|
-
return workspace.id.localeCompare(cursor.id);
|
|
4771
|
-
}
|
|
4772
|
-
matchesWorkspaceFilter(input) {
|
|
4773
|
-
const { workspace, filter } = input;
|
|
4774
|
-
if (!filter) {
|
|
4775
|
-
return true;
|
|
4776
|
-
}
|
|
4777
|
-
if (filter.projectId && filter.projectId.trim().length > 0) {
|
|
4778
|
-
if (workspace.projectId !== filter.projectId.trim()) {
|
|
4779
|
-
return false;
|
|
4780
|
-
}
|
|
4781
|
-
}
|
|
4782
|
-
if (filter.idPrefix && filter.idPrefix.trim().length > 0) {
|
|
4783
|
-
if (!String(workspace.id).startsWith(filter.idPrefix.trim())) {
|
|
4784
|
-
return false;
|
|
4785
|
-
}
|
|
4786
|
-
}
|
|
4787
|
-
if (filter.query && filter.query.trim().length > 0) {
|
|
4788
|
-
const query = filter.query.trim().toLocaleLowerCase();
|
|
4789
|
-
const haystacks = [workspace.name, String(workspace.projectId), String(workspace.id)];
|
|
4790
|
-
if (!haystacks.some((value) => value.toLocaleLowerCase().includes(query))) {
|
|
4791
|
-
return false;
|
|
4792
|
-
}
|
|
4793
|
-
}
|
|
4794
|
-
return true;
|
|
4795
|
-
}
|
|
4796
|
-
async listFetchWorkspacesEntries(request) {
|
|
4797
|
-
const filter = request.filter;
|
|
4798
|
-
const sort = this.normalizeFetchWorkspacesSort(request.sort);
|
|
4799
|
-
let entries = await this.listWorkspaceDescriptors();
|
|
4800
|
-
const listedCount = entries.length;
|
|
4801
|
-
entries = entries.filter((workspace) => this.matchesWorkspaceFilter({ workspace, filter }));
|
|
4802
|
-
const filteredCount = entries.length;
|
|
4803
|
-
entries.sort((left, right) => this.compareFetchWorkspacesEntries(left, right, sort));
|
|
4804
|
-
const cursorToken = request.page?.cursor;
|
|
4805
|
-
if (cursorToken) {
|
|
4806
|
-
const cursor = this.decodeFetchWorkspacesCursor(cursorToken, sort);
|
|
4807
|
-
entries = entries.filter((workspace) => this.compareWorkspaceWithCursor(workspace, cursor, sort) > 0);
|
|
4808
|
-
}
|
|
4809
|
-
const limit = request.page?.limit ?? 200;
|
|
4810
|
-
const pagedEntries = entries.slice(0, limit);
|
|
4811
|
-
const hasMore = entries.length > limit;
|
|
4812
|
-
const nextCursor = hasMore && pagedEntries.length > 0
|
|
4813
|
-
? this.encodeFetchWorkspacesCursor(pagedEntries[pagedEntries.length - 1], sort)
|
|
4814
|
-
: null;
|
|
4815
|
-
this.sessionLogger.debug({
|
|
4816
|
-
requestId: request.requestId,
|
|
4817
|
-
filter: request.filter ?? null,
|
|
4818
|
-
sort,
|
|
4819
|
-
page: request.page ?? null,
|
|
4820
|
-
listedCount,
|
|
4821
|
-
filteredCount,
|
|
4822
|
-
returnedCount: pagedEntries.length,
|
|
4823
|
-
hasMore,
|
|
4824
|
-
nextCursor,
|
|
4825
|
-
}, "fetch_workspaces_entries_listed");
|
|
4826
|
-
return {
|
|
4827
|
-
entries: pagedEntries,
|
|
4828
|
-
pageInfo: {
|
|
4829
|
-
nextCursor,
|
|
4830
|
-
prevCursor: request.page?.cursor ?? null,
|
|
4831
|
-
hasMore,
|
|
4832
|
-
},
|
|
4833
|
-
};
|
|
4834
|
-
}
|
|
4835
|
-
bufferOrEmitWorkspaceUpdate(subscription, payload) {
|
|
4836
|
-
if (subscription.isBootstrapping) {
|
|
4837
|
-
const workspaceId = payload.kind === "upsert" ? payload.workspace.id : payload.id;
|
|
4838
|
-
subscription.pendingUpdatesByWorkspaceId.set(workspaceId, payload);
|
|
4839
|
-
return;
|
|
4602
|
+
bufferOrEmitWorkspaceUpdate(subscription, payload) {
|
|
4603
|
+
if (subscription.isBootstrapping) {
|
|
4604
|
+
const workspaceId = payload.kind === "upsert" ? payload.workspace.id : payload.id;
|
|
4605
|
+
subscription.pendingUpdatesByWorkspaceId.set(workspaceId, payload);
|
|
4606
|
+
return;
|
|
4840
4607
|
}
|
|
4841
4608
|
const workspaceId = payload.kind === "upsert" ? payload.workspace.id : payload.id;
|
|
4842
4609
|
subscription.lastEmittedByWorkspaceId.set(workspaceId, payload);
|
|
@@ -5154,14 +4921,6 @@ export class Session {
|
|
|
5154
4921
|
const workspaceId = this.resolveRegisteredWorkspaceIdForCwd(cwd, workspaces);
|
|
5155
4922
|
await this.emitWorkspaceUpdatesForWorkspaceIds([workspaceId], options);
|
|
5156
4923
|
}
|
|
5157
|
-
async emitWorkspaceUpdatesForCwds(cwds) {
|
|
5158
|
-
const workspaces = await this.workspaceRegistry.list();
|
|
5159
|
-
const uniqueWorkspaceIds = new Set();
|
|
5160
|
-
for (const cwd of cwds) {
|
|
5161
|
-
uniqueWorkspaceIds.add(this.resolveRegisteredWorkspaceIdForCwd(cwd, workspaces));
|
|
5162
|
-
}
|
|
5163
|
-
await this.emitWorkspaceUpdatesForWorkspaceIds(uniqueWorkspaceIds);
|
|
5164
|
-
}
|
|
5165
4924
|
async handleFetchAgents(request) {
|
|
5166
4925
|
const requestedSubscriptionId = request.subscribe?.subscriptionId?.trim();
|
|
5167
4926
|
const subscriptionId = resolveSubscriptionId(request.subscribe, requestedSubscriptionId);
|
|
@@ -5350,6 +5109,7 @@ export class Session {
|
|
|
5350
5109
|
return buildWorkspaceScriptPayloads({
|
|
5351
5110
|
workspaceId,
|
|
5352
5111
|
workspaceDirectory,
|
|
5112
|
+
paseoConfig: readPaseoConfigForProjection(workspaceDirectory, this.sessionLogger),
|
|
5353
5113
|
routeStore: this.scriptRouteStore,
|
|
5354
5114
|
runtimeStore: this.scriptRuntimeStore,
|
|
5355
5115
|
daemonPort: this.getDaemonTcpPort?.() ?? null,
|
|
@@ -5500,15 +5260,15 @@ export class Session {
|
|
|
5500
5260
|
paseoHome: this.paseoHome,
|
|
5501
5261
|
describeWorkspaceRecord: (result) => this.describeCreatedWorktreeWorkspace(result),
|
|
5502
5262
|
emit: (message) => this.emit(message),
|
|
5503
|
-
createPaseoWorktree: (input) => this.createPaseoWorktree(input),
|
|
5504
|
-
warmWorkspaceGitData: (workspace) => this.warmWorkspaceGitDataForWorkspace(workspace),
|
|
5505
5263
|
sessionLogger: this.sessionLogger,
|
|
5506
|
-
|
|
5264
|
+
createPaseoWorktreeWorkflow: (input) => this.createPaseoWorktreeWorkflow(input),
|
|
5507
5265
|
}, request);
|
|
5508
5266
|
}
|
|
5509
|
-
async
|
|
5510
|
-
return
|
|
5267
|
+
async createPaseoWorktreeWorkflow(input, options) {
|
|
5268
|
+
return createWorktreeWorkflow({
|
|
5511
5269
|
paseoHome: this.paseoHome,
|
|
5270
|
+
createPaseoWorktree: (workflowInput, serviceOptions) => this.createPaseoWorktree(workflowInput, serviceOptions),
|
|
5271
|
+
warmWorkspaceGitData: (workspace) => this.warmWorkspaceGitDataForWorkspace(workspace),
|
|
5512
5272
|
emitWorkspaceUpdateForCwd: (cwd, emitOptions) => this.emitWorkspaceUpdateForCwd(cwd, emitOptions),
|
|
5513
5273
|
cacheWorkspaceSetupSnapshot: (workspaceId, snapshot) => {
|
|
5514
5274
|
this.workspaceSetupSnapshots.set(workspaceId, snapshot);
|
|
@@ -5524,7 +5284,7 @@ export class Session {
|
|
|
5524
5284
|
onScriptsChanged: (workspaceId, workspaceDirectory) => {
|
|
5525
5285
|
this.emitWorkspaceScriptStatusUpdate(workspaceId, workspaceDirectory);
|
|
5526
5286
|
},
|
|
5527
|
-
}, options);
|
|
5287
|
+
}, input, options);
|
|
5528
5288
|
}
|
|
5529
5289
|
async handleWorkspaceSetupStatusRequest(request) {
|
|
5530
5290
|
return handleWorkspaceSetupStatusRequestMessage({
|
|
@@ -5597,13 +5357,12 @@ export class Session {
|
|
|
5597
5357
|
});
|
|
5598
5358
|
}
|
|
5599
5359
|
loadProjectedTimelineWindow(params) {
|
|
5600
|
-
const { agentId, direction, cursor, requestedLimit
|
|
5360
|
+
const { agentId, direction, cursor, requestedLimit } = params;
|
|
5601
5361
|
let timeline = params.timeline;
|
|
5602
5362
|
const projectedLimit = Math.max(1, Math.floor(requestedLimit));
|
|
5603
5363
|
let fetchLimit = projectedLimit;
|
|
5604
5364
|
let projectedWindow = selectTimelineWindowByProjectedLimit({
|
|
5605
5365
|
rows: timeline.rows,
|
|
5606
|
-
provider,
|
|
5607
5366
|
direction,
|
|
5608
5367
|
limit: projectedLimit,
|
|
5609
5368
|
collapseToolLifecycle: false,
|
|
@@ -5632,7 +5391,6 @@ export class Session {
|
|
|
5632
5391
|
});
|
|
5633
5392
|
projectedWindow = selectTimelineWindowByProjectedLimit({
|
|
5634
5393
|
rows: timeline.rows,
|
|
5635
|
-
provider,
|
|
5636
5394
|
direction,
|
|
5637
5395
|
limit: projectedLimit,
|
|
5638
5396
|
collapseToolLifecycle: false,
|
|
@@ -5685,11 +5443,10 @@ export class Session {
|
|
|
5685
5443
|
direction,
|
|
5686
5444
|
cursor,
|
|
5687
5445
|
requestedLimit,
|
|
5688
|
-
provider: snapshot.provider,
|
|
5689
5446
|
timeline,
|
|
5690
5447
|
});
|
|
5691
5448
|
timeline = projectedResult.timeline;
|
|
5692
|
-
entries = projectTimelineRows(projectedResult.selectedRows,
|
|
5449
|
+
entries = projectTimelineRows({ rows: projectedResult.selectedRows, mode: projection });
|
|
5693
5450
|
if (projectedResult.minSeq !== null && projectedResult.maxSeq !== null) {
|
|
5694
5451
|
startCursor = { epoch: timeline.epoch, seq: projectedResult.minSeq };
|
|
5695
5452
|
endCursor = { epoch: timeline.epoch, seq: projectedResult.maxSeq };
|
|
@@ -5702,7 +5459,7 @@ export class Session {
|
|
|
5702
5459
|
const lastRow = timeline.rows[timeline.rows.length - 1];
|
|
5703
5460
|
startCursor = firstRow ? { epoch: timeline.epoch, seq: firstRow.seq } : null;
|
|
5704
5461
|
endCursor = lastRow ? { epoch: timeline.epoch, seq: lastRow.seq } : null;
|
|
5705
|
-
entries = projectTimelineRows(timeline.rows,
|
|
5462
|
+
entries = projectTimelineRows({ rows: timeline.rows, mode: projection });
|
|
5706
5463
|
}
|
|
5707
5464
|
this.emit({
|
|
5708
5465
|
type: "fetch_agent_timeline_response",
|
|
@@ -5721,7 +5478,17 @@ export class Session {
|
|
|
5721
5478
|
endCursor,
|
|
5722
5479
|
hasOlder,
|
|
5723
5480
|
hasNewer,
|
|
5724
|
-
entries
|
|
5481
|
+
entries: entries.map((entry) => ({
|
|
5482
|
+
provider: snapshot.provider,
|
|
5483
|
+
item: entry.item,
|
|
5484
|
+
timestamp: entry.timestamp,
|
|
5485
|
+
seqStart: entry.seqStart,
|
|
5486
|
+
seqEnd: entry.seqEnd,
|
|
5487
|
+
sourceSeqRanges: entry.sourceSeqRanges,
|
|
5488
|
+
collapsed: this.supports(CLIENT_CAPS.reasoningMergeEnum)
|
|
5489
|
+
? entry.collapsed
|
|
5490
|
+
: entry.collapsed.filter((value) => value !== "reasoning_merge"),
|
|
5491
|
+
})),
|
|
5725
5492
|
error: null,
|
|
5726
5493
|
},
|
|
5727
5494
|
});
|
|
@@ -6474,17 +6241,7 @@ export class Session {
|
|
|
6474
6241
|
}
|
|
6475
6242
|
await this.disableVoiceModeForActiveAgent(true);
|
|
6476
6243
|
this.isVoiceMode = false;
|
|
6477
|
-
|
|
6478
|
-
if (this.unsubscribeTerminalsChanged) {
|
|
6479
|
-
this.unsubscribeTerminalsChanged();
|
|
6480
|
-
this.unsubscribeTerminalsChanged = null;
|
|
6481
|
-
}
|
|
6482
|
-
this.subscribedTerminalDirectories.clear();
|
|
6483
|
-
for (const unsubscribeExit of this.terminalExitSubscriptions.values()) {
|
|
6484
|
-
unsubscribeExit();
|
|
6485
|
-
}
|
|
6486
|
-
this.terminalExitSubscriptions.clear();
|
|
6487
|
-
this.disposeTerminalSubscriptions();
|
|
6244
|
+
this.terminalController.dispose();
|
|
6488
6245
|
for (const unsubscribe of this.checkoutDiffSubscriptions.values()) {
|
|
6489
6246
|
unsubscribe();
|
|
6490
6247
|
}
|
|
@@ -6494,26 +6251,6 @@ export class Session {
|
|
|
6494
6251
|
}
|
|
6495
6252
|
this.workspaceGitSubscriptions.clear();
|
|
6496
6253
|
}
|
|
6497
|
-
// ----------------------------------------------------------------------------
|
|
6498
|
-
// Terminal Handlers
|
|
6499
|
-
// ----------------------------------------------------------------------------
|
|
6500
|
-
ensureTerminalExitSubscription(terminal) {
|
|
6501
|
-
if (this.terminalExitSubscriptions.has(terminal.id)) {
|
|
6502
|
-
return;
|
|
6503
|
-
}
|
|
6504
|
-
const unsubscribeExit = terminal.onExit(() => {
|
|
6505
|
-
this.handleTerminalExited(terminal.id);
|
|
6506
|
-
});
|
|
6507
|
-
this.terminalExitSubscriptions.set(terminal.id, unsubscribeExit);
|
|
6508
|
-
}
|
|
6509
|
-
handleTerminalExited(terminalId) {
|
|
6510
|
-
const unsubscribeExit = this.terminalExitSubscriptions.get(terminalId);
|
|
6511
|
-
if (unsubscribeExit) {
|
|
6512
|
-
unsubscribeExit();
|
|
6513
|
-
this.terminalExitSubscriptions.delete(terminalId);
|
|
6514
|
-
}
|
|
6515
|
-
this.detachTerminalStream(terminalId, { emitExit: true });
|
|
6516
|
-
}
|
|
6517
6254
|
emitChatRpcError(request, error) {
|
|
6518
6255
|
const message = error instanceof Error ? error.message : "Chat request failed";
|
|
6519
6256
|
const code = error instanceof ChatServiceError ? error.code : "chat_request_failed";
|
|
@@ -6926,458 +6663,6 @@ export class Session {
|
|
|
6926
6663
|
this.emitLoopRpcError(request, error);
|
|
6927
6664
|
}
|
|
6928
6665
|
}
|
|
6929
|
-
emitTerminalsChangedSnapshot(input) {
|
|
6930
|
-
this.emit({
|
|
6931
|
-
type: "terminals_changed",
|
|
6932
|
-
payload: {
|
|
6933
|
-
cwd: input.cwd,
|
|
6934
|
-
terminals: input.terminals,
|
|
6935
|
-
},
|
|
6936
|
-
});
|
|
6937
|
-
}
|
|
6938
|
-
filterStandaloneTerminals(terminals) {
|
|
6939
|
-
return terminals;
|
|
6940
|
-
}
|
|
6941
|
-
toTerminalInfo(terminal) {
|
|
6942
|
-
const title = terminal.getTitle();
|
|
6943
|
-
return {
|
|
6944
|
-
id: terminal.id,
|
|
6945
|
-
name: terminal.name,
|
|
6946
|
-
...(title ? { title } : {}),
|
|
6947
|
-
};
|
|
6948
|
-
}
|
|
6949
|
-
handleTerminalsChanged(event) {
|
|
6950
|
-
if (!this.subscribedTerminalDirectories.has(event.cwd)) {
|
|
6951
|
-
return;
|
|
6952
|
-
}
|
|
6953
|
-
this.emitTerminalsChangedSnapshot({
|
|
6954
|
-
cwd: event.cwd,
|
|
6955
|
-
terminals: this.filterStandaloneTerminals(event.terminals).map((terminal) => Object.assign({ id: terminal.id, name: terminal.name }, terminal.title ? { title: terminal.title } : {})),
|
|
6956
|
-
});
|
|
6957
|
-
}
|
|
6958
|
-
handleSubscribeTerminalsRequest(msg) {
|
|
6959
|
-
this.subscribedTerminalDirectories.add(msg.cwd);
|
|
6960
|
-
void this.emitInitialTerminalsChangedSnapshot(msg.cwd);
|
|
6961
|
-
}
|
|
6962
|
-
handleUnsubscribeTerminalsRequest(msg) {
|
|
6963
|
-
this.subscribedTerminalDirectories.delete(msg.cwd);
|
|
6964
|
-
}
|
|
6965
|
-
async emitInitialTerminalsChangedSnapshot(cwd) {
|
|
6966
|
-
if (!this.terminalManager || !this.subscribedTerminalDirectories.has(cwd)) {
|
|
6967
|
-
return;
|
|
6968
|
-
}
|
|
6969
|
-
try {
|
|
6970
|
-
const terminals = this.filterStandaloneTerminals(await this.terminalManager.getTerminals(cwd));
|
|
6971
|
-
for (const terminal of terminals) {
|
|
6972
|
-
this.ensureTerminalExitSubscription(terminal);
|
|
6973
|
-
}
|
|
6974
|
-
if (!this.subscribedTerminalDirectories.has(cwd)) {
|
|
6975
|
-
return;
|
|
6976
|
-
}
|
|
6977
|
-
this.emitTerminalsChangedSnapshot({
|
|
6978
|
-
cwd,
|
|
6979
|
-
terminals: terminals.map((terminal) => this.toTerminalInfo(terminal)),
|
|
6980
|
-
});
|
|
6981
|
-
}
|
|
6982
|
-
catch (error) {
|
|
6983
|
-
this.sessionLogger.warn({ err: error, cwd }, "Failed to emit initial terminal snapshot");
|
|
6984
|
-
}
|
|
6985
|
-
}
|
|
6986
|
-
async handleListTerminalsRequest(msg) {
|
|
6987
|
-
if (!this.terminalManager) {
|
|
6988
|
-
this.emit({
|
|
6989
|
-
type: "list_terminals_response",
|
|
6990
|
-
payload: {
|
|
6991
|
-
...(msg.cwd ? { cwd: msg.cwd } : {}),
|
|
6992
|
-
terminals: [],
|
|
6993
|
-
requestId: msg.requestId,
|
|
6994
|
-
},
|
|
6995
|
-
});
|
|
6996
|
-
return;
|
|
6997
|
-
}
|
|
6998
|
-
try {
|
|
6999
|
-
const terminals = this.filterStandaloneTerminals(typeof msg.cwd === "string"
|
|
7000
|
-
? await this.terminalManager.getTerminals(msg.cwd)
|
|
7001
|
-
: await this.getAllTerminalSessions());
|
|
7002
|
-
for (const terminal of terminals) {
|
|
7003
|
-
this.ensureTerminalExitSubscription(terminal);
|
|
7004
|
-
}
|
|
7005
|
-
this.emit({
|
|
7006
|
-
type: "list_terminals_response",
|
|
7007
|
-
payload: {
|
|
7008
|
-
...(msg.cwd ? { cwd: msg.cwd } : {}),
|
|
7009
|
-
terminals: terminals.map((terminal) => this.toTerminalInfo(terminal)),
|
|
7010
|
-
requestId: msg.requestId,
|
|
7011
|
-
},
|
|
7012
|
-
});
|
|
7013
|
-
}
|
|
7014
|
-
catch (error) {
|
|
7015
|
-
this.sessionLogger.error({ err: error, cwd: msg.cwd }, "Failed to list terminals");
|
|
7016
|
-
this.emit({
|
|
7017
|
-
type: "list_terminals_response",
|
|
7018
|
-
payload: {
|
|
7019
|
-
...(msg.cwd ? { cwd: msg.cwd } : {}),
|
|
7020
|
-
terminals: [],
|
|
7021
|
-
requestId: msg.requestId,
|
|
7022
|
-
},
|
|
7023
|
-
});
|
|
7024
|
-
}
|
|
7025
|
-
}
|
|
7026
|
-
async getAllTerminalSessions() {
|
|
7027
|
-
if (!this.terminalManager) {
|
|
7028
|
-
return [];
|
|
7029
|
-
}
|
|
7030
|
-
const directories = this.terminalManager.listDirectories();
|
|
7031
|
-
const terminalsByDirectory = await Promise.all(directories.map((cwd) => this.terminalManager.getTerminals(cwd)));
|
|
7032
|
-
return terminalsByDirectory.flat();
|
|
7033
|
-
}
|
|
7034
|
-
async handleCreateTerminalRequest(msg) {
|
|
7035
|
-
if (!this.terminalManager) {
|
|
7036
|
-
this.emit({
|
|
7037
|
-
type: "create_terminal_response",
|
|
7038
|
-
payload: {
|
|
7039
|
-
terminal: null,
|
|
7040
|
-
error: "Terminal manager not available",
|
|
7041
|
-
requestId: msg.requestId,
|
|
7042
|
-
},
|
|
7043
|
-
});
|
|
7044
|
-
return;
|
|
7045
|
-
}
|
|
7046
|
-
try {
|
|
7047
|
-
if (msg.agentId) {
|
|
7048
|
-
this.emit({
|
|
7049
|
-
type: "create_terminal_response",
|
|
7050
|
-
payload: {
|
|
7051
|
-
terminal: null,
|
|
7052
|
-
error: `Agent-backed terminals are no longer supported for agent ${msg.agentId}`,
|
|
7053
|
-
requestId: msg.requestId,
|
|
7054
|
-
},
|
|
7055
|
-
});
|
|
7056
|
-
return;
|
|
7057
|
-
}
|
|
7058
|
-
const session = await this.terminalManager.createTerminal({
|
|
7059
|
-
cwd: msg.cwd,
|
|
7060
|
-
name: msg.name,
|
|
7061
|
-
command: msg.command,
|
|
7062
|
-
args: msg.args,
|
|
7063
|
-
});
|
|
7064
|
-
this.ensureTerminalExitSubscription(session);
|
|
7065
|
-
this.emit({
|
|
7066
|
-
type: "create_terminal_response",
|
|
7067
|
-
payload: {
|
|
7068
|
-
terminal: {
|
|
7069
|
-
id: session.id,
|
|
7070
|
-
name: session.name,
|
|
7071
|
-
cwd: session.cwd,
|
|
7072
|
-
...(session.getTitle() ? { title: session.getTitle() } : {}),
|
|
7073
|
-
},
|
|
7074
|
-
error: null,
|
|
7075
|
-
requestId: msg.requestId,
|
|
7076
|
-
},
|
|
7077
|
-
});
|
|
7078
|
-
}
|
|
7079
|
-
catch (error) {
|
|
7080
|
-
this.sessionLogger.error({ err: error, cwd: msg.cwd }, "Failed to create terminal");
|
|
7081
|
-
this.emit({
|
|
7082
|
-
type: "create_terminal_response",
|
|
7083
|
-
payload: {
|
|
7084
|
-
terminal: null,
|
|
7085
|
-
error: error.message,
|
|
7086
|
-
requestId: msg.requestId,
|
|
7087
|
-
},
|
|
7088
|
-
});
|
|
7089
|
-
}
|
|
7090
|
-
}
|
|
7091
|
-
async handleSubscribeTerminalRequest(msg) {
|
|
7092
|
-
if (!this.terminalManager) {
|
|
7093
|
-
this.emit({
|
|
7094
|
-
type: "subscribe_terminal_response",
|
|
7095
|
-
payload: {
|
|
7096
|
-
terminalId: msg.terminalId,
|
|
7097
|
-
error: "Terminal manager not available",
|
|
7098
|
-
requestId: msg.requestId,
|
|
7099
|
-
},
|
|
7100
|
-
});
|
|
7101
|
-
return;
|
|
7102
|
-
}
|
|
7103
|
-
const session = this.terminalManager.getTerminal(msg.terminalId);
|
|
7104
|
-
if (!session) {
|
|
7105
|
-
this.emit({
|
|
7106
|
-
type: "subscribe_terminal_response",
|
|
7107
|
-
payload: {
|
|
7108
|
-
terminalId: msg.terminalId,
|
|
7109
|
-
error: "Terminal not found",
|
|
7110
|
-
requestId: msg.requestId,
|
|
7111
|
-
},
|
|
7112
|
-
});
|
|
7113
|
-
return;
|
|
7114
|
-
}
|
|
7115
|
-
this.ensureTerminalExitSubscription(session);
|
|
7116
|
-
const slot = this.bindActiveTerminalStream(session);
|
|
7117
|
-
if (slot === null) {
|
|
7118
|
-
this.sessionLogger.warn({
|
|
7119
|
-
terminalId: msg.terminalId,
|
|
7120
|
-
activeTerminalStreamCount: this.activeTerminalStreams.size,
|
|
7121
|
-
}, "Terminal stream slot exhaustion");
|
|
7122
|
-
this.emit({
|
|
7123
|
-
type: "subscribe_terminal_response",
|
|
7124
|
-
payload: {
|
|
7125
|
-
terminalId: msg.terminalId,
|
|
7126
|
-
error: "No terminal stream slots available",
|
|
7127
|
-
requestId: msg.requestId,
|
|
7128
|
-
},
|
|
7129
|
-
});
|
|
7130
|
-
return;
|
|
7131
|
-
}
|
|
7132
|
-
this.emit({
|
|
7133
|
-
type: "subscribe_terminal_response",
|
|
7134
|
-
payload: {
|
|
7135
|
-
terminalId: msg.terminalId,
|
|
7136
|
-
slot,
|
|
7137
|
-
error: null,
|
|
7138
|
-
requestId: msg.requestId,
|
|
7139
|
-
},
|
|
7140
|
-
});
|
|
7141
|
-
const activeStream = this.activeTerminalStreams.get(slot);
|
|
7142
|
-
if (activeStream) {
|
|
7143
|
-
this.trySendTerminalSnapshot(activeStream);
|
|
7144
|
-
}
|
|
7145
|
-
}
|
|
7146
|
-
handleUnsubscribeTerminalRequest(msg) {
|
|
7147
|
-
this.detachTerminalStream(msg.terminalId, { emitExit: false });
|
|
7148
|
-
}
|
|
7149
|
-
handleTerminalInput(msg) {
|
|
7150
|
-
if (!this.terminalManager) {
|
|
7151
|
-
return;
|
|
7152
|
-
}
|
|
7153
|
-
const session = this.terminalManager.getTerminal(msg.terminalId);
|
|
7154
|
-
if (!session) {
|
|
7155
|
-
this.sessionLogger.warn({ terminalId: msg.terminalId }, "Terminal not found for input");
|
|
7156
|
-
return;
|
|
7157
|
-
}
|
|
7158
|
-
this.ensureTerminalExitSubscription(session);
|
|
7159
|
-
if (msg.message.type === "resize") {
|
|
7160
|
-
const currentSize = session.getSize();
|
|
7161
|
-
if (currentSize.rows === msg.message.rows && currentSize.cols === msg.message.cols) {
|
|
7162
|
-
return;
|
|
7163
|
-
}
|
|
7164
|
-
}
|
|
7165
|
-
session.send(msg.message);
|
|
7166
|
-
}
|
|
7167
|
-
killTrackedTerminal(terminalId, options) {
|
|
7168
|
-
this.detachTerminalStream(terminalId, { emitExit: options?.emitExit ?? true });
|
|
7169
|
-
this.terminalManager?.killTerminal(terminalId);
|
|
7170
|
-
}
|
|
7171
|
-
async killTerminalsUnderPath(rootPath) {
|
|
7172
|
-
return killWorktreeTerminalsUnderPath({
|
|
7173
|
-
isPathWithinRoot: (pathRoot, candidatePath) => this.isPathWithinRoot(pathRoot, candidatePath),
|
|
7174
|
-
killTrackedTerminal: (terminalId, options) => this.killTrackedTerminal(terminalId, options),
|
|
7175
|
-
detachTerminalStream: (terminalId, options) => void this.detachTerminalStream(terminalId, options),
|
|
7176
|
-
sessionLogger: this.sessionLogger,
|
|
7177
|
-
terminalManager: this.terminalManager,
|
|
7178
|
-
}, rootPath);
|
|
7179
|
-
}
|
|
7180
|
-
async handleKillTerminalRequest(msg) {
|
|
7181
|
-
const result = this.killTerminalForClose(msg.terminalId);
|
|
7182
|
-
this.emit({
|
|
7183
|
-
type: "kill_terminal_response",
|
|
7184
|
-
payload: {
|
|
7185
|
-
terminalId: result.terminalId,
|
|
7186
|
-
success: result.success,
|
|
7187
|
-
requestId: msg.requestId,
|
|
7188
|
-
},
|
|
7189
|
-
});
|
|
7190
|
-
}
|
|
7191
|
-
killTerminalForClose(terminalId) {
|
|
7192
|
-
if (!this.terminalManager) {
|
|
7193
|
-
return {
|
|
7194
|
-
terminalId,
|
|
7195
|
-
success: false,
|
|
7196
|
-
};
|
|
7197
|
-
}
|
|
7198
|
-
this.killTrackedTerminal(terminalId, { emitExit: true });
|
|
7199
|
-
return {
|
|
7200
|
-
terminalId,
|
|
7201
|
-
success: true,
|
|
7202
|
-
};
|
|
7203
|
-
}
|
|
7204
|
-
async handleCaptureTerminalRequest(msg) {
|
|
7205
|
-
if (!this.terminalManager) {
|
|
7206
|
-
this.emit({
|
|
7207
|
-
type: "capture_terminal_response",
|
|
7208
|
-
payload: {
|
|
7209
|
-
terminalId: msg.terminalId,
|
|
7210
|
-
lines: [],
|
|
7211
|
-
totalLines: 0,
|
|
7212
|
-
requestId: msg.requestId,
|
|
7213
|
-
},
|
|
7214
|
-
});
|
|
7215
|
-
return;
|
|
7216
|
-
}
|
|
7217
|
-
const session = this.terminalManager.getTerminal(msg.terminalId);
|
|
7218
|
-
if (!session) {
|
|
7219
|
-
this.emit({
|
|
7220
|
-
type: "capture_terminal_response",
|
|
7221
|
-
payload: {
|
|
7222
|
-
terminalId: msg.terminalId,
|
|
7223
|
-
lines: [],
|
|
7224
|
-
totalLines: 0,
|
|
7225
|
-
requestId: msg.requestId,
|
|
7226
|
-
},
|
|
7227
|
-
});
|
|
7228
|
-
return;
|
|
7229
|
-
}
|
|
7230
|
-
this.ensureTerminalExitSubscription(session);
|
|
7231
|
-
try {
|
|
7232
|
-
const capture = captureTerminalLines(session, {
|
|
7233
|
-
start: msg.start,
|
|
7234
|
-
end: msg.end,
|
|
7235
|
-
stripAnsi: msg.stripAnsi,
|
|
7236
|
-
});
|
|
7237
|
-
this.emit({
|
|
7238
|
-
type: "capture_terminal_response",
|
|
7239
|
-
payload: {
|
|
7240
|
-
terminalId: msg.terminalId,
|
|
7241
|
-
lines: capture.lines,
|
|
7242
|
-
totalLines: capture.totalLines,
|
|
7243
|
-
requestId: msg.requestId,
|
|
7244
|
-
},
|
|
7245
|
-
});
|
|
7246
|
-
}
|
|
7247
|
-
catch (error) {
|
|
7248
|
-
this.sessionLogger.error({ err: error, terminalId: msg.terminalId }, "Failed to capture terminal");
|
|
7249
|
-
this.emit({
|
|
7250
|
-
type: "capture_terminal_response",
|
|
7251
|
-
payload: {
|
|
7252
|
-
terminalId: msg.terminalId,
|
|
7253
|
-
lines: [],
|
|
7254
|
-
totalLines: 0,
|
|
7255
|
-
requestId: msg.requestId,
|
|
7256
|
-
},
|
|
7257
|
-
});
|
|
7258
|
-
}
|
|
7259
|
-
}
|
|
7260
|
-
bindActiveTerminalStream(terminal) {
|
|
7261
|
-
if (!this.onBinaryMessage) {
|
|
7262
|
-
return null;
|
|
7263
|
-
}
|
|
7264
|
-
const existingSlot = this.terminalIdToSlot.get(terminal.id);
|
|
7265
|
-
if (typeof existingSlot === "number") {
|
|
7266
|
-
const existingStream = this.activeTerminalStreams.get(existingSlot);
|
|
7267
|
-
if (existingStream) {
|
|
7268
|
-
existingStream.needsSnapshot = true;
|
|
7269
|
-
return existingSlot;
|
|
7270
|
-
}
|
|
7271
|
-
this.terminalIdToSlot.delete(terminal.id);
|
|
7272
|
-
}
|
|
7273
|
-
const slot = this.allocateTerminalSlot();
|
|
7274
|
-
if (slot === null) {
|
|
7275
|
-
return null;
|
|
7276
|
-
}
|
|
7277
|
-
const activeStream = {
|
|
7278
|
-
terminalId: terminal.id,
|
|
7279
|
-
slot,
|
|
7280
|
-
unsubscribe: () => { },
|
|
7281
|
-
needsSnapshot: true,
|
|
7282
|
-
outputCoalescer: new TerminalOutputCoalescer({
|
|
7283
|
-
timers: { setTimeout, clearTimeout },
|
|
7284
|
-
onFlush: ({ payload }) => {
|
|
7285
|
-
if (this.activeTerminalStreams.get(slot) !== activeStream) {
|
|
7286
|
-
return;
|
|
7287
|
-
}
|
|
7288
|
-
this.emitBinary(encodeTerminalStreamFrame({
|
|
7289
|
-
opcode: TerminalStreamOpcode.Output,
|
|
7290
|
-
slot,
|
|
7291
|
-
payload,
|
|
7292
|
-
}));
|
|
7293
|
-
},
|
|
7294
|
-
}),
|
|
7295
|
-
};
|
|
7296
|
-
this.activeTerminalStreams.set(slot, activeStream);
|
|
7297
|
-
this.terminalIdToSlot.set(terminal.id, slot);
|
|
7298
|
-
activeStream.unsubscribe = terminal.subscribe((message) => {
|
|
7299
|
-
if (this.activeTerminalStreams.get(slot) !== activeStream) {
|
|
7300
|
-
return;
|
|
7301
|
-
}
|
|
7302
|
-
if (message.type === "snapshot") {
|
|
7303
|
-
activeStream.outputCoalescer.flush();
|
|
7304
|
-
activeStream.needsSnapshot = true;
|
|
7305
|
-
this.trySendTerminalSnapshot(activeStream);
|
|
7306
|
-
return;
|
|
7307
|
-
}
|
|
7308
|
-
if (message.type === "titleChange") {
|
|
7309
|
-
return;
|
|
7310
|
-
}
|
|
7311
|
-
if (activeStream.needsSnapshot || message.data.length === 0) {
|
|
7312
|
-
return;
|
|
7313
|
-
}
|
|
7314
|
-
activeStream.outputCoalescer.handle(message.data);
|
|
7315
|
-
});
|
|
7316
|
-
return slot;
|
|
7317
|
-
}
|
|
7318
|
-
trySendTerminalSnapshot(activeStream) {
|
|
7319
|
-
if (this.activeTerminalStreams.get(activeStream.slot) !== activeStream ||
|
|
7320
|
-
!activeStream.needsSnapshot) {
|
|
7321
|
-
return;
|
|
7322
|
-
}
|
|
7323
|
-
const terminal = this.terminalManager?.getTerminal(activeStream.terminalId);
|
|
7324
|
-
if (!terminal) {
|
|
7325
|
-
this.detachTerminalStream(activeStream.terminalId, { emitExit: true });
|
|
7326
|
-
return;
|
|
7327
|
-
}
|
|
7328
|
-
activeStream.outputCoalescer.flush();
|
|
7329
|
-
activeStream.needsSnapshot = false;
|
|
7330
|
-
this.emitBinary(encodeTerminalStreamFrame({
|
|
7331
|
-
opcode: TerminalStreamOpcode.Snapshot,
|
|
7332
|
-
slot: activeStream.slot,
|
|
7333
|
-
payload: encodeTerminalSnapshotPayload(terminal.getState()),
|
|
7334
|
-
}));
|
|
7335
|
-
}
|
|
7336
|
-
allocateTerminalSlot() {
|
|
7337
|
-
for (let attempt = 0; attempt < MAX_TERMINAL_STREAM_SLOTS; attempt += 1) {
|
|
7338
|
-
const slot = (this.nextTerminalSlot + attempt) % MAX_TERMINAL_STREAM_SLOTS;
|
|
7339
|
-
if (this.activeTerminalStreams.has(slot)) {
|
|
7340
|
-
continue;
|
|
7341
|
-
}
|
|
7342
|
-
this.nextTerminalSlot = (slot + 1) % MAX_TERMINAL_STREAM_SLOTS;
|
|
7343
|
-
return slot;
|
|
7344
|
-
}
|
|
7345
|
-
return null;
|
|
7346
|
-
}
|
|
7347
|
-
detachTerminalStream(terminalId, options) {
|
|
7348
|
-
const slot = this.terminalIdToSlot.get(terminalId);
|
|
7349
|
-
if (typeof slot !== "number") {
|
|
7350
|
-
return false;
|
|
7351
|
-
}
|
|
7352
|
-
const activeStream = this.activeTerminalStreams.get(slot);
|
|
7353
|
-
if (!activeStream) {
|
|
7354
|
-
this.terminalIdToSlot.delete(terminalId);
|
|
7355
|
-
return false;
|
|
7356
|
-
}
|
|
7357
|
-
activeStream.outputCoalescer.flush();
|
|
7358
|
-
this.activeTerminalStreams.delete(slot);
|
|
7359
|
-
this.terminalIdToSlot.delete(terminalId);
|
|
7360
|
-
try {
|
|
7361
|
-
activeStream.unsubscribe();
|
|
7362
|
-
}
|
|
7363
|
-
catch (error) {
|
|
7364
|
-
this.sessionLogger.warn({ err: error }, "Failed to unsubscribe terminal stream");
|
|
7365
|
-
}
|
|
7366
|
-
if (options?.emitExit) {
|
|
7367
|
-
this.emit({
|
|
7368
|
-
type: "terminal_stream_exit",
|
|
7369
|
-
payload: {
|
|
7370
|
-
terminalId: activeStream.terminalId,
|
|
7371
|
-
},
|
|
7372
|
-
});
|
|
7373
|
-
}
|
|
7374
|
-
return true;
|
|
7375
|
-
}
|
|
7376
|
-
disposeTerminalSubscriptions() {
|
|
7377
|
-
for (const terminalId of Array.from(this.terminalIdToSlot.keys())) {
|
|
7378
|
-
this.detachTerminalStream(terminalId, { emitExit: false });
|
|
7379
|
-
}
|
|
7380
|
-
}
|
|
7381
6666
|
}
|
|
7382
6667
|
// ---------------------------------------------------------------------------
|
|
7383
6668
|
// Stash handlers
|