@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,262 @@
|
|
|
1
|
+
import { type RecoveryConfig } from "./recovery-config.js";
|
|
2
|
+
import type { Project } from "./projects.js";
|
|
3
|
+
/**
|
|
4
|
+
* The kinds of transcript entry the hang detector cares about. Everything that
|
|
5
|
+
* isn't a keeper `assistant` turn or a terminated `<task-notification>` (system
|
|
6
|
+
* lines, attachments, queue-operations, tool_result user turns, clean/running
|
|
7
|
+
* notifications, …) is `other` and doesn't move the pending state.
|
|
8
|
+
*/
|
|
9
|
+
export type TailEvent = "assistant" | "terminated-notification" | "other";
|
|
10
|
+
/** A minimal structural view of one parsed transcript JSONL entry. */
|
|
11
|
+
interface TranscriptEntry {
|
|
12
|
+
type?: string;
|
|
13
|
+
message?: {
|
|
14
|
+
role?: string;
|
|
15
|
+
content?: unknown;
|
|
16
|
+
};
|
|
17
|
+
/** Present on a `type:"queue-operation"` entry (`enqueue`/`dequeue`). */
|
|
18
|
+
operation?: string;
|
|
19
|
+
/** The payload of a `queue-operation` enqueue — for a killed background task
|
|
20
|
+
* this IS the `<task-notification>` (string, or an array of text blocks). #347 */
|
|
21
|
+
content?: unknown;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Classify one already-parsed transcript entry as the hang detector sees it.
|
|
25
|
+
*
|
|
26
|
+
* A terminated `<task-notification>` reaches the transcript in TWO shapes, and we
|
|
27
|
+
* must catch both (issue #347):
|
|
28
|
+
* - As a materialised `type:"user"` entry — but that only appears once a LATER
|
|
29
|
+
* turn flushes the SDK's input queue, often tens of seconds after the kill.
|
|
30
|
+
* - At the moment of the kill, as a `type:"queue-operation"` `enqueue` whose
|
|
31
|
+
* `content` IS the notification. This is the one that lands inside the watch
|
|
32
|
+
* window; missing it (the original bug) let every real hang go undetected.
|
|
33
|
+
*
|
|
34
|
+
* A keeper reply is `assistant`; a killed/stopped notification in either shape is
|
|
35
|
+
* `terminated-notification`; everything else (system lines, attachments, a
|
|
36
|
+
* `dequeue`, a completed/running notification, plain user text) is `other`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function classifyEntry(entry: TranscriptEntry): TailEvent;
|
|
39
|
+
/**
|
|
40
|
+
* Parse one raw JSONL line into its {@link TailEvent} plus, for a terminated
|
|
41
|
+
* notification, its human-readable `<summary>` (used by the live surface frame).
|
|
42
|
+
* Unparseable/blank → `other`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function parseTailLine(line: string): {
|
|
45
|
+
event: TailEvent;
|
|
46
|
+
summary?: string;
|
|
47
|
+
};
|
|
48
|
+
/** Classify one raw JSONL line (unparseable/blank → `other`). */
|
|
49
|
+
export declare function classifyLine(line: string): TailEvent;
|
|
50
|
+
/**
|
|
51
|
+
* Reduce an ordered stream of tail events to whether the transcript currently ends
|
|
52
|
+
* (logically) with an un-answered terminated task-notification — the hung
|
|
53
|
+
* signature. An `assistant` entry clears any pending notification (the keeper
|
|
54
|
+
* woke); a terminated notification sets it; `other` entries are inert. True iff a
|
|
55
|
+
* terminated notification is still pending after the last event.
|
|
56
|
+
*/
|
|
57
|
+
export declare function detectHang(events: Iterable<TailEvent>): boolean;
|
|
58
|
+
/** A scheduled-timer handle — opaque; produced by `setTimer`, passed to `clearTimer`. */
|
|
59
|
+
export type TimerHandle = ReturnType<typeof setTimeout>;
|
|
60
|
+
/** Appended-transcript reader result: the new text and the file's current size. */
|
|
61
|
+
export interface TranscriptTail {
|
|
62
|
+
/** UTF-8 text appended since the requested offset (may be empty). */
|
|
63
|
+
text: string;
|
|
64
|
+
/** The file's current size in bytes (the next offset to read from). */
|
|
65
|
+
size: number;
|
|
66
|
+
}
|
|
67
|
+
/** Injectable primitives so the timing-sensitive watch is deterministically testable. */
|
|
68
|
+
export interface RecoveryEngineDeps {
|
|
69
|
+
/** Resolve the recovery config for a dispatch — instance default here; the engine
|
|
70
|
+
* layers the per-project override on top via {@link resolveRecoveryConfig}. */
|
|
71
|
+
cfg: {
|
|
72
|
+
recovery: RecoveryConfig;
|
|
73
|
+
};
|
|
74
|
+
/** Look up the target project (for its `.chats/` dir + `recovery` override). Throws
|
|
75
|
+
* for an unknown/deleted project — the engine treats that as "nothing to recover". */
|
|
76
|
+
getProject: (slug: string) => Promise<Project>;
|
|
77
|
+
/** Inject the recovery nudge into the still-alive session (Layer 2's exact path:
|
|
78
|
+
* `startAgentTurn({ resume, prompt: RECOVERY_NUDGE, sender: { kind: "recovery" } })`). */
|
|
79
|
+
reDrive: (project: Project, sessionId: string) => Promise<void>;
|
|
80
|
+
/** Surface the detected kill to any attached client LIVE (Layer 2 `surfaceKilledTask`)
|
|
81
|
+
* so the "keeper is idle / Continue" affordance appears WITHOUT a refresh — the
|
|
82
|
+
* notification is otherwise trapped in the SDK input queue until the next turn
|
|
83
|
+
* flushes it (issue #347). `summary` is the notification's `<summary>`, if any.
|
|
84
|
+
* Default no-op (surfacing off). */
|
|
85
|
+
surface?: (project: Project, sessionId: string, summary: string | undefined) => void;
|
|
86
|
+
/**
|
|
87
|
+
* Is a live turn ALREADY driving this session? (issue #352 — double-dispatch
|
|
88
|
+
* guard.) When the watch decides a keeper is hung, something else may have begun
|
|
89
|
+
* driving the same session in the meantime — a human message, a queued-message
|
|
90
|
+
* drain, or a prior recovery nudge. Under session-mode `chatSession(resume)` a
|
|
91
|
+
* SECOND resume interrupts the first, so an auto re-drive fired into a live turn
|
|
92
|
+
* would be swallowed (the "first message swallowed" symptom, #350/#347). If this
|
|
93
|
+
* reports the session busy, the engine DEFERS (see {@link sdkSessionLive}) rather
|
|
94
|
+
* than firing — the keeper is not idle. Default: never busy (so the pure
|
|
95
|
+
* unit-tested engine behaviour is unchanged).
|
|
96
|
+
*/
|
|
97
|
+
isBusy?: (sessionId: string) => boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Is a `claude` SUBPROCESS still alive on this session at the herdctl/SDK layer?
|
|
100
|
+
* (issue #397.) {@link isBusy} only reflects turns PADDOCK's hub started, so it
|
|
101
|
+
* is BLIND to a session herdctl's `SessionReaper` is keeping alive for a killed
|
|
102
|
+
* background task (keepAlive) or holding through its ~15s re-invocation grace —
|
|
103
|
+
* throughout which `reaper.isSessionLive` is TRUE. Auto re-drive resumes via a
|
|
104
|
+
* FRESH `openChatSession` = a NEW subprocess on the same session id; firing while
|
|
105
|
+
* a prior subprocess is still live spawns a COMPETING concurrent resume that the
|
|
106
|
+
* SDK resolves by interrupting the in-flight turn (`[Request interrupted by
|
|
107
|
+
* user]`) — the recovery turn then produces nothing and the user is still stuck.
|
|
108
|
+
* Wired in prod to `getSessionLifecycle()?.reaper.isSessionLive(sessionId)`;
|
|
109
|
+
* default `() => false` keeps the pure engine behaviour unchanged (batch mode /
|
|
110
|
+
* no reaper). Treated as a busy signal ORed with {@link isBusy}; when EITHER is
|
|
111
|
+
* set the engine DEFERS the fire (not a permanent stand-down — see {@link
|
|
112
|
+
* DEFAULT_SETTLE_WINDOW_MS}) so the nudge lands exactly once, after the session
|
|
113
|
+
* is genuinely idle (reaper reaped, hub not running).
|
|
114
|
+
*/
|
|
115
|
+
sdkSessionLive?: (sessionId: string) => boolean;
|
|
116
|
+
/** Wall clock (ms). Default `Date.now`. */
|
|
117
|
+
now?: () => number;
|
|
118
|
+
/** Schedule a one-shot timer. Default `setTimeout`. */
|
|
119
|
+
setTimer?: (fn: () => void, ms: number) => TimerHandle;
|
|
120
|
+
/** Cancel a scheduled timer. Default `clearTimeout`. */
|
|
121
|
+
clearTimer?: (h: TimerHandle) => void;
|
|
122
|
+
/** Read the transcript bytes appended since `offset`. Default reads `<dir>/.chats/<id>.jsonl`. */
|
|
123
|
+
readTail?: (file: string, offset: number) => Promise<TranscriptTail>;
|
|
124
|
+
/**
|
|
125
|
+
* Cheap current size (bytes) of the transcript, for the arm-time baseline — a
|
|
126
|
+
* `stat`, NOT a full read. Called once per armed watch on EVERY session-mode
|
|
127
|
+
* keeper turn, so it must not slurp the (multi-MB) transcript. A missing file
|
|
128
|
+
* (turn wrote none yet) → 0. Default `fsp.stat(file).size`.
|
|
129
|
+
*/
|
|
130
|
+
fileSize?: (file: string) => Promise<number>;
|
|
131
|
+
/** Poll cadence (ms) while watching. Default {@link DEFAULT_POLL_MS}. */
|
|
132
|
+
pollMs?: number;
|
|
133
|
+
/** Extra window (ms) beyond `debounceMs` to allow the kill to land. Default {@link DEFAULT_KILL_GRACE_MS}. */
|
|
134
|
+
killGraceMs?: number;
|
|
135
|
+
/**
|
|
136
|
+
* Cadence (ms) at which a DEFERRED fire re-checks liveness while the session is
|
|
137
|
+
* still held live by the hub or the reaper (issue #397). Default {@link
|
|
138
|
+
* DEFAULT_SETTLE_POLL_MS}.
|
|
139
|
+
*/
|
|
140
|
+
settlePollMs?: number;
|
|
141
|
+
/**
|
|
142
|
+
* Total budget (ms) to keep deferring a fire while the session stays live before
|
|
143
|
+
* giving up, so a session the reaper never releases can't defer forever (issue
|
|
144
|
+
* #397). Default {@link DEFAULT_SETTLE_WINDOW_MS}.
|
|
145
|
+
*/
|
|
146
|
+
settleWindowMs?: number;
|
|
147
|
+
/** Optional structured-log sink for observability; default no-op. */
|
|
148
|
+
log?: (msg: string, meta?: Record<string, unknown>) => void;
|
|
149
|
+
}
|
|
150
|
+
/** Default poll cadence — brisk enough to catch the kill+debounce, cheap enough to ignore. */
|
|
151
|
+
export declare const DEFAULT_POLL_MS = 750;
|
|
152
|
+
/**
|
|
153
|
+
* How long past `debounceMs` we keep watching for the kill to land. herdctl#374's
|
|
154
|
+
* teardown fires ~2–8s after the turn boundary, so the notification can appear late;
|
|
155
|
+
* this grace ensures its full `debounceMs` quiet window still fits inside the watch.
|
|
156
|
+
*/
|
|
157
|
+
export declare const DEFAULT_KILL_GRACE_MS = 12000;
|
|
158
|
+
/**
|
|
159
|
+
* Cadence (ms) at which a deferred fire re-checks whether the session has gone
|
|
160
|
+
* idle (issue #397). Brisk enough to fire promptly once the reaper releases,
|
|
161
|
+
* cheap enough to ignore.
|
|
162
|
+
*/
|
|
163
|
+
export declare const DEFAULT_SETTLE_POLL_MS = 1000;
|
|
164
|
+
/**
|
|
165
|
+
* How long (ms) the engine keeps DEFERRING an otherwise-ready fire while the
|
|
166
|
+
* session is still live (hub or reaper) before standing down (issue #397). Must
|
|
167
|
+
* comfortably exceed herdctl's `DEFAULT_REINVOCATION_GRACE_MS` (15s) — the reaper
|
|
168
|
+
* holds a just-killed-task session live for that grace — plus margin, so the
|
|
169
|
+
* deferred nudge lands once the reaper reaps rather than giving up early. Bounds
|
|
170
|
+
* the defer so a session the reaper never releases can't retry forever.
|
|
171
|
+
*/
|
|
172
|
+
export declare const DEFAULT_SETTLE_WINDOW_MS = 30000;
|
|
173
|
+
/**
|
|
174
|
+
* Layer 3 automatic-recovery engine (issue #301). Arm a post-turn transcript watch
|
|
175
|
+
* after each session-mode keeper turn; if a killed-at-turn-boundary notification
|
|
176
|
+
* appears with no keeper response, auto-inject a recovery nudge — guarded by
|
|
177
|
+
* debounce + a per-session retry cap, and cleared when a human next messages.
|
|
178
|
+
*/
|
|
179
|
+
export declare class RecoveryEngine {
|
|
180
|
+
private readonly deps;
|
|
181
|
+
private readonly now;
|
|
182
|
+
private readonly setTimer;
|
|
183
|
+
private readonly clearTimer;
|
|
184
|
+
private readonly readTail;
|
|
185
|
+
private readonly fileSize;
|
|
186
|
+
private readonly pollMs;
|
|
187
|
+
private readonly killGraceMs;
|
|
188
|
+
private readonly settlePollMs;
|
|
189
|
+
private readonly settleWindowMs;
|
|
190
|
+
private readonly log;
|
|
191
|
+
private readonly surface;
|
|
192
|
+
private readonly isBusy;
|
|
193
|
+
private readonly sdkSessionLive;
|
|
194
|
+
/** Per-session retry/debounce bookkeeping — cleared on a human message. */
|
|
195
|
+
private readonly guards;
|
|
196
|
+
/** In-flight watches keyed by session id — re-arming or a human message cancels one. */
|
|
197
|
+
private readonly watches;
|
|
198
|
+
constructor(deps: RecoveryEngineDeps);
|
|
199
|
+
/**
|
|
200
|
+
* Arm (or re-arm) a post-turn watch for a just-completed SESSION-mode keeper turn.
|
|
201
|
+
* No-op unless SOMETHING wants it for this project: Layer 2 `surfaceKilledTask`
|
|
202
|
+
* (surface the kill live) or Layer 3 `autoReDrive` (auto re-drive, while the
|
|
203
|
+
* session has retries left). Re-arming cancels any prior watch for the same
|
|
204
|
+
* session. Safe to call fire-and-forget; never throws (a lookup/IO failure just
|
|
205
|
+
* declines to watch).
|
|
206
|
+
*/
|
|
207
|
+
armWatch(input: {
|
|
208
|
+
slug: string;
|
|
209
|
+
sessionId: string;
|
|
210
|
+
}): void;
|
|
211
|
+
private armWatchAsync;
|
|
212
|
+
/**
|
|
213
|
+
* A detected turn-boundary kill is ready to recover — kick off the fire, giving
|
|
214
|
+
* it a bounded {@link settleWindowMs} budget to wait out any still-live session.
|
|
215
|
+
* See {@link fireWhenIdle} for the surface/re-drive actions and the defer guard.
|
|
216
|
+
*/
|
|
217
|
+
private fire;
|
|
218
|
+
/**
|
|
219
|
+
* Handle a detected turn-boundary kill. Two independent, resolved-config-gated
|
|
220
|
+
* actions, in order:
|
|
221
|
+
* 1. `surfaceKilledTask` — push the kill to attached clients LIVE so the
|
|
222
|
+
* "keeper is idle / Continue" affordance appears without a refresh. Never
|
|
223
|
+
* throws; a bad sink can't wedge the engine.
|
|
224
|
+
* 2. `autoReDrive` (under the retry cap) — bump the session's retry bookkeeping
|
|
225
|
+
* FIRST (so a re-drive that itself hangs can't exceed the cap), then inject
|
|
226
|
+
* the recovery nudge via the shared Layer 2 path. A failed inject is logged.
|
|
227
|
+
*
|
|
228
|
+
* Liveness guard + defer (issues #352 / #397). Two busy signals mean the session
|
|
229
|
+
* is NOT idle and firing would collide with a live `claude` subprocess:
|
|
230
|
+
* - {@link isBusy} (hub) — a live turn PADDOCK itself started (a human message,
|
|
231
|
+
* a queued-message drain, or a prior recovery nudge).
|
|
232
|
+
* - {@link sdkSessionLive} (reaper) — a subprocess herdctl is keeping alive for
|
|
233
|
+
* the just-killed background task, or holding through its ~15s re-invocation
|
|
234
|
+
* grace. `isBusy` is BLIND to this, which is what let the auto re-drive fire a
|
|
235
|
+
* COMPETING resume and get interrupted (`[Request interrupted by user]`, #397).
|
|
236
|
+
*
|
|
237
|
+
* When EITHER is set we must NOT fire — but standing down *permanently* leaves
|
|
238
|
+
* recovery incomplete: the reaper reaps SILENTLY (no turn completes, so nothing
|
|
239
|
+
* re-arms a watch), and the user stays stuck. So instead we DEFER: re-check after
|
|
240
|
+
* {@link settlePollMs} and fire exactly once the session is genuinely idle
|
|
241
|
+
* (reaper reaped, hub not running). The deferral is bounded by
|
|
242
|
+
* {@link settleWindowMs} so a session that never releases can't retry forever, and
|
|
243
|
+
* is tracked in {@link watches} so a completing turn ({@link armWatch}) or a human
|
|
244
|
+
* message ({@link onHumanMessage}) supersedes/cancels it — no double fire.
|
|
245
|
+
*/
|
|
246
|
+
private fireWhenIdle;
|
|
247
|
+
/**
|
|
248
|
+
* A human just messaged this session — clear its retry/debounce bookkeeping and
|
|
249
|
+
* cancel any in-flight watch, so a genuinely-new later hang is recovered fresh
|
|
250
|
+
* (the retry cap counts auto re-drives BETWEEN human messages).
|
|
251
|
+
*/
|
|
252
|
+
onHumanMessage(sessionId: string): void;
|
|
253
|
+
/** Cancel the in-flight watch for a session, if any (idempotent). */
|
|
254
|
+
private cancelWatch;
|
|
255
|
+
/** Cancel every in-flight watch (e.g. on shutdown). */
|
|
256
|
+
stopAll(): void;
|
|
257
|
+
/** Test/inspection helper: the current retry count for a session (0 if untracked). */
|
|
258
|
+
retryCountFor(sessionId: string): number;
|
|
259
|
+
/** Test/inspection helper: whether a watch is currently armed for a session. */
|
|
260
|
+
isWatching(sessionId: string): boolean;
|
|
261
|
+
}
|
|
262
|
+
export {};
|