@edspencer/paddock 0.0.1 → 0.56.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/README.md +69 -0
- package/install-notice.mjs +29 -0
- package/package.json +58 -5
- package/packages/server/dist/adoptable.d.ts +127 -0
- package/packages/server/dist/adoptable.js +315 -0
- package/packages/server/dist/app.d.ts +67 -0
- package/packages/server/dist/app.js +325 -0
- package/packages/server/dist/archive.d.ts +42 -0
- package/packages/server/dist/archive.js +125 -0
- package/packages/server/dist/attachments-config.d.ts +114 -0
- package/packages/server/dist/attachments-config.js +165 -0
- package/packages/server/dist/attachments-hint.d.ts +53 -0
- package/packages/server/dist/attachments-hint.js +140 -0
- package/packages/server/dist/attachments.d.ts +33 -0
- package/packages/server/dist/attachments.js +117 -0
- package/packages/server/dist/auth.d.ts +84 -0
- package/packages/server/dist/auth.js +247 -0
- package/packages/server/dist/background.d.ts +36 -0
- package/packages/server/dist/background.js +137 -0
- package/packages/server/dist/bind-safety.d.ts +56 -0
- package/packages/server/dist/bind-safety.js +51 -0
- package/packages/server/dist/brand.d.ts +44 -0
- package/packages/server/dist/brand.js +88 -0
- package/packages/server/dist/chat-dto.d.ts +210 -0
- package/packages/server/dist/chat-dto.js +308 -0
- package/packages/server/dist/cli/import-chats.d.ts +21 -0
- package/packages/server/dist/cli/import-chats.js +197 -0
- package/packages/server/dist/cli/paddock.d.ts +2 -0
- package/packages/server/dist/cli/paddock.js +247 -0
- package/packages/server/dist/concurrency.d.ts +57 -0
- package/packages/server/dist/concurrency.js +71 -0
- package/packages/server/dist/config.d.ts +512 -0
- package/packages/server/dist/config.js +593 -0
- package/packages/server/dist/curation-config.d.ts +55 -0
- package/packages/server/dist/curation-config.js +64 -0
- package/packages/server/dist/event-bus.d.ts +87 -0
- package/packages/server/dist/event-bus.js +48 -0
- package/packages/server/dist/git.d.ts +128 -0
- package/packages/server/dist/git.js +446 -0
- package/packages/server/dist/github-auth.d.ts +44 -0
- package/packages/server/dist/github-auth.js +169 -0
- package/packages/server/dist/herdctl-agent-config.d.ts +98 -0
- package/packages/server/dist/herdctl-agent-config.js +331 -0
- package/packages/server/dist/herdctl-agent-names.d.ts +172 -0
- package/packages/server/dist/herdctl-agent-names.js +228 -0
- package/packages/server/dist/herdctl-jobs-index.d.ts +65 -0
- package/packages/server/dist/herdctl-jobs-index.js +225 -0
- package/packages/server/dist/herdctl-jobs.d.ts +98 -0
- package/packages/server/dist/herdctl-jobs.js +225 -0
- package/packages/server/dist/herdctl.d.ts +572 -0
- package/packages/server/dist/herdctl.js +1218 -0
- package/packages/server/dist/hook-config.d.ts +217 -0
- package/packages/server/dist/hook-config.js +224 -0
- package/packages/server/dist/http-bytes.d.ts +24 -0
- package/packages/server/dist/http-bytes.js +52 -0
- package/packages/server/dist/index.d.ts +1 -0
- package/packages/server/dist/index.js +14 -0
- package/packages/server/dist/instance-config.d.ts +135 -0
- package/packages/server/dist/instance-config.js +345 -0
- package/packages/server/dist/localcommand.d.ts +23 -0
- package/packages/server/dist/localcommand.js +140 -0
- package/packages/server/dist/management-auth.d.ts +99 -0
- package/packages/server/dist/management-auth.js +165 -0
- package/packages/server/dist/management-config.d.ts +164 -0
- package/packages/server/dist/management-config.js +286 -0
- package/packages/server/dist/management-mcp-server.d.ts +20 -0
- package/packages/server/dist/management-mcp-server.js +95 -0
- package/packages/server/dist/management-metadata.d.ts +67 -0
- package/packages/server/dist/management-metadata.js +56 -0
- package/packages/server/dist/management-ops.d.ts +110 -0
- package/packages/server/dist/management-ops.js +449 -0
- package/packages/server/dist/management-policy.d.ts +172 -0
- package/packages/server/dist/management-policy.js +259 -0
- package/packages/server/dist/message-provenance.d.ts +112 -0
- package/packages/server/dist/message-provenance.js +260 -0
- package/packages/server/dist/models.d.ts +164 -0
- package/packages/server/dist/models.js +208 -0
- package/packages/server/dist/openapi.d.ts +66 -0
- package/packages/server/dist/openapi.js +183 -0
- package/packages/server/dist/parent-detach.d.ts +30 -0
- package/packages/server/dist/parent-detach.js +109 -0
- package/packages/server/dist/preload.d.ts +43 -0
- package/packages/server/dist/preload.js +55 -0
- package/packages/server/dist/project-files.d.ts +76 -0
- package/packages/server/dist/project-files.js +191 -0
- package/packages/server/dist/project-mime.d.ts +35 -0
- package/packages/server/dist/project-mime.js +55 -0
- package/packages/server/dist/project-paths.d.ts +88 -0
- package/packages/server/dist/project-paths.js +126 -0
- package/packages/server/dist/project-types.d.ts +316 -0
- package/packages/server/dist/project-types.js +48 -0
- package/packages/server/dist/projects.d.ts +202 -0
- package/packages/server/dist/projects.js +894 -0
- package/packages/server/dist/queued-message.d.ts +35 -0
- package/packages/server/dist/queued-message.js +116 -0
- package/packages/server/dist/read-state.d.ts +46 -0
- package/packages/server/dist/read-state.js +139 -0
- package/packages/server/dist/recovery-config.d.ts +111 -0
- package/packages/server/dist/recovery-config.js +105 -0
- package/packages/server/dist/recovery.d.ts +262 -0
- package/packages/server/dist/recovery.js +490 -0
- package/packages/server/dist/resume-drain.d.ts +122 -0
- package/packages/server/dist/resume-drain.js +158 -0
- package/packages/server/dist/route-context.d.ts +168 -0
- package/packages/server/dist/route-context.js +39 -0
- package/packages/server/dist/route-errors.d.ts +8 -0
- package/packages/server/dist/route-errors.js +13 -0
- package/packages/server/dist/routes/chats.d.ts +12 -0
- package/packages/server/dist/routes/chats.js +1406 -0
- package/packages/server/dist/routes/git.d.ts +13 -0
- package/packages/server/dist/routes/git.js +236 -0
- package/packages/server/dist/routes/mcp.d.ts +83 -0
- package/packages/server/dist/routes/mcp.js +250 -0
- package/packages/server/dist/routes/meta.d.ts +15 -0
- package/packages/server/dist/routes/meta.js +447 -0
- package/packages/server/dist/routes/projects.d.ts +25 -0
- package/packages/server/dist/routes/projects.js +736 -0
- package/packages/server/dist/routes/triggers.d.ts +21 -0
- package/packages/server/dist/routes/triggers.js +324 -0
- package/packages/server/dist/routes/workspace-mount.d.ts +48 -0
- package/packages/server/dist/routes/workspace-mount.js +32 -0
- package/packages/server/dist/routes.d.ts +26 -0
- package/packages/server/dist/routes.js +29 -0
- package/packages/server/dist/run-provenance.d.ts +109 -0
- package/packages/server/dist/run-provenance.js +206 -0
- package/packages/server/dist/runs.d.ts +92 -0
- package/packages/server/dist/runs.js +72 -0
- package/packages/server/dist/schedule-config.d.ts +68 -0
- package/packages/server/dist/schedule-config.js +75 -0
- package/packages/server/dist/schedule-session.d.ts +34 -0
- package/packages/server/dist/schedule-session.js +120 -0
- package/packages/server/dist/self-mcp-descriptions.d.ts +20 -0
- package/packages/server/dist/self-mcp-descriptions.js +145 -0
- package/packages/server/dist/self-mcp-read.d.ts +15 -0
- package/packages/server/dist/self-mcp-read.js +97 -0
- package/packages/server/dist/self-mcp-triggers.d.ts +30 -0
- package/packages/server/dist/self-mcp-triggers.js +144 -0
- package/packages/server/dist/self-mcp-types.d.ts +248 -0
- package/packages/server/dist/self-mcp-types.js +10 -0
- package/packages/server/dist/self-mcp-util.d.ts +73 -0
- package/packages/server/dist/self-mcp-util.js +157 -0
- package/packages/server/dist/self-mcp-write.d.ts +35 -0
- package/packages/server/dist/self-mcp-write.js +215 -0
- package/packages/server/dist/self-mcp.d.ts +95 -0
- package/packages/server/dist/self-mcp.js +448 -0
- package/packages/server/dist/send-file-mcp.d.ts +46 -0
- package/packages/server/dist/send-file-mcp.js +281 -0
- package/packages/server/dist/session-hub.d.ts +186 -0
- package/packages/server/dist/session-hub.js +291 -0
- package/packages/server/dist/spawn-capability.d.ts +99 -0
- package/packages/server/dist/spawn-capability.js +106 -0
- package/packages/server/dist/star.d.ts +26 -0
- package/packages/server/dist/star.js +94 -0
- package/packages/server/dist/start.d.ts +1 -0
- package/packages/server/dist/start.js +21 -0
- package/packages/server/dist/subagents.d.ts +337 -0
- package/packages/server/dist/subagents.js +626 -0
- package/packages/server/dist/sweep.d.ts +122 -0
- package/packages/server/dist/sweep.js +696 -0
- package/packages/server/dist/tooldetails.d.ts +33 -0
- package/packages/server/dist/tooldetails.js +466 -0
- package/packages/server/dist/transcribe.d.ts +69 -0
- package/packages/server/dist/transcribe.js +119 -0
- package/packages/server/dist/transcripts.d.ts +42 -0
- package/packages/server/dist/transcripts.js +164 -0
- package/packages/server/dist/trigger-config.d.ts +278 -0
- package/packages/server/dist/trigger-config.js +417 -0
- package/packages/server/dist/trigger-runtime.d.ts +94 -0
- package/packages/server/dist/trigger-runtime.js +82 -0
- package/packages/server/dist/trigger-session.d.ts +34 -0
- package/packages/server/dist/trigger-session.js +122 -0
- package/packages/server/dist/triggers.d.ts +83 -0
- package/packages/server/dist/triggers.js +124 -0
- package/packages/server/dist/trusted-proxy.d.ts +57 -0
- package/packages/server/dist/trusted-proxy.js +157 -0
- package/packages/server/dist/turn-notice.d.ts +154 -0
- package/packages/server/dist/turn-notice.js +271 -0
- package/packages/server/dist/unread.d.ts +36 -0
- package/packages/server/dist/unread.js +123 -0
- package/packages/server/dist/usage.d.ts +66 -0
- package/packages/server/dist/usage.js +243 -0
- package/packages/server/dist/wake-injection.d.ts +156 -0
- package/packages/server/dist/wake-injection.js +136 -0
- package/packages/server/dist/whisper-local.d.ts +9 -0
- package/packages/server/dist/whisper-local.js +122 -0
- package/packages/server/dist/ws-context.d.ts +158 -0
- package/packages/server/dist/ws-context.js +1 -0
- package/packages/server/dist/ws-protocol.d.ts +423 -0
- package/packages/server/dist/ws-protocol.js +251 -0
- package/packages/server/dist/ws-self-mcp.d.ts +52 -0
- package/packages/server/dist/ws-self-mcp.js +28 -0
- package/packages/server/dist/ws-triggers.d.ts +29 -0
- package/packages/server/dist/ws-triggers.js +261 -0
- package/packages/server/dist/ws-turn.d.ts +86 -0
- package/packages/server/dist/ws-turn.js +751 -0
- package/packages/server/dist/ws.d.ts +62 -0
- package/packages/server/dist/ws.js +1024 -0
- package/packages/server/package.json +6 -0
- package/packages/web/dist/assets/InstanceConfigPage-4cQGTj9Y.js +2 -0
- package/packages/web/dist/assets/MarkdownRenderer-CJZFLj_8.js +30 -0
- package/packages/web/dist/assets/ProjectRedirect-BHwVbbqB.js +2 -0
- package/packages/web/dist/assets/ProjectView-BOvuAITg.js +13 -0
- package/packages/web/dist/assets/ProjectsGrid-BCojZOGY.js +2 -0
- package/packages/web/dist/assets/arc-BVyxvnkl.js +2 -0
- package/packages/web/dist/assets/architectureDiagram-3BPJPVTR-Cd9Puqg1.js +37 -0
- package/packages/web/dist/assets/bash-I8pq0VWm.js +2 -0
- package/packages/web/dist/assets/blockDiagram-GPEHLZMM-B_AQt_Rx.js +133 -0
- package/packages/web/dist/assets/c-D2F1sQUL.js +2 -0
- package/packages/web/dist/assets/c4Diagram-AAUBKEIU-7e0TQtXB.js +11 -0
- package/packages/web/dist/assets/channel-C_1aISNn.js +2 -0
- package/packages/web/dist/assets/chunk-2J33WTMH-Dz4WZmUZ.js +2 -0
- package/packages/web/dist/assets/chunk-4BX2VUAB-DVWYldo6.js +2 -0
- package/packages/web/dist/assets/chunk-55IACEB6-BIMrizQd.js +2 -0
- package/packages/web/dist/assets/chunk-727SXJPM-CL2q9fF1.js +207 -0
- package/packages/web/dist/assets/chunk-AQP2D5EJ-BSq3HlQ9.js +232 -0
- package/packages/web/dist/assets/chunk-FMBD7UC4-Bgt64pLE.js +16 -0
- package/packages/web/dist/assets/chunk-ND2GUHAM-C8Pr2A38.js +2 -0
- package/packages/web/dist/assets/chunk-QZHKN3VN-BAOqh-MN.js +2 -0
- package/packages/web/dist/assets/classDiagram-4FO5ZUOK-B8JE7cHJ.js +2 -0
- package/packages/web/dist/assets/classDiagram-v2-Q7XG4LA2-B8JE7cHJ.js +2 -0
- package/packages/web/dist/assets/core-lJeGACQg.js +4 -0
- package/packages/web/dist/assets/cose-bilkent-S5V4N54A-BF4tameA.js +2 -0
- package/packages/web/dist/assets/cpp-Bm3ii6kY.js +2 -0
- package/packages/web/dist/assets/csharp-BNpuYZMG.js +2 -0
- package/packages/web/dist/assets/css-DazXZka4.js +2 -0
- package/packages/web/dist/assets/cytoscape.esm-CUqq0XTU.js +332 -0
- package/packages/web/dist/assets/dagre-BM42HDAG-DdsLM6ql.js +5 -0
- package/packages/web/dist/assets/defaultLocale-DX6XiGOO.js +2 -0
- package/packages/web/dist/assets/diagram-2AECGRRQ-BQ78S_RM.js +44 -0
- package/packages/web/dist/assets/diagram-5GNKFQAL-CAKE6nHE.js +11 -0
- package/packages/web/dist/assets/diagram-KO2AKTUF-fEvHRXgr.js +4 -0
- package/packages/web/dist/assets/diagram-LMA3HP47-CtWsgeSm.js +25 -0
- package/packages/web/dist/assets/diagram-OG6HWLK6-Buog8k7_.js +25 -0
- package/packages/web/dist/assets/erDiagram-TEJ5UH35-CWvVVPZS.js +86 -0
- package/packages/web/dist/assets/flowDiagram-I6XJVG4X-DsCGXtp4.js +163 -0
- package/packages/web/dist/assets/format-DamJQ9LO.js +2 -0
- package/packages/web/dist/assets/ganttDiagram-6RSMTGT7-CsdwIvvC.js +293 -0
- package/packages/web/dist/assets/gitGraphDiagram-PVQCEYII-hZvrX1sD.js +107 -0
- package/packages/web/dist/assets/go-BPx_y7j8.js +2 -0
- package/packages/web/dist/assets/graph-BlwPajkw.js +2 -0
- package/packages/web/dist/assets/index-CGbZdrm8.js +71 -0
- package/packages/web/dist/assets/index-DJDQ210-.css +1 -0
- package/packages/web/dist/assets/infoDiagram-5YYISTIA-nE_Vtz_l.js +3 -0
- package/packages/web/dist/assets/ini-B3BVgEsG.js +2 -0
- package/packages/web/dist/assets/init-Gi6I4Gst.js +2 -0
- package/packages/web/dist/assets/ishikawaDiagram-YF4QCWOH-2Nlqs0oH.js +71 -0
- package/packages/web/dist/assets/java-B5VKqtH2.js +2 -0
- package/packages/web/dist/assets/javascript-BKRaQes9.js +2 -0
- package/packages/web/dist/assets/journeyDiagram-JHISSGLW-mjU1fMc4.js +140 -0
- package/packages/web/dist/assets/json-DIYVocXf.js +2 -0
- package/packages/web/dist/assets/kanban-definition-UN3LZRKU-C_9xNaa3.js +90 -0
- package/packages/web/dist/assets/katex-C5jXJg4s.js +258 -0
- package/packages/web/dist/assets/kotlin-Cw-eF_bm.js +3 -0
- package/packages/web/dist/assets/lastTab-CU1zsF0j.js +2 -0
- package/packages/web/dist/assets/layout-Boy8vhSP.js +2 -0
- package/packages/web/dist/assets/linear-BxKd91ho.js +2 -0
- package/packages/web/dist/assets/mermaid.core-C-CuhD8I.js +310 -0
- package/packages/web/dist/assets/mindmap-definition-RKZ34NQL-Bs6yfcZv.js +97 -0
- package/packages/web/dist/assets/ordinal-Cboi1Yqb.js +2 -0
- package/packages/web/dist/assets/php-CRdd4dfS.js +3 -0
- package/packages/web/dist/assets/pieDiagram-4H26LBE5-DNdqX7O5.js +31 -0
- package/packages/web/dist/assets/python-kSlZHhJb.js +2 -0
- package/packages/web/dist/assets/quadrantDiagram-W4KKPZXB-ZMJl9OdZ.js +8 -0
- package/packages/web/dist/assets/queued-LamEU0GH.js +2 -0
- package/packages/web/dist/assets/requirementDiagram-4Y6WPE33-RmxMdm2q.js +85 -0
- package/packages/web/dist/assets/ruby-7T4ktxyv.js +2 -0
- package/packages/web/dist/assets/rust-HC3MBUhY.js +2 -0
- package/packages/web/dist/assets/sankeyDiagram-5OEKKPKP-OAAhWcfK.js +41 -0
- package/packages/web/dist/assets/scss-DZLA7YXz.js +2 -0
- package/packages/web/dist/assets/sequenceDiagram-3UESZ5HK-BWF7rCyN.js +163 -0
- package/packages/web/dist/assets/sql-BC88IN-V.js +2 -0
- package/packages/web/dist/assets/stateDiagram-AJRCARHV-M3b_zqqo.js +2 -0
- package/packages/web/dist/assets/stateDiagram-v2-BHNVJYJU-CI7saFDa.js +2 -0
- package/packages/web/dist/assets/swift-Dn8kKO_1.js +2 -0
- package/packages/web/dist/assets/timeline-definition-PNZ67QCA-BpOCh63N.js +121 -0
- package/packages/web/dist/assets/typescript-C2FFdlUC.js +2 -0
- package/packages/web/dist/assets/vennDiagram-CIIHVFJN-D_iPH45Q.js +35 -0
- package/packages/web/dist/assets/wardley-L42UT6IY-DVD9I2Om.js +174 -0
- package/packages/web/dist/assets/wardleyDiagram-YWT4CUSO-Bn1IKwjn.js +79 -0
- package/packages/web/dist/assets/xml-BXBhIUeX.js +2 -0
- package/packages/web/dist/assets/xychartDiagram-2RQKCTM6-DwtI0bkR.js +8 -0
- package/packages/web/dist/assets/yaml-drsDnIKT.js +2 -0
- package/packages/web/dist/favicon.ico +0 -0
- package/packages/web/dist/fonts/inter-latin.woff2 +0 -0
- package/packages/web/dist/fonts/jetbrains-mono-latin.woff2 +0 -0
- package/packages/web/dist/icons/apple-touch-icon.png +0 -0
- package/packages/web/dist/icons/favicon-16.png +0 -0
- package/packages/web/dist/icons/favicon-32.png +0 -0
- package/packages/web/dist/icons/icon-192.png +0 -0
- package/packages/web/dist/icons/icon-512.png +0 -0
- package/packages/web/dist/icons/maskable-512.png +0 -0
- package/packages/web/dist/index.html +56 -0
- package/packages/web/dist/manifest.webmanifest +32 -0
- package/packages/web/dist/sw.js +139 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat DTO + chat-list helpers for the REST surface. Pure, module-level, and
|
|
3
|
+
* shared by the chat/project route groups: the wire `ChatUsage` shape and its
|
|
4
|
+
* `toChatUsage` builder, the `toChatDto` projection, the preview-cleaning
|
|
5
|
+
* `buildProjectChats`, the `makeTriggerResolver` capability-descriptor resolver,
|
|
6
|
+
* and the run-history page-size constants + `clampRunsLimit`.
|
|
7
|
+
*/
|
|
8
|
+
import type { DiscoveredSession } from "@herdctl/core";
|
|
9
|
+
import type { Project } from "./projects.js";
|
|
10
|
+
import { type SessionTokenUsage } from "./usage.js";
|
|
11
|
+
import type { RunProvenance } from "./run-provenance.js";
|
|
12
|
+
import { type ChatTriggerInfo } from "./trigger-config.js";
|
|
13
|
+
/** A session id safe to interpolate into a transcript file path (issue #329). */
|
|
14
|
+
export declare const SAFE_SESSION_ID: RegExp;
|
|
15
|
+
/**
|
|
16
|
+
* The chat that created this one, as sent on the wire — what the sidebar files a
|
|
17
|
+
* child under. `project` is carried because a parent can live in ANOTHER project
|
|
18
|
+
* (a keeper spawning into a sibling repo); the list renders those as roots rather
|
|
19
|
+
* than hiding a chat under a parent the user can't see from here.
|
|
20
|
+
*/
|
|
21
|
+
export interface ChatParentRef {
|
|
22
|
+
project: string;
|
|
23
|
+
sessionId: string;
|
|
24
|
+
/** The parent's display name at creation time — a fallback label only. */
|
|
25
|
+
name?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Does this recorded provenance describe a chat that is a root by construction?
|
|
29
|
+
*
|
|
30
|
+
* `depth` counts spawn hops from the human/scheduled root, so depth 0 IS the root
|
|
31
|
+
* — and a root-origin marker says the same thing from the other direction. Either
|
|
32
|
+
* one means "this chat has no parent", as opposed to "this chat's parent was
|
|
33
|
+
* never recorded", which is the only case the inference tier exists to cover.
|
|
34
|
+
*/
|
|
35
|
+
export declare function isRecordedRoot(p: RunProvenance): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Build the `parentOf` resolver for {@link buildProjectChats} — the chat-list
|
|
38
|
+
* parent edge, resolved in two tiers behind one override:
|
|
39
|
+
*
|
|
40
|
+
* 0. `ParentDetachStore` — the user explicitly DETACHED this chat (#508). Checked
|
|
41
|
+
* ahead of both tiers, because detach cannot be expressed by clearing an edge:
|
|
42
|
+
* most live edges are INFERRED (tier 2), so a cleared edge is simply re-derived
|
|
43
|
+
* on the next load. Detached ⇒ root, full stop.
|
|
44
|
+
* 1. `RunProvenance.parentSessionId` — the RECORDED edge, stamped at creation.
|
|
45
|
+
* 2. `MessageProvenanceStore.parentChat()` — a backfill for chats created before
|
|
46
|
+
* tier 1 existed, inferred from who injected the kickoff prompt.
|
|
47
|
+
*
|
|
48
|
+
* Tier 2 only applies to a chat whose parent is genuinely UNKNOWN. A chat with a
|
|
49
|
+
* recorded root marker (#491) is skipped outright: it isn't missing an edge, it
|
|
50
|
+
* has none. Without that guard, the documented report-back workflow re-parents a
|
|
51
|
+
* human's own chat — manager spawns child, child `send_message`s its report home,
|
|
52
|
+
* the manager now carries a `chat`-kind marker and infers its own child as its
|
|
53
|
+
* parent. Both edges then point at each other and `buildChatTree`'s cycle guard
|
|
54
|
+
* decides, per render, which of the two gets promoted to a root.
|
|
55
|
+
*
|
|
56
|
+
* All reads are in-memory sidecar lookups, so this is cheap enough to resolve
|
|
57
|
+
* inline for every row (unlike the usage ring, #116). A chat with neither tier is
|
|
58
|
+
* simply a root.
|
|
59
|
+
*/
|
|
60
|
+
export declare function makeParentResolver(runProvenance: {
|
|
61
|
+
get(sessionId: string): Promise<RunProvenance | undefined>;
|
|
62
|
+
}, messageProvenance: {
|
|
63
|
+
parentChat(sessionId: string): Promise<{
|
|
64
|
+
project: string;
|
|
65
|
+
sessionId: string;
|
|
66
|
+
name?: string;
|
|
67
|
+
} | null>;
|
|
68
|
+
}, projectSlug: string,
|
|
69
|
+
/**
|
|
70
|
+
* Has the user detached this chat from its parent (#508)? Optional so the many
|
|
71
|
+
* call sites that predate detach — and the unit tests — stay correct without it;
|
|
72
|
+
* omitted means "nothing is detached".
|
|
73
|
+
*/
|
|
74
|
+
isDetached?: (sessionId: string) => Promise<boolean>): (s: DiscoveredSession) => Promise<ChatParentRef | null>;
|
|
75
|
+
/**
|
|
76
|
+
* Cap on how many chats one batch subtree operation may touch (#508). The client
|
|
77
|
+
* derives the set from a rendered subtree, so it is bounded by the chat list in
|
|
78
|
+
* practice; this is the "a hand-rolled request can't ask us to rewrite the whole
|
|
79
|
+
* sidecar" bound, not a UX limit.
|
|
80
|
+
*/
|
|
81
|
+
export declare const BATCH_SESSIONS_MAX = 500;
|
|
82
|
+
/**
|
|
83
|
+
* Validate + normalise a batch route's `sessionIds` body field (#508).
|
|
84
|
+
*
|
|
85
|
+
* Returns the de-duplicated ids, or `null` when the body is unusable — which the
|
|
86
|
+
* routes turn into a 400. Deliberately ALL-OR-NOTHING: one malformed id fails the
|
|
87
|
+
* whole request rather than silently applying to the rest, because these
|
|
88
|
+
* operations are meant to keep a chat family in one state. Silently dropping an id
|
|
89
|
+
* would produce exactly the torn family the batch routes exist to prevent.
|
|
90
|
+
*/
|
|
91
|
+
export declare function normalizeBatchSessionIds(raw: unknown): string[] | null;
|
|
92
|
+
/** Claude Code's own preview cap (mirrors extractFirstMessagePreview). */
|
|
93
|
+
export declare const PREVIEW_MAX = 100;
|
|
94
|
+
/**
|
|
95
|
+
* Reserved read-state session id for the per-project, per-user "runs last seen"
|
|
96
|
+
* watermark (the since-last-visit digest, #268). A real Claude Code session id is
|
|
97
|
+
* a UUID (`/^[0-9a-f-]+$/`), so the double-underscore sentinel can never collide
|
|
98
|
+
* with one — the watermark keys cleanly alongside per-chat read-state.
|
|
99
|
+
*/
|
|
100
|
+
export declare const RUNS_SEEN_SESSION = "__runs__";
|
|
101
|
+
/** Default + cap for the run-history page size. */
|
|
102
|
+
export declare const RUNS_LIMIT_DEFAULT = 100;
|
|
103
|
+
export declare const RUNS_LIMIT_MAX = 500;
|
|
104
|
+
/** Parse + clamp the `?limit=` query for the run-history endpoint. */
|
|
105
|
+
export declare function clampRunsLimit(raw: string | undefined): number;
|
|
106
|
+
/**
|
|
107
|
+
* A chat's usage for the UI: the last-turn context fill (issue #77) plus the
|
|
108
|
+
* chat's cumulative lifetime token totals and a ballpark dollar estimate at API
|
|
109
|
+
* rates (issue #152). The cumulative totals and `costUsd` include every sub-agent
|
|
110
|
+
* the chat spawned (issue #242); `contextTokens` stays main-only (last-turn
|
|
111
|
+
* window fill). `costUsd` is null for a model with no known pricing.
|
|
112
|
+
*/
|
|
113
|
+
export type ChatUsage = {
|
|
114
|
+
contextTokens: number;
|
|
115
|
+
contextLimit: number;
|
|
116
|
+
inputTokens: number;
|
|
117
|
+
outputTokens: number;
|
|
118
|
+
cacheReadTokens: number;
|
|
119
|
+
cacheCreationTokens: number;
|
|
120
|
+
totalTokens: number;
|
|
121
|
+
costUsd: number | null;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Which chats the bulk usage endpoint computes rings for (issue #537). Usage has
|
|
125
|
+
* no stored counter — it is derived by streaming each transcript end to end — so
|
|
126
|
+
* scoping the request to what the sidebar actually renders is the difference
|
|
127
|
+
* between a few MB and a few hundred. `active` is the default because the
|
|
128
|
+
* Archived group is collapsed on open.
|
|
129
|
+
*/
|
|
130
|
+
export declare const CHAT_USAGE_SCOPES: readonly ["active", "archived", "all"];
|
|
131
|
+
/** One of {@link CHAT_USAGE_SCOPES}. */
|
|
132
|
+
export type ChatUsageScope = (typeof CHAT_USAGE_SCOPES)[number];
|
|
133
|
+
/**
|
|
134
|
+
* Build the wire `ChatUsage` from a parsed {@link SessionTokenUsage} and the
|
|
135
|
+
* chat's model. Returns null when the transcript has no usage yet, so the ring
|
|
136
|
+
* simply hides. `totalTokens` is a headline "tokens this chat consumed" figure —
|
|
137
|
+
* output plus the (context-growing) input/cache reads — while `costUsd` prices
|
|
138
|
+
* each class separately (see {@link estimateCostUsd}).
|
|
139
|
+
*/
|
|
140
|
+
export declare function toChatUsage(u: SessionTokenUsage, model: string): ChatUsage | null;
|
|
141
|
+
export declare function toChatDto(s: DiscoveredSession, previewOverride?: string, usage?: ChatUsage | null, archived?: boolean, lastTurnCompletedAt?: string, lastSeen?: number, provenance?: RunProvenance | null, trigger?: ChatTriggerInfo | null, starred?: boolean, unread?: boolean, parent?: ChatParentRef | null): {
|
|
142
|
+
trigger?: ChatTriggerInfo | undefined;
|
|
143
|
+
parent?: ChatParentRef | undefined;
|
|
144
|
+
provenance?: RunProvenance | undefined;
|
|
145
|
+
contextTokens?: number | undefined;
|
|
146
|
+
contextLimit?: number | undefined;
|
|
147
|
+
inputTokens?: number | undefined;
|
|
148
|
+
outputTokens?: number | undefined;
|
|
149
|
+
cacheReadTokens?: number | undefined;
|
|
150
|
+
cacheCreationTokens?: number | undefined;
|
|
151
|
+
totalTokens?: number | undefined;
|
|
152
|
+
costUsd?: number | null | undefined;
|
|
153
|
+
unread?: boolean | undefined;
|
|
154
|
+
lastSeen?: number | undefined;
|
|
155
|
+
lastTurnCompletedAt?: string | undefined;
|
|
156
|
+
sessionId: string;
|
|
157
|
+
workingDirectory: string;
|
|
158
|
+
name: string;
|
|
159
|
+
updatedAt: string;
|
|
160
|
+
resumable: boolean;
|
|
161
|
+
preview: string | undefined;
|
|
162
|
+
archived: boolean;
|
|
163
|
+
starred: boolean;
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Build the chat DTOs for a PROJECT's sessions, cleaning names polluted by the
|
|
167
|
+
* preload wrapper (issue #62). When a chat has no better name (no user rename,
|
|
168
|
+
* no Claude-generated summary) AND its preview is the injected `<project-context>`
|
|
169
|
+
* block, we read the untruncated first user message and strip the wrapper so the
|
|
170
|
+
* name reflects the user's actual request. Only preload chats trigger the extra
|
|
171
|
+
* (head-of-file) read; everything else maps straight through.
|
|
172
|
+
*/
|
|
173
|
+
export declare function buildProjectChats(projectDir: string, sessions: DiscoveredSession[], usageOf?: (s: DiscoveredSession) => Promise<ChatUsage | null>, archivedOf?: (s: DiscoveredSession) => Promise<boolean>, lastTurnAt?: ReadonlyMap<string, string>, lastSeenOf?: (s: DiscoveredSession) => Promise<number>, provenanceOf?: (s: DiscoveredSession) => Promise<RunProvenance | undefined>, triggerOf?: (s: DiscoveredSession) => Promise<ChatTriggerInfo | undefined>, starredOf?: (s: DiscoveredSession) => Promise<boolean>, unreadOf?: (s: DiscoveredSession) => Promise<boolean>, parentOf?: (s: DiscoveredSession) => Promise<ChatParentRef | null>): Promise<{
|
|
174
|
+
trigger?: ChatTriggerInfo | undefined;
|
|
175
|
+
parent?: ChatParentRef | undefined;
|
|
176
|
+
provenance?: RunProvenance | undefined;
|
|
177
|
+
contextTokens?: number | undefined;
|
|
178
|
+
contextLimit?: number | undefined;
|
|
179
|
+
inputTokens?: number | undefined;
|
|
180
|
+
outputTokens?: number | undefined;
|
|
181
|
+
cacheReadTokens?: number | undefined;
|
|
182
|
+
cacheCreationTokens?: number | undefined;
|
|
183
|
+
totalTokens?: number | undefined;
|
|
184
|
+
costUsd?: number | null | undefined;
|
|
185
|
+
unread?: boolean | undefined;
|
|
186
|
+
lastSeen?: number | undefined;
|
|
187
|
+
lastTurnCompletedAt?: string | undefined;
|
|
188
|
+
sessionId: string;
|
|
189
|
+
workingDirectory: string;
|
|
190
|
+
name: string;
|
|
191
|
+
updatedAt: string;
|
|
192
|
+
resumable: boolean;
|
|
193
|
+
preview: string | undefined;
|
|
194
|
+
archived: boolean;
|
|
195
|
+
starred: boolean;
|
|
196
|
+
}[]>;
|
|
197
|
+
/**
|
|
198
|
+
* Build the `triggerOf` resolver for {@link buildProjectChats} (Epic T / T4): for
|
|
199
|
+
* a chat whose attributed agent is a `trigger-<slug>-<name>` agent, resolve its
|
|
200
|
+
* trigger's truthful-from-config capability descriptor for the floating capability
|
|
201
|
+
* banner. Returns `undefined` for every non-trigger chat (the common case).
|
|
202
|
+
*
|
|
203
|
+
* Built from the ALREADY-LOADED project record (no extra disk reads): the project's
|
|
204
|
+
* declared triggers are projected ONCE into an `agentName -> ChatTriggerInfo` map
|
|
205
|
+
* (via the same `toTriggerDto` → `toChatTriggerInfo` projection the trigger service
|
|
206
|
+
* uses, so the descriptor is truthful from config), and each trigger chat is an O(1)
|
|
207
|
+
* map lookup. The `TRIGGER_AGENT_PREFIX` fast-path skips even the lookup for the
|
|
208
|
+
* keeper chats that dominate the list.
|
|
209
|
+
*/
|
|
210
|
+
export declare function makeTriggerResolver(project: Project): (s: DiscoveredSession) => Promise<ChatTriggerInfo | undefined>;
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { TRIGGER_AGENT_PREFIX, triggerAgentName } from "./herdctl.js";
|
|
2
|
+
import { getContextLimit, estimateCostUsdByModel } from "./models.js";
|
|
3
|
+
import { toTriggerDto } from "./triggers.js";
|
|
4
|
+
import { toChatTriggerInfo } from "./trigger-config.js";
|
|
5
|
+
import { readFirstUserText } from "./transcripts.js";
|
|
6
|
+
import { PRELOAD_CONTEXT_OPEN, stripPreloadWrapper } from "./preload.js";
|
|
7
|
+
import { stripAttachmentsWrapper, ATTACHMENTS_OPEN } from "./attachments-hint.js";
|
|
8
|
+
/** A session id safe to interpolate into a transcript file path (issue #329). */
|
|
9
|
+
export const SAFE_SESSION_ID = /^[A-Za-z0-9._-]+$/;
|
|
10
|
+
/**
|
|
11
|
+
* Which origins are, by definition, the ROOT of a chat tree. `spawned` is the
|
|
12
|
+
* only one that isn't: a human typed it, a schedule fired it, an event hook did,
|
|
13
|
+
* or it was IMPORTED from the user's own terminal history (#588) — none of those
|
|
14
|
+
* is created BY another chat, so none can acquire a parent.
|
|
15
|
+
*
|
|
16
|
+
* `adopted` belongs here for a stronger reason than the rest: the session ran
|
|
17
|
+
* OUTSIDE Paddock entirely, so there is no chat here that COULD be its parent.
|
|
18
|
+
* It is named explicitly rather than left to the `depth === 0` arm of
|
|
19
|
+
* {@link isRecordedRoot}, because depth is a value the import happens to stamp
|
|
20
|
+
* as 0 — if that ever changed, or a legacy marker carried a depth, the inference
|
|
21
|
+
* tier would go looking for a parent that cannot exist and file the chat under
|
|
22
|
+
* whichever chat happened to inject a prompt near it.
|
|
23
|
+
*
|
|
24
|
+
* A total `Record`, not a `Set`: a set of strings silently accepts a union that
|
|
25
|
+
* has grown past it, whereas a missing key here is a compile error — so adding
|
|
26
|
+
* an origin forces a deliberate root/child decision.
|
|
27
|
+
*/
|
|
28
|
+
const ORIGIN_IS_ROOT = {
|
|
29
|
+
human: true,
|
|
30
|
+
scheduled: true,
|
|
31
|
+
hook: true,
|
|
32
|
+
adopted: true,
|
|
33
|
+
spawned: false,
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Does this recorded provenance describe a chat that is a root by construction?
|
|
37
|
+
*
|
|
38
|
+
* `depth` counts spawn hops from the human/scheduled root, so depth 0 IS the root
|
|
39
|
+
* — and a root-origin marker says the same thing from the other direction. Either
|
|
40
|
+
* one means "this chat has no parent", as opposed to "this chat's parent was
|
|
41
|
+
* never recorded", which is the only case the inference tier exists to cover.
|
|
42
|
+
*/
|
|
43
|
+
export function isRecordedRoot(p) {
|
|
44
|
+
return p.depth === 0 || ORIGIN_IS_ROOT[p.origin] === true;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Build the `parentOf` resolver for {@link buildProjectChats} — the chat-list
|
|
48
|
+
* parent edge, resolved in two tiers behind one override:
|
|
49
|
+
*
|
|
50
|
+
* 0. `ParentDetachStore` — the user explicitly DETACHED this chat (#508). Checked
|
|
51
|
+
* ahead of both tiers, because detach cannot be expressed by clearing an edge:
|
|
52
|
+
* most live edges are INFERRED (tier 2), so a cleared edge is simply re-derived
|
|
53
|
+
* on the next load. Detached ⇒ root, full stop.
|
|
54
|
+
* 1. `RunProvenance.parentSessionId` — the RECORDED edge, stamped at creation.
|
|
55
|
+
* 2. `MessageProvenanceStore.parentChat()` — a backfill for chats created before
|
|
56
|
+
* tier 1 existed, inferred from who injected the kickoff prompt.
|
|
57
|
+
*
|
|
58
|
+
* Tier 2 only applies to a chat whose parent is genuinely UNKNOWN. A chat with a
|
|
59
|
+
* recorded root marker (#491) is skipped outright: it isn't missing an edge, it
|
|
60
|
+
* has none. Without that guard, the documented report-back workflow re-parents a
|
|
61
|
+
* human's own chat — manager spawns child, child `send_message`s its report home,
|
|
62
|
+
* the manager now carries a `chat`-kind marker and infers its own child as its
|
|
63
|
+
* parent. Both edges then point at each other and `buildChatTree`'s cycle guard
|
|
64
|
+
* decides, per render, which of the two gets promoted to a root.
|
|
65
|
+
*
|
|
66
|
+
* All reads are in-memory sidecar lookups, so this is cheap enough to resolve
|
|
67
|
+
* inline for every row (unlike the usage ring, #116). A chat with neither tier is
|
|
68
|
+
* simply a root.
|
|
69
|
+
*/
|
|
70
|
+
export function makeParentResolver(runProvenance, messageProvenance, projectSlug,
|
|
71
|
+
/**
|
|
72
|
+
* Has the user detached this chat from its parent (#508)? Optional so the many
|
|
73
|
+
* call sites that predate detach — and the unit tests — stay correct without it;
|
|
74
|
+
* omitted means "nothing is detached".
|
|
75
|
+
*/
|
|
76
|
+
isDetached) {
|
|
77
|
+
return async (s) => {
|
|
78
|
+
// Tier 0: an explicit detach beats everything, recorded or inferred.
|
|
79
|
+
if (isDetached && (await isDetached(s.sessionId).catch(() => false)))
|
|
80
|
+
return null;
|
|
81
|
+
const p = await runProvenance.get(s.sessionId).catch(() => undefined);
|
|
82
|
+
// `parentProject` is a WORKSPACE KEY, and the root workspace's key is the
|
|
83
|
+
// empty string — a falsy test here would discard every edge whose parent is
|
|
84
|
+
// a root chat and silently fall through to the inference tier, rendering
|
|
85
|
+
// those chats as orphan roots. Only ABSENCE means "no recorded edge".
|
|
86
|
+
if (p?.parentSessionId && p.parentProject !== undefined)
|
|
87
|
+
return { project: p.parentProject, sessionId: p.parentSessionId };
|
|
88
|
+
// A recorded root never falls through to inference (#491).
|
|
89
|
+
if (p && isRecordedRoot(p))
|
|
90
|
+
return null;
|
|
91
|
+
const inferred = await messageProvenance.parentChat(s.sessionId).catch(() => null);
|
|
92
|
+
if (!inferred)
|
|
93
|
+
return null;
|
|
94
|
+
// Defensive: a chat can't parent itself. The tree builder guards cycles, but
|
|
95
|
+
// a self-edge here would silently drop the row from the roots.
|
|
96
|
+
if (inferred.sessionId === s.sessionId)
|
|
97
|
+
return null;
|
|
98
|
+
return { ...inferred, project: inferred.project || projectSlug };
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Cap on how many chats one batch subtree operation may touch (#508). The client
|
|
103
|
+
* derives the set from a rendered subtree, so it is bounded by the chat list in
|
|
104
|
+
* practice; this is the "a hand-rolled request can't ask us to rewrite the whole
|
|
105
|
+
* sidecar" bound, not a UX limit.
|
|
106
|
+
*/
|
|
107
|
+
export const BATCH_SESSIONS_MAX = 500;
|
|
108
|
+
/**
|
|
109
|
+
* Validate + normalise a batch route's `sessionIds` body field (#508).
|
|
110
|
+
*
|
|
111
|
+
* Returns the de-duplicated ids, or `null` when the body is unusable — which the
|
|
112
|
+
* routes turn into a 400. Deliberately ALL-OR-NOTHING: one malformed id fails the
|
|
113
|
+
* whole request rather than silently applying to the rest, because these
|
|
114
|
+
* operations are meant to keep a chat family in one state. Silently dropping an id
|
|
115
|
+
* would produce exactly the torn family the batch routes exist to prevent.
|
|
116
|
+
*/
|
|
117
|
+
export function normalizeBatchSessionIds(raw) {
|
|
118
|
+
if (!Array.isArray(raw) || raw.length === 0 || raw.length > BATCH_SESSIONS_MAX)
|
|
119
|
+
return null;
|
|
120
|
+
const seen = new Set();
|
|
121
|
+
for (const v of raw) {
|
|
122
|
+
if (typeof v !== "string" || !SAFE_SESSION_ID.test(v))
|
|
123
|
+
return null;
|
|
124
|
+
seen.add(v);
|
|
125
|
+
}
|
|
126
|
+
return [...seen];
|
|
127
|
+
}
|
|
128
|
+
/** Claude Code's own preview cap (mirrors extractFirstMessagePreview). */
|
|
129
|
+
export const PREVIEW_MAX = 100;
|
|
130
|
+
/**
|
|
131
|
+
* Reserved read-state session id for the per-project, per-user "runs last seen"
|
|
132
|
+
* watermark (the since-last-visit digest, #268). A real Claude Code session id is
|
|
133
|
+
* a UUID (`/^[0-9a-f-]+$/`), so the double-underscore sentinel can never collide
|
|
134
|
+
* with one — the watermark keys cleanly alongside per-chat read-state.
|
|
135
|
+
*/
|
|
136
|
+
export const RUNS_SEEN_SESSION = "__runs__";
|
|
137
|
+
/** Default + cap for the run-history page size. */
|
|
138
|
+
export const RUNS_LIMIT_DEFAULT = 100;
|
|
139
|
+
export const RUNS_LIMIT_MAX = 500;
|
|
140
|
+
/** Parse + clamp the `?limit=` query for the run-history endpoint. */
|
|
141
|
+
export function clampRunsLimit(raw) {
|
|
142
|
+
const n = raw === undefined ? RUNS_LIMIT_DEFAULT : Number.parseInt(raw, 10);
|
|
143
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
144
|
+
return RUNS_LIMIT_DEFAULT;
|
|
145
|
+
return Math.min(n, RUNS_LIMIT_MAX);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Which chats the bulk usage endpoint computes rings for (issue #537). Usage has
|
|
149
|
+
* no stored counter — it is derived by streaming each transcript end to end — so
|
|
150
|
+
* scoping the request to what the sidebar actually renders is the difference
|
|
151
|
+
* between a few MB and a few hundred. `active` is the default because the
|
|
152
|
+
* Archived group is collapsed on open.
|
|
153
|
+
*/
|
|
154
|
+
export const CHAT_USAGE_SCOPES = ["active", "archived", "all"];
|
|
155
|
+
/**
|
|
156
|
+
* Build the wire `ChatUsage` from a parsed {@link SessionTokenUsage} and the
|
|
157
|
+
* chat's model. Returns null when the transcript has no usage yet, so the ring
|
|
158
|
+
* simply hides. `totalTokens` is a headline "tokens this chat consumed" figure —
|
|
159
|
+
* output plus the (context-growing) input/cache reads — while `costUsd` prices
|
|
160
|
+
* each class separately (see {@link estimateCostUsd}).
|
|
161
|
+
*/
|
|
162
|
+
export function toChatUsage(u, model) {
|
|
163
|
+
if (!u.hasData)
|
|
164
|
+
return null;
|
|
165
|
+
const totals = {
|
|
166
|
+
inputTokens: u.inputTotal,
|
|
167
|
+
outputTokens: u.outputTotal,
|
|
168
|
+
cacheReadTokens: u.cacheReadTotal,
|
|
169
|
+
cacheCreationTokens: u.cacheCreationTotal,
|
|
170
|
+
};
|
|
171
|
+
return {
|
|
172
|
+
contextTokens: u.contextTokens,
|
|
173
|
+
contextLimit: getContextLimit(model),
|
|
174
|
+
...totals,
|
|
175
|
+
totalTokens: u.inputTotal + u.outputTotal + u.cacheReadTotal + u.cacheCreationTotal,
|
|
176
|
+
// Price per the model each turn actually ran on (u.byModel), not the passed
|
|
177
|
+
// project/chat default — a chat's turns can span models, so a single blended
|
|
178
|
+
// rate misprices (a Haiku chat billed at the Opus default would be 5× high).
|
|
179
|
+
costUsd: estimateCostUsdByModel(u.byModel),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
export function toChatDto(s, previewOverride, usage, archived = false, lastTurnCompletedAt, lastSeen, provenance, trigger, starred = false, unread = false, parent) {
|
|
183
|
+
const preview = previewOverride ?? s.preview;
|
|
184
|
+
return {
|
|
185
|
+
sessionId: s.sessionId,
|
|
186
|
+
workingDirectory: s.workingDirectory,
|
|
187
|
+
name: s.customName ?? s.autoName ?? preview ?? s.sessionId.slice(0, 8),
|
|
188
|
+
updatedAt: s.mtime,
|
|
189
|
+
resumable: s.resumable,
|
|
190
|
+
preview,
|
|
191
|
+
// Whether this chat is filed away in the Archived section (#95). Always
|
|
192
|
+
// present so the client can partition the list without a fallback.
|
|
193
|
+
archived,
|
|
194
|
+
// Whether this chat is starred/pinned (#373) — sorts to the top of its
|
|
195
|
+
// population (active or archived). Always present, orthogonal to `archived`.
|
|
196
|
+
starred,
|
|
197
|
+
// ISO timestamp of the last turn the agent FINISHED (from job records, not
|
|
198
|
+
// mtime) — the unread signal (#160). Absent when no completed job record
|
|
199
|
+
// exists yet (session-mode chats, or a brand-new chat still on turn 1).
|
|
200
|
+
...(lastTurnCompletedAt ? { lastTurnCompletedAt } : {}),
|
|
201
|
+
// Epoch-ms the user last viewed this chat (server-side read-state, #189) —
|
|
202
|
+
// the source of truth for the unread affordance, so it follows the user
|
|
203
|
+
// across devices. 0/absent means never seen on this instance.
|
|
204
|
+
...(lastSeen ? { lastSeen } : {}),
|
|
205
|
+
// Whether the user MANUALLY flagged this chat unread (#458) — a per-user
|
|
206
|
+
// override on top of the derived unread signal, so a chat resurfaces its cue
|
|
207
|
+
// even after its last turn was seen. Only emitted when set (absent ⇒ false),
|
|
208
|
+
// keeping the payload compact like `lastSeen`.
|
|
209
|
+
...(unread ? { unread: true } : {}),
|
|
210
|
+
// The context-window fill as of the session's last completed turn (for the
|
|
211
|
+
// per-chat usage ring) plus the chat's cumulative token totals and cost
|
|
212
|
+
// estimate (issue #152), so the list can render both without opening the
|
|
213
|
+
// chat. Only present when the transcript has usage data.
|
|
214
|
+
...(usage ?? {}),
|
|
215
|
+
// How this chat was created (#267): A1's provenance marker (#261) — origin
|
|
216
|
+
// (human / scheduled / spawned / hook) + spawn depth — so the list can badge the
|
|
217
|
+
// "ran without me" cases. Absent when no marker was recorded (older chats,
|
|
218
|
+
// or ones created before A1). Human origin renders no badge (the default).
|
|
219
|
+
...(provenance ? { provenance } : {}),
|
|
220
|
+
// The chat that created this one, so the list can nest it under its parent
|
|
221
|
+
// instead of only badging it "spawned". Recorded at creation on newer chats
|
|
222
|
+
// (RunProvenance.parentSessionId) and backfilled from the first injected-message
|
|
223
|
+
// sender on older ones. Absent for roots and for chats with no recoverable edge.
|
|
224
|
+
...(parent ? { parent } : {}),
|
|
225
|
+
// For a TRIGGER chat (Epic T / T4): the truthful-from-config capability
|
|
226
|
+
// descriptor — trigger type (schedule/event/webhook) + WHEN it fires + granted
|
|
227
|
+
// tools — read from the same `trigger-<slug>-<name>` agent config herdctl
|
|
228
|
+
// enforces. Drives the floating capability banner atop the chat. Absent for
|
|
229
|
+
// non-trigger chats.
|
|
230
|
+
...(trigger ? { trigger } : {}),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Build the chat DTOs for a PROJECT's sessions, cleaning names polluted by the
|
|
235
|
+
* preload wrapper (issue #62). When a chat has no better name (no user rename,
|
|
236
|
+
* no Claude-generated summary) AND its preview is the injected `<project-context>`
|
|
237
|
+
* block, we read the untruncated first user message and strip the wrapper so the
|
|
238
|
+
* name reflects the user's actual request. Only preload chats trigger the extra
|
|
239
|
+
* (head-of-file) read; everything else maps straight through.
|
|
240
|
+
*/
|
|
241
|
+
export async function buildProjectChats(projectDir, sessions, usageOf, archivedOf, lastTurnAt, lastSeenOf, provenanceOf, triggerOf, starredOf, unreadOf, parentOf) {
|
|
242
|
+
return Promise.all(sessions.map(async (s) => {
|
|
243
|
+
// Resolve the usage ring, archived flag, read-state, provenance, trigger, star, unread, name.
|
|
244
|
+
const usage = usageOf ? await usageOf(s).catch(() => null) : null;
|
|
245
|
+
const archived = archivedOf ? await archivedOf(s).catch(() => false) : false;
|
|
246
|
+
const turnAt = lastTurnAt?.get(s.sessionId);
|
|
247
|
+
const lastSeen = lastSeenOf ? await lastSeenOf(s).catch(() => 0) : 0;
|
|
248
|
+
const provenance = provenanceOf ? await provenanceOf(s).catch(() => null) : null;
|
|
249
|
+
const trigger = triggerOf ? await triggerOf(s).catch(() => undefined) : undefined;
|
|
250
|
+
const starred = starredOf ? await starredOf(s).catch(() => false) : false;
|
|
251
|
+
const unread = unreadOf ? await unreadOf(s).catch(() => false) : false;
|
|
252
|
+
const parent = parentOf ? await parentOf(s).catch(() => null) : null;
|
|
253
|
+
// A name polluted by a machine-prepended wrapper: the preload context block
|
|
254
|
+
// (#1) and/or the composer-attachment block (#328). Either makes the raw
|
|
255
|
+
// first message a poor display name, so recover the real request below.
|
|
256
|
+
//
|
|
257
|
+
// `autoName` has to be tested, not merely checked for absence. @herdctl/core
|
|
258
|
+
// used to leave it undefined for a transcript with no title entry — which is
|
|
259
|
+
// nearly every CLI transcript — so "no autoName" was a reliable proxy for
|
|
260
|
+
// "the preview is all we have". It now falls back to the first user message
|
|
261
|
+
// itself (custom-title → ai-title → summary → preview), so a preload chat
|
|
262
|
+
// arrives with `autoName` ALREADY set to the `<project-context>` block. Kept
|
|
263
|
+
// as an absence check, this whole branch became unreachable and every
|
|
264
|
+
// preload chat was titled with the injected overview.
|
|
265
|
+
const isWrapped = (v) => v !== undefined && (v.startsWith(PRELOAD_CONTEXT_OPEN) || v.startsWith(ATTACHMENTS_OPEN));
|
|
266
|
+
// A polluted autoName is worse than none: it would beat the cleaned preview
|
|
267
|
+
// in `toChatDto`'s name precedence, so drop it rather than compete with it.
|
|
268
|
+
const session = isWrapped(s.autoName) ? { ...s, autoName: undefined } : s;
|
|
269
|
+
const pollutedPreview = !session.customName &&
|
|
270
|
+
!session.autoName &&
|
|
271
|
+
(isWrapped(s.preview) || isWrapped(s.autoName));
|
|
272
|
+
if (!pollutedPreview)
|
|
273
|
+
return toChatDto(session, undefined, usage, archived, turnAt, lastSeen, provenance, trigger, starred, unread, parent);
|
|
274
|
+
const full = await readFirstUserText(projectDir, s.sessionId).catch(() => undefined);
|
|
275
|
+
// Strip preload FIRST (it wraps the whole thing), then the attachment block
|
|
276
|
+
// nested inside it, leaving just the user's typed request.
|
|
277
|
+
const cleaned = stripAttachmentsWrapper(stripPreloadWrapper(full ?? s.preview ?? s.autoName ?? "")).trim();
|
|
278
|
+
// couldn't recover
|
|
279
|
+
if (!cleaned)
|
|
280
|
+
return toChatDto(session, undefined, usage, archived, turnAt, lastSeen, provenance, trigger, starred, unread, parent);
|
|
281
|
+
const preview = cleaned.length > PREVIEW_MAX ? `${cleaned.slice(0, PREVIEW_MAX)}...` : cleaned;
|
|
282
|
+
return toChatDto(session, preview, usage, archived, turnAt, lastSeen, provenance, trigger, starred, unread, parent);
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Build the `triggerOf` resolver for {@link buildProjectChats} (Epic T / T4): for
|
|
287
|
+
* a chat whose attributed agent is a `trigger-<slug>-<name>` agent, resolve its
|
|
288
|
+
* trigger's truthful-from-config capability descriptor for the floating capability
|
|
289
|
+
* banner. Returns `undefined` for every non-trigger chat (the common case).
|
|
290
|
+
*
|
|
291
|
+
* Built from the ALREADY-LOADED project record (no extra disk reads): the project's
|
|
292
|
+
* declared triggers are projected ONCE into an `agentName -> ChatTriggerInfo` map
|
|
293
|
+
* (via the same `toTriggerDto` → `toChatTriggerInfo` projection the trigger service
|
|
294
|
+
* uses, so the descriptor is truthful from config), and each trigger chat is an O(1)
|
|
295
|
+
* map lookup. The `TRIGGER_AGENT_PREFIX` fast-path skips even the lookup for the
|
|
296
|
+
* keeper chats that dominate the list.
|
|
297
|
+
*/
|
|
298
|
+
export function makeTriggerResolver(project) {
|
|
299
|
+
const byAgentName = new Map();
|
|
300
|
+
for (const [name, trigger] of Object.entries(project.triggers ?? {})) {
|
|
301
|
+
byAgentName.set(triggerAgentName(project.slug, name), toChatTriggerInfo(toTriggerDto(project.slug, name, trigger)));
|
|
302
|
+
}
|
|
303
|
+
return async (s) => {
|
|
304
|
+
if (!s.agentName || !s.agentName.startsWith(TRIGGER_AGENT_PREFIX))
|
|
305
|
+
return undefined;
|
|
306
|
+
return byAgentName.get(s.agentName);
|
|
307
|
+
};
|
|
308
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
interface Args {
|
|
2
|
+
project?: string;
|
|
3
|
+
from?: string;
|
|
4
|
+
move: boolean;
|
|
5
|
+
dryRun: boolean;
|
|
6
|
+
dataDir?: string;
|
|
7
|
+
json: boolean;
|
|
8
|
+
help: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parse argv. Hand-rolled because this is the repo's FIRST script taking flags
|
|
12
|
+
* (`pm` parses its own, in CommonJS) — there is no convention to follow and no
|
|
13
|
+
* argument parser in the dependency tree, and adding one for six flags would be
|
|
14
|
+
* the larger change.
|
|
15
|
+
*
|
|
16
|
+
* `--project ""` must survive: the root workspace's key is the empty string, so
|
|
17
|
+
* a "missing value" check written as truthiness would reject the one workspace
|
|
18
|
+
* that always exists.
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseArgs(argv: string[]): Args;
|
|
21
|
+
export {};
|