claude-task-worker 0.115.0 → 0.117.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.
Files changed (64) hide show
  1. package/README.md +53 -7
  2. package/dist/chunk-3RY43CYR.js +41 -0
  3. package/dist/chunk-FAD5CNP4.js +3396 -0
  4. package/dist/chunk-N42YU3GJ.js +184 -0
  5. package/dist/claude-args.d.ts +62 -0
  6. package/dist/commands/cloud-setup.d.ts +14 -0
  7. package/dist/commands/codegraph.d.ts +39 -0
  8. package/dist/commands/design-md.d.ts +11 -0
  9. package/dist/commands/init.d.ts +17 -0
  10. package/dist/commands/install.d.ts +14 -0
  11. package/dist/commands/pen.d.ts +15 -0
  12. package/dist/commands/playwright.d.ts +27 -0
  13. package/dist/commands/run-command.d.ts +10 -0
  14. package/dist/commands/update.d.ts +1 -0
  15. package/dist/commands/version.d.ts +4 -0
  16. package/dist/config.d.ts +77 -0
  17. package/dist/custom-workers.d.ts +16 -0
  18. package/dist/dispatch-args.d.ts +13 -0
  19. package/dist/dispatcher-JKUMXTJ2.js +410 -0
  20. package/dist/dispatcher.d.ts +77 -0
  21. package/dist/gh.d.ts +112 -0
  22. package/dist/git.d.ts +7 -0
  23. package/dist/herdr-NDLUM6JF.js +309 -0
  24. package/dist/herdr-runner-33D6C2KI.js +307 -0
  25. package/dist/herdr-runner.d.ts +136 -0
  26. package/dist/herdr.d.ts +85 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +855 -4984
  29. package/dist/last-run-pr.d.ts +16 -0
  30. package/dist/lib.d.ts +20 -0
  31. package/dist/lib.js +123 -0
  32. package/dist/process-manager.d.ts +38 -0
  33. package/dist/random-name.d.ts +2 -0
  34. package/dist/run-command-O2TMRPZK.js +8 -0
  35. package/dist/runcat.d.ts +23 -0
  36. package/dist/slack.d.ts +46 -0
  37. package/dist/table-DWVMNDS3.js +32 -0
  38. package/dist/table.d.ts +55 -0
  39. package/dist/task-result.d.ts +25 -0
  40. package/dist/transcript.d.ts +27 -0
  41. package/dist/user-config.d.ts +30 -0
  42. package/dist/workers/answer-issue-questions.d.ts +1 -0
  43. package/dist/workers/apply-ui-design.d.ts +1 -0
  44. package/dist/workers/check-dependabot.d.ts +1 -0
  45. package/dist/workers/create-issue.d.ts +1 -0
  46. package/dist/workers/create-ui-design.d.ts +2 -0
  47. package/dist/workers/epic-issue.d.ts +2 -0
  48. package/dist/workers/exec-issue.d.ts +15 -0
  49. package/dist/workers/fix-review-point.d.ts +1 -0
  50. package/dist/workers/issue-worker.d.ts +18 -0
  51. package/dist/workers/pr-worker.d.ts +18 -0
  52. package/dist/workers/registry.d.ts +8 -0
  53. package/dist/workers/resolve-conflict.d.ts +2 -0
  54. package/dist/workers/scheduled-worker.d.ts +8 -0
  55. package/dist/workers/triage-created-issue.d.ts +1 -0
  56. package/dist/workers/triage-pr.d.ts +5 -0
  57. package/dist/workers/ui-design.d.ts +14 -0
  58. package/dist/workers/update-coding-guidelines.d.ts +1 -0
  59. package/dist/workers/update-design-md.d.ts +1 -0
  60. package/dist/workers/update-issue.d.ts +1 -0
  61. package/dist/workers/update-requirement-rules.d.ts +1 -0
  62. package/dist/workers/worker-definition.d.ts +18 -0
  63. package/dist/worktree.d.ts +21 -0
  64. package/package.json +14 -3
