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,200 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
|
|
3
|
+
import type { AttachmentRef } from "#protocol/Command";
|
|
4
|
+
import {
|
|
5
|
+
isDurableEvent,
|
|
6
|
+
type ResponseEvent,
|
|
7
|
+
type ServerEvent,
|
|
8
|
+
} from "#protocol/ServerEvent";
|
|
9
|
+
import { ProbeClient } from "./ProbeClient";
|
|
10
|
+
|
|
11
|
+
const USAGE = `pim probe — CLI client for pim-server, dumps every frame as JSONL
|
|
12
|
+
|
|
13
|
+
bun run probe [options]
|
|
14
|
+
|
|
15
|
+
--url <ws://host:port> server to connect to (default ws://127.0.0.1:4319)
|
|
16
|
+
--session <uuid> attach to an existing session (default: create one)
|
|
17
|
+
--cwd <path> cwd for a session this probe creates
|
|
18
|
+
--from-seq <n> resume from this durable seq (default 0)
|
|
19
|
+
--prompt <text> send a user message once attached; sent into a turn
|
|
20
|
+
already running it steers that turn
|
|
21
|
+
--pick-files <query> ask the server to complete an @ path, print the rows
|
|
22
|
+
--pick-commands <query> ask the server for matching skills and commands
|
|
23
|
+
--list-sessions print pi's session catalogue and exit
|
|
24
|
+
--archived with --list-sessions, print the archived sessions
|
|
25
|
+
instead of the live ones
|
|
26
|
+
--per-project <n> with --list-sessions, keep at most n sessions per
|
|
27
|
+
working directory
|
|
28
|
+
--archive put --session out of the live listing
|
|
29
|
+
--unarchive bring --session back into it
|
|
30
|
+
--mark-unread hold --session unread until something reads it
|
|
31
|
+
--rename <text> name --session through pi's own session name;
|
|
32
|
+
an empty string clears it
|
|
33
|
+
--pin <path> pin a working directory, so its sessions sort first
|
|
34
|
+
--unpin <path> unpin it again
|
|
35
|
+
--list-models print the models this server can switch to
|
|
36
|
+
--upload <path> transfer a local file to the server, attach it to
|
|
37
|
+
--prompt (repeatable)
|
|
38
|
+
--cancel cancel the current turn
|
|
39
|
+
--dequeue take back what the turn in flight is holding,
|
|
40
|
+
without stopping it
|
|
41
|
+
--wait keep streaming after the turn ends (Ctrl-C to stop)
|
|
42
|
+
--quiet print only durable events
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
const { values } = parseArgs({
|
|
46
|
+
options: {
|
|
47
|
+
url: { type: "string", default: "ws://127.0.0.1:4319" },
|
|
48
|
+
session: { type: "string" },
|
|
49
|
+
cwd: { type: "string" },
|
|
50
|
+
"from-seq": { type: "string", default: "0" },
|
|
51
|
+
prompt: { type: "string" },
|
|
52
|
+
"pick-files": { type: "string" },
|
|
53
|
+
"pick-commands": { type: "string" },
|
|
54
|
+
"list-sessions": { type: "boolean", default: false },
|
|
55
|
+
archived: { type: "boolean", default: false },
|
|
56
|
+
"per-project": { type: "string" },
|
|
57
|
+
archive: { type: "boolean", default: false },
|
|
58
|
+
unarchive: { type: "boolean", default: false },
|
|
59
|
+
"mark-unread": { type: "boolean", default: false },
|
|
60
|
+
rename: { type: "string" },
|
|
61
|
+
pin: { type: "string" },
|
|
62
|
+
unpin: { type: "string" },
|
|
63
|
+
"list-models": { type: "boolean", default: false },
|
|
64
|
+
upload: { type: "string", multiple: true },
|
|
65
|
+
cancel: { type: "boolean", default: false },
|
|
66
|
+
dequeue: { type: "boolean", default: false },
|
|
67
|
+
wait: { type: "boolean", default: false },
|
|
68
|
+
quiet: { type: "boolean", default: false },
|
|
69
|
+
help: { type: "boolean", default: false },
|
|
70
|
+
},
|
|
71
|
+
allowPositionals: false,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (values.help) {
|
|
75
|
+
process.stdout.write(USAGE);
|
|
76
|
+
process.exit(0);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function dump(event: ServerEvent): void {
|
|
80
|
+
if (values.quiet && !isDurableEvent(event)) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
process.stdout.write(`${JSON.stringify(event)}\n`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const probe = new ProbeClient({
|
|
87
|
+
url: values.url,
|
|
88
|
+
...(values.session === undefined ? {} : { sessionId: values.session }),
|
|
89
|
+
...(values.cwd === undefined ? {} : { cwd: values.cwd }),
|
|
90
|
+
fromSeq: Number(values["from-seq"]),
|
|
91
|
+
onEvent: dump,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const attached = await probe.connect();
|
|
95
|
+
if (!attached.success) {
|
|
96
|
+
process.stderr.write(`attach failed: ${attached.error ?? "unknown error"}\n`);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const mark = probe.events.length;
|
|
101
|
+
if (values["pick-files"] !== undefined) {
|
|
102
|
+
const started = Bun.nanoseconds();
|
|
103
|
+
const items = await probe.files.rank(values["pick-files"], { limit: 50 });
|
|
104
|
+
const ms = (Bun.nanoseconds() - started) / 1e6;
|
|
105
|
+
process.stderr.write(
|
|
106
|
+
`pick-files: ${items?.length ?? 0} rows in ${ms.toFixed(1)}ms\n`
|
|
107
|
+
);
|
|
108
|
+
for (const item of items ?? []) {
|
|
109
|
+
process.stdout.write(`${JSON.stringify(item)}\n`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (values["list-sessions"]) {
|
|
113
|
+
const started = Bun.nanoseconds();
|
|
114
|
+
const { sessions, projects } = await probe.catalogue({
|
|
115
|
+
...(values.cwd === undefined ? {} : { cwd: values.cwd }),
|
|
116
|
+
...(values.archived ? { archived: true } : {}),
|
|
117
|
+
...(values["per-project"] === undefined
|
|
118
|
+
? {}
|
|
119
|
+
: { perProject: Number(values["per-project"]) }),
|
|
120
|
+
});
|
|
121
|
+
const ms = (Bun.nanoseconds() - started) / 1e6;
|
|
122
|
+
for (const summary of sessions) {
|
|
123
|
+
process.stdout.write(`${JSON.stringify(summary)}\n`);
|
|
124
|
+
}
|
|
125
|
+
process.stderr.write(
|
|
126
|
+
`list-sessions: ${sessions.length} rows from ${projects.length} projects in ${ms.toFixed(1)}ms\n`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
if (values["list-models"]) {
|
|
130
|
+
process.stdout.write(`${JSON.stringify(await probe.listModels())}\n`);
|
|
131
|
+
}
|
|
132
|
+
if (values["pick-commands"] !== undefined) {
|
|
133
|
+
const items = await probe.pickCommands(values["pick-commands"]);
|
|
134
|
+
process.stderr.write(`pick-commands: ${items.length} rows\n`);
|
|
135
|
+
for (const item of items) {
|
|
136
|
+
process.stdout.write(`${JSON.stringify(item)}\n`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const uploaded: AttachmentRef[] = [];
|
|
141
|
+
for (const path of values.upload ?? []) {
|
|
142
|
+
const file = await probe.upload(path);
|
|
143
|
+
uploaded.push({ id: file.id });
|
|
144
|
+
process.stderr.write(`uploaded: ${JSON.stringify(file)}\n`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (values.cancel) {
|
|
148
|
+
const response = await probe.send({
|
|
149
|
+
type: "cancel",
|
|
150
|
+
sessionId: probe.sessionId ?? "",
|
|
151
|
+
});
|
|
152
|
+
process.stderr.write(`cancel: ${JSON.stringify(response)}\n`);
|
|
153
|
+
}
|
|
154
|
+
if (values.dequeue) {
|
|
155
|
+
const response = await probe.send({
|
|
156
|
+
type: "dequeue",
|
|
157
|
+
sessionId: probe.sessionId ?? "",
|
|
158
|
+
});
|
|
159
|
+
process.stderr.write(`dequeue: ${JSON.stringify(response)}\n`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const sessionId = probe.sessionId ?? "";
|
|
163
|
+
const report = (what: string, response: ResponseEvent): void => {
|
|
164
|
+
process.stderr.write(`${what}: ${JSON.stringify(response)}\n`);
|
|
165
|
+
};
|
|
166
|
+
if (values.rename !== undefined) {
|
|
167
|
+
report("rename", await probe.rename(sessionId, values.rename || null));
|
|
168
|
+
}
|
|
169
|
+
if (values.archive) {
|
|
170
|
+
report("archive", await probe.setArchived(sessionId, true));
|
|
171
|
+
}
|
|
172
|
+
if (values.unarchive) {
|
|
173
|
+
report("unarchive", await probe.setArchived(sessionId, false));
|
|
174
|
+
}
|
|
175
|
+
if (values["mark-unread"]) {
|
|
176
|
+
report("mark-unread", await probe.markUnread(sessionId, true));
|
|
177
|
+
}
|
|
178
|
+
if (values.pin !== undefined) {
|
|
179
|
+
report("pin", await probe.setPinned(values.pin, true));
|
|
180
|
+
}
|
|
181
|
+
if (values.unpin !== undefined) {
|
|
182
|
+
report("unpin", await probe.setPinned(values.unpin, false));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (values.prompt !== undefined) {
|
|
186
|
+
await probe.promptWith(values.prompt, uploaded);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (values.prompt !== undefined) {
|
|
190
|
+
await probe.waitFor(
|
|
191
|
+
(event) => event.type === "session_state" && event.status === "idle",
|
|
192
|
+
{ timeoutMs: 10 * 60_000, from: mark }
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (values.wait) {
|
|
197
|
+
await new Promise<never>(() => {});
|
|
198
|
+
}
|
|
199
|
+
probe.close();
|
|
200
|
+
process.stderr.write(`session ${probe.sessionId} @ seq ${probe.seq}\n`);
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Bot as Grammy } from "grammy";
|
|
3
|
+
|
|
4
|
+
import type { AgentRuntime } from "#core/session/AgentRuntime";
|
|
5
|
+
import { PimVersion } from "#core/shared/PimVersion";
|
|
6
|
+
import { Commands } from "./Commands";
|
|
7
|
+
import type { TelegramConfig } from "./Config";
|
|
8
|
+
import { Message, type Prompt } from "./Message";
|
|
9
|
+
import { Renderer, type TurnEndState } from "./Renderer";
|
|
10
|
+
import { Session, type SessionId } from "./Session";
|
|
11
|
+
import { SessionRegistry } from "./SessionRegistry";
|
|
12
|
+
import type { ScheduledTask } from "./TaskSchema";
|
|
13
|
+
import { TaskScheduler } from "./TaskScheduler";
|
|
14
|
+
import { UpdateConfirm } from "./UpdateConfirm";
|
|
15
|
+
|
|
16
|
+
export class Bot {
|
|
17
|
+
private readonly grammy: Grammy;
|
|
18
|
+
private readonly allowSet: ReadonlySet<number>;
|
|
19
|
+
private readonly registry: SessionRegistry;
|
|
20
|
+
private readonly scheduler: TaskScheduler;
|
|
21
|
+
private readonly config: TelegramConfig;
|
|
22
|
+
private readonly commands: Commands;
|
|
23
|
+
/** Grammy's long poll, which only settles once the bot is stopped. */
|
|
24
|
+
private polling: Promise<void> | undefined;
|
|
25
|
+
|
|
26
|
+
public constructor(config: TelegramConfig, runtime: AgentRuntime) {
|
|
27
|
+
this.config = config;
|
|
28
|
+
this.grammy = new Grammy(config.token);
|
|
29
|
+
this.allowSet = new Set(config.allow);
|
|
30
|
+
this.scheduler = new TaskScheduler({
|
|
31
|
+
configDir: config.configDir,
|
|
32
|
+
runTask: (task) => this.runScheduledTask(task),
|
|
33
|
+
});
|
|
34
|
+
this.registry = new SessionRegistry(
|
|
35
|
+
config,
|
|
36
|
+
this.grammy.api,
|
|
37
|
+
this.scheduler,
|
|
38
|
+
runtime
|
|
39
|
+
);
|
|
40
|
+
this.commands = new Commands(config, this.grammy.api, this.registry);
|
|
41
|
+
|
|
42
|
+
this.grammy.on("callback_query:data", async (ctx) => {
|
|
43
|
+
if (!this.allowSet.has(ctx.chat?.id ?? -1)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
await this.commands.handleCallback(ctx);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
this.grammy.on("message", async (ctx) => {
|
|
50
|
+
const chatId = ctx.chat.id;
|
|
51
|
+
if (!this.allowSet.has(chatId)) {
|
|
52
|
+
console.log(`[recv] reject chatId=${chatId} (not in allow-list)`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const sessionId: SessionId = {
|
|
56
|
+
chatId,
|
|
57
|
+
threadId: ctx.message.message_thread_id,
|
|
58
|
+
};
|
|
59
|
+
let prompt: Prompt | undefined;
|
|
60
|
+
try {
|
|
61
|
+
prompt = await Message.toPrompt(
|
|
62
|
+
ctx,
|
|
63
|
+
this.config.token,
|
|
64
|
+
this.config.configDir,
|
|
65
|
+
sessionId
|
|
66
|
+
);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
await this.reportFailure(sessionId, "attachment download failed", err);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (!prompt) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const preview = prompt.text.slice(0, 120).replace(/\s+/g, " ");
|
|
75
|
+
console.log(
|
|
76
|
+
`[recv] chatId=${chatId} threadId=${sessionId.threadId ?? "main"} ${preview}`
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const session = this.registry.get(sessionId);
|
|
80
|
+
if (prompt.text.startsWith("/") && !prompt.options.images?.length) {
|
|
81
|
+
await this.commands.handleCommand(ctx, session, prompt.text);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
void session
|
|
86
|
+
.run(
|
|
87
|
+
(agent) => this.handleTurn(session, agent, prompt),
|
|
88
|
+
session.temporary ? { isolated: true } : undefined
|
|
89
|
+
)
|
|
90
|
+
.catch((err: unknown) =>
|
|
91
|
+
this.reportFailure(sessionId, "turn setup failed", err)
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
this.grammy.catch((err) => {
|
|
96
|
+
console.error("[bot] handler error:", err.error);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public async start(): Promise<void> {
|
|
101
|
+
await this.registry.init();
|
|
102
|
+
await this.grammy.init();
|
|
103
|
+
await this.processBootUpdateConfirm();
|
|
104
|
+
await this.grammy.api.deleteWebhook({ drop_pending_updates: true });
|
|
105
|
+
const username = this.grammy.botInfo.username;
|
|
106
|
+
this.registry.setBotUsername(username);
|
|
107
|
+
console.log(`bot @${username} ready`);
|
|
108
|
+
await this.scheduler.start();
|
|
109
|
+
// Polling outlives this call: awaiting it would hold every other surface down with it.
|
|
110
|
+
this.polling = this.grammy.start().catch((err: unknown) => {
|
|
111
|
+
console.error("[bot] polling stopped:", err);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
public async stop(): Promise<void> {
|
|
116
|
+
await this.scheduler.stop();
|
|
117
|
+
await this.grammy.stop();
|
|
118
|
+
await this.polling;
|
|
119
|
+
await this.registry.disposeAll();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private async runScheduledTask(task: ScheduledTask): Promise<void> {
|
|
123
|
+
const sessionId: SessionId = {
|
|
124
|
+
chatId: task.chatId,
|
|
125
|
+
threadId: task.threadId,
|
|
126
|
+
};
|
|
127
|
+
const prompt: Prompt = { text: task.prompt, options: {} };
|
|
128
|
+
const session = this.registry.get(sessionId);
|
|
129
|
+
await session
|
|
130
|
+
.run(
|
|
131
|
+
(agent) => this.handleTurn(session, agent, prompt),
|
|
132
|
+
task.isolatedSession ? { isolated: true } : undefined
|
|
133
|
+
)
|
|
134
|
+
.catch((err: unknown) =>
|
|
135
|
+
this.reportFailure(sessionId, `scheduled task ${task.id} failed`, err)
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private async reportFailure(
|
|
140
|
+
sessionId: SessionId,
|
|
141
|
+
label: string,
|
|
142
|
+
err: unknown
|
|
143
|
+
): Promise<void> {
|
|
144
|
+
console.error(`[bot] ${label}:`, err);
|
|
145
|
+
await this.grammy.api
|
|
146
|
+
.sendMessage(
|
|
147
|
+
sessionId.chatId,
|
|
148
|
+
`⚠️ ${(err as Error).message ?? String(err)}`,
|
|
149
|
+
{
|
|
150
|
+
message_thread_id: sessionId.threadId,
|
|
151
|
+
link_preview_options: { is_disabled: true },
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
.catch((e: unknown) => console.error(`[send] plain failed:`, e));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private async processBootUpdateConfirm(): Promise<void> {
|
|
158
|
+
const entries = await UpdateConfirm.read(this.config.configDir);
|
|
159
|
+
if (entries.length === 0) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const [version, piVersion] = await Promise.all([
|
|
163
|
+
PimVersion.current(),
|
|
164
|
+
PimVersion.pi(),
|
|
165
|
+
]);
|
|
166
|
+
const text = `✅ Pim Agent updated to v${version} (pi v${piVersion})!`;
|
|
167
|
+
await Promise.all(
|
|
168
|
+
entries.map((e) =>
|
|
169
|
+
this.grammy.api
|
|
170
|
+
.editMessageText(e.chatId, e.messageId, text, {
|
|
171
|
+
link_preview_options: { is_disabled: true },
|
|
172
|
+
})
|
|
173
|
+
.catch((err) => console.warn(`[update-confirm] edit failed:`, err))
|
|
174
|
+
)
|
|
175
|
+
);
|
|
176
|
+
await UpdateConfirm.clear(this.config.configDir);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private async handleTurn(
|
|
180
|
+
session: Session,
|
|
181
|
+
agent: AgentSession,
|
|
182
|
+
prompt: Prompt
|
|
183
|
+
): Promise<void> {
|
|
184
|
+
const renderer = new Renderer(session, this.grammy.api);
|
|
185
|
+
const unsubscribe = agent.subscribe((event) => renderer.handleEvent(event));
|
|
186
|
+
await renderer.start();
|
|
187
|
+
try {
|
|
188
|
+
await agent.prompt(prompt.text, {
|
|
189
|
+
...prompt.options,
|
|
190
|
+
source: "rpc",
|
|
191
|
+
});
|
|
192
|
+
const final = extractFinalResult(agent);
|
|
193
|
+
await renderer.finish(final.text, final.state);
|
|
194
|
+
} catch (err) {
|
|
195
|
+
const msg = (err as Error).message ?? String(err);
|
|
196
|
+
console.error(`[bot] turn failed:`, err);
|
|
197
|
+
await renderer.finish(`⚠️ ${msg}`, "error");
|
|
198
|
+
} finally {
|
|
199
|
+
unsubscribe();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function extractFinalResult(agent: AgentSession): {
|
|
205
|
+
readonly text: string;
|
|
206
|
+
readonly state: TurnEndState;
|
|
207
|
+
} {
|
|
208
|
+
const messages = agent.messages;
|
|
209
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
210
|
+
const msg = messages[i]!;
|
|
211
|
+
if (msg.role !== "assistant") {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (msg.stopReason === "error") {
|
|
215
|
+
return { text: msg.errorMessage ?? "", state: "error" };
|
|
216
|
+
}
|
|
217
|
+
if (msg.stopReason === "aborted") {
|
|
218
|
+
return { text: msg.errorMessage ?? "", state: "cancelled" };
|
|
219
|
+
}
|
|
220
|
+
const parts: string[] = [];
|
|
221
|
+
for (const block of msg.content) {
|
|
222
|
+
if (block.type === "text") {
|
|
223
|
+
parts.push(block.text);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return { text: parts.join("").trim(), state: "ok" };
|
|
227
|
+
}
|
|
228
|
+
return { text: "", state: "ok" };
|
|
229
|
+
}
|