pim-agent 0.0.1 → 0.10.0
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/LICENSE +21 -0
- package/README.md +293 -7
- package/bin/pim.ts +151 -0
- package/package.json +82 -4
- package/packages/core/src/attachments/AttachmentStore.ts +99 -0
- package/packages/core/src/attachments/Attachments.ts +69 -0
- package/packages/core/src/extensions/CoreExtensions.ts +54 -0
- package/packages/core/src/extensions/apply-patch/coordinator.ts +53 -0
- package/packages/core/src/extensions/apply-patch/executor.ts +550 -0
- package/packages/core/src/extensions/apply-patch/index.ts +69 -0
- package/packages/core/src/extensions/apply-patch/matcher.ts +47 -0
- package/packages/core/src/extensions/apply-patch/model.ts +39 -0
- package/packages/core/src/extensions/apply-patch/parser.ts +379 -0
- package/packages/core/src/extensions/apply-patch/render.ts +177 -0
- package/packages/core/src/extensions/apply-patch/schema.ts +37 -0
- package/packages/core/src/extensions/apply-patch/types.ts +30 -0
- package/packages/core/src/extensions/bash/capture.ts +86 -0
- package/packages/core/src/extensions/bash/format.ts +110 -0
- package/packages/core/src/extensions/bash/image.ts +31 -0
- package/packages/core/src/extensions/bash/index.ts +88 -0
- package/packages/core/src/extensions/bash/render.ts +31 -0
- package/packages/core/src/extensions/bash/run.ts +221 -0
- package/packages/core/src/extensions/bash/schema.ts +64 -0
- package/packages/core/src/extensions/edit/edit.ts +366 -0
- package/packages/core/src/extensions/edit/index.ts +36 -0
- package/packages/core/src/extensions/edit/render.ts +16 -0
- package/packages/core/src/extensions/edit/schema.ts +37 -0
- package/packages/core/src/extensions/glob/glob.ts +34 -0
- package/packages/core/src/extensions/glob/index.ts +83 -0
- package/packages/core/src/extensions/glob/render.ts +72 -0
- package/packages/core/src/extensions/glob/schema.ts +66 -0
- package/packages/core/src/extensions/grep/grep.ts +256 -0
- package/packages/core/src/extensions/grep/index.ts +114 -0
- package/packages/core/src/extensions/grep/render.ts +271 -0
- package/packages/core/src/extensions/grep/schema.ts +92 -0
- package/packages/core/src/extensions/read/ImageMemory.ts +42 -0
- package/packages/core/src/extensions/read/image.ts +57 -0
- package/packages/core/src/extensions/read/index.ts +88 -0
- package/packages/core/src/extensions/read/read.ts +191 -0
- package/packages/core/src/extensions/read/render.ts +58 -0
- package/packages/core/src/extensions/read/schema.ts +49 -0
- package/packages/core/src/extensions/subagent/index.ts +33 -0
- package/packages/core/src/extensions/subagent/render.ts +127 -0
- package/packages/core/src/extensions/subagent/schema.ts +9 -0
- package/packages/core/src/extensions/subagent/subagent.ts +419 -0
- package/packages/core/src/extensions/system-prompt/index.ts +36 -0
- package/packages/core/src/extensions/system-prompt/prompt.ts +232 -0
- package/packages/core/src/extensions/todo/index.ts +123 -0
- package/packages/core/src/extensions/todo/render.ts +156 -0
- package/packages/core/src/extensions/todo/schema.ts +24 -0
- package/packages/core/src/extensions/todo/todo.ts +185 -0
- package/packages/core/src/extensions/web-fetch/JinaReaderClient.ts +187 -0
- package/packages/core/src/extensions/web-fetch/WebViewFetchClient.ts +183 -0
- package/packages/core/src/extensions/web-fetch/WebViewMarkdownSnapshot.ts +512 -0
- package/packages/core/src/extensions/web-fetch/fetch.ts +265 -0
- package/packages/core/src/extensions/web-fetch/image.ts +165 -0
- package/packages/core/src/extensions/web-fetch/index.ts +83 -0
- package/packages/core/src/extensions/web-fetch/render.ts +100 -0
- package/packages/core/src/extensions/web-fetch/schema.ts +46 -0
- package/packages/core/src/extensions/web-search/ExaMcpClient.ts +251 -0
- package/packages/core/src/extensions/web-search/SearchBreaker.ts +147 -0
- package/packages/core/src/extensions/web-search/SearchChain.ts +77 -0
- package/packages/core/src/extensions/web-search/index.ts +90 -0
- package/packages/core/src/extensions/web-search/providers/DuckDuckGoProvider.ts +195 -0
- package/packages/core/src/extensions/web-search/providers/ExaProvider.ts +54 -0
- package/packages/core/src/extensions/web-search/providers/FirecrawlProvider.ts +144 -0
- package/packages/core/src/extensions/web-search/providers/SearchProvider.ts +118 -0
- package/packages/core/src/extensions/web-search/render.ts +49 -0
- package/packages/core/src/extensions/web-search/schema.ts +29 -0
- package/packages/core/src/extensions/web-search/search.ts +23 -0
- package/packages/core/src/extensions/write/index.ts +58 -0
- package/packages/core/src/extensions/write/render.ts +16 -0
- package/packages/core/src/extensions/write/schema.ts +14 -0
- package/packages/core/src/extensions/write/write.ts +87 -0
- package/packages/core/src/picker/FilePickerSuggestionEngine.ts +14 -0
- package/packages/core/src/picker/InProcessFilePickerSuggestionEngine.ts +52 -0
- package/packages/core/src/picker/PickerItem.ts +6 -0
- package/packages/core/src/picker/PickerService.ts +132 -0
- package/packages/core/src/picker/RemoteFilePickerSuggestionEngine.ts +56 -0
- package/packages/core/src/picker/WorkerFilePickerSuggestionEngine.ts +268 -0
- package/packages/core/src/picker/catalog.ts +220 -0
- package/packages/core/src/picker/commandRanker.ts +17 -0
- package/packages/core/src/picker/filePickerWorker.ts +66 -0
- package/packages/core/src/picker/filePickerWorkerMessages.ts +39 -0
- package/packages/core/src/picker/ranker.ts +245 -0
- package/packages/core/src/picker/token.ts +77 -0
- package/packages/core/src/session/AgentRuntime.ts +85 -0
- package/packages/core/src/session/EventLog.ts +156 -0
- package/packages/core/src/session/MessageText.ts +21 -0
- package/packages/core/src/session/ReadCursors.ts +94 -0
- package/packages/core/src/session/SearchIndex.ts +752 -0
- package/packages/core/src/session/SearchTokens.ts +97 -0
- package/packages/core/src/session/SessionCache.ts +68 -0
- package/packages/core/src/session/SessionDigest.ts +161 -0
- package/packages/core/src/session/SessionHost.ts +892 -0
- package/packages/core/src/session/SessionLease.ts +279 -0
- package/packages/core/src/session/SessionMeta.ts +267 -0
- package/packages/core/src/session/SessionName.ts +18 -0
- package/packages/core/src/session/SessionRegistry.ts +244 -0
- package/packages/core/src/session/WriteMark.ts +51 -0
- package/packages/core/src/shared/Cli.ts +25 -0
- package/packages/core/src/shared/DaemonUnit.ts +13 -0
- package/packages/core/src/shared/DiffLines.ts +304 -0
- package/packages/core/src/shared/DiffPatch.ts +70 -0
- package/packages/core/src/shared/DiffRenderer.ts +283 -0
- package/packages/core/src/shared/DiffView.ts +29 -0
- package/packages/core/src/shared/Directories.ts +61 -0
- package/packages/core/src/shared/EditMatcher.ts +767 -0
- package/packages/core/src/shared/Errors.ts +9 -0
- package/packages/core/src/shared/ExtensionToggles.ts +130 -0
- package/packages/core/src/shared/FileEnumerator.ts +361 -0
- package/packages/core/src/shared/FileScanner.ts +57 -0
- package/packages/core/src/shared/FileWatch.ts +121 -0
- package/packages/core/src/shared/Format.ts +74 -0
- package/packages/core/src/shared/Fs.ts +91 -0
- package/packages/core/src/shared/FsErrors.ts +67 -0
- package/packages/core/src/shared/FuzzyMatcher.ts +71 -0
- package/packages/core/src/shared/Git.ts +530 -0
- package/packages/core/src/shared/GitMonitor.ts +221 -0
- package/packages/core/src/shared/GlobExclusions.ts +13 -0
- package/packages/core/src/shared/Http.ts +14 -0
- package/packages/core/src/shared/ImageMime.ts +36 -0
- package/packages/core/src/shared/Images.ts +357 -0
- package/packages/core/src/shared/Json.ts +19 -0
- package/packages/core/src/shared/Languages.ts +95 -0
- package/packages/core/src/shared/Levenshtein.ts +83 -0
- package/packages/core/src/shared/Lines.ts +79 -0
- package/packages/core/src/shared/McpClient.ts +444 -0
- package/packages/core/src/shared/OutputBudget.ts +84 -0
- package/packages/core/src/shared/PatchSummary.ts +40 -0
- package/packages/core/src/shared/Paths.ts +80 -0
- package/packages/core/src/shared/PimSettings.ts +128 -0
- package/packages/core/src/shared/PimVersion.ts +93 -0
- package/packages/core/src/shared/Pool.ts +26 -0
- package/packages/core/src/shared/Proc.ts +50 -0
- package/packages/core/src/shared/RateLimiter.ts +50 -0
- package/packages/core/src/shared/Renderer.ts +371 -0
- package/packages/core/src/shared/RepoDiff.ts +741 -0
- package/packages/core/src/shared/SafePath.ts +17 -0
- package/packages/core/src/shared/SearchRender.ts +82 -0
- package/packages/core/src/shared/SendFile.ts +23 -0
- package/packages/core/src/shared/SpillCache.ts +78 -0
- package/packages/core/src/shared/SubagentLogs.ts +102 -0
- package/packages/core/src/shared/Supervisor.ts +460 -0
- package/packages/core/src/shared/Surface.ts +2 -0
- package/packages/core/src/shared/Sweeper.ts +32 -0
- package/packages/core/src/shared/Tools.ts +801 -0
- package/packages/core/src/shared/Updater.ts +209 -0
- package/packages/core/src/view/AnsiPainter.ts +227 -0
- package/packages/core/src/view/BodyRenderer.ts +102 -0
- package/packages/core/src/view/DiffBlocks.ts +52 -0
- package/packages/core/src/view/DiffExpand.ts +214 -0
- package/packages/core/src/view/DiffLayout.ts +69 -0
- package/packages/core/src/view/DiffPairs.ts +47 -0
- package/packages/core/src/view/MarkdownPainter.ts +314 -0
- package/packages/core/src/view/MovePath.ts +70 -0
- package/packages/core/src/view/Painting.ts +182 -0
- package/packages/core/src/view/ViewBlock.ts +124 -0
- package/packages/daemon/src/Daemon.ts +63 -0
- package/packages/daemon/src/DaemonInstall.ts +115 -0
- package/packages/daemon/src/Surfaces.ts +66 -0
- package/packages/daemon/src/TelegramSurface.ts +30 -0
- package/packages/daemon/src/WebSurface.ts +56 -0
- package/packages/daemon/src/index.ts +37 -0
- package/packages/protocol/src/Command.ts +219 -0
- package/packages/protocol/src/Diff.ts +15 -0
- package/packages/protocol/src/ImageRoute.ts +23 -0
- package/packages/protocol/src/ServerEvent.ts +406 -0
- package/packages/server/src/AttachmentEndpoint.ts +184 -0
- package/packages/server/src/ClientConnection.ts +277 -0
- package/packages/server/src/ImageEndpoint.ts +36 -0
- package/packages/server/src/ProbeClient.ts +374 -0
- package/packages/server/src/Reloader.ts +93 -0
- package/packages/server/src/SendFileTool.ts +88 -0
- package/packages/server/src/SessionCatalogue.ts +456 -0
- package/packages/server/src/SessionProjection.ts +194 -0
- package/packages/server/src/SessionStream.ts +627 -0
- package/packages/server/src/StaticClient.ts +178 -0
- package/packages/server/src/WebOptions.ts +72 -0
- package/packages/server/src/WsGateway.ts +798 -0
- package/packages/server/src/probe.ts +200 -0
- package/packages/telegram/src/Bot.ts +229 -0
- package/packages/telegram/src/Commands.ts +768 -0
- package/packages/telegram/src/Config.ts +148 -0
- package/packages/telegram/src/Markdown.ts +235 -0
- package/packages/telegram/src/Message.ts +152 -0
- package/packages/telegram/src/Renderer.ts +456 -0
- package/packages/telegram/src/SendFileSchema.ts +18 -0
- package/packages/telegram/src/SendFileTool.ts +65 -0
- package/packages/telegram/src/Session.ts +253 -0
- package/packages/telegram/src/SessionRegistry.ts +126 -0
- package/packages/telegram/src/TaskScheduler.ts +296 -0
- package/packages/telegram/src/TaskSchema.ts +88 -0
- package/packages/telegram/src/TaskStore.ts +67 -0
- package/packages/telegram/src/TaskTool.ts +208 -0
- package/packages/telegram/src/TelegramHtml.ts +288 -0
- package/packages/telegram/src/TypingIndicator.ts +43 -0
- package/packages/telegram/src/UpdateConfirm.ts +61 -0
- package/packages/tui/src/extensions/_init/index.ts +144 -0
- package/packages/tui/src/extensions/command-picker/index.ts +75 -0
- package/packages/tui/src/extensions/file-picker/index.ts +156 -0
- package/packages/tui/src/extensions/footer/index.ts +93 -0
- package/packages/tui/src/extensions/footer/powerline.ts +87 -0
- package/packages/tui/src/extensions/footer/segments.ts +202 -0
- package/packages/tui/src/extensions/pim/index.ts +89 -0
- package/packages/tui/src/extensions/session-lease/index.ts +253 -0
- package/packages/tui/src/extensions/tps/index.ts +118 -0
- package/packages/tui/src/extensions/working-indicator/index.ts +75 -0
- package/packages/tui/src/extensions/wrapProvider.ts +18 -0
- package/packages/tui/src/themes/pim-dark.json +84 -0
- package/packages/tui/src/themes/pim-light.json +84 -0
- package/packages/tui/src/themes/themeCliArgs.ts +20 -0
- package/packages/web/dist/client/apple-touch-icon.png +0 -0
- package/packages/web/dist/client/assets/bash-LmRjUKlR.js +1 -0
- package/packages/web/dist/client/assets/c-DBVbJFpC.js +1 -0
- package/packages/web/dist/client/assets/clojure-CR7dvLJ4.js +1 -0
- package/packages/web/dist/client/assets/cmake-CgP6gB4W.js +1 -0
- package/packages/web/dist/client/assets/commit-mono-latin-300-italic-B48RJMeK.woff +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-300-italic-DBq5bJCE.woff2 +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-300-normal-B-iV2FbL.woff2 +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-300-normal-kM0OTZCv.woff +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-400-italic-BXinMwCi.woff +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-400-italic-DjSHLl2N.woff2 +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-400-normal-s0S3qwFW.woff +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-400-normal-wzhe4RuD.woff2 +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-600-normal-BuQDXT0M.woff +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-600-normal-SwgSWHaV.woff2 +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-700-normal-1-GU0IUE.woff +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-700-normal-DU8mrtj2.woff2 +0 -0
- package/packages/web/dist/client/assets/core-CA0aSzPu.js +3 -0
- package/packages/web/dist/client/assets/cpp-N2d8M7Nc.js +1 -0
- package/packages/web/dist/client/assets/csharp-yYt0Ikks.js +1 -0
- package/packages/web/dist/client/assets/css-D8M8GYaw.js +1 -0
- package/packages/web/dist/client/assets/diff-BhdZizse.js +1 -0
- package/packages/web/dist/client/assets/dockerfile-C0zufGsU.js +1 -0
- package/packages/web/dist/client/assets/elixir-DXIHlGsR.js +1 -0
- package/packages/web/dist/client/assets/erlang-BjYI8dhz.js +1 -0
- package/packages/web/dist/client/assets/go-CYstVVJV.js +1 -0
- package/packages/web/dist/client/assets/graphql-BHHhacaR.js +1 -0
- package/packages/web/dist/client/assets/haskell-DUkhXIda.js +1 -0
- package/packages/web/dist/client/assets/index-B9be-i9d.css +1 -0
- package/packages/web/dist/client/assets/index-CpinM5NK.js +45 -0
- package/packages/web/dist/client/assets/ini-B1CroMsf.js +1 -0
- package/packages/web/dist/client/assets/java-Dde-Wnb4.js +1 -0
- package/packages/web/dist/client/assets/javascript-8AmHuH5O.js +1 -0
- package/packages/web/dist/client/assets/json-PmyKWHmm.js +1 -0
- package/packages/web/dist/client/assets/kotlin-DgSK8bu-.js +2 -0
- package/packages/web/dist/client/assets/less-Bp0mgmpG.js +1 -0
- package/packages/web/dist/client/assets/lua-1dXYaL-H.js +1 -0
- package/packages/web/dist/client/assets/makefile-BX-o3EIz.js +1 -0
- package/packages/web/dist/client/assets/markdown-D6FrnlqJ.js +1 -0
- package/packages/web/dist/client/assets/ocaml-B-P1Uyq8.js +1 -0
- package/packages/web/dist/client/assets/perl-CvqMZYuf.js +1 -0
- package/packages/web/dist/client/assets/php-DuZl5e-L.js +2 -0
- package/packages/web/dist/client/assets/powershell-BUe16zE4.js +1 -0
- package/packages/web/dist/client/assets/protobuf-DGOW1qT8.js +1 -0
- package/packages/web/dist/client/assets/python-gHmu6VPG.js +1 -0
- package/packages/web/dist/client/assets/r-DNWPCnjT.js +1 -0
- package/packages/web/dist/client/assets/ruby-BGpZ3p4_.js +1 -0
- package/packages/web/dist/client/assets/rust-BIKPVoi6.js +1 -0
- package/packages/web/dist/client/assets/scala-C9lP4FWY.js +1 -0
- package/packages/web/dist/client/assets/scss-gOyOKiXd.js +1 -0
- package/packages/web/dist/client/assets/sql-DWRKyvJ-.js +1 -0
- package/packages/web/dist/client/assets/swift-BTn9__hL.js +1 -0
- package/packages/web/dist/client/assets/typescript-CxkPLmSc.js +1 -0
- package/packages/web/dist/client/assets/vim-Dhhcn_CA.js +1 -0
- package/packages/web/dist/client/assets/xml-COl5yFSC.js +1 -0
- package/packages/web/dist/client/assets/yaml-DjOhhVLG.js +1 -0
- package/packages/web/dist/client/favicon.svg +1 -0
- package/packages/web/dist/client/icon-192.png +0 -0
- package/packages/web/dist/client/icon-512.png +0 -0
- package/packages/web/dist/client/icon-maskable.png +0 -0
- package/packages/web/dist/client/index.html +24 -0
- package/packages/web/dist/client/manifest.webmanifest +21 -0
- package/packages/web/dist/client/wordmark.svg +1 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import type { DiffBase, LineSpan } from "./Diff";
|
|
2
|
+
|
|
3
|
+
/** A file already uploaded via `POST /upload`, by the id that endpoint answered with; never a client-local path. */
|
|
4
|
+
export type AttachmentRef = {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/** Client → server. Every command carries an `id` so its response correlates. */
|
|
9
|
+
export type Command =
|
|
10
|
+
/** Opening frame of every connection; omit `sessionId` to start a new session in `cwd`. */
|
|
11
|
+
| {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly type: "attach";
|
|
14
|
+
readonly sessionId?: string;
|
|
15
|
+
readonly cwd?: string;
|
|
16
|
+
/** Copy model, thinking level and cwd from this session; ignored when `sessionId` is set or the session is not held open. */
|
|
17
|
+
readonly like?: string;
|
|
18
|
+
readonly fromSeq: number;
|
|
19
|
+
/** Whether the client is looking at this session right now; absent means yes. */
|
|
20
|
+
readonly attentive?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** Whether this connection's reader is present: an inattentive one never consumes a turn as read. */
|
|
23
|
+
| { readonly id: string; readonly type: "attention"; readonly value: boolean }
|
|
24
|
+
/** Sent into a running turn it steers that turn, landing before the next model call. */
|
|
25
|
+
| {
|
|
26
|
+
readonly id: string;
|
|
27
|
+
readonly type: "user_message";
|
|
28
|
+
readonly sessionId: string;
|
|
29
|
+
readonly text: string;
|
|
30
|
+
readonly attachments?: readonly AttachmentRef[];
|
|
31
|
+
}
|
|
32
|
+
| { readonly id: string; readonly type: "cancel"; readonly sessionId: string }
|
|
33
|
+
/** Take back the queued messages without stopping the turn; they come back on `restored`. */
|
|
34
|
+
| {
|
|
35
|
+
readonly id: string;
|
|
36
|
+
readonly type: "dequeue";
|
|
37
|
+
readonly sessionId: string;
|
|
38
|
+
}
|
|
39
|
+
| {
|
|
40
|
+
readonly id: string;
|
|
41
|
+
readonly type: "pick_files";
|
|
42
|
+
readonly sessionId: string;
|
|
43
|
+
readonly query: string;
|
|
44
|
+
readonly limit: number;
|
|
45
|
+
}
|
|
46
|
+
| {
|
|
47
|
+
readonly id: string;
|
|
48
|
+
readonly type: "pick_commands";
|
|
49
|
+
readonly sessionId: string;
|
|
50
|
+
readonly query: string;
|
|
51
|
+
readonly limit?: number;
|
|
52
|
+
}
|
|
53
|
+
| {
|
|
54
|
+
readonly id: string;
|
|
55
|
+
readonly type: "set_cwd" | "set_model" | "set_thinking";
|
|
56
|
+
readonly sessionId: string;
|
|
57
|
+
readonly value: string;
|
|
58
|
+
}
|
|
59
|
+
/** The session catalogue; answers before any `attach`. */
|
|
60
|
+
| {
|
|
61
|
+
readonly id: string;
|
|
62
|
+
readonly type: "list_sessions";
|
|
63
|
+
/** Restrict to one working directory; omit for every session on disk. */
|
|
64
|
+
readonly cwd?: string;
|
|
65
|
+
readonly limit?: number;
|
|
66
|
+
/** Keep at most this many sessions per working directory, so one busy project cannot fill the page. */
|
|
67
|
+
readonly perProject?: number;
|
|
68
|
+
/** List the archived sessions instead of the live ones. */
|
|
69
|
+
readonly archived?: boolean;
|
|
70
|
+
}
|
|
71
|
+
/** Ranked search over every session on disk, titles and what was said; an empty `query` warms the index and answers with no hits. */
|
|
72
|
+
| {
|
|
73
|
+
readonly id: string;
|
|
74
|
+
readonly type: "search_sessions";
|
|
75
|
+
readonly query: string;
|
|
76
|
+
readonly limit?: number;
|
|
77
|
+
/** Restrict to one working directory; omit for every session on disk. */
|
|
78
|
+
readonly cwd?: string;
|
|
79
|
+
/** Omitted, the archived are searched too and their hits say so; `false` leaves them out, `true` searches only them. */
|
|
80
|
+
readonly archived?: boolean;
|
|
81
|
+
}
|
|
82
|
+
/** Names a session through pi's own `session_info`, so its terminal picker shows the name too; `null` clears it. */
|
|
83
|
+
| {
|
|
84
|
+
readonly id: string;
|
|
85
|
+
readonly type: "set_session_name";
|
|
86
|
+
readonly sessionId: string;
|
|
87
|
+
readonly value: string | null;
|
|
88
|
+
}
|
|
89
|
+
/** pim's own overrides on a session: out of the default listing, or held unread until it is answered. */
|
|
90
|
+
| {
|
|
91
|
+
readonly id: string;
|
|
92
|
+
readonly type: "set_session_archived" | "set_session_unread";
|
|
93
|
+
readonly sessionId: string;
|
|
94
|
+
readonly value: boolean;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* pim's own overrides on a working directory rather than a session: pinned
|
|
98
|
+
* sorts it above every other, expanded stands its sidebar group unfolded.
|
|
99
|
+
*/
|
|
100
|
+
| {
|
|
101
|
+
readonly id: string;
|
|
102
|
+
readonly type: "set_project_pinned" | "set_project_expanded";
|
|
103
|
+
readonly cwd: string;
|
|
104
|
+
readonly value: boolean;
|
|
105
|
+
}
|
|
106
|
+
/** Names a working directory for the listings; `null` puts it back to its base name, and the directory is never renamed. */
|
|
107
|
+
| {
|
|
108
|
+
readonly id: string;
|
|
109
|
+
readonly type: "set_project_label";
|
|
110
|
+
readonly cwd: string;
|
|
111
|
+
readonly value: string | null;
|
|
112
|
+
}
|
|
113
|
+
/** Re-orders the pinned projects. The whole order, never a move: two surfaces settle on the last one sent. */
|
|
114
|
+
| {
|
|
115
|
+
readonly id: string;
|
|
116
|
+
readonly type: "set_pin_order";
|
|
117
|
+
readonly order: readonly string[];
|
|
118
|
+
}
|
|
119
|
+
/** The models this server can switch to, plus the current model's thinking levels; answers without a session. */
|
|
120
|
+
| { readonly id: string; readonly type: "list_models" }
|
|
121
|
+
/** Subdirectories of `path` on the server's filesystem; errors rather than answering empty when it is not a readable directory. */
|
|
122
|
+
| { readonly id: string; readonly type: "list_dirs"; readonly path: string }
|
|
123
|
+
/** Re-read the cwd's git state now; `fetch` asks the remote first, which is the only thing that moves ahead and behind. */
|
|
124
|
+
| {
|
|
125
|
+
readonly id: string;
|
|
126
|
+
readonly type: "refresh_git";
|
|
127
|
+
readonly sessionId: string;
|
|
128
|
+
readonly fetch?: boolean;
|
|
129
|
+
}
|
|
130
|
+
/** The cwd's local branches, trunk first and the rest by how recently they were worked on. */
|
|
131
|
+
| {
|
|
132
|
+
readonly id: string;
|
|
133
|
+
readonly type: "list_branches";
|
|
134
|
+
readonly sessionId: string;
|
|
135
|
+
}
|
|
136
|
+
/** Every changed file of one diff base, without a hunk of any of them; read-only, so never refused. */
|
|
137
|
+
| {
|
|
138
|
+
readonly id: string;
|
|
139
|
+
readonly type: "list_changes";
|
|
140
|
+
readonly sessionId: string;
|
|
141
|
+
readonly base: DiffBase;
|
|
142
|
+
}
|
|
143
|
+
/** One file's hunks, computed only once a reader expands it; `context` defaults to 3. */
|
|
144
|
+
| {
|
|
145
|
+
readonly id: string;
|
|
146
|
+
readonly type: "file_diff";
|
|
147
|
+
readonly sessionId: string;
|
|
148
|
+
readonly base: DiffBase;
|
|
149
|
+
readonly path: string;
|
|
150
|
+
readonly context?: number;
|
|
151
|
+
}
|
|
152
|
+
/** The file's own lines behind a gap between hunks, asked for when a reader opens one. */
|
|
153
|
+
| {
|
|
154
|
+
readonly id: string;
|
|
155
|
+
readonly type: "read_lines";
|
|
156
|
+
readonly sessionId: string;
|
|
157
|
+
readonly base: DiffBase;
|
|
158
|
+
readonly path: string;
|
|
159
|
+
readonly spans: readonly LineSpan[];
|
|
160
|
+
}
|
|
161
|
+
/** Refused while any session in the same directory is mid-turn: the agent may be halfway through an edit. */
|
|
162
|
+
| {
|
|
163
|
+
readonly id: string;
|
|
164
|
+
readonly type: "checkout";
|
|
165
|
+
readonly sessionId: string;
|
|
166
|
+
readonly branch: string;
|
|
167
|
+
}
|
|
168
|
+
/** `pull` is fast-forward only; `push` adopts an upstream the first time a branch is published. */
|
|
169
|
+
| {
|
|
170
|
+
readonly id: string;
|
|
171
|
+
readonly type: "pull" | "push";
|
|
172
|
+
readonly sessionId: string;
|
|
173
|
+
}
|
|
174
|
+
/** Path-limited: exactly `paths` are staged and committed, everything else changed stays dirty. Refused mid-turn, like `checkout`. */
|
|
175
|
+
| {
|
|
176
|
+
readonly id: string;
|
|
177
|
+
readonly type: "commit";
|
|
178
|
+
readonly sessionId: string;
|
|
179
|
+
readonly message: string;
|
|
180
|
+
/** A renamed file contributes both of its names, or its old one is left behind. */
|
|
181
|
+
readonly paths: readonly string[];
|
|
182
|
+
}
|
|
183
|
+
/** Read-only view of a subagent's transcript; `callId` is the parent's tool call and `sessionId` must be this connection's session. */
|
|
184
|
+
| {
|
|
185
|
+
readonly id: string;
|
|
186
|
+
readonly type: "watch_subagent";
|
|
187
|
+
readonly sessionId: string;
|
|
188
|
+
readonly callId: string;
|
|
189
|
+
readonly fromSeq: number;
|
|
190
|
+
}
|
|
191
|
+
/** Stop watching; succeeds whether or not this connection holds the watch. */
|
|
192
|
+
| {
|
|
193
|
+
readonly id: string;
|
|
194
|
+
readonly type: "unwatch_subagent";
|
|
195
|
+
readonly callId: string;
|
|
196
|
+
}
|
|
197
|
+
/** Update this install and restart it unconditionally; refused while any session is mid-turn unless `force`. */
|
|
198
|
+
| { readonly id: string; readonly type: "reload"; readonly force?: boolean };
|
|
199
|
+
|
|
200
|
+
export type CommandType = Command["type"];
|
|
201
|
+
|
|
202
|
+
/** A command before the transport stamps its correlation id. */
|
|
203
|
+
export type CommandDraft = Command extends infer T
|
|
204
|
+
? T extends Command
|
|
205
|
+
? Omit<T, "id">
|
|
206
|
+
: never
|
|
207
|
+
: never;
|
|
208
|
+
|
|
209
|
+
/** Which sessions a listing is asking for: the `list_sessions` payload, named so a client can pass it around. */
|
|
210
|
+
export type SessionScope = Omit<
|
|
211
|
+
Extract<CommandDraft, { readonly type: "list_sessions" }>,
|
|
212
|
+
"type"
|
|
213
|
+
>;
|
|
214
|
+
|
|
215
|
+
/** Which sessions a search may reach: the `search_sessions` payload without the query it is asked with. */
|
|
216
|
+
export type SearchScope = Omit<
|
|
217
|
+
Extract<CommandDraft, { readonly type: "search_sessions" }>,
|
|
218
|
+
"type" | "query"
|
|
219
|
+
>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The diff wire shape. Declared beside the git that produces it, the way
|
|
3
|
+
* `GitBranch` and `ToolView` are, and named here so the two commands that
|
|
4
|
+
* carry it read as protocol.
|
|
5
|
+
*/
|
|
6
|
+
export type {
|
|
7
|
+
ChangeList,
|
|
8
|
+
ChangeStatus,
|
|
9
|
+
ChangeSummary,
|
|
10
|
+
DiffBase,
|
|
11
|
+
FileLines,
|
|
12
|
+
FileDiff,
|
|
13
|
+
LineRun,
|
|
14
|
+
LineSpan,
|
|
15
|
+
} from "#core/shared/RepoDiff";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const PREFIX = "/image/";
|
|
2
|
+
|
|
3
|
+
/** Only the names a tool writes: a hex digest and one of the formats a provider takes. */
|
|
4
|
+
const NAME = /^[0-9a-f]{64}\.(?:png|jpg|gif|webp)$/;
|
|
5
|
+
|
|
6
|
+
/** Server-relative address of a picture a tool view names, the way an attachment's `url` is. */
|
|
7
|
+
function url(sha256: string, extension: string): string {
|
|
8
|
+
return `${PREFIX}${sha256}.${extension}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Whether the gateway should route this path to its picture handler. */
|
|
12
|
+
function owns(pathname: string): boolean {
|
|
13
|
+
return pathname.startsWith(PREFIX);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** The `<sha256>.<ext>` a request addresses, or null when it is not a name we ever served. */
|
|
17
|
+
function nameOf(pathname: string): string | null {
|
|
18
|
+
const name = pathname.slice(PREFIX.length);
|
|
19
|
+
return owns(pathname) && NAME.test(name) ? name : null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** `GET /image/<sha256>.<ext>`: the one spelling the server answers and the client asks for. */
|
|
23
|
+
export const ImageRoute = { url, owns, nameOf };
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import type { DirectoryListing } from "#core/shared/Directories";
|
|
2
|
+
import type { CommitResult, GitBranch } from "#core/shared/Git";
|
|
3
|
+
import type { PickerItem } from "#core/picker/PickerItem";
|
|
4
|
+
import type { SearchRange, SearchSnippet } from "#core/session/SearchIndex";
|
|
5
|
+
import type { LeaseFrontend } from "#core/session/SessionLease";
|
|
6
|
+
import type { UpdateSkip } from "#core/shared/Updater";
|
|
7
|
+
import type { NoticeSeverity, ToolView } from "#core/view/ViewBlock";
|
|
8
|
+
import type { ChangeList, FileDiff, FileLines } from "./Diff";
|
|
9
|
+
|
|
10
|
+
export type SessionStatus = "idle" | "thinking" | "streaming" | "tool";
|
|
11
|
+
|
|
12
|
+
export type TurnStats = {
|
|
13
|
+
readonly inputTokens: number;
|
|
14
|
+
readonly outputTokens: number;
|
|
15
|
+
readonly costUsd: number;
|
|
16
|
+
readonly durationMs: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type ToolCallView = {
|
|
20
|
+
readonly callId: string;
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly view: ToolView;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** A file a user message carried; `url` is server-relative. */
|
|
26
|
+
export type AttachmentView = {
|
|
27
|
+
/** What to call it on screen; never a path. */
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly url: string;
|
|
30
|
+
readonly isImage: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** Projected from pi's session JSONL; `seq` is the line ordinal, so one line must emit at most one durable event and a client resumes at `seq > n`. */
|
|
34
|
+
export type DurableEvent =
|
|
35
|
+
| {
|
|
36
|
+
readonly seq: number;
|
|
37
|
+
readonly type: "message";
|
|
38
|
+
readonly messageId: string;
|
|
39
|
+
readonly role: "user" | "assistant";
|
|
40
|
+
readonly text: string;
|
|
41
|
+
/** When pi appended the entry, in epoch ms. */
|
|
42
|
+
readonly timestamp: number;
|
|
43
|
+
readonly thinking?: string;
|
|
44
|
+
/** Only ever set on a user message, which may carry nothing else. */
|
|
45
|
+
readonly attachments?: readonly AttachmentView[];
|
|
46
|
+
readonly toolCalls?: readonly ToolCallView[];
|
|
47
|
+
/** The model call failed, with what it said; only ever set on an assistant message. */
|
|
48
|
+
readonly error?: string;
|
|
49
|
+
}
|
|
50
|
+
| {
|
|
51
|
+
readonly seq: number;
|
|
52
|
+
readonly type: "tool_result";
|
|
53
|
+
readonly callId: string;
|
|
54
|
+
readonly name: string;
|
|
55
|
+
readonly view: ToolView;
|
|
56
|
+
readonly isError: boolean;
|
|
57
|
+
}
|
|
58
|
+
| {
|
|
59
|
+
readonly seq: number;
|
|
60
|
+
readonly type: "notice";
|
|
61
|
+
readonly severity: NoticeSeverity;
|
|
62
|
+
readonly text: string;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/** Progress of the one `reload` this server can have in flight. */
|
|
66
|
+
export type UpdateStateEvent =
|
|
67
|
+
| {
|
|
68
|
+
readonly type: "update_state";
|
|
69
|
+
readonly phase: "step";
|
|
70
|
+
readonly label: string;
|
|
71
|
+
}
|
|
72
|
+
| {
|
|
73
|
+
readonly type: "update_state";
|
|
74
|
+
readonly phase: "restarting";
|
|
75
|
+
readonly from: string;
|
|
76
|
+
readonly to: string;
|
|
77
|
+
readonly skipped: readonly UpdateSkip[];
|
|
78
|
+
}
|
|
79
|
+
/** New code is on disk but nothing will restart this process: no supervisor owns it. */
|
|
80
|
+
| {
|
|
81
|
+
readonly type: "update_state";
|
|
82
|
+
readonly phase: "stranded";
|
|
83
|
+
readonly from: string;
|
|
84
|
+
readonly to: string;
|
|
85
|
+
readonly skipped: readonly UpdateSkip[];
|
|
86
|
+
}
|
|
87
|
+
| {
|
|
88
|
+
readonly type: "update_state";
|
|
89
|
+
readonly phase: "failed";
|
|
90
|
+
readonly error: string;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/** Unsequenced live state, never replayed by ordinal; live `tool_call`s reappear in a durable message's `toolCalls`, so dedupe on `callId`. */
|
|
94
|
+
export type EphemeralEvent =
|
|
95
|
+
| {
|
|
96
|
+
readonly type: "attached";
|
|
97
|
+
readonly sessionId: string;
|
|
98
|
+
readonly cwd: string;
|
|
99
|
+
/** Highest durable `seq` at attach time; replay follows immediately. */
|
|
100
|
+
readonly head: number;
|
|
101
|
+
/** The server's build; a client built from another one is stale and should reload. */
|
|
102
|
+
readonly pimVersion: string;
|
|
103
|
+
readonly piVersion: string;
|
|
104
|
+
}
|
|
105
|
+
/** Several events in one frame; apply them in order, each as if it had arrived alone. */
|
|
106
|
+
| { readonly type: "replay"; readonly events: readonly StreamEvent[] }
|
|
107
|
+
| {
|
|
108
|
+
readonly type: "message_start";
|
|
109
|
+
readonly role: "assistant";
|
|
110
|
+
readonly messageId: string;
|
|
111
|
+
}
|
|
112
|
+
/** Drop the live message with this id; the durable `message` superseding it was sent immediately before. */
|
|
113
|
+
| { readonly type: "message_retire"; readonly messageId: string }
|
|
114
|
+
| {
|
|
115
|
+
readonly type: "text_delta";
|
|
116
|
+
readonly messageId: string;
|
|
117
|
+
readonly delta: string;
|
|
118
|
+
}
|
|
119
|
+
| {
|
|
120
|
+
readonly type: "thinking_delta";
|
|
121
|
+
readonly messageId: string;
|
|
122
|
+
readonly delta: string;
|
|
123
|
+
}
|
|
124
|
+
| {
|
|
125
|
+
readonly type: "tool_call";
|
|
126
|
+
readonly callId: string;
|
|
127
|
+
readonly name: string;
|
|
128
|
+
readonly messageId: string;
|
|
129
|
+
readonly view: ToolView;
|
|
130
|
+
}
|
|
131
|
+
| {
|
|
132
|
+
readonly type: "tool_update";
|
|
133
|
+
readonly callId: string;
|
|
134
|
+
readonly view: ToolView;
|
|
135
|
+
}
|
|
136
|
+
/** Settled view until pi appends the `tool_result` for the same `callId`, which supersedes it. */
|
|
137
|
+
| {
|
|
138
|
+
readonly type: "tool_end";
|
|
139
|
+
readonly callId: string;
|
|
140
|
+
readonly view: ToolView;
|
|
141
|
+
readonly isError: boolean;
|
|
142
|
+
}
|
|
143
|
+
/** Every picker answer a client holds for this cwd is stale; drop the cache and re-query. */
|
|
144
|
+
| {
|
|
145
|
+
readonly type: "picker_invalidate";
|
|
146
|
+
readonly scope: "files" | "commands" | "all";
|
|
147
|
+
readonly cwd: string;
|
|
148
|
+
}
|
|
149
|
+
/** A child's events, applied against the child's transcript; sent only to the connection that asked, and never resumed. */
|
|
150
|
+
| {
|
|
151
|
+
readonly type: "subagent_events";
|
|
152
|
+
/** The parent tool call the watch was opened on. */
|
|
153
|
+
readonly callId: string;
|
|
154
|
+
readonly events: readonly StreamEvent[];
|
|
155
|
+
}
|
|
156
|
+
| { readonly type: "turn_end"; readonly stats: TurnStats }
|
|
157
|
+
/** Sent to every connection, not just those attached; only sessions this server holds open are reported. */
|
|
158
|
+
| {
|
|
159
|
+
readonly type: "session_activity";
|
|
160
|
+
readonly sessionId: string;
|
|
161
|
+
readonly status: SessionStatus;
|
|
162
|
+
}
|
|
163
|
+
/** Sent to every connection; the read cursor is one per session, not one per client. */
|
|
164
|
+
| { readonly type: "session_read"; readonly sessionId: string }
|
|
165
|
+
/** Sent to every connection: one session's name or overrides changed, so a listing a client holds can be patched in place. */
|
|
166
|
+
| {
|
|
167
|
+
readonly type: "session_meta";
|
|
168
|
+
readonly sessionId: string;
|
|
169
|
+
/** The session's own name, `null` once it is cleared. */
|
|
170
|
+
readonly name?: string | null;
|
|
171
|
+
readonly archived?: boolean;
|
|
172
|
+
readonly unread?: boolean;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Sent to every connection; keyed by absolute working directory, not by
|
|
176
|
+
* session. A patch like `session_meta`: only what changed is said, so a
|
|
177
|
+
* fold carries no claim about the pin beside it.
|
|
178
|
+
*/
|
|
179
|
+
| {
|
|
180
|
+
readonly type: "project_meta";
|
|
181
|
+
readonly cwd: string;
|
|
182
|
+
readonly pinned?: boolean;
|
|
183
|
+
readonly expanded?: boolean;
|
|
184
|
+
/** What the project is called in a listing, `null` once it is cleared. */
|
|
185
|
+
readonly label?: string | null;
|
|
186
|
+
}
|
|
187
|
+
/** Sent to every connection: the pinned projects, in the order they are shown. */
|
|
188
|
+
| { readonly type: "pins_changed"; readonly order: readonly string[] }
|
|
189
|
+
/** Sent to every connection: the sessions on disk changed, so any listing a client holds is stale. */
|
|
190
|
+
| { readonly type: "sessions_changed" }
|
|
191
|
+
/** Sent to every connection; the restart it ends in drops every socket. */
|
|
192
|
+
| UpdateStateEvent
|
|
193
|
+
| {
|
|
194
|
+
readonly type: "session_state";
|
|
195
|
+
readonly cwd: string;
|
|
196
|
+
readonly model: string;
|
|
197
|
+
/** Display name for `model`; absent until one resolves. */
|
|
198
|
+
readonly modelLabel?: string;
|
|
199
|
+
readonly thinking: string;
|
|
200
|
+
readonly cost: number;
|
|
201
|
+
readonly status: SessionStatus;
|
|
202
|
+
/** False while another process holds this session's turn lease. */
|
|
203
|
+
readonly writable: boolean;
|
|
204
|
+
/** A session in the same working directory is mid-turn, so nothing may move the repository under it. */
|
|
205
|
+
readonly repoBusy?: boolean;
|
|
206
|
+
/** Who holds it; absent when nothing does, or when their record is torn. */
|
|
207
|
+
readonly heldBy?: {
|
|
208
|
+
readonly frontend: LeaseFrontend;
|
|
209
|
+
readonly pid: number;
|
|
210
|
+
};
|
|
211
|
+
readonly tps?: number;
|
|
212
|
+
/** Elapsed run time of the turn in flight, by the server's clock; absent when idle. */
|
|
213
|
+
readonly turnElapsedMs?: number;
|
|
214
|
+
/** Context filled, 0–100. Absent until a turn has reported usage. */
|
|
215
|
+
readonly contextPercent?: number;
|
|
216
|
+
readonly contextWindow?: number;
|
|
217
|
+
/** The cwd's git branch, absent outside a repository. */
|
|
218
|
+
readonly branch?: string;
|
|
219
|
+
/** Paths git reports as changed; zero is a clean tree. */
|
|
220
|
+
readonly dirtyCount?: number;
|
|
221
|
+
readonly ahead?: number;
|
|
222
|
+
readonly behind?: number;
|
|
223
|
+
/** Changes whenever the working copy does, content of a dirty file included. */
|
|
224
|
+
readonly repoRevision?: string;
|
|
225
|
+
}
|
|
226
|
+
/** A frame the server could not attribute to any command. */
|
|
227
|
+
| { readonly type: "error"; readonly message: string };
|
|
228
|
+
|
|
229
|
+
/** One row of the session catalogue. */
|
|
230
|
+
export type SessionSummaryView = {
|
|
231
|
+
readonly sessionId: string;
|
|
232
|
+
readonly cwd: string;
|
|
233
|
+
readonly createdAt: number;
|
|
234
|
+
/** End of the last completed turn and the catalogue's sort key, never the file mtime; falls back to `createdAt`. */
|
|
235
|
+
readonly settledAt: number;
|
|
236
|
+
/** The session's first user message, trimmed; absent when it has none. */
|
|
237
|
+
readonly title?: string;
|
|
238
|
+
/** True when `title` is a name somebody wrote, rather than the first message. */
|
|
239
|
+
readonly named?: true;
|
|
240
|
+
readonly archived?: true;
|
|
241
|
+
/** Has answered since anything last read it; absent means it has not. */
|
|
242
|
+
readonly unread?: boolean;
|
|
243
|
+
/** Absent when idle, and for a session this server does not hold open. */
|
|
244
|
+
readonly status?: SessionStatus;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
/** One working directory the catalogue holds sessions for, counted before any per-project cut. */
|
|
248
|
+
export type ProjectView = {
|
|
249
|
+
readonly cwd: string;
|
|
250
|
+
/** Every session in it the listing's scope allows, including the ones the cut dropped. */
|
|
251
|
+
readonly count: number;
|
|
252
|
+
readonly pinned?: true;
|
|
253
|
+
/** Where it sorts among the pinned, 0 first; absent unless it is pinned. */
|
|
254
|
+
readonly pinRank?: number;
|
|
255
|
+
/** The sidebar group stands unfolded; absent is folded. */
|
|
256
|
+
readonly expanded?: true;
|
|
257
|
+
/** What to call it instead of its base name; absent when it goes by the directory. */
|
|
258
|
+
readonly label?: string;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* One session a search matched. Deliberately not a `SessionSummaryView`: what
|
|
263
|
+
* a ranked list needs is why the row matched, and `unread` is the sidebar's
|
|
264
|
+
* triage while `status` resolves itself the moment the session opens.
|
|
265
|
+
*/
|
|
266
|
+
export type SearchHitView = {
|
|
267
|
+
readonly sessionId: string;
|
|
268
|
+
readonly cwd: string;
|
|
269
|
+
/** Its name if it has one, else the clamped opening message; the digest's, so it agrees with the sidebar's. */
|
|
270
|
+
readonly title?: string;
|
|
271
|
+
/** Offsets into the clamped `title`, empty when the title did not match. */
|
|
272
|
+
readonly titleRanges: readonly SearchRange[];
|
|
273
|
+
/** The session's opening ask, for a row whose name is all that matched and so has no snippet to show. */
|
|
274
|
+
readonly opening?: string;
|
|
275
|
+
/** End of the last completed turn and the row's clock, never the file mtime; falls back to when the session started, as the sidebar's does. */
|
|
276
|
+
readonly settledAt: number;
|
|
277
|
+
/** Searched and found anyway: the badge that makes "archived are in scope" honest. */
|
|
278
|
+
readonly archived?: true;
|
|
279
|
+
/** What matched, in the words it was said in, each with its own ranges. */
|
|
280
|
+
readonly snippets: readonly SearchSnippet[];
|
|
281
|
+
/** Matching messages in this session, before the snippet cut. */
|
|
282
|
+
readonly total: number;
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
/** One answer to `list_sessions`: the page of rows, and every directory that had one, counted whole. */
|
|
286
|
+
export type SessionListing = {
|
|
287
|
+
readonly sessions: readonly SessionSummaryView[];
|
|
288
|
+
/** Counted before the per-project cut, so a group can say what a page of it leaves out. */
|
|
289
|
+
readonly projects: readonly ProjectView[];
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
/** One answer to `search_sessions`: the ranked rows, the words no session held, and the scope it read. */
|
|
293
|
+
export type SessionSearch = {
|
|
294
|
+
readonly hits: readonly SearchHitView[];
|
|
295
|
+
readonly dropped: readonly string[];
|
|
296
|
+
/** Sessions searched, whole: what the empty state and the result footer say out loud. */
|
|
297
|
+
readonly scanned: number;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
/** One model the server can be switched to, for the composer's model menu. */
|
|
301
|
+
export type ModelView = {
|
|
302
|
+
readonly id: string;
|
|
303
|
+
readonly label: string;
|
|
304
|
+
/** The provider half of `id`, so a client can tag a row without parsing it. */
|
|
305
|
+
readonly provider: string;
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
/** Answer to one `Command`, correlated by its `id`. Never sequenced. */
|
|
309
|
+
export type ResponseEvent = {
|
|
310
|
+
readonly type: "response";
|
|
311
|
+
readonly id: string;
|
|
312
|
+
readonly success: boolean;
|
|
313
|
+
readonly error?: string;
|
|
314
|
+
/** Ranked rows, for the commands that answer with data (`pick_*`). */
|
|
315
|
+
readonly items?: readonly PickerItem[];
|
|
316
|
+
/** The catalogue, for `list_sessions`. */
|
|
317
|
+
readonly sessions?: readonly SessionSummaryView[];
|
|
318
|
+
/** The directories those sessions came from, for `list_sessions`. */
|
|
319
|
+
readonly projects?: readonly ProjectView[];
|
|
320
|
+
/** The ranked sessions, for `search_sessions`. */
|
|
321
|
+
readonly hits?: readonly SearchHitView[];
|
|
322
|
+
/** Query words no session held, dropped so the rest could match, for `search_sessions`. */
|
|
323
|
+
readonly dropped?: readonly string[];
|
|
324
|
+
/** Sessions the query actually searched, for `search_sessions`. */
|
|
325
|
+
readonly scanned?: number;
|
|
326
|
+
/** The model catalogue, for `list_models`. */
|
|
327
|
+
readonly models?: readonly ModelView[];
|
|
328
|
+
/** What the *current* model supports, on the same answer. */
|
|
329
|
+
readonly thinkingLevels?: readonly string[];
|
|
330
|
+
/** One directory's subdirectories, for `list_dirs`. */
|
|
331
|
+
readonly directory?: DirectoryListing;
|
|
332
|
+
/** The cwd's local branches, for `list_branches`. */
|
|
333
|
+
readonly branches?: readonly GitBranch[];
|
|
334
|
+
/** The commit that landed, for `commit`. */
|
|
335
|
+
readonly commit?: Pick<Extract<CommitResult, { readonly ok: true }>, "sha">;
|
|
336
|
+
/** The change list, for `list_changes`. */
|
|
337
|
+
readonly changes?: ChangeList;
|
|
338
|
+
/** One file's hunks, for `file_diff`. */
|
|
339
|
+
readonly fileDiff?: FileDiff;
|
|
340
|
+
/** The lines behind one gap, for `read_lines`. */
|
|
341
|
+
readonly fileLines?: FileLines;
|
|
342
|
+
/** For `cancel` and `dequeue`: queued messages pi gave back, now owned by the client that asked. */
|
|
343
|
+
readonly restored?: readonly string[];
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
export type ServerEvent = DurableEvent | EphemeralEvent | ResponseEvent;
|
|
347
|
+
|
|
348
|
+
/** Anything a session emits: everything on the wire but an answer to a command. */
|
|
349
|
+
export type StreamEvent = DurableEvent | EphemeralEvent;
|
|
350
|
+
|
|
351
|
+
export type ServerEventType = ServerEvent["type"];
|
|
352
|
+
|
|
353
|
+
export function isDurableEvent(event: ServerEvent): event is DurableEvent {
|
|
354
|
+
return "seq" in event;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Whether the frame speaks for the one session a connection is attached to,
|
|
359
|
+
* and so says nothing to a client whose attach is still in flight. The rest
|
|
360
|
+
* name their own subject — another session, a directory, or nothing at all —
|
|
361
|
+
* and gating those on the attach window drops a broadcast every time a client
|
|
362
|
+
* switches session.
|
|
363
|
+
*
|
|
364
|
+
* Exhaustive by construction: a new event is a type error here until it is
|
|
365
|
+
* classified, which is the only reason this lives beside the wire types
|
|
366
|
+
* rather than in the client that gates on it.
|
|
367
|
+
*/
|
|
368
|
+
export function isAttachScoped(event: ServerEvent): boolean {
|
|
369
|
+
switch (event.type) {
|
|
370
|
+
case "attached":
|
|
371
|
+
case "replay":
|
|
372
|
+
case "message":
|
|
373
|
+
case "message_start":
|
|
374
|
+
case "message_retire":
|
|
375
|
+
case "text_delta":
|
|
376
|
+
case "thinking_delta":
|
|
377
|
+
case "tool_call":
|
|
378
|
+
case "tool_update":
|
|
379
|
+
case "tool_end":
|
|
380
|
+
case "tool_result":
|
|
381
|
+
case "notice":
|
|
382
|
+
case "subagent_events":
|
|
383
|
+
case "turn_end":
|
|
384
|
+
case "session_state":
|
|
385
|
+
// Names a cwd, but only ever reaches a client down the session stream it
|
|
386
|
+
// is attached to, so the old session's is the only one that can arrive
|
|
387
|
+
// mid-attach — and re-querying for it would warm the wrong cache.
|
|
388
|
+
case "picker_invalidate":
|
|
389
|
+
return true;
|
|
390
|
+
case "session_activity":
|
|
391
|
+
case "session_read":
|
|
392
|
+
case "session_meta":
|
|
393
|
+
case "project_meta":
|
|
394
|
+
case "pins_changed":
|
|
395
|
+
case "sessions_changed":
|
|
396
|
+
case "update_state":
|
|
397
|
+
// Sent for a frame the server could not read at all, which is likeliest
|
|
398
|
+
// before an attach has settled: gating it swallows the diagnostic.
|
|
399
|
+
case "error":
|
|
400
|
+
case "response":
|
|
401
|
+
return false;
|
|
402
|
+
default:
|
|
403
|
+
event satisfies never;
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
}
|