pim-agent 0.12.1 → 0.14.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/package.json +4 -3
- package/packages/core/src/attachments/AttachmentStore.ts +8 -13
- package/packages/core/src/attachments/Attachments.ts +2 -4
- package/packages/core/src/extensions/CoreExtensions.ts +1 -1
- package/packages/core/src/extensions/apply-patch/coordinator.ts +1 -1
- package/packages/core/src/extensions/apply-patch/executor.ts +21 -25
- package/packages/core/src/extensions/apply-patch/matcher.ts +1 -1
- package/packages/core/src/extensions/apply-patch/model.ts +2 -6
- package/packages/core/src/extensions/apply-patch/parser.ts +60 -162
- package/packages/core/src/extensions/apply-patch/schema.ts +0 -1
- package/packages/core/src/extensions/bash/MemoryCap.ts +5 -4
- package/packages/core/src/extensions/bash/capture.ts +16 -23
- package/packages/core/src/extensions/bash/format.ts +2 -2
- package/packages/core/src/extensions/bash/image.ts +4 -8
- package/packages/core/src/extensions/bash/index.ts +1 -1
- package/packages/core/src/extensions/bash/render.ts +1 -1
- package/packages/core/src/extensions/bash/run.ts +9 -15
- package/packages/core/src/extensions/bash/schema.ts +4 -4
- package/packages/core/src/extensions/edit/edit.ts +14 -33
- package/packages/core/src/extensions/glob/glob.ts +1 -3
- package/packages/core/src/extensions/glob/index.ts +7 -9
- package/packages/core/src/extensions/glob/render.ts +2 -4
- package/packages/core/src/extensions/glob/schema.ts +1 -1
- package/packages/core/src/extensions/grep/grep.ts +7 -7
- package/packages/core/src/extensions/grep/render.ts +5 -6
- package/packages/core/src/extensions/grep/schema.ts +3 -7
- package/packages/core/src/extensions/read/ImageMemory.ts +2 -8
- package/packages/core/src/extensions/read/image.ts +3 -3
- package/packages/core/src/extensions/read/index.ts +2 -14
- package/packages/core/src/extensions/read/read.ts +5 -6
- package/packages/core/src/extensions/read/render.ts +1 -1
- package/packages/core/src/extensions/read/schema.ts +2 -2
- package/packages/core/src/extensions/subagent/render.ts +2 -2
- package/packages/core/src/extensions/subagent/subagent.ts +10 -21
- package/packages/core/src/extensions/system-prompt/index.ts +3 -2
- package/packages/core/src/extensions/system-prompt/prompt.ts +13 -3
- package/packages/core/src/extensions/todo/render.ts +1 -4
- package/packages/core/src/extensions/todo/todo.ts +16 -23
- package/packages/core/src/extensions/web-fetch/JinaReaderClient.ts +3 -11
- package/packages/core/src/extensions/web-fetch/WebViewFetchClient.ts +1 -3
- package/packages/core/src/extensions/web-fetch/fetch.ts +4 -12
- package/packages/core/src/extensions/web-fetch/image.ts +3 -4
- package/packages/core/src/extensions/web-fetch/index.ts +2 -13
- package/packages/core/src/extensions/web-fetch/render.ts +3 -4
- package/packages/core/src/extensions/web-fetch/schema.ts +3 -4
- package/packages/core/src/extensions/web-search/ExaMcpClient.ts +23 -31
- package/packages/core/src/extensions/web-search/SearchBreaker.ts +1 -1
- package/packages/core/src/extensions/web-search/SearchChain.ts +6 -5
- package/packages/core/src/extensions/web-search/providers/DuckDuckGoProvider.ts +2 -2
- package/packages/core/src/extensions/web-search/providers/FirecrawlProvider.ts +1 -2
- package/packages/core/src/extensions/web-search/providers/SearchProvider.ts +3 -3
- package/packages/core/src/extensions/write/write.ts +1 -1
- package/packages/core/src/picker/InProcessFilePickerSuggestionEngine.ts +1 -3
- package/packages/core/src/picker/PickerItem.ts +1 -1
- package/packages/core/src/picker/PickerService.ts +3 -7
- package/packages/core/src/picker/RemoteFilePickerSuggestionEngine.ts +1 -2
- package/packages/core/src/picker/catalog.ts +7 -15
- package/packages/core/src/picker/filePickerWorker.ts +1 -3
- package/packages/core/src/picker/ranker.ts +1 -1
- package/packages/core/src/picker/token.ts +3 -4
- package/packages/core/src/session/AgentRuntime.ts +5 -8
- package/packages/core/src/session/EventLog.ts +6 -12
- package/packages/core/src/session/ReadCursors.ts +3 -4
- package/packages/core/src/session/SearchIndex.ts +49 -86
- package/packages/core/src/session/SearchTokens.ts +4 -7
- package/packages/core/src/session/SessionCache.ts +8 -6
- package/packages/core/src/session/SessionDigest.ts +10 -19
- package/packages/core/src/session/SessionHost.ts +53 -83
- package/packages/core/src/session/SessionLease.ts +12 -15
- package/packages/core/src/session/SessionMeta.ts +16 -32
- package/packages/core/src/session/SessionName.ts +3 -7
- package/packages/core/src/session/SessionRegistry.ts +13 -29
- package/packages/core/src/session/SessionUi.ts +12 -16
- package/packages/core/src/session/WriteMark.ts +7 -16
- package/packages/core/src/shared/Cli.ts +1 -1
- package/packages/core/src/shared/DaemonUnit.ts +1 -2
- package/packages/core/src/shared/DiffLines.ts +16 -34
- package/packages/core/src/shared/DiffRenderer.ts +8 -27
- package/packages/core/src/shared/Directories.ts +4 -4
- package/packages/core/src/shared/EditMatcher.ts +18 -50
- package/packages/core/src/shared/ExtensionToggles.ts +3 -6
- package/packages/core/src/shared/FileEnumerator.ts +73 -57
- package/packages/core/src/shared/FileScanner.ts +5 -13
- package/packages/core/src/shared/FileWatch.ts +13 -17
- package/packages/core/src/shared/Format.ts +2 -3
- package/packages/core/src/shared/FuzzyMatcher.ts +18 -19
- package/packages/core/src/shared/Git.ts +22 -47
- package/packages/core/src/shared/GitMonitor.ts +12 -23
- package/packages/core/src/shared/GlobExclusions.ts +0 -4
- package/packages/core/src/shared/Http.ts +1 -1
- package/packages/core/src/shared/ImageMime.ts +3 -5
- package/packages/core/src/shared/Images.ts +17 -27
- package/packages/core/src/shared/Languages.ts +2 -2
- package/packages/core/src/shared/Levenshtein.ts +1 -6
- package/packages/core/src/shared/Lines.ts +1 -1
- package/packages/core/src/shared/McpClient.ts +2 -2
- package/packages/core/src/shared/PatchSummary.ts +1 -1
- package/packages/core/src/shared/Paths.ts +4 -1
- package/packages/core/src/shared/PiExtensions.ts +5 -12
- package/packages/core/src/shared/PimSettings.ts +14 -15
- package/packages/core/src/shared/Proc.ts +2 -3
- package/packages/core/src/shared/RateLimiter.ts +1 -1
- package/packages/core/src/shared/Renderer.ts +1 -3
- package/packages/core/src/shared/RepoDiff.ts +24 -46
- package/packages/core/src/shared/SearchRender.ts +3 -4
- package/packages/core/src/shared/SpillCache.ts +2 -3
- package/packages/core/src/shared/SubagentLogs.ts +4 -6
- package/packages/core/src/shared/Supervisor.ts +19 -32
- package/packages/core/src/shared/Surface.ts +1 -1
- package/packages/core/src/shared/Sweeper.ts +1 -1
- package/packages/core/src/shared/Tools.ts +17 -30
- package/packages/core/src/shared/Updater.ts +15 -15
- package/packages/core/src/view/BodyRenderer.ts +1 -1
- package/packages/core/src/view/DiffBlocks.ts +0 -1
- package/packages/core/src/view/DiffExpand.ts +10 -25
- package/packages/core/src/view/DiffLayout.ts +2 -2
- package/packages/core/src/view/DiffPairs.ts +2 -2
- package/packages/core/src/view/MarkdownPainter.ts +2 -19
- package/packages/core/src/view/MovePath.ts +4 -17
- package/packages/core/src/view/Painting.ts +4 -4
- package/packages/core/src/view/ViewBlock.ts +11 -12
- package/packages/daemon/src/Daemon.ts +2 -7
- package/packages/daemon/src/DaemonInstall.ts +5 -12
- package/packages/daemon/src/Surfaces.ts +3 -12
- package/packages/daemon/src/TelegramSurface.ts +0 -1
- package/packages/daemon/src/index.ts +0 -1
- package/packages/protocol/src/Command.ts +33 -41
- package/packages/protocol/src/Diff.ts +0 -5
- package/packages/protocol/src/ImageRoute.ts +2 -5
- package/packages/protocol/src/ServerEvent.ts +68 -105
- package/packages/server/src/AttachmentEndpoint.ts +45 -19
- package/packages/server/src/ClientConnection.ts +11 -12
- package/packages/server/src/ImageEndpoint.ts +3 -4
- package/packages/server/src/ProbeClient.ts +26 -59
- package/packages/server/src/Reloader.ts +4 -7
- package/packages/server/src/SendFileTool.ts +1 -1
- package/packages/server/src/SessionCatalogue.ts +35 -47
- package/packages/server/src/SessionProjection.ts +7 -12
- package/packages/server/src/SessionStream.ts +33 -80
- package/packages/server/src/StaticClient.ts +7 -8
- package/packages/server/src/WebOptions.ts +2 -6
- package/packages/server/src/WsGateway.ts +35 -96
- package/packages/server/src/probe.ts +6 -18
- package/packages/telegram/src/Bot.ts +2 -2
- package/packages/telegram/src/Commands.ts +108 -65
- package/packages/telegram/src/Config.ts +1 -6
- package/packages/telegram/src/Markdown.ts +4 -13
- package/packages/telegram/src/Renderer.ts +1 -1
- package/packages/telegram/src/Session.ts +3 -10
- package/packages/telegram/src/TaskScheduler.ts +31 -10
- package/packages/telegram/src/TaskStore.ts +11 -1
- package/packages/telegram/src/TaskTool.ts +1 -1
- package/packages/telegram/src/UpdateConfirm.ts +2 -2
- package/packages/tui/src/extensions/_init/index.ts +2 -5
- package/packages/tui/src/extensions/file-picker/index.ts +2 -6
- package/packages/tui/src/extensions/footer/index.ts +0 -1
- package/packages/tui/src/extensions/footer/powerline.ts +16 -28
- package/packages/tui/src/extensions/pim/index.ts +1 -1
- package/packages/tui/src/extensions/session-lease/index.ts +5 -6
- package/packages/tui/src/extensions/working-indicator/index.ts +1 -2
- package/packages/tui/src/themes/themeCliArgs.ts +1 -1
- package/packages/web/dist/client/assets/abnfDiagram-O67JEVCF-WweOU5DK.js +1 -0
- package/packages/web/dist/client/assets/arc-O2PmZXWt.js +1 -0
- package/packages/web/dist/client/assets/architecture-7GRP2DOG-COBxVilU.js +1 -0
- package/packages/web/dist/client/assets/architectureDiagram-NJMV4G6O-DwYq1YaM.js +36 -0
- package/packages/web/dist/client/assets/array-BifhSqXX.js +1 -0
- package/packages/web/dist/client/assets/blockDiagram-BEXU5L5S-CVab041n.js +140 -0
- package/packages/web/dist/client/assets/c4Diagram-YGBWAQC7-BmotHez3.js +38 -0
- package/packages/web/dist/client/assets/channel-BKV-IcTA.js +1 -0
- package/packages/web/dist/client/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/packages/web/dist/client/assets/chunk-3FUC2YCW-CWNS7-n4.js +2 -0
- package/packages/web/dist/client/assets/chunk-5DYCD2WN-DflvXohu.js +1 -0
- package/packages/web/dist/client/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/packages/web/dist/client/assets/chunk-742MDFTN-B_0t6_RE.js +1 -0
- package/packages/web/dist/client/assets/chunk-7INBJB4K-wKj0Q3Kv.js +62 -0
- package/packages/web/dist/client/assets/chunk-7M6MHVWA-BMvUghc8.js +213 -0
- package/packages/web/dist/client/assets/chunk-7PRAP22T-BY41wkcT.js +1 -0
- package/packages/web/dist/client/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
- package/packages/web/dist/client/assets/chunk-FOHPRMQF-CoA4358r.js +161 -0
- package/packages/web/dist/client/assets/chunk-GTNCS2PH-PqY_YoS0.js +88 -0
- package/packages/web/dist/client/assets/chunk-GWA4HPMP-fozSU5ty.js +1 -0
- package/packages/web/dist/client/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
- package/packages/web/dist/client/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/packages/web/dist/client/assets/chunk-MBY4JIJT-CrHrC3jw.js +72 -0
- package/packages/web/dist/client/assets/chunk-NETBCI7D-DWdAxieG.js +1 -0
- package/packages/web/dist/client/assets/chunk-O7XYJQB3-B6uOn9iS.js +128 -0
- package/packages/web/dist/client/assets/chunk-UA2S7LBM-BxYSHqyy.js +1 -0
- package/packages/web/dist/client/assets/chunk-WEXAMYUT-CtwIaQDa.js +1 -0
- package/packages/web/dist/client/assets/chunk-XXDRQBXY-fgBFe-PX.js +1 -0
- package/packages/web/dist/client/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/packages/web/dist/client/assets/chunk-Z7XXMR3K-CPonoV3p.js +10 -0
- package/packages/web/dist/client/assets/chunk-ZIGJFQKS-Cdu87cTj.js +2 -0
- package/packages/web/dist/client/assets/classDiagram-v2-NBCMYWYE-DSWsPaa7.js +217 -0
- package/packages/web/dist/client/assets/core-CBoS7AST.js +3 -0
- package/packages/web/dist/client/assets/cose-bilkent-JH36ORCC-CTwlldtJ.js +1 -0
- package/packages/web/dist/client/assets/cynefin-OW5HDTMX-CZQ4EPNJ.js +1 -0
- package/packages/web/dist/client/assets/cynefinDiagram-VND7K2PF-C2gp6H_Q.js +62 -0
- package/packages/web/dist/client/assets/cytoscape.esm-B-1iW2ft.js +321 -0
- package/packages/web/dist/client/assets/dagre-6A5THRUB-CDxTP47t.js +4 -0
- package/packages/web/dist/client/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/packages/web/dist/client/assets/diagram-22UHCM2B-BIohE1lI.js +200 -0
- package/packages/web/dist/client/assets/diagram-3UASUU5V-TwAUir-w.js +24 -0
- package/packages/web/dist/client/assets/diagram-ATOU4E4O-BM1JQrIu.js +3 -0
- package/packages/web/dist/client/assets/diagram-CDSNMT55-BKIqhxSI.js +30 -0
- package/packages/web/dist/client/assets/diagram-MLGK6HIB-CVe3QDHr.js +24 -0
- package/packages/web/dist/client/assets/diagram-MPIPVDR6-Bj-MQIFI.js +41 -0
- package/packages/web/dist/client/assets/dist-DlZpcyc2.js +1 -0
- package/packages/web/dist/client/assets/ebnfDiagram-ZINNZB2B-C06mmvmz.js +1 -0
- package/packages/web/dist/client/assets/elk-276RUBZZ-CizJSNEj.js +27 -0
- package/packages/web/dist/client/assets/erDiagram-OPXOYQCR-M3FQkKI1.js +99 -0
- package/packages/web/dist/client/assets/eventmodeling-NTZA5JFV-D_UXLpIu.js +1 -0
- package/packages/web/dist/client/assets/flowDiagram-KWPJA3E3-BT4eEEVm.js +1 -0
- package/packages/web/dist/client/assets/ganttDiagram-FUAMR5RP-CojF9Q16.js +292 -0
- package/packages/web/dist/client/assets/gitGraph-4MIJSDKK-YvKqGEPk.js +1 -0
- package/packages/web/dist/client/assets/gitGraphDiagram-X574FWY7-XqNqIFo9.js +106 -0
- package/packages/web/dist/client/assets/graphlib-DS17s2tU.js +1 -0
- package/packages/web/dist/client/assets/index--2CDtzvY.css +1 -0
- package/packages/web/dist/client/assets/index-bT6QPJZs.js +47 -0
- package/packages/web/dist/client/assets/info-A6RAGUB7-B_1BCzM2.js +1 -0
- package/packages/web/dist/client/assets/infoDiagram-VRGFBTTK-zFX8B-dP.js +2 -0
- package/packages/web/dist/client/assets/init-C-OQMol4.js +1 -0
- package/packages/web/dist/client/assets/ishikawaDiagram-OU5B5YK6-DLzQ_Uyf.js +70 -0
- package/packages/web/dist/client/assets/journeyDiagram-ZHPQQLJL-S3WRMmoa.js +139 -0
- package/packages/web/dist/client/assets/kanban-definition-PNTS6WVX-B5Z8I-kM.js +89 -0
- package/packages/web/dist/client/assets/katex-ZlcWpGUi.js +257 -0
- package/packages/web/dist/client/assets/line-BGMH-i2X.js +1 -0
- package/packages/web/dist/client/assets/linear-KsrestXG.js +1 -0
- package/packages/web/dist/client/assets/mermaid-parser.core-DNffyJJA.js +7 -0
- package/packages/web/dist/client/assets/mermaid.core-Cz3vJeQG.js +44 -0
- package/packages/web/dist/client/assets/mindmap-definition-NLK3R4M7-CzA61XUf.js +96 -0
- package/packages/web/dist/client/assets/ordinal-BDEzSJ7C.js +1 -0
- package/packages/web/dist/client/assets/packet-AYTQ26CC-BO55e7cR.js +1 -0
- package/packages/web/dist/client/assets/path-fybaL0A-.js +1 -0
- package/packages/web/dist/client/assets/pegDiagram-GJSIUBJH-DeRiXYNd.js +1 -0
- package/packages/web/dist/client/assets/pie-WAS4IAKB-D9ibrQRe.js +1 -0
- package/packages/web/dist/client/assets/pieDiagram-5QR66LMP-BaBpPP33.js +39 -0
- package/packages/web/dist/client/assets/quadrantDiagram-O4NWA36T-DIfn4GjM.js +7 -0
- package/packages/web/dist/client/assets/radar-RG4KPBEZ-Dmn5-nJq.js +1 -0
- package/packages/web/dist/client/assets/railroad-74A4TZTK-ZCWNEd2Z.js +1 -0
- package/packages/web/dist/client/assets/railroad-abnf-HS5TGJTU-_swqhy0y.js +1 -0
- package/packages/web/dist/client/assets/railroad-ebnf-LZEXJU2U-CytbazPx.js +1 -0
- package/packages/web/dist/client/assets/railroad-peg-WCYAUIDC-Bg8QW17T.js +1 -0
- package/packages/web/dist/client/assets/railroadDiagram-XR7U4H2S-CDPZ39sy.js +1 -0
- package/packages/web/dist/client/assets/requirementDiagram-PLB6GJNP-CyCI8Yul.js +84 -0
- package/packages/web/dist/client/assets/rough.esm-Dy-Kn_BL.js +1 -0
- package/packages/web/dist/client/assets/sankeyDiagram-IPEJSGJF-p_XAPv72.js +40 -0
- package/packages/web/dist/client/assets/sequenceDiagram-PO4LG4MO-DmR6IP_o.js +169 -0
- package/packages/web/dist/client/assets/sizeCapture-INFHLROL-B0uUizjq.js +1 -0
- package/packages/web/dist/client/assets/src-gnv7pJ1z.js +1 -0
- package/packages/web/dist/client/assets/stateDiagram-v2-GCMORJYK-7ZTHl6ih.js +291 -0
- package/packages/web/dist/client/assets/swimlanes-2SLR337P-N6bj71pw.js +1 -0
- package/packages/web/dist/client/assets/swimlanesDiagram-TC7HE7FX-BJQ2CBsp.js +8 -0
- package/packages/web/dist/client/assets/timeline-definition-EJHVYXUP-GNez0kW8.js +120 -0
- package/packages/web/dist/client/assets/treeView-Q6P3EWNA-DyQZkboR.js +1 -0
- package/packages/web/dist/client/assets/treemap-WGGIJYW6-_J8Pw2TM.js +1 -0
- package/packages/web/dist/client/assets/usecaseDiagram-POWQR4AR-Bg-CzKjl.js +328 -0
- package/packages/web/dist/client/assets/vennDiagram-UO4OBE2U-DBQKHXhK.js +34 -0
- package/packages/web/dist/client/assets/wardley-WFR3VGLG-B5aAsQ3F.js +1 -0
- package/packages/web/dist/client/assets/wardleyDiagram-VNRHLVJA-BKUQ1LK6.js +78 -0
- package/packages/web/dist/client/assets/xychartDiagram-PMCCYNJV-BI2sVbks.js +7 -0
- package/packages/web/dist/client/index.html +2 -2
- package/packages/web/dist/client/assets/core-C5fJJN75.js +0 -3
- package/packages/web/dist/client/assets/index-C5D7B_8d.js +0 -45
- package/packages/web/dist/client/assets/index-OVLzpES-.css +0 -1
|
@@ -4,16 +4,10 @@ import type { ImageContent } from "@earendil-works/pi-ai";
|
|
|
4
4
|
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
5
5
|
|
|
6
6
|
import { Attachments } from "#core/attachments/Attachments";
|
|
7
|
-
import type { PickerItem } from "#core/picker/PickerItem";
|
|
8
7
|
import { Directories } from "#core/shared/Directories";
|
|
9
|
-
import
|
|
10
|
-
import { Git, type GitBranch } from "#core/shared/Git";
|
|
8
|
+
import { Git } from "#core/shared/Git";
|
|
11
9
|
import { GitMonitor, type GitRun } from "#core/shared/GitMonitor";
|
|
12
|
-
import {
|
|
13
|
-
PiExtensions,
|
|
14
|
-
type ExtensionEntry,
|
|
15
|
-
type ExtensionScope,
|
|
16
|
-
} from "#core/shared/PiExtensions";
|
|
10
|
+
import { PiExtensions, type ExtensionScope } from "#core/shared/PiExtensions";
|
|
17
11
|
import { RepoDiff } from "#core/shared/RepoDiff";
|
|
18
12
|
import { ReadCursors } from "#core/session/ReadCursors";
|
|
19
13
|
import type { SessionHost } from "#core/session/SessionHost";
|
|
@@ -23,14 +17,7 @@ import { PimVersion } from "#core/shared/PimVersion";
|
|
|
23
17
|
import { SubagentLogs } from "#core/shared/SubagentLogs";
|
|
24
18
|
import type { UpdateOutcome } from "#core/shared/Updater";
|
|
25
19
|
import type { Command } from "#protocol/Command";
|
|
26
|
-
import type {
|
|
27
|
-
import type {
|
|
28
|
-
ModelView,
|
|
29
|
-
ProjectView,
|
|
30
|
-
SearchHitView,
|
|
31
|
-
ServerEvent,
|
|
32
|
-
SessionSummaryView,
|
|
33
|
-
} from "#protocol/ServerEvent";
|
|
20
|
+
import type { ResponseEvent, ServerEvent } from "#protocol/ServerEvent";
|
|
34
21
|
import { ClientConnection } from "./ClientConnection";
|
|
35
22
|
import { Reloader } from "./Reloader";
|
|
36
23
|
import { SessionCatalogue } from "./SessionCatalogue";
|
|
@@ -46,48 +33,28 @@ export type WsGatewayDeps = {
|
|
|
46
33
|
readonly hostname?: string;
|
|
47
34
|
/** 0 asks the OS for a free port; read it back from `port`. */
|
|
48
35
|
readonly port?: number;
|
|
49
|
-
/**
|
|
36
|
+
/** Defaults to `~/.pim/attachments`. */
|
|
50
37
|
readonly attachmentsRoot?: string;
|
|
51
|
-
/**
|
|
38
|
+
/** Images cached by `read`. Defaults to `~/.pim/cache`. */
|
|
52
39
|
readonly imagesRoot?: string;
|
|
53
|
-
/**
|
|
40
|
+
/** Defaults to `~/.pim/read.json`. */
|
|
54
41
|
readonly readCursorsPath?: string;
|
|
55
|
-
/**
|
|
42
|
+
/** Defaults to `~/.pim/sessions.json`. */
|
|
56
43
|
readonly sessionMetaPath?: string;
|
|
57
|
-
/**
|
|
44
|
+
/** Session file poll interval. Defaults to 1s. */
|
|
58
45
|
readonly pollMs?: number;
|
|
59
|
-
/**
|
|
46
|
+
/** Defaults to 3 minutes. */
|
|
60
47
|
readonly requestCeilingMs?: number;
|
|
61
|
-
/**
|
|
48
|
+
/** Defaults to 15s. */
|
|
62
49
|
readonly detachGraceMs?: number;
|
|
63
|
-
/**
|
|
50
|
+
/** Defaults to the bundled web client. */
|
|
64
51
|
readonly clientDir?: string;
|
|
65
|
-
/** Runs the update a `reload` asks for, reporting each step as it starts. */
|
|
66
52
|
readonly update?: (onStep: (label: string) => void) => Promise<UpdateOutcome>;
|
|
67
|
-
/** Ends this process so the supervisor replaces it with the updated code. */
|
|
68
53
|
readonly shutdown?: () => Promise<void>;
|
|
69
54
|
};
|
|
70
55
|
|
|
71
|
-
type Outcome = {
|
|
72
|
-
|
|
73
|
-
readonly items?: readonly PickerItem[];
|
|
74
|
-
readonly sessions?: readonly SessionSummaryView[];
|
|
75
|
-
readonly projects?: readonly ProjectView[];
|
|
76
|
-
readonly hits?: readonly SearchHitView[];
|
|
77
|
-
readonly dropped?: readonly string[];
|
|
78
|
-
readonly scanned?: number;
|
|
79
|
-
readonly models?: readonly ModelView[];
|
|
80
|
-
readonly thinkingLevels?: readonly string[];
|
|
81
|
-
readonly extensions?: readonly ExtensionEntry[];
|
|
82
|
-
readonly directory?: DirectoryListing;
|
|
83
|
-
readonly branches?: readonly GitBranch[];
|
|
84
|
-
readonly commit?: { readonly sha: string };
|
|
85
|
-
readonly changes?: ChangeList;
|
|
86
|
-
readonly fileDiff?: FileDiff;
|
|
87
|
-
readonly fileLines?: FileLines;
|
|
88
|
-
readonly restored?: readonly string[];
|
|
89
|
-
/** The message was dispatched as an extension command: no turn started, no entry written. */
|
|
90
|
-
readonly dispatched?: boolean;
|
|
56
|
+
type Outcome = Omit<ResponseEvent, "type" | "id" | "success"> & {
|
|
57
|
+
/** Runs after the response is sent. */
|
|
91
58
|
readonly after?: () => void;
|
|
92
59
|
};
|
|
93
60
|
|
|
@@ -101,19 +68,16 @@ export const DEFAULT_PORT = 4319;
|
|
|
101
68
|
|
|
102
69
|
export const DEFAULT_HOSTNAME = "127.0.0.1";
|
|
103
70
|
|
|
104
|
-
// Bun 1.3.14
|
|
71
|
+
// Bun 1.3.14 can hang in `Server.stop(true)` after closing sockets server-side.
|
|
105
72
|
const STOP_GRACE_MS = 250;
|
|
106
73
|
|
|
107
|
-
/**
|
|
108
|
-
* Pi's own dispatch parse, three lines of `_tryExecuteExtensionCommand`
|
|
109
|
-
* (`agent-session.js:954`); a pi upgrade re-verifies it.
|
|
110
|
-
*/
|
|
74
|
+
/** Mirrors pi's `_tryExecuteExtensionCommand` parse (`agent-session.js:954`). */
|
|
111
75
|
function commandNameOf(text: string): string {
|
|
112
76
|
const spaceIndex = text.indexOf(" ");
|
|
113
77
|
return spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);
|
|
114
78
|
}
|
|
115
79
|
|
|
116
|
-
/**
|
|
80
|
+
/** WebSocket + HTTP endpoint over the session runtime in `core`. */
|
|
117
81
|
export class WsGateway {
|
|
118
82
|
private readonly registry: SessionRegistry;
|
|
119
83
|
private readonly hostname: string;
|
|
@@ -140,8 +104,6 @@ export class WsGateway {
|
|
|
140
104
|
|
|
141
105
|
public constructor(deps: WsGatewayDeps) {
|
|
142
106
|
this.registry = deps.registry;
|
|
143
|
-
// `SessionStream` is the sink, so the whole routing is a lookup: a host
|
|
144
|
-
// outlives the stream that speaks for it, and pi asks per call.
|
|
145
107
|
this.registry.setUi((host) =>
|
|
146
108
|
host.sessionId === undefined
|
|
147
109
|
? undefined
|
|
@@ -213,13 +175,13 @@ export class WsGateway {
|
|
|
213
175
|
if (ImageEndpoint.owns(pathname)) {
|
|
214
176
|
return this.images.handle(req);
|
|
215
177
|
}
|
|
216
|
-
//
|
|
178
|
+
// The socket must answer on every path.
|
|
217
179
|
return server.upgrade(req) ? undefined : this.client.handle(req);
|
|
218
180
|
},
|
|
219
181
|
websocket: {
|
|
220
182
|
backpressureLimit: 8 << 20,
|
|
221
183
|
closeOnBackpressureLimit: false,
|
|
222
|
-
// Dedicated
|
|
184
|
+
// Dedicated keeps the window between frames, which shrinks the repeated envelopes.
|
|
223
185
|
perMessageDeflate: { compress: "dedicated", decompress: "dedicated" },
|
|
224
186
|
open: (ws) => {
|
|
225
187
|
this.connections.set(ws, new ClientConnection(ws));
|
|
@@ -324,11 +286,7 @@ export class WsGateway {
|
|
|
324
286
|
return await this.catalogue.list(command);
|
|
325
287
|
case "search_sessions":
|
|
326
288
|
return await this.catalogue.search(command);
|
|
327
|
-
//
|
|
328
|
-
// is archived or renamed from the sidebar without being attached to, so
|
|
329
|
-
// none of them may reach for a stream. A sidecar write moves no session
|
|
330
|
-
// file and produces no `sessions_changed`, so the broadcast each ends
|
|
331
|
-
// with is the only word a listing already in a client's hands gets.
|
|
289
|
+
// These may target a session this server never opened, so they must not need a stream.
|
|
332
290
|
case "set_session_name": {
|
|
333
291
|
const name = await this.registry.setName(
|
|
334
292
|
command.sessionId,
|
|
@@ -364,8 +322,6 @@ export class WsGateway {
|
|
|
364
322
|
cwd: command.cwd,
|
|
365
323
|
pinned: command.value,
|
|
366
324
|
});
|
|
367
|
-
// The flag and the place it takes are one fact to a sidebar; a pin
|
|
368
|
-
// that arrived without a rank would sort by nothing until the next listing.
|
|
369
325
|
this.broadcast({ type: "pins_changed", order: await this.meta.pins() });
|
|
370
326
|
return {};
|
|
371
327
|
case "set_project_expanded":
|
|
@@ -425,7 +381,7 @@ export class WsGateway {
|
|
|
425
381
|
this.requireStream(connection).host.cwd
|
|
426
382
|
),
|
|
427
383
|
};
|
|
428
|
-
//
|
|
384
|
+
// Read-only, so allowed while `repoBusy`.
|
|
429
385
|
case "list_changes":
|
|
430
386
|
return {
|
|
431
387
|
changes: await RepoDiff.listChanges(
|
|
@@ -550,7 +506,7 @@ export class WsGateway {
|
|
|
550
506
|
return sessionId ? this.streams.get(sessionId) : undefined;
|
|
551
507
|
}
|
|
552
508
|
|
|
553
|
-
/**
|
|
509
|
+
/** Uses the process cwd when the connection is not attached. */
|
|
554
510
|
private scopeFor(connection: ClientConnection): ExtensionScope {
|
|
555
511
|
return {
|
|
556
512
|
cwd: this.streamFor(connection)?.host.cwd ?? process.cwd(),
|
|
@@ -561,7 +517,7 @@ export class WsGateway {
|
|
|
561
517
|
private rebuildAgents(): void {
|
|
562
518
|
for (const stream of this.streams.values()) {
|
|
563
519
|
const { host } = stream;
|
|
564
|
-
//
|
|
520
|
+
// Queued behind any running turn, which detaching the agent would kill.
|
|
565
521
|
void host
|
|
566
522
|
.serialize(() => host.invalidate())
|
|
567
523
|
.catch((err: unknown) => {
|
|
@@ -579,7 +535,7 @@ export class WsGateway {
|
|
|
579
535
|
return stream;
|
|
580
536
|
}
|
|
581
537
|
|
|
582
|
-
//
|
|
538
|
+
// The log path is derived server-side; never accept a client-sent path.
|
|
583
539
|
private async watchSubagent(
|
|
584
540
|
connection: ClientConnection,
|
|
585
541
|
stream: SessionStream,
|
|
@@ -594,7 +550,7 @@ export class WsGateway {
|
|
|
594
550
|
if (path === null) {
|
|
595
551
|
return { error: `malformed call id: ${command.callId}` };
|
|
596
552
|
}
|
|
597
|
-
// A running call may not have written its log yet
|
|
553
|
+
// A running call may not have written its log yet.
|
|
598
554
|
if (!stream.isRunning(command.callId) && !(await Bun.file(path).exists())) {
|
|
599
555
|
return { error: `no subagent log for call ${command.callId}` };
|
|
600
556
|
}
|
|
@@ -641,19 +597,16 @@ export class WsGateway {
|
|
|
641
597
|
return {};
|
|
642
598
|
}
|
|
643
599
|
|
|
644
|
-
/**
|
|
600
|
+
/** Watches only attached sessions, and the catalogue only while anyone is connected. */
|
|
645
601
|
private syncWatches(): void {
|
|
646
602
|
for (const [sessionId, stream] of this.streams) {
|
|
647
|
-
//
|
|
603
|
+
// Attached, not attentive: hidden tabs still need updates.
|
|
648
604
|
stream.watchFiles(this.isAttached(sessionId));
|
|
649
605
|
}
|
|
650
606
|
this.catalogue.watch(this.connections.size > 0);
|
|
651
607
|
}
|
|
652
608
|
|
|
653
|
-
/**
|
|
654
|
-
* An operation that moves the working tree cannot run under an agent that
|
|
655
|
-
* may be halfway through an edit; `push` leaves the tree alone, so it can.
|
|
656
|
-
*/
|
|
609
|
+
/** `movesTree` operations are refused while an agent in the cwd is mid-turn. */
|
|
657
610
|
private async runGit<T = never>(
|
|
658
611
|
stream: SessionStream,
|
|
659
612
|
movesTree: boolean,
|
|
@@ -701,7 +654,7 @@ export class WsGateway {
|
|
|
701
654
|
return false;
|
|
702
655
|
}
|
|
703
656
|
|
|
704
|
-
/**
|
|
657
|
+
/** Pushes fresh state to the other sessions in `cwd` when its busy flag flips. */
|
|
705
658
|
private syncRepoBusy(cwd: string, source: string): void {
|
|
706
659
|
const busy = this.repoBusy(cwd);
|
|
707
660
|
if (this.busyRepos.has(cwd) === busy) {
|
|
@@ -747,7 +700,6 @@ export class WsGateway {
|
|
|
747
700
|
.some((connection) => connection.sessionId === sessionId);
|
|
748
701
|
}
|
|
749
702
|
|
|
750
|
-
// The TUI never attaches, so reading a session there clears no dot here and a turn it runs trips no mark.
|
|
751
703
|
private isBeingRead(sessionId: string): boolean {
|
|
752
704
|
return this.connections
|
|
753
705
|
.values()
|
|
@@ -796,7 +748,7 @@ export class WsGateway {
|
|
|
796
748
|
...(target.cwd === undefined ? {} : { cwd: target.cwd }),
|
|
797
749
|
...(target.like === undefined ? {} : { like: target.like }),
|
|
798
750
|
});
|
|
799
|
-
// Pi assigns the id and
|
|
751
|
+
// Pi assigns the id and session file only once an agent exists.
|
|
800
752
|
const agent = host.agentSession ?? (await host.ensureAgent());
|
|
801
753
|
const id = agent.sessionId;
|
|
802
754
|
const path = agent.sessionFile ?? host.settings.sessionPath;
|
|
@@ -816,8 +768,7 @@ export class WsGateway {
|
|
|
816
768
|
});
|
|
817
769
|
stream.start();
|
|
818
770
|
this.catalogue.track(id, host.status);
|
|
819
|
-
//
|
|
820
|
-
// never leaves is a session that is never detached.
|
|
771
|
+
// Observe, not subscribe, so this doesn't count as an attached client.
|
|
821
772
|
stream.observe((event) => {
|
|
822
773
|
if (event.type === "session_state") {
|
|
823
774
|
this.catalogue.onStatus(id, event.status);
|
|
@@ -832,18 +783,14 @@ export class WsGateway {
|
|
|
832
783
|
stream: SessionStream,
|
|
833
784
|
command: Command & { readonly type: "user_message" }
|
|
834
785
|
): Promise<Outcome> {
|
|
835
|
-
//
|
|
836
|
-
// fallthrough trims it, so a client that does not trim would otherwise
|
|
837
|
-
// slip a command past this check and into pi's own.
|
|
786
|
+
// Pi trims before dispatching, so trim here too or a padded command slips past this check.
|
|
838
787
|
const text = command.text.trim();
|
|
839
|
-
// Read before anything is rendered: what a command is, is its own text.
|
|
840
788
|
const dispatched = await this.dispatchCommand(stream, text);
|
|
841
789
|
const taken = this.uploads.take(
|
|
842
790
|
stream.sessionId,
|
|
843
791
|
(command.attachments ?? []).map((ref) => ref.id)
|
|
844
792
|
);
|
|
845
|
-
//
|
|
846
|
-
// endpoint stops holding their bytes for the rest of the session.
|
|
793
|
+
// Uploads are taken even for commands, so the endpoint releases them.
|
|
847
794
|
if (dispatched) {
|
|
848
795
|
return { dispatched: true };
|
|
849
796
|
}
|
|
@@ -852,11 +799,7 @@ export class WsGateway {
|
|
|
852
799
|
return {};
|
|
853
800
|
}
|
|
854
801
|
|
|
855
|
-
/**
|
|
856
|
-
* Whether `text` names an extension command, having started it if it does.
|
|
857
|
-
* Only the decision is awaited: the handler runs on beyond the ack, exactly
|
|
858
|
-
* as a turn does.
|
|
859
|
-
*/
|
|
802
|
+
/** Starts `text` if it is an extension command; only the decision is awaited. */
|
|
860
803
|
private async dispatchCommand(
|
|
861
804
|
stream: SessionStream,
|
|
862
805
|
text: string
|
|
@@ -865,8 +808,6 @@ export class WsGateway {
|
|
|
865
808
|
if (!text.startsWith("/")) {
|
|
866
809
|
return false;
|
|
867
810
|
}
|
|
868
|
-
// The picker offers extension commands only once an agent exists, so this
|
|
869
|
-
// is someone typing from memory; cold is idle, so nothing is queued behind it.
|
|
870
811
|
const agent = host.agentSession ?? (await host.ensureAgent());
|
|
871
812
|
const name = commandNameOf(text);
|
|
872
813
|
if (agent.extensionRunner.getCommand(name) === undefined) {
|
|
@@ -874,9 +815,7 @@ export class WsGateway {
|
|
|
874
815
|
}
|
|
875
816
|
void stream
|
|
876
817
|
.dispatch(`/${name}`, async () => {
|
|
877
|
-
//
|
|
878
|
-
// with the earlier message, and pi reads the leading `/` before it
|
|
879
|
-
// looks at `isStreaming` — so the turn we are holding dispatches it.
|
|
818
|
+
// Never merged into the queue, or the leading `/` would be lost.
|
|
880
819
|
if (host.isStreaming) {
|
|
881
820
|
await agent.prompt(text, {
|
|
882
821
|
streamingBehavior: "steer",
|
|
@@ -894,7 +833,7 @@ export class WsGateway {
|
|
|
894
833
|
return true;
|
|
895
834
|
}
|
|
896
835
|
|
|
897
|
-
// Merge
|
|
836
|
+
// Merge with the queued message: pi holds at most one.
|
|
898
837
|
private prompt(
|
|
899
838
|
stream: SessionStream,
|
|
900
839
|
text: string,
|
|
@@ -160,25 +160,16 @@ for (const path of values.upload ?? []) {
|
|
|
160
160
|
process.stderr.write(`uploaded: ${JSON.stringify(file)}\n`);
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
if (values.cancel) {
|
|
164
|
-
const response = await probe.send({
|
|
165
|
-
type: "cancel",
|
|
166
|
-
sessionId: probe.sessionId ?? "",
|
|
167
|
-
});
|
|
168
|
-
process.stderr.write(`cancel: ${JSON.stringify(response)}\n`);
|
|
169
|
-
}
|
|
170
|
-
if (values.dequeue) {
|
|
171
|
-
const response = await probe.send({
|
|
172
|
-
type: "dequeue",
|
|
173
|
-
sessionId: probe.sessionId ?? "",
|
|
174
|
-
});
|
|
175
|
-
process.stderr.write(`dequeue: ${JSON.stringify(response)}\n`);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
163
|
const sessionId = probe.sessionId ?? "";
|
|
179
164
|
const report = (what: string, response: ResponseEvent): void => {
|
|
180
165
|
process.stderr.write(`${what}: ${JSON.stringify(response)}\n`);
|
|
181
166
|
};
|
|
167
|
+
if (values.cancel) {
|
|
168
|
+
report("cancel", await probe.send({ type: "cancel", sessionId }));
|
|
169
|
+
}
|
|
170
|
+
if (values.dequeue) {
|
|
171
|
+
report("dequeue", await probe.send({ type: "dequeue", sessionId }));
|
|
172
|
+
}
|
|
182
173
|
if (values.rename !== undefined) {
|
|
183
174
|
report("rename", await probe.rename(sessionId, values.rename || null));
|
|
184
175
|
}
|
|
@@ -200,9 +191,6 @@ if (values.unpin !== undefined) {
|
|
|
200
191
|
|
|
201
192
|
if (values.prompt !== undefined) {
|
|
202
193
|
await probe.promptWith(values.prompt, uploaded);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (values.prompt !== undefined) {
|
|
206
194
|
await probe.waitFor(
|
|
207
195
|
(event) => event.type === "session_state" && event.status === "idle",
|
|
208
196
|
{ timeoutMs: 10 * 60_000, from: mark }
|
|
@@ -20,7 +20,7 @@ export class Bot {
|
|
|
20
20
|
private readonly scheduler: TaskScheduler;
|
|
21
21
|
private readonly config: TelegramConfig;
|
|
22
22
|
private readonly commands: Commands;
|
|
23
|
-
/**
|
|
23
|
+
/** Settles only once the bot is stopped. */
|
|
24
24
|
private polling: Promise<void> | undefined;
|
|
25
25
|
|
|
26
26
|
public constructor(config: TelegramConfig, runtime: AgentRuntime) {
|
|
@@ -106,7 +106,7 @@ export class Bot {
|
|
|
106
106
|
this.registry.setBotUsername(username);
|
|
107
107
|
console.log(`bot @${username} ready`);
|
|
108
108
|
await this.scheduler.start();
|
|
109
|
-
//
|
|
109
|
+
// Not awaited: it resolves only when polling stops.
|
|
110
110
|
this.polling = this.grammy.start().catch((err: unknown) => {
|
|
111
111
|
console.error("[bot] polling stopped:", err);
|
|
112
112
|
});
|
|
@@ -34,6 +34,12 @@ const CB_EFFORT = "effort";
|
|
|
34
34
|
const CB_LOGS = "logs";
|
|
35
35
|
const CB_MODEL = "model";
|
|
36
36
|
const CB_TEMPORARY = "temporary";
|
|
37
|
+
const MODEL_PICKS_CAP = 100;
|
|
38
|
+
|
|
39
|
+
type ModelPick = {
|
|
40
|
+
readonly sessionId: SessionId;
|
|
41
|
+
readonly candidates: readonly string[];
|
|
42
|
+
};
|
|
37
43
|
|
|
38
44
|
type BotCommand = { readonly command: string; readonly description: string };
|
|
39
45
|
|
|
@@ -175,6 +181,7 @@ export class Commands {
|
|
|
175
181
|
private readonly config: TelegramConfig;
|
|
176
182
|
private readonly api: Api;
|
|
177
183
|
private readonly registry: SessionRegistry;
|
|
184
|
+
private readonly modelPicks: Map<string, ModelPick>;
|
|
178
185
|
|
|
179
186
|
private static readonly COMMANDS: readonly CommandSpec[] = [
|
|
180
187
|
{
|
|
@@ -261,6 +268,7 @@ export class Commands {
|
|
|
261
268
|
this.config = config;
|
|
262
269
|
this.api = api;
|
|
263
270
|
this.registry = registry;
|
|
271
|
+
this.modelPicks = new Map();
|
|
264
272
|
}
|
|
265
273
|
|
|
266
274
|
public async handleCommand(
|
|
@@ -295,34 +303,8 @@ export class Commands {
|
|
|
295
303
|
ctx: Filter<Context, "callback_query:data">
|
|
296
304
|
): Promise<void> {
|
|
297
305
|
const data = ctx.callbackQuery.data;
|
|
298
|
-
|
|
299
306
|
if (data.startsWith(`${CB_MODEL}|`)) {
|
|
300
|
-
|
|
301
|
-
const idx2 = data.lastIndexOf("|");
|
|
302
|
-
const modelId = data.slice(idx1 + 1, idx2);
|
|
303
|
-
const keyPart = data.slice(idx2 + 1);
|
|
304
|
-
const session = this.registry.get(decodeId(keyPart));
|
|
305
|
-
await ctx.answerCallbackQuery({ text: `Model: ${modelId}` });
|
|
306
|
-
try {
|
|
307
|
-
const result = await session.setModel(modelId);
|
|
308
|
-
if (result.ok) {
|
|
309
|
-
await safeEditMessage(
|
|
310
|
-
ctx,
|
|
311
|
-
`<b>Model</b> → <code>${Markdown.escape(result.id)}</code>`
|
|
312
|
-
);
|
|
313
|
-
} else {
|
|
314
|
-
await safeEditMessage(
|
|
315
|
-
ctx,
|
|
316
|
-
strikeOriginal(ctx, `⚠️ model set failed: ${modelId}`)
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
} catch (err) {
|
|
320
|
-
console.error(`[bot] model callback failed for ${modelId}:`, err);
|
|
321
|
-
await safeEditMessage(
|
|
322
|
-
ctx,
|
|
323
|
-
strikeOriginal(ctx, `⚠️ model set failed: ${(err as Error).message}`)
|
|
324
|
-
);
|
|
325
|
-
}
|
|
307
|
+
await this.onModelPicked(ctx, data);
|
|
326
308
|
return;
|
|
327
309
|
}
|
|
328
310
|
|
|
@@ -331,24 +313,7 @@ export class Commands {
|
|
|
331
313
|
const keyPart = colon >= 0 ? data.slice(colon + 1) : "";
|
|
332
314
|
|
|
333
315
|
if (action === CB_CLEAR_CONFIRM && keyPart) {
|
|
334
|
-
|
|
335
|
-
const wasBusy = session.isStreaming;
|
|
336
|
-
await ctx.answerCallbackQuery({
|
|
337
|
-
text: wasBusy ? "Queued — clearing after current turn" : "Cleared",
|
|
338
|
-
});
|
|
339
|
-
try {
|
|
340
|
-
await session.clear();
|
|
341
|
-
await safeEditMessage(
|
|
342
|
-
ctx,
|
|
343
|
-
strikeOriginal(ctx, "Context window cleared.")
|
|
344
|
-
);
|
|
345
|
-
} catch (err) {
|
|
346
|
-
console.error(`[bot] queued clear failed:`, err);
|
|
347
|
-
await safeEditMessage(
|
|
348
|
-
ctx,
|
|
349
|
-
strikeOriginal(ctx, `⚠️ clear failed: ${(err as Error).message}`)
|
|
350
|
-
);
|
|
351
|
-
}
|
|
316
|
+
await this.onClearConfirmed(ctx, keyPart);
|
|
352
317
|
return;
|
|
353
318
|
}
|
|
354
319
|
if (action === CB_CLEAR_CANCEL) {
|
|
@@ -358,24 +323,87 @@ export class Commands {
|
|
|
358
323
|
}
|
|
359
324
|
const picker = PICKERS.find((one) => one.action === action);
|
|
360
325
|
if (picker && keyPart) {
|
|
361
|
-
|
|
362
|
-
const applyTo = parts && picker.apply(parts.value);
|
|
363
|
-
if (!parts || !applyTo) {
|
|
364
|
-
await ctx.answerCallbackQuery();
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
const session = this.registry.get(decodeId(parts.key));
|
|
368
|
-
await applyTo(session);
|
|
369
|
-
await ctx.answerCallbackQuery({
|
|
370
|
-
text: `${picker.title}: ${picker.label(parts.value)}`,
|
|
371
|
-
});
|
|
372
|
-
const { kb, html } = buildPicker(picker, session, parts.value);
|
|
373
|
-
await safeEditMessage(ctx, html, kb);
|
|
326
|
+
await this.onPicked(ctx, picker, keyPart);
|
|
374
327
|
return;
|
|
375
328
|
}
|
|
376
329
|
await ctx.answerCallbackQuery();
|
|
377
330
|
}
|
|
378
331
|
|
|
332
|
+
private async onModelPicked(
|
|
333
|
+
ctx: Filter<Context, "callback_query:data">,
|
|
334
|
+
data: string
|
|
335
|
+
): Promise<void> {
|
|
336
|
+
const [, token = "", index = ""] = data.split("|");
|
|
337
|
+
const pick = this.modelPicks.get(token);
|
|
338
|
+
const modelId = pick?.candidates[Number(index)];
|
|
339
|
+
if (!pick || modelId === undefined) {
|
|
340
|
+
await ctx.answerCallbackQuery({ text: "Expired — run /model again" });
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const session = this.registry.get(pick.sessionId);
|
|
344
|
+
await ctx.answerCallbackQuery({ text: `Model: ${modelId}` });
|
|
345
|
+
try {
|
|
346
|
+
const result = await session.setModel(modelId);
|
|
347
|
+
await safeEditMessage(
|
|
348
|
+
ctx,
|
|
349
|
+
result.ok
|
|
350
|
+
? `<b>Model</b> → <code>${Markdown.escape(result.id)}</code>`
|
|
351
|
+
: strikeOriginal(ctx, `⚠️ model set failed: ${modelId}`)
|
|
352
|
+
);
|
|
353
|
+
} catch (err) {
|
|
354
|
+
console.error(`[bot] model callback failed for ${modelId}:`, err);
|
|
355
|
+
await safeEditMessage(
|
|
356
|
+
ctx,
|
|
357
|
+
strikeOriginal(ctx, `⚠️ model set failed: ${(err as Error).message}`)
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private async onClearConfirmed(
|
|
363
|
+
ctx: Filter<Context, "callback_query:data">,
|
|
364
|
+
keyPart: string
|
|
365
|
+
): Promise<void> {
|
|
366
|
+
const session = this.registry.get(decodeId(keyPart));
|
|
367
|
+
await ctx.answerCallbackQuery({
|
|
368
|
+
text: session.isStreaming
|
|
369
|
+
? "Queued — clearing after current turn"
|
|
370
|
+
: "Cleared",
|
|
371
|
+
});
|
|
372
|
+
try {
|
|
373
|
+
await session.clear();
|
|
374
|
+
await safeEditMessage(
|
|
375
|
+
ctx,
|
|
376
|
+
strikeOriginal(ctx, "Context window cleared.")
|
|
377
|
+
);
|
|
378
|
+
} catch (err) {
|
|
379
|
+
console.error(`[bot] queued clear failed:`, err);
|
|
380
|
+
await safeEditMessage(
|
|
381
|
+
ctx,
|
|
382
|
+
strikeOriginal(ctx, `⚠️ clear failed: ${(err as Error).message}`)
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
private async onPicked(
|
|
388
|
+
ctx: Filter<Context, "callback_query:data">,
|
|
389
|
+
picker: Picker,
|
|
390
|
+
keyPart: string
|
|
391
|
+
): Promise<void> {
|
|
392
|
+
const parts = splitValueAndKey(keyPart);
|
|
393
|
+
const applyTo = parts && picker.apply(parts.value);
|
|
394
|
+
if (!parts || !applyTo) {
|
|
395
|
+
await ctx.answerCallbackQuery();
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const session = this.registry.get(decodeId(parts.key));
|
|
399
|
+
await applyTo(session);
|
|
400
|
+
await ctx.answerCallbackQuery({
|
|
401
|
+
text: `${picker.title}: ${picker.label(parts.value)}`,
|
|
402
|
+
});
|
|
403
|
+
const { kb, html } = buildPicker(picker, session, parts.value);
|
|
404
|
+
await safeEditMessage(ctx, html, kb);
|
|
405
|
+
}
|
|
406
|
+
|
|
379
407
|
private async runQueued(
|
|
380
408
|
ctx: Filter<Context, "message">,
|
|
381
409
|
session: Session,
|
|
@@ -497,11 +525,14 @@ export class Commands {
|
|
|
497
525
|
private async cmdModelWrite(session: Session, args: string): Promise<void> {
|
|
498
526
|
const result = await session.setModel(args);
|
|
499
527
|
if (!result.ok) {
|
|
500
|
-
const
|
|
528
|
+
const token = this.rememberModelPick({
|
|
529
|
+
sessionId: session.id,
|
|
530
|
+
candidates: result.candidates,
|
|
531
|
+
});
|
|
501
532
|
const kb = new InlineKeyboard();
|
|
502
|
-
|
|
503
|
-
kb.text(c, `${CB_MODEL}|${
|
|
504
|
-
}
|
|
533
|
+
result.candidates.forEach((c, i) => {
|
|
534
|
+
kb.text(c, `${CB_MODEL}|${token}|${i}`).row();
|
|
535
|
+
});
|
|
505
536
|
const header =
|
|
506
537
|
result.kind === "ambiguous"
|
|
507
538
|
? `⚠️ Multiple matches for "${Markdown.escape(args)}". Please choose one below or use /model with a more specific name.`
|
|
@@ -515,6 +546,18 @@ export class Commands {
|
|
|
515
546
|
);
|
|
516
547
|
}
|
|
517
548
|
|
|
549
|
+
private rememberModelPick(pick: ModelPick): string {
|
|
550
|
+
const token = Math.random().toString(36).slice(2, 10);
|
|
551
|
+
this.modelPicks.set(token, pick);
|
|
552
|
+
if (this.modelPicks.size > MODEL_PICKS_CAP) {
|
|
553
|
+
const oldest = this.modelPicks.keys().next().value;
|
|
554
|
+
if (oldest !== undefined) {
|
|
555
|
+
this.modelPicks.delete(oldest);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return token;
|
|
559
|
+
}
|
|
560
|
+
|
|
518
561
|
private async cmdEffort(session: Session): Promise<void> {
|
|
519
562
|
const supported = session.supportedThinkingLevels;
|
|
520
563
|
if (supported.length <= 1) {
|
|
@@ -583,7 +626,7 @@ export class Commands {
|
|
|
583
626
|
const notes = outcome.skipped.map(
|
|
584
627
|
(s) => `\nSkipped ${s.label}: ${s.reason}.`
|
|
585
628
|
);
|
|
586
|
-
//
|
|
629
|
+
// This process restarts by exiting, so restart siblings first.
|
|
587
630
|
await Supervisor.restartSiblings(DaemonUnit);
|
|
588
631
|
if (!Supervisor.isSupervised()) {
|
|
589
632
|
await progress(
|
|
@@ -738,7 +781,7 @@ function strikeOriginal(
|
|
|
738
781
|
note: string
|
|
739
782
|
): string {
|
|
740
783
|
const original = ctx.callbackQuery.message?.text ?? "";
|
|
741
|
-
return `<s>${Markdown.escape(original)}</s>\n\n<i>${note}</i>`;
|
|
784
|
+
return `<s>${Markdown.escape(original)}</s>\n\n<i>${Markdown.escape(note)}</i>`;
|
|
742
785
|
}
|
|
743
786
|
|
|
744
787
|
function renderCompactSuccess(result: SessionCompactResult): string {
|
|
@@ -763,6 +806,6 @@ async function safeEditMessage(
|
|
|
763
806
|
reply_markup: replyMarkup,
|
|
764
807
|
});
|
|
765
808
|
} catch {
|
|
766
|
-
//
|
|
809
|
+
// The message may be past Telegram's edit window.
|
|
767
810
|
}
|
|
768
811
|
}
|
|
@@ -10,7 +10,6 @@ export type Cli = {
|
|
|
10
10
|
readonly cwd?: string;
|
|
11
11
|
readonly model?: string;
|
|
12
12
|
readonly configDir?: string;
|
|
13
|
-
readonly printConfig: boolean;
|
|
14
13
|
};
|
|
15
14
|
|
|
16
15
|
export type TelegramConfig = {
|
|
@@ -40,7 +39,6 @@ function parseArgs(args: ReadonlyArray<string>): Cli {
|
|
|
40
39
|
let cwd: string | undefined;
|
|
41
40
|
let model: string | undefined;
|
|
42
41
|
let configDir: string | undefined;
|
|
43
|
-
let printConfig = false;
|
|
44
42
|
|
|
45
43
|
Argv.scan(args, (key, take) => {
|
|
46
44
|
switch (key) {
|
|
@@ -59,15 +57,12 @@ function parseArgs(args: ReadonlyArray<string>): Cli {
|
|
|
59
57
|
case "--config-dir":
|
|
60
58
|
configDir = take();
|
|
61
59
|
break;
|
|
62
|
-
case "--print-config":
|
|
63
|
-
printConfig = true;
|
|
64
|
-
break;
|
|
65
60
|
case "--mode":
|
|
66
61
|
take();
|
|
67
62
|
break;
|
|
68
63
|
}
|
|
69
64
|
});
|
|
70
|
-
return { token, allow, cwd, model, configDir
|
|
65
|
+
return { token, allow, cwd, model, configDir };
|
|
71
66
|
}
|
|
72
67
|
|
|
73
68
|
async function load(cli: Cli): Promise<TelegramConfig> {
|