pim-agent 0.13.0 → 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 +3 -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 +1 -2
- package/packages/core/src/extensions/system-prompt/prompt.ts +2 -4
- 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-CZ7V84ot.js → abnfDiagram-O67JEVCF-WweOU5DK.js} +1 -1
- package/packages/web/dist/client/assets/{arc-DiHGkUvc.js → arc-O2PmZXWt.js} +1 -1
- package/packages/web/dist/client/assets/architecture-7GRP2DOG-COBxVilU.js +1 -0
- package/packages/web/dist/client/assets/{architectureDiagram-NJMV4G6O-XS5DhfJB.js → architectureDiagram-NJMV4G6O-DwYq1YaM.js} +1 -1
- package/packages/web/dist/client/assets/{blockDiagram-BEXU5L5S-BCsgLvy1.js → blockDiagram-BEXU5L5S-CVab041n.js} +1 -1
- package/packages/web/dist/client/assets/{c4Diagram-YGBWAQC7-Bj9LcWNm.js → c4Diagram-YGBWAQC7-BmotHez3.js} +1 -1
- package/packages/web/dist/client/assets/channel-BKV-IcTA.js +1 -0
- package/packages/web/dist/client/assets/{chunk-3FUC2YCW-BmDurFxp.js → chunk-3FUC2YCW-CWNS7-n4.js} +1 -1
- package/packages/web/dist/client/assets/{chunk-5DYCD2WN-DAjG-pqn.js → chunk-5DYCD2WN-DflvXohu.js} +1 -1
- package/packages/web/dist/client/assets/{chunk-742MDFTN-Bmxs5puY.js → chunk-742MDFTN-B_0t6_RE.js} +1 -1
- package/packages/web/dist/client/assets/{chunk-7INBJB4K-Di7kIxrQ.js → chunk-7INBJB4K-wKj0Q3Kv.js} +1 -1
- package/packages/web/dist/client/assets/{chunk-7M6MHVWA-CSc8sR0b.js → chunk-7M6MHVWA-BMvUghc8.js} +1 -1
- package/packages/web/dist/client/assets/{chunk-7PRAP22T-C8kd5QvY.js → chunk-7PRAP22T-BY41wkcT.js} +1 -1
- package/packages/web/dist/client/assets/{chunk-GTNCS2PH-DG2b2LYE.js → chunk-GTNCS2PH-PqY_YoS0.js} +1 -1
- package/packages/web/dist/client/assets/{chunk-GWA4HPMP-DdLaLQCu.js → chunk-GWA4HPMP-fozSU5ty.js} +1 -1
- package/packages/web/dist/client/assets/{chunk-MBY4JIJT-C1j0yQlk.js → chunk-MBY4JIJT-CrHrC3jw.js} +1 -1
- package/packages/web/dist/client/assets/{chunk-NETBCI7D-Bfq9H74p.js → chunk-NETBCI7D-DWdAxieG.js} +1 -1
- package/packages/web/dist/client/assets/{chunk-O7XYJQB3-ZHvBmBO-.js → chunk-O7XYJQB3-B6uOn9iS.js} +1 -1
- package/packages/web/dist/client/assets/{chunk-UA2S7LBM-ku1JeHSK.js → chunk-UA2S7LBM-BxYSHqyy.js} +1 -1
- package/packages/web/dist/client/assets/{chunk-WEXAMYUT-BP1iga_7.js → chunk-WEXAMYUT-CtwIaQDa.js} +1 -1
- package/packages/web/dist/client/assets/{chunk-XXDRQBXY-BBeccPcX.js → chunk-XXDRQBXY-fgBFe-PX.js} +1 -1
- package/packages/web/dist/client/assets/{chunk-Z7XXMR3K-SMHZugaR.js → chunk-Z7XXMR3K-CPonoV3p.js} +1 -1
- package/packages/web/dist/client/assets/{chunk-ZIGJFQKS-CFU_JLG4.js → chunk-ZIGJFQKS-Cdu87cTj.js} +1 -1
- package/packages/web/dist/client/assets/{classDiagram-v2-NBCMYWYE-CYviuQzJ.js → classDiagram-v2-NBCMYWYE-DSWsPaa7.js} +1 -1
- package/packages/web/dist/client/assets/{core-DyHsgs5k.js → core-CBoS7AST.js} +1 -1
- package/packages/web/dist/client/assets/{cose-bilkent-JH36ORCC-9v2ZkJzo.js → cose-bilkent-JH36ORCC-CTwlldtJ.js} +1 -1
- package/packages/web/dist/client/assets/{cynefin-OW5HDTMX-Bx5xRadi.js → cynefin-OW5HDTMX-CZQ4EPNJ.js} +1 -1
- package/packages/web/dist/client/assets/{cynefinDiagram-VND7K2PF-BRfGDKqa.js → cynefinDiagram-VND7K2PF-C2gp6H_Q.js} +1 -1
- package/packages/web/dist/client/assets/{dagre-6A5THRUB-C4l-2L2e.js → dagre-6A5THRUB-CDxTP47t.js} +1 -1
- package/packages/web/dist/client/assets/{diagram-22UHCM2B-d_hQ3kyi.js → diagram-22UHCM2B-BIohE1lI.js} +1 -1
- package/packages/web/dist/client/assets/{diagram-3UASUU5V-DrEeguPI.js → diagram-3UASUU5V-TwAUir-w.js} +1 -1
- package/packages/web/dist/client/assets/{diagram-ATOU4E4O-BmfQ22pD.js → diagram-ATOU4E4O-BM1JQrIu.js} +1 -1
- package/packages/web/dist/client/assets/{diagram-CDSNMT55-BjVWjuPF.js → diagram-CDSNMT55-BKIqhxSI.js} +1 -1
- package/packages/web/dist/client/assets/{diagram-MLGK6HIB-wDjk-O8i.js → diagram-MLGK6HIB-CVe3QDHr.js} +1 -1
- package/packages/web/dist/client/assets/{diagram-MPIPVDR6-DqXQFE1s.js → diagram-MPIPVDR6-Bj-MQIFI.js} +1 -1
- package/packages/web/dist/client/assets/{dist-BddGOpie.js → dist-DlZpcyc2.js} +1 -1
- package/packages/web/dist/client/assets/{ebnfDiagram-ZINNZB2B-UB8kOITM.js → ebnfDiagram-ZINNZB2B-C06mmvmz.js} +1 -1
- package/packages/web/dist/client/assets/{elk-276RUBZZ-D4miYQnD.js → elk-276RUBZZ-CizJSNEj.js} +1 -1
- package/packages/web/dist/client/assets/{erDiagram-OPXOYQCR--RXKv7KH.js → erDiagram-OPXOYQCR-M3FQkKI1.js} +1 -1
- 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-C0CFpE5x.js → ganttDiagram-FUAMR5RP-CojF9Q16.js} +1 -1
- package/packages/web/dist/client/assets/{gitGraph-4MIJSDKK-VPL7nv2c.js → gitGraph-4MIJSDKK-YvKqGEPk.js} +1 -1
- package/packages/web/dist/client/assets/{gitGraphDiagram-X574FWY7-CgxJA-T8.js → gitGraphDiagram-X574FWY7-XqNqIFo9.js} +1 -1
- package/packages/web/dist/client/assets/index--2CDtzvY.css +1 -0
- package/packages/web/dist/client/assets/{index-XTlTnQmx.js → index-bT6QPJZs.js} +7 -7
- package/packages/web/dist/client/assets/{info-A6RAGUB7-P0-o68pj.js → info-A6RAGUB7-B_1BCzM2.js} +1 -1
- package/packages/web/dist/client/assets/{infoDiagram-VRGFBTTK-DFJZNRUP.js → infoDiagram-VRGFBTTK-zFX8B-dP.js} +1 -1
- package/packages/web/dist/client/assets/{ishikawaDiagram-OU5B5YK6-ZkyhNd1T.js → ishikawaDiagram-OU5B5YK6-DLzQ_Uyf.js} +1 -1
- package/packages/web/dist/client/assets/{journeyDiagram-ZHPQQLJL-DFevH8my.js → journeyDiagram-ZHPQQLJL-S3WRMmoa.js} +1 -1
- package/packages/web/dist/client/assets/{kanban-definition-PNTS6WVX-l7e7ZZDC.js → kanban-definition-PNTS6WVX-B5Z8I-kM.js} +1 -1
- package/packages/web/dist/client/assets/{line-DO_s6Ujd.js → line-BGMH-i2X.js} +1 -1
- package/packages/web/dist/client/assets/{linear-C4xH5PPc.js → linear-KsrestXG.js} +1 -1
- package/packages/web/dist/client/assets/{mermaid-parser.core-nQaTP3VY.js → mermaid-parser.core-DNffyJJA.js} +3 -3
- package/packages/web/dist/client/assets/{mermaid.core-Cz05yTZh.js → mermaid.core-Cz3vJeQG.js} +4 -4
- package/packages/web/dist/client/assets/{mindmap-definition-NLK3R4M7-CHOEuHm2.js → mindmap-definition-NLK3R4M7-CzA61XUf.js} +1 -1
- package/packages/web/dist/client/assets/{packet-AYTQ26CC-BH3ehPMo.js → packet-AYTQ26CC-BO55e7cR.js} +1 -1
- package/packages/web/dist/client/assets/{pegDiagram-GJSIUBJH-CkWBLtad.js → pegDiagram-GJSIUBJH-DeRiXYNd.js} +1 -1
- package/packages/web/dist/client/assets/{pie-WAS4IAKB-nNVEq3dx.js → pie-WAS4IAKB-D9ibrQRe.js} +1 -1
- package/packages/web/dist/client/assets/{pieDiagram-5QR66LMP-DimUBAgN.js → pieDiagram-5QR66LMP-BaBpPP33.js} +1 -1
- package/packages/web/dist/client/assets/{quadrantDiagram-O4NWA36T-BR0XpRGQ.js → quadrantDiagram-O4NWA36T-DIfn4GjM.js} +1 -1
- package/packages/web/dist/client/assets/{radar-RG4KPBEZ-paA-H3mb.js → radar-RG4KPBEZ-Dmn5-nJq.js} +1 -1
- package/packages/web/dist/client/assets/{railroad-74A4TZTK-yEiKB3rz.js → railroad-74A4TZTK-ZCWNEd2Z.js} +1 -1
- 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--TXe6rdU.js → railroadDiagram-XR7U4H2S-CDPZ39sy.js} +1 -1
- package/packages/web/dist/client/assets/{requirementDiagram-PLB6GJNP-DyqH36Za.js → requirementDiagram-PLB6GJNP-CyCI8Yul.js} +1 -1
- package/packages/web/dist/client/assets/{sankeyDiagram-IPEJSGJF-Bg6e3BlM.js → sankeyDiagram-IPEJSGJF-p_XAPv72.js} +1 -1
- package/packages/web/dist/client/assets/{sequenceDiagram-PO4LG4MO-ClGf85My.js → sequenceDiagram-PO4LG4MO-DmR6IP_o.js} +1 -1
- package/packages/web/dist/client/assets/{src-DF7hQfMh.js → src-gnv7pJ1z.js} +1 -1
- package/packages/web/dist/client/assets/{stateDiagram-v2-GCMORJYK-BK8TpxNo.js → stateDiagram-v2-GCMORJYK-7ZTHl6ih.js} +1 -1
- package/packages/web/dist/client/assets/{swimlanes-2SLR337P-DDnTIT9M.js → swimlanes-2SLR337P-N6bj71pw.js} +1 -1
- package/packages/web/dist/client/assets/swimlanesDiagram-TC7HE7FX-BJQ2CBsp.js +8 -0
- package/packages/web/dist/client/assets/{timeline-definition-EJHVYXUP-mBBbH3bk.js → timeline-definition-EJHVYXUP-GNez0kW8.js} +1 -1
- package/packages/web/dist/client/assets/{treeView-Q6P3EWNA-CTK7tRwR.js → treeView-Q6P3EWNA-DyQZkboR.js} +1 -1
- package/packages/web/dist/client/assets/{treemap-WGGIJYW6-vOI-Ef_m.js → treemap-WGGIJYW6-_J8Pw2TM.js} +1 -1
- package/packages/web/dist/client/assets/{usecaseDiagram-POWQR4AR-COcii8x-.js → usecaseDiagram-POWQR4AR-Bg-CzKjl.js} +1 -1
- package/packages/web/dist/client/assets/{vennDiagram-UO4OBE2U-D6kmDGpM.js → vennDiagram-UO4OBE2U-DBQKHXhK.js} +1 -1
- package/packages/web/dist/client/assets/{wardley-WFR3VGLG-CtE2KI9r.js → wardley-WFR3VGLG-B5aAsQ3F.js} +1 -1
- package/packages/web/dist/client/assets/{wardleyDiagram-VNRHLVJA-ncxNGWnW.js → wardleyDiagram-VNRHLVJA-BKUQ1LK6.js} +1 -1
- package/packages/web/dist/client/assets/{xychartDiagram-PMCCYNJV-DUvd2Qze.js → xychartDiagram-PMCCYNJV-BI2sVbks.js} +1 -1
- package/packages/web/dist/client/index.html +2 -2
- package/packages/web/dist/client/assets/architecture-7GRP2DOG-HIzEIkwQ.js +0 -1
- package/packages/web/dist/client/assets/channel-BCA6qe9I.js +0 -1
- package/packages/web/dist/client/assets/eventmodeling-NTZA5JFV-l6SYFw1s.js +0 -1
- package/packages/web/dist/client/assets/flowDiagram-KWPJA3E3-CWW9qnqm.js +0 -1
- package/packages/web/dist/client/assets/index-D9_XJLSc.css +0 -1
- package/packages/web/dist/client/assets/railroad-abnf-HS5TGJTU-mVQx9aVM.js +0 -1
- package/packages/web/dist/client/assets/railroad-ebnf-LZEXJU2U-CllXx1OJ.js +0 -1
- package/packages/web/dist/client/assets/railroad-peg-WCYAUIDC-BBD2Q5W_.js +0 -1
- package/packages/web/dist/client/assets/swimlanesDiagram-TC7HE7FX-CM8UnP5t.js +0 -8
|
@@ -6,11 +6,11 @@ import { ImageRoute } from "#protocol/ImageRoute";
|
|
|
6
6
|
import { IMMUTABLE, serveFile } from "./StaticClient";
|
|
7
7
|
|
|
8
8
|
export type ImageEndpointDeps = {
|
|
9
|
-
/** Defaults to `~/.pim/cache
|
|
9
|
+
/** Defaults to `~/.pim/cache`. */
|
|
10
10
|
readonly root?: string;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
/**
|
|
13
|
+
/** Serves images cached by `read`; 404 once the TTL sweep removed them. */
|
|
14
14
|
export class ImageEndpoint {
|
|
15
15
|
private readonly root: string;
|
|
16
16
|
|
|
@@ -18,7 +18,6 @@ export class ImageEndpoint {
|
|
|
18
18
|
this.root = deps.root ?? SpillCache.dir();
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/** Whether this endpoint owns the path, so the gateway can route to it. */
|
|
22
21
|
public static owns(pathname: string): boolean {
|
|
23
22
|
return ImageRoute.owns(pathname);
|
|
24
23
|
}
|
|
@@ -27,7 +26,7 @@ export class ImageEndpoint {
|
|
|
27
26
|
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
28
27
|
return new Response("expected GET", { status: 405 });
|
|
29
28
|
}
|
|
30
|
-
//
|
|
29
|
+
// `nameOf` only admits `<hex>.<ext>`, so no path traversal.
|
|
31
30
|
const name = ImageRoute.nameOf(new URL(request.url).pathname);
|
|
32
31
|
return name === null
|
|
33
32
|
? new Response("not found", { status: 404 })
|
|
@@ -20,23 +20,21 @@ import {
|
|
|
20
20
|
|
|
21
21
|
export type ProbeOptions = {
|
|
22
22
|
readonly url: string;
|
|
23
|
-
/** Omit to have the server create
|
|
23
|
+
/** Omit to have the server create one. */
|
|
24
24
|
readonly sessionId?: string;
|
|
25
25
|
readonly cwd?: string;
|
|
26
26
|
readonly fromSeq?: number;
|
|
27
|
-
/** Sent on the attach frame; false is a client that is connected but not looking. */
|
|
28
27
|
readonly attentive?: boolean;
|
|
29
|
-
/** Called for every frame, in arrival order. */
|
|
30
28
|
readonly onEvent?: (event: ServerEvent) => void;
|
|
31
29
|
/** Keystroke debounce for `files`; 0 makes tests deterministic. */
|
|
32
30
|
readonly debounceMs?: number;
|
|
33
31
|
};
|
|
34
32
|
|
|
35
|
-
/**
|
|
33
|
+
/** The `POST /upload` response. */
|
|
36
34
|
export type UploadedFile = {
|
|
37
35
|
readonly id: string;
|
|
38
36
|
readonly path: string;
|
|
39
|
-
/**
|
|
37
|
+
/** Server-relative. */
|
|
40
38
|
readonly url: string;
|
|
41
39
|
readonly mimeType: string;
|
|
42
40
|
readonly isImage: boolean;
|
|
@@ -47,12 +45,11 @@ type Pending = {
|
|
|
47
45
|
readonly reject: (err: Error) => void;
|
|
48
46
|
};
|
|
49
47
|
|
|
50
|
-
/**
|
|
48
|
+
/** Headless protocol client for tests and the probe CLI. */
|
|
51
49
|
export class ProbeClient {
|
|
52
50
|
public sessionId: string | undefined;
|
|
53
51
|
public seq: number;
|
|
54
52
|
public readonly events: ServerEvent[] = [];
|
|
55
|
-
/** The `@` picker, answered by the server one query at a time. */
|
|
56
53
|
public readonly files: RemoteFilePickerSuggestionEngine;
|
|
57
54
|
private readonly options: ProbeOptions;
|
|
58
55
|
private readonly pending = new Map<string, Pending>();
|
|
@@ -105,7 +102,6 @@ export class ProbeClient {
|
|
|
105
102
|
});
|
|
106
103
|
}
|
|
107
104
|
|
|
108
|
-
/** Says whether this client's reader is present; regaining it reads the session. */
|
|
109
105
|
public attention(value: boolean): Promise<ResponseEvent> {
|
|
110
106
|
return this.send({ type: "attention", value });
|
|
111
107
|
}
|
|
@@ -141,7 +137,7 @@ export class ProbeClient {
|
|
|
141
137
|
query,
|
|
142
138
|
limit,
|
|
143
139
|
});
|
|
144
|
-
return
|
|
140
|
+
return ok(response).items ?? [];
|
|
145
141
|
}
|
|
146
142
|
|
|
147
143
|
public async pickCommands(
|
|
@@ -154,10 +150,10 @@ export class ProbeClient {
|
|
|
154
150
|
query,
|
|
155
151
|
...(limit === undefined ? {} : { limit }),
|
|
156
152
|
});
|
|
157
|
-
return
|
|
153
|
+
return ok(response).items ?? [];
|
|
158
154
|
}
|
|
159
155
|
|
|
160
|
-
/**
|
|
156
|
+
/** Child events stay enveloped in `events`. */
|
|
161
157
|
public watchSubagent(callId: string, fromSeq = 0): Promise<ResponseEvent> {
|
|
162
158
|
return this.send({
|
|
163
159
|
type: "watch_subagent",
|
|
@@ -171,38 +167,27 @@ export class ProbeClient {
|
|
|
171
167
|
return this.send({ type: "unwatch_subagent", callId });
|
|
172
168
|
}
|
|
173
169
|
|
|
174
|
-
/** Pi's session catalogue; answers whether or not this probe is attached. */
|
|
175
170
|
public async listSessions(
|
|
176
171
|
scope: SessionScope = {}
|
|
177
172
|
): Promise<SessionListing["sessions"]> {
|
|
178
173
|
return (await this.catalogue(scope)).sessions;
|
|
179
174
|
}
|
|
180
175
|
|
|
181
|
-
/** The listing whole: the rows the page kept and the projects they came from. */
|
|
182
176
|
public async catalogue(scope: SessionScope = {}): Promise<SessionListing> {
|
|
183
|
-
const response = await this.send({ type: "list_sessions", ...scope });
|
|
184
|
-
if (!response.success) {
|
|
185
|
-
throw new Error(response.error ?? "list_sessions failed");
|
|
186
|
-
}
|
|
177
|
+
const response = ok(await this.send({ type: "list_sessions", ...scope }));
|
|
187
178
|
return {
|
|
188
179
|
sessions: response.sessions ?? [],
|
|
189
180
|
projects: response.projects ?? [],
|
|
190
181
|
};
|
|
191
182
|
}
|
|
192
183
|
|
|
193
|
-
/** Searches every session on disk; an empty query warms the index and answers with no hits. */
|
|
194
184
|
public async search(
|
|
195
185
|
query: string,
|
|
196
186
|
scope: SearchScope = {}
|
|
197
187
|
): Promise<SessionSearch> {
|
|
198
|
-
const response =
|
|
199
|
-
type: "search_sessions",
|
|
200
|
-
|
|
201
|
-
...scope,
|
|
202
|
-
});
|
|
203
|
-
if (!response.success) {
|
|
204
|
-
throw new Error(response.error ?? "search_sessions failed");
|
|
205
|
-
}
|
|
188
|
+
const response = ok(
|
|
189
|
+
await this.send({ type: "search_sessions", query, ...scope })
|
|
190
|
+
);
|
|
206
191
|
return {
|
|
207
192
|
hits: response.hits ?? [],
|
|
208
193
|
dropped: response.dropped ?? [],
|
|
@@ -210,7 +195,6 @@ export class ProbeClient {
|
|
|
210
195
|
};
|
|
211
196
|
}
|
|
212
197
|
|
|
213
|
-
/** Names a session through pi's own name; `null` clears it back to its opening message. */
|
|
214
198
|
public rename(
|
|
215
199
|
sessionId: string,
|
|
216
200
|
value: string | null
|
|
@@ -218,7 +202,6 @@ export class ProbeClient {
|
|
|
218
202
|
return this.send({ type: "set_session_name", sessionId, value });
|
|
219
203
|
}
|
|
220
204
|
|
|
221
|
-
/** Puts a session out of the live listing, or brings it back. */
|
|
222
205
|
public setArchived(
|
|
223
206
|
sessionId: string,
|
|
224
207
|
value: boolean
|
|
@@ -226,67 +209,45 @@ export class ProbeClient {
|
|
|
226
209
|
return this.send({ type: "set_session_archived", sessionId, value });
|
|
227
210
|
}
|
|
228
211
|
|
|
229
|
-
/** Holds a session unread until something reads it; survives a listing and a re-attach. */
|
|
230
212
|
public markUnread(sessionId: string, value: boolean): Promise<ResponseEvent> {
|
|
231
213
|
return this.send({ type: "set_session_unread", sessionId, value });
|
|
232
214
|
}
|
|
233
215
|
|
|
234
|
-
/** Pins a working directory, not a session. */
|
|
235
216
|
public setPinned(cwd: string, value: boolean): Promise<ResponseEvent> {
|
|
236
217
|
return this.send({ type: "set_project_pinned", cwd, value });
|
|
237
218
|
}
|
|
238
219
|
|
|
239
|
-
/** Re-orders the pinned directories; the whole order, pinned ones only. */
|
|
240
220
|
public setPinOrder(order: readonly string[]): Promise<ResponseEvent> {
|
|
241
221
|
return this.send({ type: "set_pin_order", order });
|
|
242
222
|
}
|
|
243
223
|
|
|
244
|
-
/** Unfolds a working directory's sidebar group, or folds it. */
|
|
245
224
|
public setExpanded(cwd: string, value: boolean): Promise<ResponseEvent> {
|
|
246
225
|
return this.send({ type: "set_project_expanded", cwd, value });
|
|
247
226
|
}
|
|
248
227
|
|
|
249
|
-
/** Names a working directory for the listings; `null` puts it back to its base name. */
|
|
250
228
|
public setLabel(cwd: string, value: string | null): Promise<ResponseEvent> {
|
|
251
229
|
return this.send({ type: "set_project_label", cwd, value });
|
|
252
230
|
}
|
|
253
231
|
|
|
254
|
-
/** The model catalogue, plus this session's thinking levels; empty when unattached. */
|
|
255
232
|
public async listModels(): Promise<{
|
|
256
233
|
readonly models: readonly ModelView[];
|
|
257
234
|
readonly thinkingLevels: readonly string[];
|
|
258
235
|
}> {
|
|
259
|
-
const response = await this.send({ type: "list_models" });
|
|
260
|
-
if (!response.success) {
|
|
261
|
-
throw new Error(response.error ?? "list_models failed");
|
|
262
|
-
}
|
|
236
|
+
const response = ok(await this.send({ type: "list_models" }));
|
|
263
237
|
return {
|
|
264
238
|
models: response.models ?? [],
|
|
265
239
|
thinkingLevels: response.thinkingLevels ?? [],
|
|
266
240
|
};
|
|
267
241
|
}
|
|
268
242
|
|
|
269
|
-
/** Every extension this server can switch, scoped to the attached session's cwd. */
|
|
270
243
|
public async listExtensions(): Promise<readonly ExtensionEntry[]> {
|
|
271
|
-
|
|
272
|
-
if (!response.success) {
|
|
273
|
-
throw new Error(response.error ?? "list_extensions failed");
|
|
274
|
-
}
|
|
275
|
-
return response.extensions ?? [];
|
|
244
|
+
return ok(await this.send({ type: "list_extensions" })).extensions ?? [];
|
|
276
245
|
}
|
|
277
246
|
|
|
278
247
|
public async setExtension(id: string, value: boolean): Promise<void> {
|
|
279
|
-
|
|
280
|
-
type: "set_extension",
|
|
281
|
-
extensionId: id,
|
|
282
|
-
value,
|
|
283
|
-
});
|
|
284
|
-
if (!response.success) {
|
|
285
|
-
throw new Error(response.error ?? "set_extension failed");
|
|
286
|
-
}
|
|
248
|
+
ok(await this.send({ type: "set_extension", extensionId: id, value }));
|
|
287
249
|
}
|
|
288
250
|
|
|
289
|
-
/** Transfers a client-local file into the server's world; only the bytes and bare filename are sent. */
|
|
290
251
|
public async upload(localPath: string): Promise<UploadedFile> {
|
|
291
252
|
const file = Bun.file(localPath);
|
|
292
253
|
const form = new FormData();
|
|
@@ -331,8 +292,6 @@ export class ProbeClient {
|
|
|
331
292
|
return Promise.resolve(hit);
|
|
332
293
|
}
|
|
333
294
|
return new Promise<ServerEvent>((resolve, reject) => {
|
|
334
|
-
const waiter = { test, resolve };
|
|
335
|
-
this.waiters.add(waiter);
|
|
336
295
|
const timer = setTimeout(() => {
|
|
337
296
|
this.waiters.delete(waiter);
|
|
338
297
|
reject(
|
|
@@ -340,10 +299,18 @@ export class ProbeClient {
|
|
|
340
299
|
);
|
|
341
300
|
}, timeoutMs);
|
|
342
301
|
timer.unref?.();
|
|
302
|
+
const waiter = {
|
|
303
|
+
test,
|
|
304
|
+
resolve: (event: ServerEvent) => {
|
|
305
|
+
clearTimeout(timer);
|
|
306
|
+
resolve(event);
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
this.waiters.add(waiter);
|
|
343
310
|
});
|
|
344
311
|
}
|
|
345
312
|
|
|
346
|
-
/**
|
|
313
|
+
/** Closes with an abnormal code, like a killed client. */
|
|
347
314
|
public kill(): void {
|
|
348
315
|
this.socket?.close(4000, "probe killed");
|
|
349
316
|
this.socket = undefined;
|
|
@@ -387,9 +354,9 @@ export class ProbeClient {
|
|
|
387
354
|
}
|
|
388
355
|
}
|
|
389
356
|
|
|
390
|
-
function
|
|
357
|
+
function ok(response: ResponseEvent): ResponseEvent {
|
|
391
358
|
if (!response.success) {
|
|
392
|
-
throw new Error(response.error ?? "
|
|
359
|
+
throw new Error(response.error ?? "command failed");
|
|
393
360
|
}
|
|
394
|
-
return response
|
|
361
|
+
return response;
|
|
395
362
|
}
|
|
@@ -4,21 +4,19 @@ import { Updater, type UpdateOutcome } from "#core/shared/Updater";
|
|
|
4
4
|
import type { ServerEvent } from "#protocol/ServerEvent";
|
|
5
5
|
|
|
6
6
|
export type ReloaderDeps = {
|
|
7
|
-
/** To every connection: the restart takes all of them down together. */
|
|
8
7
|
readonly announce: (event: ServerEvent) => void;
|
|
9
|
-
/** Runs the update, reporting each step as it starts. */
|
|
10
8
|
readonly update?: (onStep: (label: string) => void) => Promise<UpdateOutcome>;
|
|
11
|
-
/**
|
|
9
|
+
/** Exits so the supervisor restarts on the new code. */
|
|
12
10
|
readonly shutdown?: () => Promise<void>;
|
|
13
11
|
};
|
|
14
12
|
|
|
15
|
-
//
|
|
13
|
+
// SIGTERM rather than exit, so the handler stops the gateway and flushes read cursors.
|
|
16
14
|
async function restartAndExit(): Promise<void> {
|
|
17
15
|
await Supervisor.restartSiblings(DaemonUnit);
|
|
18
16
|
process.kill(process.pid, "SIGTERM");
|
|
19
17
|
}
|
|
20
18
|
|
|
21
|
-
/** Runs
|
|
19
|
+
/** Runs update then restart, announcing each phase. */
|
|
22
20
|
export class Reloader {
|
|
23
21
|
private readonly announce: (event: ServerEvent) => void;
|
|
24
22
|
private readonly update: (
|
|
@@ -34,7 +32,7 @@ export class Reloader {
|
|
|
34
32
|
this.running = undefined;
|
|
35
33
|
}
|
|
36
34
|
|
|
37
|
-
/**
|
|
35
|
+
/** Concurrent callers share one run. */
|
|
38
36
|
public start(): Promise<void> {
|
|
39
37
|
this.running ??= this.run().finally(() => {
|
|
40
38
|
this.running = undefined;
|
|
@@ -78,7 +76,6 @@ export class Reloader {
|
|
|
78
76
|
try {
|
|
79
77
|
await this.shutdown();
|
|
80
78
|
} catch (err) {
|
|
81
|
-
// Must follow the `restarting` frame: it corrects a client already told to expect the drop.
|
|
82
79
|
this.failed(messageOf(err));
|
|
83
80
|
}
|
|
84
81
|
}
|
|
@@ -54,7 +54,7 @@ function build(
|
|
|
54
54
|
isImage: stored.mimeType.startsWith("image/"),
|
|
55
55
|
};
|
|
56
56
|
return {
|
|
57
|
-
//
|
|
57
|
+
// Omit the URL so the model can't repeat it.
|
|
58
58
|
content: [{ type: "text", text: `Sent ${details.name} (${size} B)` }],
|
|
59
59
|
details,
|
|
60
60
|
};
|
|
@@ -27,9 +27,7 @@ import type {
|
|
|
27
27
|
|
|
28
28
|
export type SessionCatalogueDeps = {
|
|
29
29
|
readonly registry: SessionRegistry;
|
|
30
|
-
/** Which sessions have been read, shared by every client. */
|
|
31
30
|
readonly cursors: ReadCursors;
|
|
32
|
-
/** Archived, held-unread and pinned, shared by every client. */
|
|
33
31
|
readonly meta: SessionMeta;
|
|
34
32
|
readonly liveStatus: (sessionId: string) => SessionStatus | undefined;
|
|
35
33
|
readonly liveSessionIds: () => Iterable<string>;
|
|
@@ -39,10 +37,10 @@ export type SessionCatalogueDeps = {
|
|
|
39
37
|
|
|
40
38
|
const DEFAULT_SESSION_LIMIT = 50;
|
|
41
39
|
|
|
42
|
-
/**
|
|
40
|
+
/** Concurrent digest reads; each reads a whole file into memory. */
|
|
43
41
|
const DIGEST_READS = 16;
|
|
44
42
|
|
|
45
|
-
/**
|
|
43
|
+
/** Debounce for `sessions_changed`. */
|
|
46
44
|
const ANNOUNCE_MS = 500;
|
|
47
45
|
|
|
48
46
|
type CachedDigest = SessionDigest & { readonly modifiedAt: number };
|
|
@@ -51,7 +49,6 @@ type Overrides = ReadonlyMap<string, SessionEntry>;
|
|
|
51
49
|
|
|
52
50
|
type PageScope = {
|
|
53
51
|
readonly overrides: Overrides;
|
|
54
|
-
/** True while listing the archived sessions rather than the live ones. */
|
|
55
52
|
readonly archived: boolean;
|
|
56
53
|
readonly limit: number;
|
|
57
54
|
readonly perProject: number;
|
|
@@ -72,12 +69,7 @@ export class SessionCatalogue {
|
|
|
72
69
|
this.index = this.newIndex();
|
|
73
70
|
}
|
|
74
71
|
|
|
75
|
-
/**
|
|
76
|
-
* Follow the sessions tree while anyone is connected. A session the terminal
|
|
77
|
-
* or a vanilla `pi` starts is a file this process never hears about
|
|
78
|
-
* otherwise, and a list nobody asked to re-fetch is a list that never learns
|
|
79
|
-
* about it.
|
|
80
|
-
*/
|
|
72
|
+
/** Watches the sessions tree, so sessions started by other processes are announced. */
|
|
81
73
|
public watch(active: boolean): void {
|
|
82
74
|
if (active === this.watching) {
|
|
83
75
|
return;
|
|
@@ -103,7 +95,7 @@ export class SessionCatalogue {
|
|
|
103
95
|
this.deps.meta.sessions(),
|
|
104
96
|
this.deps.meta.pinning(),
|
|
105
97
|
]);
|
|
106
|
-
// Prune only
|
|
98
|
+
// Prune only when unfiltered, or other directories would be forgotten.
|
|
107
99
|
if (command.cwd === undefined) {
|
|
108
100
|
const alive = new Set([
|
|
109
101
|
...summaries.map((summary) => summary.sessionId),
|
|
@@ -113,9 +105,10 @@ export class SessionCatalogue {
|
|
|
113
105
|
this.deps.cursors.prune(alive),
|
|
114
106
|
this.deps.meta.prune(alive),
|
|
115
107
|
]);
|
|
108
|
+
this.forget(alive, new Set(summaries.map((summary) => summary.path)));
|
|
116
109
|
}
|
|
117
110
|
const scope = command.archived === true;
|
|
118
|
-
//
|
|
111
|
+
// Filter before paging so out-of-scope rows don't use the page budget.
|
|
119
112
|
const inScope = summaries.filter(
|
|
120
113
|
(summary) =>
|
|
121
114
|
(overrides.get(summary.sessionId)?.archived === true) === scope
|
|
@@ -126,19 +119,13 @@ export class SessionCatalogue {
|
|
|
126
119
|
overrides,
|
|
127
120
|
archived: scope,
|
|
128
121
|
limit,
|
|
129
|
-
// Absent, a project may fill the page; it is the page that bounds it either way.
|
|
130
122
|
perProject: command.perProject ?? limit,
|
|
131
123
|
}),
|
|
132
|
-
// Counted before
|
|
124
|
+
// Counted before paging.
|
|
133
125
|
projects: projectsOf(inScope, pinning),
|
|
134
126
|
};
|
|
135
127
|
}
|
|
136
128
|
|
|
137
|
-
/**
|
|
138
|
-
* The index cannot see the sidecar, so the archived scope is a predicate it
|
|
139
|
-
* calls before its own limit, and the badge is a `Map.get` over the one read
|
|
140
|
-
* of `sessions.json` this query makes.
|
|
141
|
-
*/
|
|
142
129
|
public async search(
|
|
143
130
|
command: Command & { readonly type: "search_sessions" }
|
|
144
131
|
): Promise<SessionSearch> {
|
|
@@ -162,7 +149,7 @@ export class SessionCatalogue {
|
|
|
162
149
|
};
|
|
163
150
|
}
|
|
164
151
|
|
|
165
|
-
/** Announces a status
|
|
152
|
+
/** Announces a status change; must precede the read mark. */
|
|
166
153
|
public onStatus(sessionId: string, status: SessionStatus): void {
|
|
167
154
|
if (this.activity.get(sessionId) === status) {
|
|
168
155
|
return;
|
|
@@ -178,10 +165,10 @@ export class SessionCatalogue {
|
|
|
178
165
|
this.activity.set(sessionId, status);
|
|
179
166
|
}
|
|
180
167
|
|
|
181
|
-
/**
|
|
168
|
+
/** Marks read now, clears the sticky unread flag, and announces it. */
|
|
182
169
|
public async markRead(sessionId: string): Promise<void> {
|
|
183
170
|
await this.deps.cursors.mark(sessionId);
|
|
184
|
-
// The only place the sticky flag is cleared
|
|
171
|
+
// The only place the sticky flag is cleared.
|
|
185
172
|
if ((await this.deps.meta.of(sessionId)).unread === true) {
|
|
186
173
|
await this.deps.meta.setUnread(sessionId, false);
|
|
187
174
|
}
|
|
@@ -199,17 +186,28 @@ export class SessionCatalogue {
|
|
|
199
186
|
this.index = this.newIndex();
|
|
200
187
|
}
|
|
201
188
|
|
|
202
|
-
|
|
189
|
+
private forget(alive: ReadonlySet<string>, paths: ReadonlySet<string>): void {
|
|
190
|
+
for (const map of [this.activity, this.settled]) {
|
|
191
|
+
for (const sessionId of map.keys()) {
|
|
192
|
+
if (!alive.has(sessionId)) {
|
|
193
|
+
map.delete(sessionId);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
for (const path of this.digests.keys()) {
|
|
198
|
+
if (!paths.has(path)) {
|
|
199
|
+
this.digests.delete(path);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
203
204
|
private newIndex(): SearchIndex {
|
|
204
205
|
return new SearchIndex({ list: () => this.deps.registry.list() });
|
|
205
206
|
}
|
|
206
207
|
|
|
207
208
|
/**
|
|
208
|
-
* Selects by
|
|
209
|
-
*
|
|
210
|
-
* session with nothing to call itself is not a row, and the budget it spent
|
|
211
|
-
* goes back to its project, so a directory of abandoned files still shows
|
|
212
|
-
* the sessions underneath them.
|
|
209
|
+
* Selects by mtime under a per-project budget and digests only the selection.
|
|
210
|
+
* A session without a title yields no row and refunds its project's budget.
|
|
213
211
|
*/
|
|
214
212
|
private async page(
|
|
215
213
|
candidates: readonly SessionSummary[],
|
|
@@ -241,7 +239,7 @@ export class SessionCatalogue {
|
|
|
241
239
|
return rows.sort((a, b) => b.settledAt - a.settledAt);
|
|
242
240
|
}
|
|
243
241
|
|
|
244
|
-
/**
|
|
242
|
+
/** Undefined for a session with no title (never prompted or named). */
|
|
245
243
|
private async row(
|
|
246
244
|
{ sessionId, cwd, path, createdAt, modifiedAt }: SessionSummary,
|
|
247
245
|
{ overrides, archived }: PageScope
|
|
@@ -273,7 +271,7 @@ export class SessionCatalogue {
|
|
|
273
271
|
};
|
|
274
272
|
}
|
|
275
273
|
|
|
276
|
-
// One watch per directory
|
|
274
|
+
// One watch per directory: recursive `fs.watch` is unreliable across platforms.
|
|
277
275
|
private follow(): void {
|
|
278
276
|
const root = this.deps.registry.sessionsRoot;
|
|
279
277
|
const wanted = new Set([root, ...FileWatch.subdirectories(root)]);
|
|
@@ -295,7 +293,6 @@ export class SessionCatalogue {
|
|
|
295
293
|
}
|
|
296
294
|
}
|
|
297
295
|
|
|
298
|
-
// Debounced: a turn writes a dozen entries, and re-listing is a read of every session header.
|
|
299
296
|
private onTreeChange(): void {
|
|
300
297
|
if (this.announcing !== undefined || !this.watching) {
|
|
301
298
|
return;
|
|
@@ -308,7 +305,7 @@ export class SessionCatalogue {
|
|
|
308
305
|
this.announcing.unref?.();
|
|
309
306
|
}
|
|
310
307
|
|
|
311
|
-
//
|
|
308
|
+
// Mid-turn, use the last idle time: the file's time is just the latest write.
|
|
312
309
|
private answerTime(
|
|
313
310
|
sessionId: string,
|
|
314
311
|
status: SessionStatus | undefined,
|
|
@@ -339,13 +336,7 @@ export class SessionCatalogue {
|
|
|
339
336
|
return renamed(digest, name);
|
|
340
337
|
}
|
|
341
338
|
|
|
342
|
-
/**
|
|
343
|
-
* Mid-turn the file is appended to between listings, so its digest never hits
|
|
344
|
-
* the cache and the whole-file read lands on the busiest session there is.
|
|
345
|
-
* Nothing it answers can have moved: the opening message was written long
|
|
346
|
-
* ago, the settle time is deliberately not read from the file while a turn
|
|
347
|
-
* runs, and the name is the live session's own to say.
|
|
348
|
-
*/
|
|
339
|
+
/** Mid-turn the cached digest is reused despite a changed mtime: nothing it holds can have moved. */
|
|
349
340
|
private reusable(
|
|
350
341
|
path: string,
|
|
351
342
|
modifiedAt: number,
|
|
@@ -356,8 +347,7 @@ export class SessionCatalogue {
|
|
|
356
347
|
if (cached === undefined) {
|
|
357
348
|
return undefined;
|
|
358
349
|
}
|
|
359
|
-
//
|
|
360
|
-
// nameless row falls back to is in the file and nowhere else.
|
|
350
|
+
// A cleared name needs the opening message, which only the file has.
|
|
361
351
|
if (name === null && cached.named === true) {
|
|
362
352
|
return undefined;
|
|
363
353
|
}
|
|
@@ -374,7 +364,7 @@ export class SessionCatalogue {
|
|
|
374
364
|
return digest;
|
|
375
365
|
}
|
|
376
366
|
|
|
377
|
-
/**
|
|
367
|
+
/** `null` is live and unnamed; undefined is not live. */
|
|
378
368
|
private liveName(sessionId: string): string | null | undefined {
|
|
379
369
|
const agent = this.deps.registry.peek(sessionId)?.agentSession;
|
|
380
370
|
return agent === undefined
|
|
@@ -383,7 +373,6 @@ export class SessionCatalogue {
|
|
|
383
373
|
}
|
|
384
374
|
}
|
|
385
375
|
|
|
386
|
-
/** The row the wire carries: the index's hit, plus the one thing about it only the sidecar knows. */
|
|
387
376
|
function hitOf(hit: SearchHit, archived: boolean): SearchHitView {
|
|
388
377
|
return {
|
|
389
378
|
sessionId: hit.sessionId,
|
|
@@ -398,7 +387,7 @@ function hitOf(hit: SearchHit, archived: boolean): SearchHitView {
|
|
|
398
387
|
};
|
|
399
388
|
}
|
|
400
389
|
|
|
401
|
-
/** A live session's
|
|
390
|
+
/** A live session's name wins over the file's. */
|
|
402
391
|
function renamed(
|
|
403
392
|
digest: SessionDigest,
|
|
404
393
|
name: string | null | undefined
|
|
@@ -408,7 +397,7 @@ function renamed(
|
|
|
408
397
|
: digest;
|
|
409
398
|
}
|
|
410
399
|
|
|
411
|
-
/** The next sessions to digest,
|
|
400
|
+
/** The next unconsumed sessions to digest, skipping projects over budget. */
|
|
412
401
|
function choose(
|
|
413
402
|
candidates: readonly SessionSummary[],
|
|
414
403
|
consumed: Set<SessionSummary>,
|
|
@@ -431,7 +420,6 @@ function choose(
|
|
|
431
420
|
return batch;
|
|
432
421
|
}
|
|
433
422
|
|
|
434
|
-
/** Every working directory the scope holds sessions in, newest first; the count is the header scan's, so it owes nothing to the page. */
|
|
435
423
|
function projectsOf(
|
|
436
424
|
summaries: readonly SessionSummary[],
|
|
437
425
|
{ projects, order }: Pinning
|
|
@@ -15,17 +15,13 @@ import type {
|
|
|
15
15
|
} from "#protocol/ServerEvent";
|
|
16
16
|
import { attachmentUrl } from "./AttachmentEndpoint";
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
readonly name: string;
|
|
20
|
-
readonly args: unknown;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/** Projects pi's session file into durable events: one line in, at most one `DurableEvent` out, tagged with that line's ordinal. */
|
|
18
|
+
/** Projects pi's session file into durable events: at most one per line, tagged with the line ordinal. */
|
|
24
19
|
export class SessionProjection {
|
|
25
20
|
private readonly log: EventLog;
|
|
26
21
|
private readonly cwd: () => string;
|
|
27
22
|
private readonly events: DurableEvent[] = [];
|
|
28
|
-
|
|
23
|
+
/** Tool call args by call id. */
|
|
24
|
+
private readonly callArgs = new Map<string, unknown>();
|
|
29
25
|
private draining: Promise<void> = Promise.resolve();
|
|
30
26
|
private highest = 0;
|
|
31
27
|
|
|
@@ -53,7 +49,7 @@ export class SessionProjection {
|
|
|
53
49
|
return this.events.slice(low);
|
|
54
50
|
}
|
|
55
51
|
|
|
56
|
-
/**
|
|
52
|
+
/** Reads what pi appended since the last drain. Serialized: overlapping reads would double-project lines. */
|
|
57
53
|
public async drain(): Promise<readonly DurableEvent[]> {
|
|
58
54
|
const before = this.events.length;
|
|
59
55
|
const done = this.draining.then(async () => {
|
|
@@ -111,7 +107,7 @@ export class SessionProjection {
|
|
|
111
107
|
if (part.type !== "toolCall") {
|
|
112
108
|
continue;
|
|
113
109
|
}
|
|
114
|
-
this.
|
|
110
|
+
this.callArgs.set(part.id, part.arguments);
|
|
115
111
|
toolCalls.push({
|
|
116
112
|
callId: part.id,
|
|
117
113
|
name: part.name,
|
|
@@ -141,7 +137,6 @@ export class SessionProjection {
|
|
|
141
137
|
};
|
|
142
138
|
}
|
|
143
139
|
case "toolResult": {
|
|
144
|
-
const call = this.calls.get(message.toolCallId);
|
|
145
140
|
return {
|
|
146
141
|
seq,
|
|
147
142
|
type: "tool_result",
|
|
@@ -150,7 +145,7 @@ export class SessionProjection {
|
|
|
150
145
|
isError: message.isError,
|
|
151
146
|
view: Tools.viewOf({
|
|
152
147
|
name: message.toolName,
|
|
153
|
-
args:
|
|
148
|
+
args: this.callArgs.get(message.toolCallId) ?? {},
|
|
154
149
|
isError: message.isError,
|
|
155
150
|
result: {
|
|
156
151
|
content: withoutImages(message.content),
|
|
@@ -178,7 +173,7 @@ type ToolResultContent = Extract<
|
|
|
178
173
|
{ role: "toolResult" }
|
|
179
174
|
>["content"];
|
|
180
175
|
|
|
181
|
-
/**
|
|
176
|
+
/** Keeps base64 off the websocket; views load images via `details.sha256`. */
|
|
182
177
|
function withoutImages(content: ToolResultContent): ToolResultContent {
|
|
183
178
|
return content.some((part) => part.type === "image")
|
|
184
179
|
? content.map((part) =>
|