@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,158 @@
|
|
|
1
|
+
/** Bounds only the wait for a turn's FIRST message (never mid-turn): a stale
|
|
2
|
+
* (not-yet-flushed) residue read must not hang us on a backlog turn that never
|
|
3
|
+
* arrives. Comfortably longer than the CLI's inter-turn gap. */
|
|
4
|
+
const FIRST_MESSAGE_TIMEOUT_MS = 8000;
|
|
5
|
+
/** Overall ceiling so consumption can never hang indefinitely. */
|
|
6
|
+
const DEFAULT_DEADLINE_MS = 180000;
|
|
7
|
+
function isErrorResult(m) {
|
|
8
|
+
return (typeof m.subtype === "string" && m.subtype.startsWith("error")) || m.success === false;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Pump the message stream until the next terminal `result`, invoking `onMessage`
|
|
12
|
+
* per message. Drives a SINGLE in-flight `next()` (threaded via `pending`) so the
|
|
13
|
+
* iterator is never advanced twice concurrently.
|
|
14
|
+
*
|
|
15
|
+
* `firstMessageTimeoutMs` bounds ONLY the wait for this turn's first message; once
|
|
16
|
+
* a message has been received the turn is consumed to its `result` unbounded (a
|
|
17
|
+
* slow turn is never cut off). On a first-message timeout the still-in-flight
|
|
18
|
+
* `pending` is handed straight back to the caller.
|
|
19
|
+
*/
|
|
20
|
+
async function pumpToResult(iterator, pending, onMessage, onSessionId, firstMessageTimeoutMs) {
|
|
21
|
+
let awaitingFirst = true;
|
|
22
|
+
// eslint-disable-next-line no-constant-condition
|
|
23
|
+
while (true) {
|
|
24
|
+
let step;
|
|
25
|
+
if (awaitingFirst && firstMessageTimeoutMs !== undefined) {
|
|
26
|
+
const TIMED_OUT = Symbol("first-message-timeout");
|
|
27
|
+
let timer;
|
|
28
|
+
const raced = await Promise.race([
|
|
29
|
+
pending,
|
|
30
|
+
new Promise((resolve) => {
|
|
31
|
+
timer = setTimeout(() => resolve(TIMED_OUT), firstMessageTimeoutMs);
|
|
32
|
+
}),
|
|
33
|
+
]);
|
|
34
|
+
if (timer)
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
if (raced === TIMED_OUT)
|
|
37
|
+
return { pending, result: null, done: false, timedOut: true };
|
|
38
|
+
step = raced;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
step = await pending;
|
|
42
|
+
}
|
|
43
|
+
if (step.done)
|
|
44
|
+
return { pending: Promise.resolve(step), result: null, done: true, timedOut: false };
|
|
45
|
+
awaitingFirst = false;
|
|
46
|
+
const m = step.value;
|
|
47
|
+
if (m.session_id && onSessionId)
|
|
48
|
+
onSessionId(m.session_id);
|
|
49
|
+
if (onMessage)
|
|
50
|
+
await onMessage(step.value);
|
|
51
|
+
if (m.type === "result") {
|
|
52
|
+
return { pending: iterator.next(), result: m, done: false, timedOut: false };
|
|
53
|
+
}
|
|
54
|
+
pending = iterator.next();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Consume a resumed session's stream to the caller's real turn, first draining any
|
|
59
|
+
* stale async-input backlog that replays ahead of it. See the module doc.
|
|
60
|
+
*
|
|
61
|
+
* The session must already have the caller's prompt seeded (chatSession opens with
|
|
62
|
+
* the prompt; runCommand `send`s the command; the wake path is opened with its
|
|
63
|
+
* prompt by herdctl) — this only governs WHEN we stop consuming, never what runs.
|
|
64
|
+
*/
|
|
65
|
+
export async function consumeResumedTurn(session, opts) {
|
|
66
|
+
const iterator = session.messages[Symbol.asyncIterator]();
|
|
67
|
+
let pending = iterator.next();
|
|
68
|
+
let sessionId = null;
|
|
69
|
+
const onSid = (id) => {
|
|
70
|
+
sessionId = id;
|
|
71
|
+
opts.onSessionId?.(id);
|
|
72
|
+
};
|
|
73
|
+
const overallDeadline = Date.now() + (opts.deadlineMs ?? DEFAULT_DEADLINE_MS);
|
|
74
|
+
let success = false;
|
|
75
|
+
let turns = 0;
|
|
76
|
+
while (Date.now() < overallDeadline) {
|
|
77
|
+
// Bound only the wait for the next turn's first message while a backlog may
|
|
78
|
+
// still be queued (residue > 0); wait unbounded when the queue is empty (the
|
|
79
|
+
// running turn is the real one and may be slow).
|
|
80
|
+
const firstMsgTimeout = (await opts.residueProbe()) > 0 ? FIRST_MESSAGE_TIMEOUT_MS : undefined;
|
|
81
|
+
const r = await pumpToResult(iterator, pending, opts.onMessage, onSid, firstMsgTimeout);
|
|
82
|
+
pending = r.pending;
|
|
83
|
+
if (r.done)
|
|
84
|
+
break;
|
|
85
|
+
if (r.timedOut)
|
|
86
|
+
break; // no further turn arrived → the queue is effectively drained
|
|
87
|
+
if (r.result) {
|
|
88
|
+
turns++;
|
|
89
|
+
success = !isErrorResult(r.result);
|
|
90
|
+
}
|
|
91
|
+
if ((await opts.residueProbe()) === 0)
|
|
92
|
+
break; // async queue drained → real turn done
|
|
93
|
+
}
|
|
94
|
+
if (turns > 1)
|
|
95
|
+
opts.log?.(`drained ${turns - 1} backlog turn(s) ahead of the real turn`);
|
|
96
|
+
return { success, sessionId, iterator, pending };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Gap B — deliver autonomous background-completion turns.
|
|
100
|
+
*
|
|
101
|
+
* After {@link consumeResumedTurn} returns, the primary turn is done but the
|
|
102
|
+
* session may be held OPEN by the reaper because the turn launched continuous
|
|
103
|
+
* background work (`decideReap` keepAlives while `backgroundTasks.length > 0`).
|
|
104
|
+
* The `claude` subprocess keeps running and, when a background task completes,
|
|
105
|
+
* the SDK hands the parent the result as a fresh autonomous re-invocation turn —
|
|
106
|
+
* emitted on the SAME message stream. Paddock used to stop consuming at the
|
|
107
|
+
* primary `result`, so those messages buffered undelivered: the CLI still
|
|
108
|
+
* PERSISTED the re-invocation to the transcript (the subprocess writes JSONL
|
|
109
|
+
* directly), but the live UI never saw it until a refresh replayed history.
|
|
110
|
+
*
|
|
111
|
+
* This keeps consuming the handed-back {@link ConsumeResumedTurnResult.iterator}
|
|
112
|
+
* (never a second iterator — that would split the stream), forwarding every
|
|
113
|
+
* subsequent message to `onMessage`. The caller's `onMessage` renders each
|
|
114
|
+
* autonomous turn to the hub exactly like the scheduler-wake path does, so it
|
|
115
|
+
* appears live. Consumption ALSO keeps `tapLifecycleStream` advancing, so the
|
|
116
|
+
* reaper keeps receiving its mid-turn `activity` / `background_tasks_changed`
|
|
117
|
+
* signals (they fire as a side effect of the consumer pulling messages) — the
|
|
118
|
+
* behaviour the reaper was designed around.
|
|
119
|
+
*
|
|
120
|
+
* The loop ends cleanly when the reaper reaps the idle session: `close()` ends
|
|
121
|
+
* the SDK query, the iterator yields `done`, and we return. A stream error is
|
|
122
|
+
* swallowed (the turn is already over). Detached by the caller (`void`), it
|
|
123
|
+
* never throws to the event loop.
|
|
124
|
+
*/
|
|
125
|
+
export async function consumeBackgroundTurns(iterator, pending, onMessage, onDone) {
|
|
126
|
+
try {
|
|
127
|
+
// eslint-disable-next-line no-constant-condition
|
|
128
|
+
while (true) {
|
|
129
|
+
let step;
|
|
130
|
+
try {
|
|
131
|
+
step = await pending;
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return; // stream errored/closed — nothing more to deliver
|
|
135
|
+
}
|
|
136
|
+
if (step.done)
|
|
137
|
+
return; // reaper reaped the session → stream ended
|
|
138
|
+
try {
|
|
139
|
+
await onMessage(step.value);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
/* a rendering failure for one message must not abort delivery */
|
|
143
|
+
}
|
|
144
|
+
pending = iterator.next();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
// Signal the caller (the background sink) that the stream has ended so it can
|
|
149
|
+
// finalize its single hub turn (emit chat:complete + turn.end()). Runs on
|
|
150
|
+
// normal end, error, or reap. Never throws to the event loop.
|
|
151
|
+
try {
|
|
152
|
+
await onDone?.();
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
/* completion signalling must not throw to a detached consumer */
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The route dependency bag + the shared per-request context the route groups
|
|
3
|
+
* close over.
|
|
4
|
+
*
|
|
5
|
+
* `RouteDeps` is the raw service bag `app.ts` hands to {@link registerRoutes}.
|
|
6
|
+
* `buildRouteContext` constructs — ONCE — the handful of helper closures that the
|
|
7
|
+
* original single `registerRoutes` defined inline (read-state user resolution,
|
|
8
|
+
* slug→agent/dir resolution, attachment cleanup, the per-chat usage resolver) and
|
|
9
|
+
* returns them alongside every dep as a {@link RouteCtx}. Each `registerXRoutes`
|
|
10
|
+
* group then takes that context instead of relying on closure scope — which is
|
|
11
|
+
* what lets the ~50 handlers live in per-group modules while sharing one wiring.
|
|
12
|
+
*/
|
|
13
|
+
import type { FastifyRequest } from "fastify";
|
|
14
|
+
import type { DiscoveredSession } from "@herdctl/core";
|
|
15
|
+
import type { ProjectStore } from "./projects.js";
|
|
16
|
+
import { AttachmentStore } from "./attachments.js";
|
|
17
|
+
import type { HerdctlService } from "./herdctl.js";
|
|
18
|
+
import type { GitService } from "./git.js";
|
|
19
|
+
import type { GithubAuth } from "./github-auth.js";
|
|
20
|
+
import type { ArchiveStore } from "./archive.js";
|
|
21
|
+
import type { StarStore } from "./star.js";
|
|
22
|
+
import type { ReadStateStore } from "./read-state.js";
|
|
23
|
+
import type { UnreadStore } from "./unread.js";
|
|
24
|
+
import type { ParentDetachStore } from "./parent-detach.js";
|
|
25
|
+
import type { RunProvenanceStore } from "./run-provenance.js";
|
|
26
|
+
import type { MessageProvenanceStore } from "./message-provenance.js";
|
|
27
|
+
import type { PaddockConfig } from "./config.js";
|
|
28
|
+
import { type Transcriber } from "./transcribe.js";
|
|
29
|
+
import type { PaddockEventBus } from "./event-bus.js";
|
|
30
|
+
import type { ChatHandlerContext } from "./ws-context.js";
|
|
31
|
+
import type { TriggerService } from "./triggers.js";
|
|
32
|
+
import { type ChatUsage } from "./chat-dto.js";
|
|
33
|
+
/**
|
|
34
|
+
* The subset of @fastify/multipart's decorated request we use. The plugin
|
|
35
|
+
* decorates `req.file()` at runtime; we model just what we need here rather than
|
|
36
|
+
* rely on the plugin's global `declare module 'fastify'` augmentation, which is
|
|
37
|
+
* brittle under workspace hoisting (fastify can resolve to a different physical
|
|
38
|
+
* copy than the one the plugin augments).
|
|
39
|
+
*/
|
|
40
|
+
export interface UploadedFile {
|
|
41
|
+
filename?: string;
|
|
42
|
+
mimetype?: string;
|
|
43
|
+
toBuffer(): Promise<Buffer>;
|
|
44
|
+
}
|
|
45
|
+
/** A streamed multipart part (issue #328 upload): a file or a form field. */
|
|
46
|
+
export interface MultipartPart {
|
|
47
|
+
type: "file" | "field";
|
|
48
|
+
filename?: string;
|
|
49
|
+
mimetype?: string;
|
|
50
|
+
toBuffer(): Promise<Buffer>;
|
|
51
|
+
}
|
|
52
|
+
export interface MultipartLimits {
|
|
53
|
+
limits?: {
|
|
54
|
+
fileSize?: number;
|
|
55
|
+
files?: number;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export type MultipartRequest = FastifyRequest & {
|
|
59
|
+
file(): Promise<UploadedFile | undefined>;
|
|
60
|
+
parts(opts?: MultipartLimits): AsyncIterableIterator<MultipartPart>;
|
|
61
|
+
};
|
|
62
|
+
export interface RouteDeps {
|
|
63
|
+
projects: ProjectStore;
|
|
64
|
+
herdctl: HerdctlService;
|
|
65
|
+
git: GitService;
|
|
66
|
+
githubAuth: GithubAuth;
|
|
67
|
+
transcriber: Transcriber;
|
|
68
|
+
archive: ArchiveStore;
|
|
69
|
+
/** Per-chat starred/pinned-flag sidecar (#373). Orthogonal to `archive`. */
|
|
70
|
+
star: StarStore;
|
|
71
|
+
readState: ReadStateStore;
|
|
72
|
+
/**
|
|
73
|
+
* Per-user MANUAL "unread" override sidecar (#458): an explicit "mark unread"
|
|
74
|
+
* flag layered on top of the derived read-state, so a chat can resurface its
|
|
75
|
+
* unread cue even after its last turn was seen. Cleared when the chat is marked
|
|
76
|
+
* seen (opened / focused / marked read).
|
|
77
|
+
*/
|
|
78
|
+
unread: UnreadStore;
|
|
79
|
+
/**
|
|
80
|
+
* Explicit "this chat was detached from its parent" sidecar (#508). Consulted
|
|
81
|
+
* by `makeParentResolver` AHEAD of the recorded and inferred parent tiers, so a
|
|
82
|
+
* detach can't be undone by the inference that re-derives an unrecorded edge.
|
|
83
|
+
*/
|
|
84
|
+
parentDetach: ParentDetachStore;
|
|
85
|
+
runProvenance: RunProvenanceStore;
|
|
86
|
+
/**
|
|
87
|
+
* Per-MESSAGE provenance sidecar (issue #290): who injected each machine-added
|
|
88
|
+
* turn. Joined into a chat's message DTO so the history can attribute injected
|
|
89
|
+
* turns ("↩ sent by …" / "⏰ scheduled by …"). Absence ⇒ human (the default).
|
|
90
|
+
*/
|
|
91
|
+
messageProvenance: MessageProvenanceStore;
|
|
92
|
+
attachments: AttachmentStore;
|
|
93
|
+
/**
|
|
94
|
+
* Manually fire a project's TRIGGER now (Epic T follow-up / #327), backing the
|
|
95
|
+
* `POST …/triggers/:name/run` "Run now" route + the `run_trigger` self-MCP verb.
|
|
96
|
+
* Supplied by the chat handler (`makeChatHandler(...).fireTrigger`) so a manual run
|
|
97
|
+
* goes through the SAME hub path a cron / event fire uses — a first-class, badged
|
|
98
|
+
* chat, indistinguishable from an automatic fire. Fires ANY trigger type regardless
|
|
99
|
+
* of its `enabled` flag. Resolves the started chat's session id, or `null` if the
|
|
100
|
+
* project/trigger is gone or nothing started. Optional so tests that don't exercise
|
|
101
|
+
* triggers can omit it.
|
|
102
|
+
*/
|
|
103
|
+
fireTrigger?: (slug: string, triggerName: string) => Promise<string | null>;
|
|
104
|
+
/**
|
|
105
|
+
* The shared Management API ops context (issue #312 M1) — the SAME bag the
|
|
106
|
+
* in-process self-MCP is built from (`makeChatHandler(...).managementOpsContext`).
|
|
107
|
+
* Handed to the route layer so a non-socket caller can build the management ops
|
|
108
|
+
* for itself via `buildManagementOps` + `enforceManagementPolicy`: the `/mcp`
|
|
109
|
+
* transport in M2, and the turn-spawning REST endpoints in #465, which is why
|
|
110
|
+
* this is threaded now rather than when the transport lands. Optional so tests
|
|
111
|
+
* that don't exercise the management API can omit it.
|
|
112
|
+
*/
|
|
113
|
+
managementOpsContext?: ChatHandlerContext;
|
|
114
|
+
/**
|
|
115
|
+
* In-process lifecycle event bus (Epic T). The archive route emits `onArchive`
|
|
116
|
+
* on it AFTER the archive commits, so the trigger dispatcher (wired in the chat
|
|
117
|
+
* handler) fires the project's enabled onArchive event triggers. Optional so tests
|
|
118
|
+
* that don't exercise triggers can omit it.
|
|
119
|
+
*/
|
|
120
|
+
events?: PaddockEventBus;
|
|
121
|
+
/**
|
|
122
|
+
* Unified trigger CRUD service (Epic T "Unify Triggers" / T3). Backs the
|
|
123
|
+
* `/api/projects/:slug/triggers[/:name]` REST surface the Triggers tab (T4) will
|
|
124
|
+
* drive — list (with the capability-picker catalog), create/replace, edit,
|
|
125
|
+
* enable/disable (just `set` with `enabled` flipped — GG-3), delete. Delegates
|
|
126
|
+
* persistence to `project.yaml`'s single `triggers` block + herdctl arming to
|
|
127
|
+
* {@link TriggerService}. Optional so tests that don't exercise triggers can omit it.
|
|
128
|
+
*/
|
|
129
|
+
triggers?: TriggerService;
|
|
130
|
+
cfg: PaddockConfig;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* `RouteDeps` plus the shared helper closures built once by
|
|
134
|
+
* {@link buildRouteContext}. This is what every `registerXRoutes(app, ctx)` group
|
|
135
|
+
* receives.
|
|
136
|
+
*/
|
|
137
|
+
export interface RouteCtx extends RouteDeps {
|
|
138
|
+
/**
|
|
139
|
+
* Resolve the read-state key's user segment from the authenticated principal:
|
|
140
|
+
* a REAL identity (trusted-header / jwt) keys read-state by username; an
|
|
141
|
+
* anonymous principal (`none` mode) uses the shared bucket (null → no user
|
|
142
|
+
* segment). This is the ONLY place user identity is consumed — read-state is
|
|
143
|
+
* user-keyed-when-present; chat VISIBILITY is deliberately not gated (#189).
|
|
144
|
+
*/
|
|
145
|
+
readStateUser(req: FastifyRequest): string | null;
|
|
146
|
+
/** Resolve a slug to the agent name whose sessions back it. */
|
|
147
|
+
agentForSlug(slug: string): Promise<string>;
|
|
148
|
+
/** Resolve a slug to the on-disk project directory holding its `.chats/`. */
|
|
149
|
+
projectDirForSlug(slug: string): Promise<string>;
|
|
150
|
+
/**
|
|
151
|
+
* Remove the attachments a chat referenced, before its transcript is deleted
|
|
152
|
+
* (we read the transcript to find the ids). Best-effort — a failure here must
|
|
153
|
+
* never block the chat delete.
|
|
154
|
+
*/
|
|
155
|
+
cleanupAttachments(agent: string, sessionId: string): Promise<void>;
|
|
156
|
+
/**
|
|
157
|
+
* A per-session usage lookup for building a chat list's usage rings (issue
|
|
158
|
+
* #77) + cumulative token/cost figures (issue #152, incl. sub-agents #242):
|
|
159
|
+
* reads each session's transcript plus its sub-agent transcripts (both memoized
|
|
160
|
+
* on mtime) and pairs the parsed totals with the model. Returns null for a
|
|
161
|
+
* session with no usage data yet — the ring simply hides. Keyed on `projectDir`
|
|
162
|
+
* (not the agent name) because paddock resolves transcripts directly under
|
|
163
|
+
* `<projectDir>/.chats/`.
|
|
164
|
+
*/
|
|
165
|
+
chatUsageResolver(projectDir: string, model: string): (s: DiscoveredSession) => Promise<ChatUsage | null>;
|
|
166
|
+
}
|
|
167
|
+
/** Construct the shared route context (deps + the helper closures) once. */
|
|
168
|
+
export declare function buildRouteContext(deps: RouteDeps): RouteCtx;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { collectAttachmentIds } from "./attachments.js";
|
|
2
|
+
import { keeperAgentName } from "./herdctl.js";
|
|
3
|
+
import { readSessionTokenUsageWithSubagents } from "./subagents.js";
|
|
4
|
+
import { toChatUsage } from "./chat-dto.js";
|
|
5
|
+
/** Construct the shared route context (deps + the helper closures) once. */
|
|
6
|
+
export function buildRouteContext(deps) {
|
|
7
|
+
const { projects, herdctl, attachments } = deps;
|
|
8
|
+
const readStateUser = (req) => req.user && !req.user.anonymous ? req.user.username : null;
|
|
9
|
+
async function agentForSlug(slug) {
|
|
10
|
+
await projects.get(slug); // throws not_found for unknown slug
|
|
11
|
+
return keeperAgentName(slug);
|
|
12
|
+
}
|
|
13
|
+
async function projectDirForSlug(slug) {
|
|
14
|
+
return (await projects.get(slug)).dir;
|
|
15
|
+
}
|
|
16
|
+
async function cleanupAttachments(agent, sessionId) {
|
|
17
|
+
try {
|
|
18
|
+
const messages = await herdctl.sessionMessages(agent, sessionId);
|
|
19
|
+
await attachments.remove(collectAttachmentIds(messages));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
/* best-effort: orphaned attachment files are harmless */
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function chatUsageResolver(projectDir, model) {
|
|
26
|
+
return async (s) => {
|
|
27
|
+
const u = await readSessionTokenUsageWithSubagents(projectDir, s.sessionId).catch(() => null);
|
|
28
|
+
return u ? toChatUsage(u, model) : null;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
...deps,
|
|
33
|
+
readStateUser,
|
|
34
|
+
agentForSlug,
|
|
35
|
+
projectDirForSlug,
|
|
36
|
+
cleanupAttachments,
|
|
37
|
+
chatUsageResolver,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared REST error helper. `sendProjectError` maps a {@link ProjectError} onto
|
|
3
|
+
* the right HTTP status (404 not_found / 409 exists|not_directory / 400 else) and
|
|
4
|
+
* falls back to a logged 500 for anything unexpected. Used by nearly every
|
|
5
|
+
* handler, so it lands in its own module and is imported everywhere.
|
|
6
|
+
*/
|
|
7
|
+
import type { FastifyReply } from "fastify";
|
|
8
|
+
export declare function sendProjectError(reply: FastifyReply, err: unknown): FastifyReply<import("fastify").RawServerDefault, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").RouteGenericInterface, unknown, import("fastify").FastifySchema, import("fastify").FastifyTypeProviderDefault, unknown>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ProjectError } from "./projects.js";
|
|
2
|
+
export function sendProjectError(reply, err) {
|
|
3
|
+
if (err instanceof ProjectError) {
|
|
4
|
+
const code = err.code === "not_found"
|
|
5
|
+
? 404
|
|
6
|
+
: err.code === "exists" || err.code === "not_directory"
|
|
7
|
+
? 409
|
|
8
|
+
: 400;
|
|
9
|
+
return reply.code(code).send({ error: err.message, code: err.code });
|
|
10
|
+
}
|
|
11
|
+
reply.log.error({ err }, "route error");
|
|
12
|
+
return reply.code(500).send({ error: err.message ?? "internal error" });
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import type { RouteCtx } from "../route-context.js";
|
|
3
|
+
/**
|
|
4
|
+
* Workspace-scoped chat routes: paths are declared RELATIVE to the workspace
|
|
5
|
+
* (e.g. `/chats/:sessionId`), and this plugin is mounted TWICE — once at
|
|
6
|
+
* `/api/root` and once at `/api/projects/:slug` (see `workspace-mount.ts`).
|
|
7
|
+
*
|
|
8
|
+
* Handlers still read `req.params.slug`; the root mount injects `slug: ""` in an
|
|
9
|
+
* `onRequest` hook, which runs before params validation, so the `required:
|
|
10
|
+
* ["slug"]` schemas below keep validating unchanged on both mounts.
|
|
11
|
+
*/
|
|
12
|
+
export declare function registerChatWorkspaceRoutes(app: FastifyInstance, ctx: RouteCtx): void;
|