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
|
@@ -34,10 +34,8 @@ import { SessionName } from "./SessionName";
|
|
|
34
34
|
import { adaptSessionUi, type SessionUi } from "./SessionUi";
|
|
35
35
|
import { WriteMark } from "./WriteMark";
|
|
36
36
|
|
|
37
|
-
/** What the agent is doing right now. */
|
|
38
37
|
export type SessionStatus = "idle" | "thinking" | "streaming" | "tool";
|
|
39
38
|
|
|
40
|
-
/** Everything the host persists about a session; frontend-specific settings stay with the adapter. */
|
|
41
39
|
export type HostSettings = {
|
|
42
40
|
readonly cwd?: string;
|
|
43
41
|
readonly model?: string;
|
|
@@ -63,13 +61,12 @@ export type SessionCompactResult = {
|
|
|
63
61
|
readonly activeMessages: number;
|
|
64
62
|
};
|
|
65
63
|
|
|
66
|
-
/**
|
|
64
|
+
/** `restored` are the queued messages taken back from pi. */
|
|
67
65
|
export type CancelResult = {
|
|
68
66
|
readonly cancelled: boolean;
|
|
69
67
|
readonly restored: readonly string[];
|
|
70
68
|
};
|
|
71
69
|
|
|
72
|
-
/** Whether this host can mutate its session file right now, and who is stopping it. */
|
|
73
70
|
export type LeaseState = {
|
|
74
71
|
readonly writable: boolean;
|
|
75
72
|
readonly heldBy?: {
|
|
@@ -79,7 +76,7 @@ export type LeaseState = {
|
|
|
79
76
|
};
|
|
80
77
|
|
|
81
78
|
export type SessionHostDeps = {
|
|
82
|
-
/**
|
|
79
|
+
/** Log prefix. */
|
|
83
80
|
readonly label: string;
|
|
84
81
|
readonly settings: HostSettings;
|
|
85
82
|
readonly defaults: { readonly cwd: string; readonly model?: string };
|
|
@@ -88,36 +85,25 @@ export type SessionHostDeps = {
|
|
|
88
85
|
readonly modelRegistry: ModelRegistry;
|
|
89
86
|
readonly settingsManagerFor: (cwd: string) => SettingsManager;
|
|
90
87
|
readonly persistSettings: (patch: Partial<HostSettings>) => Promise<void>;
|
|
91
|
-
/**
|
|
92
|
-
* Take the session file's turn lease around every mutation, as this frontend.
|
|
93
|
-
* Leave unset where nothing else can open the file.
|
|
94
|
-
*/
|
|
88
|
+
/** Takes the turn lease around every mutation. Omit when no other process can open the file. */
|
|
95
89
|
readonly lease?: LeaseFrontend;
|
|
96
|
-
/** Omit to let pi
|
|
90
|
+
/** Omit to let pi choose the file path. */
|
|
97
91
|
readonly mainSessionPath?: () => string;
|
|
98
92
|
readonly isolatedSessionPath?: () => string;
|
|
99
93
|
readonly systemInstruction?: () => Promise<string | undefined>;
|
|
100
|
-
/** Where this host's human is reading; surfaces in the system prompt's environment block. */
|
|
101
94
|
readonly surface?: Surface;
|
|
102
95
|
readonly customTools?: (
|
|
103
96
|
context: CustomToolContext
|
|
104
97
|
) => readonly ToolDefinition[];
|
|
105
|
-
/** Runs after the agent is disposed, before
|
|
98
|
+
/** Runs after the agent is disposed, before `sessionPath` is cleared. */
|
|
106
99
|
readonly onRetire?: (sessionPath: string) => Promise<void>;
|
|
107
|
-
/**
|
|
108
|
-
* Where this host's extensions say things, resolved per call so an agent
|
|
109
|
-
* rebuilt under the host follows its live target. Omit to leave pi's no-op
|
|
110
|
-
* UI in place: pi freezes the mode at `bindExtensions`, so this is a dep
|
|
111
|
-
* rather than something a caller can turn on once a session exists.
|
|
112
|
-
*/
|
|
100
|
+
/** Extension UI sink, read per call. Omit for pi's no-op UI; pi fixes the mode at bind time. */
|
|
113
101
|
readonly ui?: () => SessionUi | undefined;
|
|
114
102
|
};
|
|
115
103
|
|
|
116
|
-
/** What a frontend's own tools are built against. */
|
|
117
104
|
export type CustomToolContext = {
|
|
118
|
-
/** Where this session's tools resolve relative paths. */
|
|
119
105
|
readonly cwd: string;
|
|
120
|
-
/**
|
|
106
|
+
/** Undefined until the agent exists. */
|
|
121
107
|
readonly sessionId: () => string | undefined;
|
|
122
108
|
};
|
|
123
109
|
|
|
@@ -126,7 +112,7 @@ type ModelResolveResult =
|
|
|
126
112
|
| { readonly kind: "ambiguous"; readonly candidates: readonly string[] }
|
|
127
113
|
| { readonly kind: "none"; readonly candidates: readonly string[] };
|
|
128
114
|
|
|
129
|
-
/**
|
|
115
|
+
/** `holder` is absent when the lease file is unreadable. */
|
|
130
116
|
type LeaseWait = { readonly holder?: LeaseRecord };
|
|
131
117
|
|
|
132
118
|
const FOREIGN_POLL_MS = 2_000;
|
|
@@ -150,7 +136,7 @@ function isOutput(event: AssistantMessageEvent): boolean {
|
|
|
150
136
|
}
|
|
151
137
|
}
|
|
152
138
|
|
|
153
|
-
/**
|
|
139
|
+
/** One in-process pi agent session, its settings and a serialized turn queue. */
|
|
154
140
|
export class SessionHost {
|
|
155
141
|
public readonly label: string;
|
|
156
142
|
public lastUsed = Date.now();
|
|
@@ -162,12 +148,12 @@ export class SessionHost {
|
|
|
162
148
|
private cachedLog: EventLog | undefined;
|
|
163
149
|
private queue: Promise<unknown> = Promise.resolve();
|
|
164
150
|
private seqLog: EventLog | undefined;
|
|
165
|
-
/**
|
|
151
|
+
/** File head after this host's last write. */
|
|
166
152
|
private lastSeen: { readonly path: string; readonly seq: number } | undefined;
|
|
167
153
|
private blockedBy: LeaseWait | undefined;
|
|
168
154
|
private readonly leaseListeners = new Set<() => void>();
|
|
169
155
|
private readonly foreignListeners = new Set<() => void>();
|
|
170
|
-
/** Set only while a held turn is watching
|
|
156
|
+
/** Set only while a held turn is watching for foreign writes. */
|
|
171
157
|
private sampleWrites: (() => Promise<void>) | undefined;
|
|
172
158
|
private readonly agentListeners = new Set<
|
|
173
159
|
(event: AgentSessionEvent) => void
|
|
@@ -204,12 +190,12 @@ export class SessionHost {
|
|
|
204
190
|
return this.cached?.sessionId;
|
|
205
191
|
}
|
|
206
192
|
|
|
207
|
-
/**
|
|
193
|
+
/** Undefined until the agent is built. */
|
|
208
194
|
public get eventLog(): EventLog | undefined {
|
|
209
195
|
return this.cachedLog;
|
|
210
196
|
}
|
|
211
197
|
|
|
212
|
-
/**
|
|
198
|
+
/** Not writable only while one of our mutations waits on another process's lease. */
|
|
213
199
|
public get leaseState(): LeaseState {
|
|
214
200
|
const blocked = this.blockedBy;
|
|
215
201
|
if (blocked === undefined) {
|
|
@@ -232,7 +218,7 @@ export class SessionHost {
|
|
|
232
218
|
};
|
|
233
219
|
}
|
|
234
220
|
|
|
235
|
-
/** Fires once per turn in which another process appended to
|
|
221
|
+
/** Fires at most once per turn in which another process appended to the file. */
|
|
236
222
|
public onForeignWrite(listener: () => void): () => void {
|
|
237
223
|
this.foreignListeners.add(listener);
|
|
238
224
|
return () => {
|
|
@@ -240,7 +226,7 @@ export class SessionHost {
|
|
|
240
226
|
};
|
|
241
227
|
}
|
|
242
228
|
|
|
243
|
-
/**
|
|
229
|
+
/** Survives agent rebuilds. */
|
|
244
230
|
public subscribe(listener: (event: AgentSessionEvent) => void): () => void {
|
|
245
231
|
this.agentListeners.add(listener);
|
|
246
232
|
return () => {
|
|
@@ -280,17 +266,17 @@ export class SessionHost {
|
|
|
280
266
|
}
|
|
281
267
|
|
|
282
268
|
public get currentModelId(): string | undefined {
|
|
283
|
-
const model = this.
|
|
269
|
+
const model = this.currentModel();
|
|
284
270
|
return model ? qualifiedModelId(model) : undefined;
|
|
285
271
|
}
|
|
286
272
|
|
|
287
|
-
/**
|
|
273
|
+
/** Display name, e.g. "Claude Opus 5.0". */
|
|
288
274
|
public get currentModelLabel(): string | undefined {
|
|
289
|
-
return
|
|
275
|
+
return this.currentModel()?.name;
|
|
290
276
|
}
|
|
291
277
|
|
|
292
278
|
public get supportedThinkingLevels(): readonly ThinkingLevel[] {
|
|
293
|
-
const model = this.
|
|
279
|
+
const model = this.currentModel();
|
|
294
280
|
return model ? getSupportedThinkingLevels(model) : [];
|
|
295
281
|
}
|
|
296
282
|
|
|
@@ -303,17 +289,16 @@ export class SessionHost {
|
|
|
303
289
|
return (sm.getDefaultThinkingLevel() as ThinkingLevel) ?? "medium";
|
|
304
290
|
}
|
|
305
291
|
|
|
306
|
-
/**
|
|
292
|
+
/** Undefined when only the directory's default applies. */
|
|
307
293
|
public get chosenThinkingLevel(): ThinkingLevel | undefined {
|
|
308
294
|
return this.currentSettings.thinkingLevel ?? this.cached?.thinkingLevel;
|
|
309
295
|
}
|
|
310
296
|
|
|
311
|
-
/**
|
|
297
|
+
/** Runs `work` as a queued turn. `isolated` uses a throwaway agent and file, without the lease. */
|
|
312
298
|
public run(
|
|
313
299
|
work: (agent: AgentSession) => Promise<void>,
|
|
314
300
|
opts?: { readonly isolated?: boolean }
|
|
315
301
|
): Promise<void> {
|
|
316
|
-
// An isolated run owns a throwaway file nobody else can name, so it takes no lease.
|
|
317
302
|
if (opts?.isolated) {
|
|
318
303
|
return this.enqueue(async () => {
|
|
319
304
|
const { agent, sessionPath } = await this.buildIsolatedAgent();
|
|
@@ -335,28 +320,28 @@ export class SessionHost {
|
|
|
335
320
|
});
|
|
336
321
|
}
|
|
337
322
|
|
|
338
|
-
/**
|
|
323
|
+
/** Builds or reuses the agent inside the turn queue. */
|
|
339
324
|
public ensureAgent(): Promise<AgentSession> {
|
|
340
325
|
return this.enqueue(() => this.ensureCached());
|
|
341
326
|
}
|
|
342
327
|
|
|
343
|
-
/**
|
|
328
|
+
/** Runs `work` in the turn queue without touching the agent. */
|
|
344
329
|
public serialize<T>(work: () => Promise<T>): Promise<T> {
|
|
345
330
|
return this.enqueue(work);
|
|
346
331
|
}
|
|
347
332
|
|
|
348
|
-
/**
|
|
333
|
+
/** Aborts the running turn and takes back its queued messages. */
|
|
349
334
|
public async cancel(): Promise<CancelResult> {
|
|
350
335
|
if (!this.cached || !this.cached.isStreaming) {
|
|
351
336
|
return { cancelled: false, restored: [] };
|
|
352
337
|
}
|
|
353
|
-
//
|
|
338
|
+
// Before aborting, or pi delivers the queue to the next turn.
|
|
354
339
|
const restored = this.takeBack();
|
|
355
340
|
await this.cached.abort();
|
|
356
341
|
return { cancelled: true, restored };
|
|
357
342
|
}
|
|
358
343
|
|
|
359
|
-
/**
|
|
344
|
+
/** Takes back messages queued behind the running turn. Not queued: the turn holds the queue. */
|
|
360
345
|
public takeBack(): readonly string[] {
|
|
361
346
|
if (!this.cached || !this.cached.isStreaming) {
|
|
362
347
|
return [];
|
|
@@ -412,10 +397,7 @@ export class SessionHost {
|
|
|
412
397
|
});
|
|
413
398
|
}
|
|
414
399
|
|
|
415
|
-
/**
|
|
416
|
-
* Rename this session through pi's own session name, so its `/resume` picker
|
|
417
|
-
* shows it too; `null` clears it. Answers with the name pi kept.
|
|
418
|
-
*/
|
|
400
|
+
/** Sets pi's session name; `null` clears it. Returns the stored name. */
|
|
419
401
|
public setName(name: string | null): Promise<string | undefined> {
|
|
420
402
|
return this.withLease(async () => {
|
|
421
403
|
const agent = await this.ensureCached();
|
|
@@ -432,7 +414,7 @@ export class SessionHost {
|
|
|
432
414
|
});
|
|
433
415
|
}
|
|
434
416
|
|
|
435
|
-
/**
|
|
417
|
+
/** Drops the agent so the next turn rebuilds it from the file. */
|
|
436
418
|
public async invalidate(): Promise<void> {
|
|
437
419
|
if (!this.cached) {
|
|
438
420
|
return;
|
|
@@ -449,7 +431,7 @@ export class SessionHost {
|
|
|
449
431
|
|
|
450
432
|
private enqueue<T>(work: () => Promise<T>): Promise<T> {
|
|
451
433
|
const next = this.queue.then(work);
|
|
452
|
-
//
|
|
434
|
+
// Keep the chain alive; callers still get their own rejection.
|
|
453
435
|
this.queue = next.catch((err: unknown) => {
|
|
454
436
|
console.error(`[${this.label}] work failed:`, err);
|
|
455
437
|
});
|
|
@@ -457,7 +439,6 @@ export class SessionHost {
|
|
|
457
439
|
return next;
|
|
458
440
|
}
|
|
459
441
|
|
|
460
|
-
/** Where this session's turn lease lives, once anything has named the file. */
|
|
461
442
|
private get leasePath(): string | undefined {
|
|
462
443
|
return (
|
|
463
444
|
this.cached?.sessionFile ??
|
|
@@ -466,11 +447,7 @@ export class SessionHost {
|
|
|
466
447
|
);
|
|
467
448
|
}
|
|
468
449
|
|
|
469
|
-
/**
|
|
470
|
-
* Serialize `work` behind the session file's turn lease, with a rehydration on
|
|
471
|
-
* either side of the acquire: waiting for the lease is exactly the window in
|
|
472
|
-
* which the other surface appends.
|
|
473
|
-
*/
|
|
450
|
+
/** Queues `work` under the turn lease, rehydrating before and after waiting for it. */
|
|
474
451
|
private withLease<T>(work: () => Promise<T>): Promise<T> {
|
|
475
452
|
return this.enqueue(async () => {
|
|
476
453
|
const frontend = this.deps.lease;
|
|
@@ -478,7 +455,7 @@ export class SessionHost {
|
|
|
478
455
|
return await work();
|
|
479
456
|
}
|
|
480
457
|
await this.rehydrateIfStale();
|
|
481
|
-
//
|
|
458
|
+
// A new session's path is only known once pi builds the agent.
|
|
482
459
|
const path = this.leasePath ?? (await this.ensureCached()).sessionFile;
|
|
483
460
|
if (path === undefined) {
|
|
484
461
|
return await work();
|
|
@@ -494,8 +471,7 @@ export class SessionHost {
|
|
|
494
471
|
try {
|
|
495
472
|
return await work();
|
|
496
473
|
} finally {
|
|
497
|
-
//
|
|
498
|
-
// agent: leave `lastSeen` behind the head so the next turn rebuilds.
|
|
474
|
+
// After a foreign write, leave `lastSeen` stale so the next turn rebuilds.
|
|
499
475
|
if (!(await settle())) {
|
|
500
476
|
await this.markSeen(path);
|
|
501
477
|
}
|
|
@@ -513,10 +489,8 @@ export class SessionHost {
|
|
|
513
489
|
}
|
|
514
490
|
|
|
515
491
|
/**
|
|
516
|
-
*
|
|
517
|
-
*
|
|
518
|
-
* aborts — the work in flight is worth more than the inconsistency it races.
|
|
519
|
-
* Resolves to whether anything foreign landed.
|
|
492
|
+
* Polls for lines another process appended during a held turn. Warns at most
|
|
493
|
+
* once and never aborts. The returned stop function resolves to whether any landed.
|
|
520
494
|
*/
|
|
521
495
|
private async watchForeignWrites(
|
|
522
496
|
path: string
|
|
@@ -531,8 +505,7 @@ export class SessionHost {
|
|
|
531
505
|
return;
|
|
532
506
|
}
|
|
533
507
|
const agent = this.cached;
|
|
534
|
-
// A rebuilt agent
|
|
535
|
-
// on migration, so its own accounting starts from whatever it just read.
|
|
508
|
+
// A rebuilt agent may have rewritten the file, so restart accounting from it.
|
|
536
509
|
if (agent?.sessionFile !== path) {
|
|
537
510
|
base = undefined;
|
|
538
511
|
return;
|
|
@@ -579,7 +552,7 @@ export class SessionHost {
|
|
|
579
552
|
}
|
|
580
553
|
}
|
|
581
554
|
|
|
582
|
-
/**
|
|
555
|
+
/** Drops the agent if the file grew since our last write. */
|
|
583
556
|
private async rehydrateIfStale(): Promise<void> {
|
|
584
557
|
const path = this.cached?.sessionFile;
|
|
585
558
|
const seen = this.lastSeen;
|
|
@@ -591,8 +564,6 @@ export class SessionHost {
|
|
|
591
564
|
}
|
|
592
565
|
}
|
|
593
566
|
|
|
594
|
-
// Reading the head is how our own writes stop looking foreign: pi repairs a torn tail and
|
|
595
|
-
// rewrites on migration, so a rebuild moves the head without anyone else touching the file.
|
|
596
567
|
private async markSeen(path: string): Promise<void> {
|
|
597
568
|
this.lastSeen = { path, seq: await this.logFor(path).head() };
|
|
598
569
|
}
|
|
@@ -626,8 +597,7 @@ export class SessionHost {
|
|
|
626
597
|
if (this.deps.lease !== undefined && agent.sessionFile) {
|
|
627
598
|
await this.markSeen(agent.sessionFile);
|
|
628
599
|
}
|
|
629
|
-
//
|
|
630
|
-
// it was before this rebuild read it.
|
|
600
|
+
// Re-anchor a watching turn on the new agent.
|
|
631
601
|
await this.sampleWrites?.();
|
|
632
602
|
await this.patchSettings({ cwd, sessionPath: agent.sessionFile });
|
|
633
603
|
return agent;
|
|
@@ -740,7 +710,7 @@ export class SessionHost {
|
|
|
740
710
|
cwd,
|
|
741
711
|
agentDir: this.deps.agentDir,
|
|
742
712
|
settingsManager,
|
|
743
|
-
//
|
|
713
|
+
// In-process: a disk-loaded copy would register views into a second `Tools`.
|
|
744
714
|
extensionFactories: CoreExtensions.gated(this.deps.surface),
|
|
745
715
|
appendSystemPromptOverride: (base) => {
|
|
746
716
|
return promptRef.wrapped ? [...base, promptRef.wrapped] : base;
|
|
@@ -784,8 +754,7 @@ export class SessionHost {
|
|
|
784
754
|
`[${this.label}] extension ${err.extensionPath} (${err.event}):`,
|
|
785
755
|
err.error
|
|
786
756
|
);
|
|
787
|
-
// Pi
|
|
788
|
-
// without this the user's message vanishes and only stderr says why.
|
|
757
|
+
// Pi treats a throwing command as success, so tell the user.
|
|
789
758
|
this.deps
|
|
790
759
|
.ui?.()
|
|
791
760
|
?.notify(
|
|
@@ -794,11 +763,10 @@ export class SessionHost {
|
|
|
794
763
|
);
|
|
795
764
|
};
|
|
796
765
|
const ui = this.deps.ui;
|
|
797
|
-
// Emits session_start;
|
|
766
|
+
// Emits session_start; extension tools are unusable without it.
|
|
798
767
|
await agent.bindExtensions(
|
|
799
768
|
ui
|
|
800
|
-
? // Any
|
|
801
|
-
// runner.js:318); "rpc" is its own name for dialog-capable but not a terminal.
|
|
769
|
+
? // Any uiContext makes pi's `hasUI()` true; "rpc" means dialogs but no terminal.
|
|
802
770
|
{
|
|
803
771
|
uiContext: adaptSessionUi(ui),
|
|
804
772
|
mode: "rpc",
|
|
@@ -834,6 +802,10 @@ export class SessionHost {
|
|
|
834
802
|
await this.deps.persistSettings(patch);
|
|
835
803
|
}
|
|
836
804
|
|
|
805
|
+
private currentModel(): Model<ModelApi> | undefined {
|
|
806
|
+
return this.cached?.model ?? this.resolveDefaultModel();
|
|
807
|
+
}
|
|
808
|
+
|
|
837
809
|
private resolveDefaultModel(): Model<ModelApi> | undefined {
|
|
838
810
|
this.deps.modelRegistry.refresh();
|
|
839
811
|
for (const candidate of [
|
|
@@ -862,23 +834,21 @@ export class SessionHost {
|
|
|
862
834
|
private resolveModel(pattern: string): ModelResolveResult {
|
|
863
835
|
this.deps.modelRegistry.refresh();
|
|
864
836
|
const available = this.deps.modelRegistry.getAvailable();
|
|
865
|
-
const
|
|
866
|
-
(m) => ({
|
|
867
|
-
item: m,
|
|
868
|
-
haystacks: [qualifiedModelId(m), m.id, m.name],
|
|
869
|
-
})
|
|
870
|
-
);
|
|
871
|
-
|
|
837
|
+
const wanted = pattern.trim();
|
|
872
838
|
const exact = available.find(
|
|
873
839
|
(m) =>
|
|
874
|
-
qualifiedModelId(m) ===
|
|
875
|
-
m.id === pattern.trim() ||
|
|
876
|
-
m.name === pattern.trim()
|
|
840
|
+
qualifiedModelId(m) === wanted || m.id === wanted || m.name === wanted
|
|
877
841
|
);
|
|
878
842
|
if (exact) {
|
|
879
843
|
return { kind: "ok", model: exact };
|
|
880
844
|
}
|
|
881
845
|
|
|
846
|
+
const candidates: FuzzyCandidate<Model<ModelApi>>[] = available.map(
|
|
847
|
+
(m) => ({
|
|
848
|
+
item: m,
|
|
849
|
+
haystacks: [qualifiedModelId(m), m.id, m.name],
|
|
850
|
+
})
|
|
851
|
+
);
|
|
882
852
|
const hits = FuzzyMatcher.rank(pattern, candidates, { limit: 5 });
|
|
883
853
|
if (hits.length === 0) {
|
|
884
854
|
return {
|
|
@@ -10,7 +10,6 @@ import { Json } from "../shared/Json";
|
|
|
10
10
|
|
|
11
11
|
export type LeaseFrontend = "tui" | "daemon";
|
|
12
12
|
|
|
13
|
-
/** What one holder wrote into `<sessionPath>.lease`. */
|
|
14
13
|
export type LeaseRecord = {
|
|
15
14
|
readonly pid: number;
|
|
16
15
|
readonly hostname: string;
|
|
@@ -24,14 +23,14 @@ export type LeaseHandle = {
|
|
|
24
23
|
|
|
25
24
|
export type AcquireResult =
|
|
26
25
|
| { readonly ok: true; readonly handle: LeaseHandle }
|
|
27
|
-
/** Absent when the
|
|
26
|
+
/** Absent when the lease file is torn or vanished mid-read. */
|
|
28
27
|
| { readonly ok: false; readonly holder?: LeaseRecord };
|
|
29
28
|
|
|
30
29
|
export type LeaseOptions = {
|
|
31
30
|
readonly heartbeatMs?: number;
|
|
32
31
|
readonly pollMs?: number;
|
|
33
32
|
readonly timeoutMs?: number;
|
|
34
|
-
/** Called
|
|
33
|
+
/** Called on each denied attempt that will be retried. */
|
|
35
34
|
readonly onBlocked?: (holder: LeaseRecord | undefined) => void;
|
|
36
35
|
};
|
|
37
36
|
|
|
@@ -73,7 +72,7 @@ async function recordAt(path: string): Promise<LeaseRecord | undefined> {
|
|
|
73
72
|
return toRecord(await Fs.readJsonOr<unknown>(path, undefined));
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
/**
|
|
75
|
+
/** Undefined when the file is absent or unparseable. */
|
|
77
76
|
async function read(sessionPath: string): Promise<LeaseRecord | undefined> {
|
|
78
77
|
return await recordAt(pathFor(sessionPath));
|
|
79
78
|
}
|
|
@@ -83,12 +82,12 @@ function isAlive(pid: number): boolean {
|
|
|
83
82
|
process.kill(pid, 0);
|
|
84
83
|
return true;
|
|
85
84
|
} catch (err) {
|
|
86
|
-
// EPERM
|
|
85
|
+
// EPERM means the process exists but belongs to another user.
|
|
87
86
|
return FsErrors.code(err) !== "ESRCH";
|
|
88
87
|
}
|
|
89
88
|
}
|
|
90
89
|
|
|
91
|
-
/**
|
|
90
|
+
/** Stale when its pid is gone or it stopped heartbeating. Never stale across hosts. */
|
|
92
91
|
function isStale(
|
|
93
92
|
record: LeaseRecord,
|
|
94
93
|
mtimeMs: number,
|
|
@@ -100,7 +99,6 @@ function isStale(
|
|
|
100
99
|
return !isAlive(record.pid) || now - mtimeMs > STALE_MS;
|
|
101
100
|
}
|
|
102
101
|
|
|
103
|
-
/** Whether this very process wrote that record — the one holder allowed to release it. */
|
|
104
102
|
function isOurs(record: LeaseRecord | undefined): boolean {
|
|
105
103
|
return record?.pid === process.pid && record.hostname === HOST;
|
|
106
104
|
}
|
|
@@ -116,7 +114,7 @@ async function stealable(
|
|
|
116
114
|
if (mtimeMs === undefined) {
|
|
117
115
|
return true;
|
|
118
116
|
}
|
|
119
|
-
// A torn file
|
|
117
|
+
// A torn file may be mid-write; only steal it once its heartbeat stops.
|
|
120
118
|
return record === undefined
|
|
121
119
|
? Date.now() - mtimeMs > STALE_MS
|
|
122
120
|
: isStale(record, mtimeMs);
|
|
@@ -136,7 +134,7 @@ async function claim(
|
|
|
136
134
|
if (FsErrors.code(err) === "EEXIST") {
|
|
137
135
|
return undefined;
|
|
138
136
|
}
|
|
139
|
-
// The
|
|
137
|
+
// The lease can be taken before pi creates the session directory.
|
|
140
138
|
if (FsErrors.code(err) === "ENOENT") {
|
|
141
139
|
return mkdir(dirname(path), { recursive: true }).then(() =>
|
|
142
140
|
open(path, "wx")
|
|
@@ -174,7 +172,7 @@ function hookExit(): void {
|
|
|
174
172
|
function handleFor(path: string, options: LeaseOptions): LeaseHandle {
|
|
175
173
|
held.add(path);
|
|
176
174
|
hookExit();
|
|
177
|
-
//
|
|
175
|
+
// Touch mtime only; rewriting could clobber the next holder's file.
|
|
178
176
|
const beat = setInterval(() => {
|
|
179
177
|
const now = new Date();
|
|
180
178
|
void utimes(path, now, now).catch(() => undefined);
|
|
@@ -197,7 +195,7 @@ function handleFor(path: string, options: LeaseOptions): LeaseHandle {
|
|
|
197
195
|
};
|
|
198
196
|
}
|
|
199
197
|
|
|
200
|
-
/** Create-exclusive
|
|
198
|
+
/** Create-exclusive; steals only a stale lease. */
|
|
201
199
|
async function acquire(
|
|
202
200
|
sessionPath: string,
|
|
203
201
|
frontend: LeaseFrontend,
|
|
@@ -217,7 +215,7 @@ async function acquire(
|
|
|
217
215
|
}
|
|
218
216
|
}
|
|
219
217
|
|
|
220
|
-
/** Polls until
|
|
218
|
+
/** Polls `acquire` until it succeeds or the timeout passes. */
|
|
221
219
|
async function waitFor(
|
|
222
220
|
sessionPath: string,
|
|
223
221
|
frontend: LeaseFrontend,
|
|
@@ -243,7 +241,7 @@ function denial(result: { readonly holder?: LeaseRecord }): string {
|
|
|
243
241
|
return `Session is busy: ${who} still holds its turn lease.`;
|
|
244
242
|
}
|
|
245
243
|
|
|
246
|
-
/**
|
|
244
|
+
/** Runs `work` under the lease, always releasing it. Throws if it cannot be taken. */
|
|
247
245
|
async function hold<T>(
|
|
248
246
|
sessionPath: string,
|
|
249
247
|
frontend: LeaseFrontend,
|
|
@@ -261,12 +259,11 @@ async function hold<T>(
|
|
|
261
259
|
}
|
|
262
260
|
}
|
|
263
261
|
|
|
264
|
-
/** Fires whenever the lease appears, changes hands, or is released. */
|
|
265
262
|
function watch(sessionPath: string, onChange: () => void): () => void {
|
|
266
263
|
return FileWatch.file(pathFor(sessionPath), onChange);
|
|
267
264
|
}
|
|
268
265
|
|
|
269
|
-
/** A turn lease over one session file,
|
|
266
|
+
/** A turn lease over one session file, stored at `<sessionPath>.lease`. */
|
|
270
267
|
export const SessionLease = {
|
|
271
268
|
pathFor,
|
|
272
269
|
read,
|