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
|
@@ -13,59 +13,56 @@ import {
|
|
|
13
13
|
} from "./SessionDigest";
|
|
14
14
|
import type { SessionSummary } from "./SessionRegistry";
|
|
15
15
|
|
|
16
|
-
/** Half-open character offsets
|
|
16
|
+
/** Half-open character offsets; non-overlapping and ascending. */
|
|
17
17
|
export type SearchRange = readonly [start: number, end: number];
|
|
18
18
|
|
|
19
19
|
export type SearchSnippet = {
|
|
20
20
|
readonly seq: number;
|
|
21
21
|
readonly role: "user" | "assistant";
|
|
22
|
-
/** The windowed snippet, verbatim from the message. */
|
|
23
22
|
readonly text: string;
|
|
24
23
|
/** Offsets into `text`, not into the message. */
|
|
25
24
|
readonly ranges: readonly SearchRange[];
|
|
26
|
-
/** The
|
|
25
|
+
/** The snippet does not start at the message start. */
|
|
27
26
|
readonly cutHead?: true;
|
|
28
27
|
};
|
|
29
28
|
|
|
30
29
|
export type SearchHit = {
|
|
31
30
|
readonly sessionId: string;
|
|
32
31
|
readonly cwd: string;
|
|
33
|
-
/**
|
|
32
|
+
/** Local file path; do not send over the wire. */
|
|
34
33
|
readonly path: string;
|
|
35
34
|
readonly title?: string;
|
|
36
35
|
readonly named?: true;
|
|
37
36
|
/** Offsets into the clamped `title`. */
|
|
38
37
|
readonly titleRanges: readonly SearchRange[];
|
|
39
|
-
/** The
|
|
38
|
+
/** The first user message; set only for named sessions. */
|
|
40
39
|
readonly opening?: string;
|
|
41
|
-
/** End of the last completed turn,
|
|
40
|
+
/** End of the last completed turn, else the session's creation time. */
|
|
42
41
|
readonly settledAt: number;
|
|
43
42
|
readonly snippets: readonly SearchSnippet[];
|
|
44
43
|
/** Matching messages in this session, before the snippet cut. */
|
|
45
44
|
readonly total: number;
|
|
46
|
-
/**
|
|
45
|
+
/** Matched only via typo expansion; ranks below exact hits. */
|
|
47
46
|
readonly typos: boolean;
|
|
48
47
|
};
|
|
49
48
|
|
|
50
49
|
export type SearchAnswer = {
|
|
51
50
|
readonly hits: readonly SearchHit[];
|
|
52
|
-
/** Query words dropped
|
|
51
|
+
/** Query words dropped to get any results, rarest first. */
|
|
53
52
|
readonly dropped: readonly string[];
|
|
54
|
-
/** Sessions searched. */
|
|
55
53
|
readonly scanned: number;
|
|
56
54
|
};
|
|
57
55
|
|
|
58
56
|
export type SearchOptions = {
|
|
59
57
|
readonly limit?: number;
|
|
60
58
|
readonly cwd?: string;
|
|
61
|
-
/** Applied before
|
|
59
|
+
/** Applied before `limit`. */
|
|
62
60
|
readonly accept?: (sessionId: string) => boolean;
|
|
63
61
|
};
|
|
64
62
|
|
|
65
63
|
export type SearchIndexDeps = {
|
|
66
|
-
/** `SessionRegistry.list` in production; a fake in tests. */
|
|
67
64
|
readonly list: () => Promise<readonly SessionSummary[]>;
|
|
68
|
-
/**
|
|
65
|
+
/** Clock for the refresh throttle. */
|
|
69
66
|
readonly now?: () => number;
|
|
70
67
|
};
|
|
71
68
|
|
|
@@ -83,9 +80,9 @@ type Entry = {
|
|
|
83
80
|
readonly path: string;
|
|
84
81
|
readonly createdAt: number;
|
|
85
82
|
modifiedAt: number;
|
|
86
|
-
/**
|
|
83
|
+
/** Bytes read so far; the next tail read starts here. */
|
|
87
84
|
offset: number;
|
|
88
|
-
/**
|
|
85
|
+
/** Lines read so far. */
|
|
89
86
|
seq: number;
|
|
90
87
|
parts: DigestParts;
|
|
91
88
|
digest: SessionDigest;
|
|
@@ -97,23 +94,22 @@ type Posting = {
|
|
|
97
94
|
readonly turn: Turn;
|
|
98
95
|
};
|
|
99
96
|
|
|
100
|
-
/**
|
|
97
|
+
/** `full` replaces the entry; otherwise `body` is appended to it. */
|
|
101
98
|
type Read = {
|
|
102
99
|
readonly full: boolean;
|
|
103
100
|
readonly body?: Durable;
|
|
104
101
|
};
|
|
105
102
|
|
|
106
|
-
/** One file's read, folded into its entry and with the bytes already let go. */
|
|
107
103
|
type Taken = {
|
|
108
104
|
readonly entry: Entry;
|
|
109
|
-
/**
|
|
105
|
+
/** Turn count before this read; later turns are new. */
|
|
110
106
|
readonly from: number;
|
|
111
|
-
/**
|
|
107
|
+
/** An existing entry was re-read in full, so postings must be rebuilt. */
|
|
112
108
|
readonly replaced: boolean;
|
|
113
109
|
};
|
|
114
110
|
|
|
115
111
|
type Plan = {
|
|
116
|
-
/**
|
|
112
|
+
/** The word itself, plus completions when it is the last word. */
|
|
117
113
|
readonly exact: readonly string[];
|
|
118
114
|
readonly typo: readonly string[];
|
|
119
115
|
};
|
|
@@ -124,11 +120,7 @@ type Group = {
|
|
|
124
120
|
exact: boolean;
|
|
125
121
|
};
|
|
126
122
|
|
|
127
|
-
/**
|
|
128
|
-
* Everything `byRank` weighs, taken from the group alone: no message is
|
|
129
|
-
* tokenised until the sort has cut the losers, and four in five matched
|
|
130
|
-
* sessions never reach a snippet.
|
|
131
|
-
*/
|
|
123
|
+
/** Rank inputs, computed without tokenising any message. */
|
|
132
124
|
type Ranked = {
|
|
133
125
|
readonly entry: Entry;
|
|
134
126
|
readonly group: Group;
|
|
@@ -140,45 +132,34 @@ type Ranked = {
|
|
|
140
132
|
|
|
141
133
|
const FILE_READS = 16;
|
|
142
134
|
|
|
143
|
-
/** The granularity at which the catalogue already decided file changes become visible. */
|
|
144
135
|
const REFRESH_MS = 500;
|
|
145
136
|
|
|
146
|
-
/** Typesense's `typo_tokens_threshold`:
|
|
137
|
+
/** Typesense's `typo_tokens_threshold`: typos are tried only below this many results. */
|
|
147
138
|
const ENOUGH = 1;
|
|
148
139
|
|
|
149
|
-
/** Typesense's `max_candidates
|
|
140
|
+
/** Typesense's `max_candidates`. */
|
|
150
141
|
const MAX_CANDIDATES = 4;
|
|
151
142
|
|
|
152
143
|
const MAX_TYPOS = 2;
|
|
153
144
|
|
|
154
|
-
/** Typesense's `highlight_affix_num_tokens
|
|
145
|
+
/** Typesense's `highlight_affix_num_tokens`: tokens shown before the first match. */
|
|
155
146
|
const AFFIX = 4;
|
|
156
147
|
|
|
157
|
-
/**
|
|
158
|
-
* How far past the window's start a snippet runs. A row is cut to pixels, and
|
|
159
|
-
* only the row knows how many it has, so the tail is not a window at all: it is
|
|
160
|
-
* more text than the widest row can draw — the modal is capped at 30rem, which
|
|
161
|
-
* is about 59 of these characters — and the client's own ellipsis does the
|
|
162
|
-
* cutting. A word count here cut short rows shorter still, on the wide screens
|
|
163
|
-
* that had the most room to spare.
|
|
164
|
-
*/
|
|
148
|
+
/** Snippet length in characters; more than the widest row shows, so the client's ellipsis cuts it. */
|
|
165
149
|
const TAIL = 100;
|
|
166
150
|
|
|
167
151
|
const SNIPPETS = 2;
|
|
168
152
|
|
|
169
|
-
/** What a row draws of an opening ask before its own box cuts it anyway. */
|
|
170
153
|
const PREVIEW = 200;
|
|
171
154
|
|
|
172
155
|
const LIMIT = 20;
|
|
173
156
|
|
|
174
|
-
/**
|
|
157
|
+
/** Lines without this are skipped before decoding. */
|
|
175
158
|
const TEXT = Buffer.from('"text"');
|
|
176
159
|
|
|
177
160
|
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
* weighs. Built lazily on the first query and refreshed by later ones, never
|
|
181
|
-
* by a watcher, so the cold cost belongs to whoever searches.
|
|
161
|
+
* Inverted index over user and assistant text in every session, plus titles.
|
|
162
|
+
* Built on the first query and refreshed incrementally by later ones.
|
|
182
163
|
*/
|
|
183
164
|
export class SearchIndex {
|
|
184
165
|
private readonly list: () => Promise<readonly SessionSummary[]>;
|
|
@@ -229,13 +210,13 @@ export class SearchIndex {
|
|
|
229
210
|
hits: ranked
|
|
230
211
|
.sort(byRank)
|
|
231
212
|
.slice(0, options.limit ?? LIMIT)
|
|
232
|
-
.map(
|
|
213
|
+
.map(hitOf),
|
|
233
214
|
dropped,
|
|
234
215
|
scanned: scope.size,
|
|
235
216
|
};
|
|
236
217
|
}
|
|
237
218
|
|
|
238
|
-
/** Exact and prefix first; typos only
|
|
219
|
+
/** Exact and prefix first; adds typos only while results are too few. */
|
|
239
220
|
private run(words: readonly string[], scope: ReadonlySet<Entry>): Ranked[] {
|
|
240
221
|
const reach = Math.min(MAX_TYPOS, Math.max(...words.map(gateOf)));
|
|
241
222
|
const last = words.length - 1;
|
|
@@ -286,15 +267,11 @@ export class SearchIndex {
|
|
|
286
267
|
}
|
|
287
268
|
|
|
288
269
|
private collect(plans: readonly Plan[], scope: ReadonlySet<Entry>): Ranked[] {
|
|
289
|
-
const turnsExact = plans.map((plan) =>
|
|
290
|
-
|
|
291
|
-
);
|
|
292
|
-
const titlesExact = plans.map((plan) =>
|
|
293
|
-
this.union(this.titles, plan.exact)
|
|
294
|
-
);
|
|
270
|
+
const turnsExact = plans.map((plan) => union(this.postings, plan.exact));
|
|
271
|
+
const titlesExact = plans.map((plan) => union(this.titles, plan.exact));
|
|
295
272
|
const everything = plans.map((plan) => [...plan.exact, ...plan.typo]);
|
|
296
|
-
const turns = everything.map((terms) =>
|
|
297
|
-
const titles = everything.map((terms) =>
|
|
273
|
+
const turns = everything.map((terms) => union(this.postings, terms));
|
|
274
|
+
const titles = everything.map((terms) => union(this.titles, terms));
|
|
298
275
|
const terms = new Set(everything.flat());
|
|
299
276
|
|
|
300
277
|
const groups = new Map<Entry, Group>();
|
|
@@ -363,19 +340,6 @@ export class SearchIndex {
|
|
|
363
340
|
.slice(0, MAX_CANDIDATES);
|
|
364
341
|
}
|
|
365
342
|
|
|
366
|
-
private union<T>(
|
|
367
|
-
index: ReadonlyMap<string, T[]>,
|
|
368
|
-
terms: readonly string[]
|
|
369
|
-
): ReadonlySet<T> {
|
|
370
|
-
const found = new Set<T>();
|
|
371
|
-
for (const term of terms) {
|
|
372
|
-
for (const value of index.get(term) ?? []) {
|
|
373
|
-
found.add(value);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
return found;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
343
|
private vocabularyOf(): readonly string[] {
|
|
380
344
|
if (this.vocabularyStale) {
|
|
381
345
|
this.vocabulary = [
|
|
@@ -421,9 +385,7 @@ export class SearchIndex {
|
|
|
421
385
|
}
|
|
422
386
|
}
|
|
423
387
|
|
|
424
|
-
//
|
|
425
|
-
// while every entry keeps the turns it had; a replaced or departed session
|
|
426
|
-
// invalidates the lot, and an appended one does not.
|
|
388
|
+
// Postings are indices into `turns`, so a replaced or removed session forces a full reindex.
|
|
427
389
|
if (replaced) {
|
|
428
390
|
this.reindex();
|
|
429
391
|
} else {
|
|
@@ -436,11 +398,7 @@ export class SearchIndex {
|
|
|
436
398
|
this.syncedAt = this.now();
|
|
437
399
|
}
|
|
438
400
|
|
|
439
|
-
/**
|
|
440
|
-
* One file's bytes end here, with the turns and parts taken out of them:
|
|
441
|
-
* held until the pool drained instead, a whole session tree would be
|
|
442
|
-
* resident at once. The entry lands in `entries` in the caller's order.
|
|
443
|
-
*/
|
|
401
|
+
/** Absorbs the file's bytes immediately so they are not all held at once. */
|
|
444
402
|
private async take(summary: SessionSummary): Promise<Taken | undefined> {
|
|
445
403
|
const read = await this.read(summary);
|
|
446
404
|
if (read === undefined) {
|
|
@@ -456,11 +414,7 @@ export class SearchIndex {
|
|
|
456
414
|
return { entry, from, replaced: read.full && known !== undefined };
|
|
457
415
|
}
|
|
458
416
|
|
|
459
|
-
/**
|
|
460
|
-
* A session file is append-only, so the usual read is the bytes past the
|
|
461
|
-
* stored offset. Only a file that shrank or whose mtime went backwards has
|
|
462
|
-
* been rewritten under us, and only that forces the whole file again.
|
|
463
|
-
*/
|
|
417
|
+
/** Reads only the appended tail, unless the file shrank or its mtime went backwards. */
|
|
464
418
|
private async read(summary: SessionSummary): Promise<Read | undefined> {
|
|
465
419
|
const known = this.entries.get(summary.sessionId);
|
|
466
420
|
const file = Bun.file(summary.path);
|
|
@@ -643,7 +597,7 @@ function rangesOf(
|
|
|
643
597
|
return rangesIn(SearchTokens.scan(text), terms, 0, text.length);
|
|
644
598
|
}
|
|
645
599
|
|
|
646
|
-
/**
|
|
600
|
+
/** Ranges within `[from, to)`, relative to `from`. */
|
|
647
601
|
function rangesIn(
|
|
648
602
|
tokens: readonly SearchToken[],
|
|
649
603
|
terms: ReadonlySet<string>,
|
|
@@ -675,12 +629,12 @@ function merge(ranges: readonly SearchRange[]): readonly SearchRange[] {
|
|
|
675
629
|
return marked;
|
|
676
630
|
}
|
|
677
631
|
|
|
678
|
-
/**
|
|
632
|
+
/** User turns first, then by seq. */
|
|
679
633
|
function byRecognition(a: Turn, b: Turn): number {
|
|
680
634
|
return a.role === b.role ? a.seq - b.seq : a.role === "user" ? -1 : 1;
|
|
681
635
|
}
|
|
682
636
|
|
|
683
|
-
/** The turn
|
|
637
|
+
/** The first turn by `byRecognition`, without sorting. */
|
|
684
638
|
function leadOf(turns: readonly Turn[]): Turn | undefined {
|
|
685
639
|
let lead: Turn | undefined;
|
|
686
640
|
for (const turn of turns) {
|
|
@@ -698,11 +652,7 @@ function byRank(a: Ranked, b: Ranked): number {
|
|
|
698
652
|
return a.field - b.field || b.settledAt - a.settledAt;
|
|
699
653
|
}
|
|
700
654
|
|
|
701
|
-
/**
|
|
702
|
-
* A title match outranks a spoken one. The title index is built from the very
|
|
703
|
-
* tokens `rangesOf` marks, so a titled group always has a range to show, and
|
|
704
|
-
* the field is settled without cutting a single snippet.
|
|
705
|
-
*/
|
|
655
|
+
/** Title match, then user turn, then assistant turn. */
|
|
706
656
|
function fieldOf(group: Group, title: string | undefined): number {
|
|
707
657
|
if (group.title && title !== undefined) {
|
|
708
658
|
return 0;
|
|
@@ -737,6 +687,19 @@ function groupIn(groups: Map<Entry, Group>, entry: Entry): Group {
|
|
|
737
687
|
return found;
|
|
738
688
|
}
|
|
739
689
|
|
|
690
|
+
function union<T>(
|
|
691
|
+
index: ReadonlyMap<string, T[]>,
|
|
692
|
+
terms: readonly string[]
|
|
693
|
+
): ReadonlySet<T> {
|
|
694
|
+
const found = new Set<T>();
|
|
695
|
+
for (const term of terms) {
|
|
696
|
+
for (const value of index.get(term) ?? []) {
|
|
697
|
+
found.add(value);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return found;
|
|
701
|
+
}
|
|
702
|
+
|
|
740
703
|
function intersect<T>(sets: readonly ReadonlySet<T>[]): ReadonlySet<T> {
|
|
741
704
|
const [first, ...rest] = [...sets].sort((a, b) => a.size - b.size);
|
|
742
705
|
if (first === undefined) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
/** One indexable word and where it sits in the string it came from. */
|
|
2
1
|
export type SearchToken = {
|
|
3
|
-
/** Lowercased
|
|
2
|
+
/** Lowercased. */
|
|
4
3
|
readonly text: string;
|
|
5
4
|
readonly start: number;
|
|
6
5
|
readonly end: number;
|
|
@@ -33,10 +32,8 @@ function scan(text: string): readonly SearchToken[] {
|
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* for `SessionLease` and for `session lease` alike; asking for `sessionlease`
|
|
39
|
-
* too would AND away the second.
|
|
35
|
+
* Query pieces only, never the whole identifier: `SessionLease` asks for
|
|
36
|
+
* `session` and `lease`, so it also matches "session lease".
|
|
40
37
|
*/
|
|
41
38
|
function words(query: string): readonly string[] {
|
|
42
39
|
const found: string[] = [];
|
|
@@ -51,7 +48,7 @@ function words(query: string): readonly string[] {
|
|
|
51
48
|
return found;
|
|
52
49
|
}
|
|
53
50
|
|
|
54
|
-
/** Half-open slices
|
|
51
|
+
/** Half-open slices: `WsGateway` → `ws`, `gateway`; `read_file` → `read`, `file`. */
|
|
55
52
|
function piecesOf(run: string): readonly (readonly [number, number])[] {
|
|
56
53
|
const pieces: (readonly [number, number])[] = [];
|
|
57
54
|
let start = -1;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** What a cache entry has to offer: a recency stamp and a way to let go. */
|
|
2
1
|
export type CachedSession = {
|
|
3
2
|
lastUsed: number;
|
|
4
3
|
dispose(): Promise<void>;
|
|
@@ -6,7 +5,7 @@ export type CachedSession = {
|
|
|
6
5
|
|
|
7
6
|
const CAPACITY = 16;
|
|
8
7
|
|
|
9
|
-
/**
|
|
8
|
+
/** LRU cache of live sessions; evicted entries are disposed. */
|
|
10
9
|
export class SessionCache<T extends CachedSession> {
|
|
11
10
|
private readonly capacity: number;
|
|
12
11
|
private readonly entries = new Map<string, T>();
|
|
@@ -19,12 +18,11 @@ export class SessionCache<T extends CachedSession> {
|
|
|
19
18
|
return this.entries.size;
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
/**
|
|
21
|
+
/** Does not count as a use. */
|
|
23
22
|
public peek(key: string): T | undefined {
|
|
24
23
|
return this.entries.get(key);
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
/** The live entry, marked used now so eviction passes over it. */
|
|
28
26
|
public touch(key: string): T | undefined {
|
|
29
27
|
const entry = this.entries.get(key);
|
|
30
28
|
if (entry) {
|
|
@@ -33,7 +31,6 @@ export class SessionCache<T extends CachedSession> {
|
|
|
33
31
|
return entry;
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
/** Store `value`, disposing the least recently used entry first when full. */
|
|
37
34
|
public adopt(key: string, value: T): T {
|
|
38
35
|
this.evictIfNeeded();
|
|
39
36
|
this.entries.set(key, value);
|
|
@@ -63,6 +60,11 @@ export class SessionCache<T extends CachedSession> {
|
|
|
63
60
|
}
|
|
64
61
|
const evicted = this.entries.get(oldestKey)!;
|
|
65
62
|
this.entries.delete(oldestKey);
|
|
66
|
-
|
|
63
|
+
evicted.dispose().catch((err: unknown) => {
|
|
64
|
+
console.warn(
|
|
65
|
+
`[pim] failed to dispose evicted session ${oldestKey}:`,
|
|
66
|
+
err
|
|
67
|
+
);
|
|
68
|
+
});
|
|
67
69
|
}
|
|
68
70
|
}
|
|
@@ -3,35 +3,27 @@ import { parseSessionEntries } from "@earendil-works/pi-coding-agent";
|
|
|
3
3
|
import { Attachments } from "../attachments/Attachments";
|
|
4
4
|
import { MessageText } from "./MessageText";
|
|
5
5
|
|
|
6
|
-
/**
|
|
6
|
+
/** Catalogue summary of a session file. */
|
|
7
7
|
export type SessionDigest = {
|
|
8
|
-
/** The session
|
|
8
|
+
/** The session name, else the first user message. */
|
|
9
9
|
readonly title?: string;
|
|
10
|
-
/**
|
|
10
|
+
/** Set when `title` is a session name. */
|
|
11
11
|
readonly named?: true;
|
|
12
|
-
/** When the agent last wrote
|
|
12
|
+
/** When the agent last wrote. Moves during a turn, so read it before starting one. */
|
|
13
13
|
readonly settledAt?: number;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
/**
|
|
17
|
-
* The digest before it is clamped, so a reader that tails an appended region
|
|
18
|
-
* can fold that region into what an earlier read of the same file found.
|
|
19
|
-
*/
|
|
16
|
+
/** The unclamped digest, so a tail read can be merged into an earlier one. */
|
|
20
17
|
export type DigestParts = {
|
|
21
18
|
readonly name?: string;
|
|
22
|
-
/** The opening ask, unclamped. */
|
|
23
19
|
readonly opening?: string;
|
|
24
20
|
readonly settledAt?: number;
|
|
25
21
|
};
|
|
26
22
|
|
|
27
|
-
/**
|
|
28
|
-
* A session file's durable bytes, undecoded: a whole session is megabytes of
|
|
29
|
-
* tool output and the digest wants a handful of lines of it, so the search
|
|
30
|
-
* runs over the bytes and only the lines it keeps become strings.
|
|
31
|
-
*/
|
|
23
|
+
/** A session file's bytes, kept undecoded so only the lines used become strings. */
|
|
32
24
|
export type Durable = {
|
|
33
25
|
readonly bytes: Buffer;
|
|
34
|
-
/**
|
|
26
|
+
/** Just past the last newline; a trailing partial line is excluded. */
|
|
35
27
|
readonly end: number;
|
|
36
28
|
};
|
|
37
29
|
|
|
@@ -41,7 +33,7 @@ const TAIL_LINES = 10;
|
|
|
41
33
|
|
|
42
34
|
const TITLE_LIMIT = 120;
|
|
43
35
|
|
|
44
|
-
/**
|
|
36
|
+
/** Searched in raw bytes before decoding a line. */
|
|
45
37
|
const NAMED = Buffer.from('"session_info"');
|
|
46
38
|
|
|
47
39
|
export const NEWLINE = 0x0a;
|
|
@@ -60,7 +52,7 @@ function partsOf(body: Durable): DigestParts {
|
|
|
60
52
|
};
|
|
61
53
|
}
|
|
62
54
|
|
|
63
|
-
/**
|
|
55
|
+
/** Keeps the earlier opening; a later name or settle time wins. */
|
|
64
56
|
function merge(before: DigestParts, appended: DigestParts): DigestParts {
|
|
65
57
|
return {
|
|
66
58
|
name: appended.name ?? before.name,
|
|
@@ -78,7 +70,7 @@ function of({ name, opening, settledAt }: DigestParts): SessionDigest {
|
|
|
78
70
|
};
|
|
79
71
|
}
|
|
80
72
|
|
|
81
|
-
/** The last `session_info`
|
|
73
|
+
/** The last `session_info` entry's name. Skips lines that merely quote the marker. */
|
|
82
74
|
function nameOf({ bytes, end }: Durable): string | undefined {
|
|
83
75
|
let at = end - 1;
|
|
84
76
|
while (at >= 0) {
|
|
@@ -140,7 +132,6 @@ function clamp(text: string): string {
|
|
|
140
132
|
: text;
|
|
141
133
|
}
|
|
142
134
|
|
|
143
|
-
/** Over the head window the caller has already cut, not the whole file. */
|
|
144
135
|
function firstUserMessage(lines: readonly string[]): string | undefined {
|
|
145
136
|
for (const line of lines) {
|
|
146
137
|
const entry = parseSessionEntries(line)[0];
|