@@ -0,0 +1,309 @@
1
+ // src/herdr.ts
2
+ import { createRequire } from "node:module";
3
+ import { resolve as resolvePath } from "node:path";
4
+ var childProcess = createRequire(import.meta.url)("node:child_process");
5
+ var HerdrUnavailableError = class extends Error {
6
+ reason;
7
+ constructor(message, reason) {
8
+ super(message);
9
+ this.name = "HerdrUnavailableError";
10
+ this.reason = reason;
11
+ }
12
+ };
13
+ var HerdrError = class extends Error {
14
+ code;
15
+ constructor(message, code) {
16
+ super(message);
17
+ this.name = "HerdrError";
18
+ this.code = code;
19
+ }
20
+ };
21
+ var HERDR_TIMEOUT_MS = 15 * 1e3;
22
+ function runHerdr(args, timeoutMs = HERDR_TIMEOUT_MS) {
23
+ return new Promise((resolve) => {
24
+ childProcess.execFile("herdr", args, { timeout: timeoutMs, killSignal: "SIGKILL" }, (error, stdout, stderr) => {
25
+ const parsed = parseHerdrResponse(stdout);
26
+ const stderrError = parsed?.error ? void 0 : parseHerdrResponse(stderr)?.error;
27
+ resolve({ execError: error, parsed, stderrError, stdout, stderr });
28
+ });
29
+ });
30
+ }
31
+ function parseHerdrResponse(output) {
32
+ try {
33
+ const parsed = JSON.parse(output);
34
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
35
+ return parsed;
36
+ } catch {
37
+ return void 0;
38
+ }
39
+ }
40
+ async function execHerdr(args, options) {
41
+ const { execError, parsed, stderrError, stdout, stderr } = await runHerdr(args, options?.timeoutMs);
42
+ const stderrSuffix = stderr.trim() ? `: ${stderr.trim()}` : "";
43
+ const errorPayload = parsed?.error ?? stderrError;
44
+ if (errorPayload) {
45
+ throw new HerdrError(
46
+ `herdr ${args.join(" ")} failed: [${errorPayload.code}] ${errorPayload.message}`,
47
+ errorPayload.code
48
+ );
49
+ }
50
+ if (execError) {
51
+ throw new Error(`herdr ${args.join(" ")} failed: ${execError.message}${stderrSuffix}`);
52
+ }
53
+ if (!parsed) {
54
+ if (options?.allowEmptyResult && stdout.trim() === "" && stderr.trim() === "") {
55
+ return void 0;
56
+ }
57
+ throw new Error(`herdr ${args.join(" ")} failed: invalid JSON output${stderrSuffix}`);
58
+ }
59
+ return parsed.result;
60
+ }
61
+ function envArgs(env) {
62
+ if (!env) return [];
63
+ return Object.entries(env).flatMap(([key, value]) => ["--env", `${key}=${value}`]);
64
+ }
65
+ function cwdArgs(cwd) {
66
+ return ["--cwd", resolvePath(cwd)];
67
+ }
68
+ async function tabCreate({
69
+ label,
70
+ cwd,
71
+ workspaceId,
72
+ env
73
+ }) {
74
+ const result = await execHerdr([
75
+ "tab",
76
+ "create",
77
+ ...workspaceId ? ["--workspace", workspaceId] : [],
78
+ "--label",
79
+ label,
80
+ ...cwdArgs(cwd),
81
+ ...envArgs(env),
82
+ "--no-focus"
83
+ ]);
84
+ const paneId = result?.root_pane?.pane_id;
85
+ const tabId = result?.tab?.tab_id;
86
+ if (!paneId || !tabId) {
87
+ throw new Error("Failed to create tab: invalid response structure from herdr");
88
+ }
89
+ return { paneId, tabId };
90
+ }
91
+ async function tabRename(tabId, label) {
92
+ await execHerdr(["tab", "rename", tabId, label]);
93
+ }
94
+ async function paneClose(paneId) {
95
+ await execHerdr(["pane", "close", paneId]);
96
+ }
97
+ async function paneGet(paneId) {
98
+ const result = await execHerdr(["pane", "get", paneId]);
99
+ const pane = result?.pane;
100
+ if (!pane?.pane_id) {
101
+ throw new Error(`Failed to get pane ${paneId}: invalid response structure from herdr`);
102
+ }
103
+ return { paneId: pane.pane_id, tabId: pane.tab_id ?? "" };
104
+ }
105
+ async function tabClose(tabId) {
106
+ await execHerdr(["tab", "close", tabId]);
107
+ }
108
+ async function tabList() {
109
+ const result = await execHerdr(["tab", "list"]);
110
+ if (!result || !Array.isArray(result.tabs)) {
111
+ return [];
112
+ }
113
+ return result.tabs.map((tab) => ({
114
+ tabId: tab.tab_id,
115
+ label: tab.label,
116
+ workspaceId: tab.workspace_id
117
+ }));
118
+ }
119
+ async function workspaceCreate({
120
+ label,
121
+ cwd,
122
+ env
123
+ }) {
124
+ const result = await execHerdr([
125
+ "workspace",
126
+ "create",
127
+ "--label",
128
+ label,
129
+ ...cwdArgs(cwd),
130
+ ...envArgs(env),
131
+ "--no-focus"
132
+ ]);
133
+ const workspaceId = result?.workspace?.workspace_id;
134
+ const paneId = result?.root_pane?.pane_id;
135
+ const tabId = result?.root_pane?.tab_id;
136
+ if (!workspaceId || !paneId || !tabId) {
137
+ throw new Error("Failed to create workspace: invalid response structure from herdr");
138
+ }
139
+ return { workspaceId, paneId, tabId };
140
+ }
141
+ async function workspaceList() {
142
+ const result = await execHerdr(["workspace", "list"]);
143
+ if (!result || !Array.isArray(result.workspaces)) {
144
+ return [];
145
+ }
146
+ return result.workspaces.map((workspace) => ({
147
+ workspaceId: workspace.workspace_id,
148
+ label: workspace.label ?? "",
149
+ focused: workspace.focused === true
150
+ }));
151
+ }
152
+ async function workspaceClose(workspaceId) {
153
+ await execHerdr(["workspace", "close", workspaceId]);
154
+ }
155
+ async function workspaceFocus(workspaceId) {
156
+ await execHerdr(["workspace", "focus", workspaceId]);
157
+ }
158
+ function toAgentStatus(value) {
159
+ return value === "working" || value === "idle" || value === "blocked" || value === "done" ? value : "unknown";
160
+ }
161
+ function toAgentInfo(agent) {
162
+ const session = agent.agent_session;
163
+ const sessionId = session?.kind === "id" && typeof session.value === "string" ? session.value : void 0;
164
+ return {
165
+ paneId: agent.pane_id ?? "",
166
+ tabId: agent.tab_id ?? "",
167
+ workspaceId: agent.workspace_id ?? "",
168
+ agentStatus: toAgentStatus(agent.agent_status),
169
+ sessionId
170
+ };
171
+ }
172
+ var AGENT_KIND = "claude";
173
+ var AGENT_START_READY_TIMEOUT_MS = 120 * 1e3;
174
+ var AGENT_START_EXEC_BUFFER_MS = 15 * 1e3;
175
+ async function agentStart(paneId, {
176
+ name,
177
+ args,
178
+ kind = AGENT_KIND,
179
+ readyTimeoutMs = AGENT_START_READY_TIMEOUT_MS
180
+ }) {
181
+ const result = await execHerdr(
182
+ ["agent", "start", name, "--kind", kind, "--pane", paneId, "--timeout", String(readyTimeoutMs), "--", ...args],
183
+ { timeoutMs: readyTimeoutMs + AGENT_START_EXEC_BUFFER_MS }
184
+ );
185
+ const agent = result?.agent;
186
+ if (!agent?.pane_id) {
187
+ throw new Error(`Failed to start agent in pane ${paneId}: invalid response structure from herdr`);
188
+ }
189
+ return toAgentInfo(agent);
190
+ }
191
+ async function agentPrompt(paneId, text) {
192
+ await execHerdr(["agent", "prompt", paneId, text], { allowEmptyResult: true });
193
+ }
194
+ async function agentGet(target) {
195
+ const result = await execHerdr(["agent", "get", target]);
196
+ const agent = result?.agent;
197
+ if (!agent?.pane_id) {
198
+ throw new Error(`Failed to get agent info for ${target}: invalid response structure from herdr`);
199
+ }
200
+ return toAgentInfo(agent);
201
+ }
202
+ function getCurrentWorkspaceId() {
203
+ const id = process.env.HERDR_WORKSPACE_ID;
204
+ return id && id.length > 0 ? id : void 0;
205
+ }
206
+ var PANE_READ_ERROR_ALLOWED_KEYS = /* @__PURE__ */ new Set(["code", "message"]);
207
+ function isPaneReadErrorPayload(value) {
208
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
209
+ return false;
210
+ }
211
+ const keys = Object.keys(value);
212
+ if (keys.some((key) => !PANE_READ_ERROR_ALLOWED_KEYS.has(key))) {
213
+ return false;
214
+ }
215
+ const payload = value;
216
+ if (typeof payload.code !== "string") {
217
+ return false;
218
+ }
219
+ if ("message" in payload && typeof payload.message !== "string") {
220
+ return false;
221
+ }
222
+ return true;
223
+ }
224
+ async function paneRead(paneId, options) {
225
+ const args = ["pane", "read", paneId, "--source", options?.source ?? "visible"];
226
+ if (options?.lines !== void 0) {
227
+ args.push("--lines", String(options.lines));
228
+ }
229
+ const { execError, stdout, stderr } = await runHerdr(args);
230
+ const trimmed = stdout.trim();
231
+ if (trimmed.startsWith("{")) {
232
+ let parsed;
233
+ try {
234
+ parsed = JSON.parse(trimmed);
235
+ } catch {
236
+ parsed = void 0;
237
+ }
238
+ if (isPaneReadErrorPayload(parsed)) {
239
+ throw new HerdrError(
240
+ `herdr ${args.join(" ")} failed: [${parsed.code}] ${parsed.message ?? ""}`.trim(),
241
+ parsed.code
242
+ );
243
+ }
244
+ }
245
+ if (execError) {
246
+ const stderrSuffix = stderr.trim() ? `: ${stderr.trim()}` : "";
247
+ throw new Error(`herdr ${args.join(" ")} failed: ${execError.message}${stderrSuffix}`);
248
+ }
249
+ return stdout;
250
+ }
251
+ async function paneSendText(paneId, text) {
252
+ await execHerdr(["pane", "send-text", paneId, text], { allowEmptyResult: true });
253
+ }
254
+ async function paneSendKeys(paneId, ...keys) {
255
+ await execHerdr(["pane", "send-keys", paneId, ...keys], { allowEmptyResult: true });
256
+ }
257
+ async function paneProcessInfo(paneId) {
258
+ const result = await execHerdr(["pane", "process-info", "--pane", paneId]);
259
+ const foregroundProcesses = result?.process_info?.foreground_processes ?? [];
260
+ return {
261
+ foregroundProcesses: foregroundProcesses.map((process2) => ({
262
+ name: process2.name,
263
+ argv: process2.argv,
264
+ cmdline: process2.cmdline,
265
+ pid: process2.pid
266
+ }))
267
+ };
268
+ }
269
+ async function checkHerdrAvailable() {
270
+ const { execError, parsed } = await runHerdr(["tab", "list"]);
271
+ if (execError?.code === "ENOENT") {
272
+ throw new HerdrUnavailableError(
273
+ "herdr\u304C\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002herdr CLI\u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
274
+ "not-installed"
275
+ );
276
+ }
277
+ if (!parsed) {
278
+ throw new HerdrUnavailableError(
279
+ "herdr\u30B5\u30FC\u30D0\u30FC\u306B\u63A5\u7D9A\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002`herdr status` \u7B49\u3067\u8D77\u52D5\u72B6\u614B\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
280
+ "server-unreachable"
281
+ );
282
+ }
283
+ }
284
+ export {
285
+ AGENT_KIND,
286
+ AGENT_START_READY_TIMEOUT_MS,
287
+ HERDR_TIMEOUT_MS,
288
+ HerdrError,
289
+ HerdrUnavailableError,
290
+ agentGet,
291
+ agentPrompt,
292
+ agentStart,
293
+ checkHerdrAvailable,
294
+ getCurrentWorkspaceId,
295
+ paneClose,
296
+ paneGet,
297
+ paneProcessInfo,
298
+ paneRead,
299
+ paneSendKeys,
300
+ paneSendText,
301
+ tabClose,
302
+ tabCreate,
303
+ tabList,
304
+ tabRename,
305
+ workspaceClose,
306
+ workspaceCreate,
307
+ workspaceFocus,
308
+ workspaceList
309
+ };
@@ -0,0 +1,307 @@
1
+ import {
2
+ CONTROL_CHARS
3
+ } from "./chunk-N42YU3GJ.js";
4
+
5
+ // src/transcript.ts
6
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
7
+ import { homedir } from "node:os";
8
+ import { join } from "node:path";
9
+ function transcriptRoot() {
10
+ return join(homedir(), ".claude", "projects");
11
+ }
12
+ function findTranscriptPath(sessionId, root = transcriptRoot()) {
13
+ if (sessionId === "") return null;
14
+ let entries;
15
+ try {
16
+ entries = readdirSync(root);
17
+ } catch {
18
+ return null;
19
+ }
20
+ for (const entry of entries) {
21
+ const candidate = join(root, entry, `${sessionId}.jsonl`);
22
+ if (existsSync(candidate)) return candidate;
23
+ }
24
+ return null;
25
+ }
26
+ function textOf(entry) {
27
+ const content = entry.message?.content;
28
+ if (typeof content === "string") return content;
29
+ if (!Array.isArray(content)) return "";
30
+ return content.filter(
31
+ (block) => typeof block === "object" && block !== null && block.type === "text" && typeof block.text === "string"
32
+ ).map((block) => block.text).join("\n").trim();
33
+ }
34
+ function extractFinalAssistantText(jsonl) {
35
+ const lines = jsonl.split("\n");
36
+ for (let i = lines.length - 1; i >= 0; i--) {
37
+ const line = lines[i].trim();
38
+ if (line === "") continue;
39
+ let entry;
40
+ try {
41
+ entry = JSON.parse(line);
42
+ } catch {
43
+ continue;
44
+ }
45
+ if (entry.type !== "assistant" || entry.isSidechain === true) continue;
46
+ const text = textOf(entry);
47
+ if (text !== "") return text;
48
+ }
49
+ return "";
50
+ }
51
+ function readFinalReport(sessionId, root = transcriptRoot()) {
52
+ if (!sessionId) return "";
53
+ const path = findTranscriptPath(sessionId, root);
54
+ if (!path) return "";
55
+ try {
56
+ return extractFinalAssistantText(readFileSync(path, "utf-8"));
57
+ } catch (err) {
58
+ console.error(`[transcript] failed to read ${path}: ${err}`);
59
+ return "";
60
+ }
61
+ }
62
+
63
+ // src/herdr-runner.ts
64
+ async function loadHerdr() {
65
+ return await import("./herdr-NDLUM6JF.js");
66
+ }
67
+ var AGENT_POLL_INTERVAL_MS = 3 * 1e3;
68
+ var PANE_OUTPUT_LINES = 300;
69
+ function taskTabLabel(projectName, number) {
70
+ return `ctw:${projectName}:#${number}`;
71
+ }
72
+ function toAgentName(label) {
73
+ const sanitized = label.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/-+/g, "-").replace(/^[-_]+/, "");
74
+ const withLeadingLetter = /^[a-z]/.test(sanitized) ? sanitized : `ctw-${sanitized}`;
75
+ return withLeadingLetter.slice(0, 32).replace(/[-_]+$/, "");
76
+ }
77
+ function createCompletionTracker() {
78
+ return { seenWorking: false, warnedBlocked: false };
79
+ }
80
+ function observeAgentStatus(tracker, status) {
81
+ if (status === "working") {
82
+ return { tracker: { ...tracker, seenWorking: true }, decision: "running" };
83
+ }
84
+ if (status === "done") {
85
+ return { tracker, decision: "completed" };
86
+ }
87
+ if (status === "blocked") {
88
+ if (tracker.warnedBlocked) return { tracker, decision: "running" };
89
+ return { tracker: { ...tracker, warnedBlocked: true }, decision: "blocked-first-seen" };
90
+ }
91
+ if (status === "idle" && tracker.seenWorking) {
92
+ return { tracker, decision: "completed" };
93
+ }
94
+ return { tracker, decision: "running" };
95
+ }
96
+ var CLOUD_SESSION_URL_RE = /https:\/\/claude\.ai\/code\/([A-Za-z0-9_-]+)/;
97
+ var CLOUD_SESSION_CREATED_RE = /Created cloud session:\s*(session_[A-Za-z0-9_-]+)/;
98
+ function extractCloudSessionId(text) {
99
+ return CLOUD_SESSION_URL_RE.exec(text)?.[1] ?? CLOUD_SESSION_CREATED_RE.exec(text)?.[1];
100
+ }
101
+ function normalizePtyOutput(text) {
102
+ return text.replace(CONTROL_CHARS, "");
103
+ }
104
+ function buildHerdrTaskResult(paneOutput, options) {
105
+ const report = options?.report?.trim() ?? "";
106
+ if (report !== "") {
107
+ return { status: "completed", output: report };
108
+ }
109
+ if (paneOutput.trim() === "") {
110
+ return {
111
+ status: "failed",
112
+ output: "[worker] the claude session became idle but its pane produced no output (session aborted before the model ran; e.g. a skill preamble command failed)"
113
+ };
114
+ }
115
+ return { status: "completed", output: paneOutput };
116
+ }
117
+ var PANE_READY_TIMEOUT_MS = 30 * 1e3;
118
+ var PANE_READY_POLL_INTERVAL_MS = 200;
119
+ var PROMPT_ACCEPT_TIMEOUT_MS = 20 * 1e3;
120
+ var PROMPT_ACCEPT_POLL_INTERVAL_MS = 500;
121
+ async function startHerdrTask({
122
+ label,
123
+ cwd,
124
+ args,
125
+ prompt,
126
+ env,
127
+ workspaceId,
128
+ herdr,
129
+ timing
130
+ }) {
131
+ const mod = herdr ?? await loadHerdr();
132
+ const { tabId, paneId } = await mod.tabCreate({ label, cwd, workspaceId, env });
133
+ try {
134
+ const ready = await waitForPaneReady(paneId, mod, {
135
+ timeoutMs: timing?.paneReadyTimeoutMs,
136
+ pollIntervalMs: timing?.paneReadyPollIntervalMs
137
+ });
138
+ if (!ready) {
139
+ console.warn(`[herdr-runner] pane ${paneId} produced no prompt before the timeout, launching anyway`);
140
+ }
141
+ await mod.agentStart(paneId, { name: toAgentName(label), args, readyTimeoutMs: timing?.agentStartReadyTimeoutMs });
142
+ await mod.agentPrompt(paneId, prompt);
143
+ await ensurePromptAccepted(paneId, prompt, mod, timing);
144
+ } catch (err) {
145
+ console.error(`[herdr-runner] failed to start task in pane ${paneId}: ${err}`);
146
+ await mod.tabClose(tabId).catch(() => {
147
+ });
148
+ throw err;
149
+ }
150
+ return { paneId, tabId };
151
+ }
152
+ async function ensurePromptAccepted(paneId, prompt, mod, timing) {
153
+ if (await waitForPromptAccepted(paneId, mod, timing)) return;
154
+ console.warn(`[herdr-runner] the prompt was not accepted in pane ${paneId} (claude stayed idle); resending it`);
155
+ await mod.agentPrompt(paneId, prompt);
156
+ if (await waitForPromptAccepted(paneId, mod, timing)) return;
157
+ throw new Error(
158
+ `the claude session in pane ${paneId} did not start the task after the prompt was submitted twice (a startup dialog most likely swallowed it)`
159
+ );
160
+ }
161
+ async function waitForPromptAccepted(paneId, mod, timing) {
162
+ const timeoutMs = timing?.promptAcceptTimeoutMs ?? PROMPT_ACCEPT_TIMEOUT_MS;
163
+ const pollIntervalMs = timing?.promptAcceptPollIntervalMs ?? PROMPT_ACCEPT_POLL_INTERVAL_MS;
164
+ const deadline = Date.now() + timeoutMs;
165
+ let loggedError = false;
166
+ for (; ; ) {
167
+ try {
168
+ const { agentStatus } = await mod.agentGet(paneId);
169
+ if (agentStatus === "working" || agentStatus === "done") return true;
170
+ } catch (err) {
171
+ if (!loggedError) {
172
+ console.error(`[herdr-runner] failed to read agent status for pane ${paneId} after the prompt: ${err}`);
173
+ loggedError = true;
174
+ }
175
+ }
176
+ if (Date.now() >= deadline) return false;
177
+ await sleep(pollIntervalMs);
178
+ }
179
+ }
180
+ async function waitForPaneReady(paneId, mod, options) {
181
+ const timeoutMs = options?.timeoutMs ?? PANE_READY_TIMEOUT_MS;
182
+ const pollIntervalMs = options?.pollIntervalMs ?? PANE_READY_POLL_INTERVAL_MS;
183
+ const deadline = Date.now() + timeoutMs;
184
+ for (; ; ) {
185
+ let content = "";
186
+ try {
187
+ content = await mod.paneRead(paneId, { source: "visible" });
188
+ } catch (err) {
189
+ console.error(`[herdr-runner] failed to read pane ${paneId} while waiting for its prompt: ${err}`);
190
+ }
191
+ if (content.trim() !== "") return true;
192
+ if (Date.now() >= deadline) return false;
193
+ await sleep(pollIntervalMs);
194
+ }
195
+ }
196
+ async function waitForHerdrTask(paneId, options) {
197
+ const mod = options?.herdr ?? await loadHerdr();
198
+ const pollIntervalMs = options?.pollIntervalMs ?? AGENT_POLL_INTERVAL_MS;
199
+ let tracker = createCompletionTracker();
200
+ let sessionId;
201
+ for (; ; ) {
202
+ if (options?.signal?.aborted) {
203
+ return { status: "failed", output: "[worker] the worker is shutting down; the task was interrupted" };
204
+ }
205
+ let status;
206
+ try {
207
+ const agent = await mod.agentGet(paneId);
208
+ status = agent.agentStatus;
209
+ if (agent.sessionId) sessionId = agent.sessionId;
210
+ } catch (err) {
211
+ if (err instanceof mod.HerdrError && (err.code === "pane_not_found" || err.code === "agent_not_found")) {
212
+ return {
213
+ status: "failed",
214
+ output: "[worker] the claude agent disappeared before the task completed (claude died or the tab was closed)"
215
+ };
216
+ }
217
+ console.error(`[herdr-runner] failed to read agent status for pane ${paneId}: ${err}`);
218
+ await sleep(pollIntervalMs);
219
+ continue;
220
+ }
221
+ options?.onStatus?.(status);
222
+ const observed = observeAgentStatus(tracker, status);
223
+ tracker = observed.tracker;
224
+ if (observed.decision === "completed") {
225
+ const output = await readPaneOutput(paneId, mod);
226
+ const report = (options?.readReport ?? readFinalReport)(sessionId);
227
+ return buildHerdrTaskResult(output, { report });
228
+ }
229
+ if (observed.decision === "blocked-first-seen") {
230
+ options?.onBlocked?.();
231
+ }
232
+ await sleep(pollIntervalMs);
233
+ }
234
+ }
235
+ async function readPaneOutput(paneId, mod) {
236
+ try {
237
+ return await mod.paneRead(paneId, { source: "recent", lines: PANE_OUTPUT_LINES });
238
+ } catch (err) {
239
+ console.error(`[herdr-runner] failed to read pane ${paneId}: ${err}`);
240
+ return "";
241
+ }
242
+ }
243
+ var CLAUDE_EXIT_TIMEOUT_MS = 15 * 1e3;
244
+ var CLAUDE_EXIT_POLL_INTERVAL_MS = 200;
245
+ async function waitForAgentGone(paneId, mod, options) {
246
+ const timeoutMs = options?.timeoutMs ?? CLAUDE_EXIT_TIMEOUT_MS;
247
+ const pollIntervalMs = options?.pollIntervalMs ?? CLAUDE_EXIT_POLL_INTERVAL_MS;
248
+ const deadline = Date.now() + timeoutMs;
249
+ for (; ; ) {
250
+ try {
251
+ await mod.agentGet(paneId);
252
+ } catch (err) {
253
+ if (err instanceof mod.HerdrError && (err.code === "agent_not_found" || err.code === "pane_not_found")) {
254
+ return true;
255
+ }
256
+ }
257
+ if (Date.now() >= deadline) return false;
258
+ await sleep(pollIntervalMs);
259
+ }
260
+ }
261
+ async function stopHerdrTask(task, herdr, options) {
262
+ const mod = herdr ?? await loadHerdr();
263
+ try {
264
+ await mod.paneSendKeys(task.paneId, "ctrl+c", "ctrl+c");
265
+ const exited = await waitForAgentGone(task.paneId, mod, {
266
+ timeoutMs: options?.exitTimeoutMs,
267
+ pollIntervalMs: options?.exitPollIntervalMs
268
+ });
269
+ if (!exited) {
270
+ console.warn(
271
+ `[herdr-runner] claude did not exit in pane ${task.paneId} after ctrl-c, closing the tab forcefully`
272
+ );
273
+ }
274
+ } catch (err) {
275
+ console.error(`[herdr-runner] failed to send ctrl-c to pane ${task.paneId}: ${err}`);
276
+ }
277
+ try {
278
+ await mod.tabClose(task.tabId);
279
+ } catch (err) {
280
+ if (err instanceof mod.HerdrError && (err.code === "tab_not_found" || err.code === "pane_not_found")) return;
281
+ console.error(`[herdr-runner] failed to close tab ${task.tabId}: ${err}`);
282
+ }
283
+ }
284
+ function sleep(ms) {
285
+ return new Promise((resolve) => setTimeout(resolve, ms));
286
+ }
287
+ export {
288
+ AGENT_POLL_INTERVAL_MS,
289
+ CLAUDE_EXIT_POLL_INTERVAL_MS,
290
+ CLAUDE_EXIT_TIMEOUT_MS,
291
+ PANE_OUTPUT_LINES,
292
+ PANE_READY_POLL_INTERVAL_MS,
293
+ PANE_READY_TIMEOUT_MS,
294
+ PROMPT_ACCEPT_POLL_INTERVAL_MS,
295
+ PROMPT_ACCEPT_TIMEOUT_MS,
296
+ buildHerdrTaskResult,
297
+ createCompletionTracker,
298
+ extractCloudSessionId,
299
+ normalizePtyOutput,
300
+ observeAgentStatus,
301
+ startHerdrTask,
302
+ stopHerdrTask,
303
+ taskTabLabel,
304
+ toAgentName,
305
+ waitForHerdrTask,
306
+ waitForPaneReady
307
+ };