@cat-factory/executor-harness 1.80.0 → 1.84.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 +1 -0
- package/dist/agent-capabilities.d.ts +130 -0
- package/dist/agent-runner.d.ts +114 -0
- package/dist/agent-runner.js +50 -32
- package/dist/agent-shared.d.ts +18 -0
- package/dist/agent.d.ts +66 -0
- package/dist/bootstrap-mode.d.ts +20 -0
- package/dist/captured-command.d.ts +58 -0
- package/dist/claude-call-aggregator.d.ts +164 -0
- package/dist/claude-call-aggregator.js +123 -17
- package/dist/claude-stream.d.ts +56 -0
- package/dist/coding-agent.d.ts +252 -0
- package/dist/coding-agent.js +69 -50
- package/dist/dependency-install.d.ts +111 -0
- package/dist/effort.d.ts +19 -0
- package/dist/embed.d.ts +4 -0
- package/dist/failure.d.ts +42 -0
- package/dist/follow-ups.d.ts +28 -0
- package/dist/frontend-infra.d.ts +25 -0
- package/dist/fs-utils.d.ts +2 -0
- package/dist/git.d.ts +394 -0
- package/dist/host-markdown.d.ts +28 -0
- package/dist/inline.d.ts +10 -0
- package/dist/job.d.ts +666 -0
- package/dist/logger.d.ts +16 -0
- package/dist/onboarding-preseed.d.ts +24 -0
- package/dist/package-registries.d.ts +32 -0
- package/dist/pi-workspace.d.ts +194 -0
- package/dist/pi.d.ts +475 -0
- package/dist/pr-description.d.ts +85 -0
- package/dist/pr-template.d.ts +101 -0
- package/dist/process-exit.d.ts +7 -0
- package/dist/process.d.ts +19 -0
- package/dist/progress-guard.d.ts +88 -0
- package/dist/progress.d.ts +87 -0
- package/dist/redact.d.ts +31 -0
- package/dist/reproduction-proof.d.ts +224 -0
- package/dist/runner.d.ts +282 -0
- package/dist/server.d.ts +3 -0
- package/dist/structured-output.d.ts +75 -0
- package/dist/subagents.d.ts +88 -0
- package/dist/transcript-retention.d.ts +21 -0
- package/dist/validation-checks.d.ts +159 -0
- package/dist/vcs-api.d.ts +73 -0
- package/dist/version.d.ts +2 -0
- package/package.json +9 -5
- package/src/agent-runner.ts +54 -29
- package/src/claude-call-aggregator.ts +181 -32
- package/src/coding-agent.ts +80 -49
package/dist/coding-agent.js
CHANGED
|
@@ -42,6 +42,64 @@ function followUpPollIntervalMs() {
|
|
|
42
42
|
* retry resumes on them. Returns the run's summary/stats, whether it pushed, and
|
|
43
43
|
* whether it resumed; callers decide what to do after a push (open a PR, or nothing).
|
|
44
44
|
*/
|
|
45
|
+
/**
|
|
46
|
+
* The work-branch push machinery for one coding run: a single coalesced push plus the periodic
|
|
47
|
+
* checkpoint that keeps mid-run commits durable. Split out of {@link runCodingAgent} for the
|
|
48
|
+
* per-function line budget; the caller owns the interval's lifetime (it clears `checkpoint`).
|
|
49
|
+
*
|
|
50
|
+
* Serialize all pushes to the work branch through a single in-flight promise. A checkpoint tick
|
|
51
|
+
* and the final push (or two slow checkpoint ticks) must never run `git push` to the same branch
|
|
52
|
+
* concurrently: overlapping pushes race on the remote ref and can make a push fail with a
|
|
53
|
+
* ref-lock / non-fast-forward error — which, on the FINAL push, would fail the whole run even
|
|
54
|
+
* though the work is committed. `pushWorkOnce` coalesces concurrent callers onto one push and only
|
|
55
|
+
* pushes once the branch has advanced past `baseSha`.
|
|
56
|
+
*
|
|
57
|
+
* Only push once the branch has advanced past its pre-run tip: pushing while it still sits at
|
|
58
|
+
* `baseSha` would create the work branch at the base commit (a zero-diff branch), which a later
|
|
59
|
+
* retry would see via `remoteBranchExists` and treat as resumable work — then fail to open a PR
|
|
60
|
+
* ("no commits between base and head"). So a run that never commits leaves NO branch behind,
|
|
61
|
+
* preserving the clean no-op outcome.
|
|
62
|
+
*/
|
|
63
|
+
function createWorkBranchPusher(args) {
|
|
64
|
+
const { dir, spec, baseSha, logger, signal } = args;
|
|
65
|
+
let pushInFlight = null;
|
|
66
|
+
const pushWorkOnce = () => {
|
|
67
|
+
if (pushInFlight)
|
|
68
|
+
return pushInFlight;
|
|
69
|
+
pushInFlight = (async () => {
|
|
70
|
+
if (!(await branchHasCommitsSince(dir, baseSha, signal)))
|
|
71
|
+
return;
|
|
72
|
+
await pushBranch(dir, spec.pushBranch, spec.ghToken, signal);
|
|
73
|
+
})().finally(() => {
|
|
74
|
+
pushInFlight = null;
|
|
75
|
+
});
|
|
76
|
+
return pushInFlight;
|
|
77
|
+
};
|
|
78
|
+
// Read the in-flight push, if any. A function (with an explicit return type) so the
|
|
79
|
+
// value isn't subject to the caller's straight-line narrowing — `pushInFlight` is
|
|
80
|
+
// only ever assigned inside closures, which flow analysis can't observe.
|
|
81
|
+
const inFlightPush = () => pushInFlight;
|
|
82
|
+
// Checkpoint the agent's committed work to the branch periodically so an eviction
|
|
83
|
+
// mid-run doesn't lose it (a retry then resumes from the pushed commits). The
|
|
84
|
+
// agent commits its own work; this only PUSHES already-committed commits, so it
|
|
85
|
+
// never races the agent's staging. Best-effort: a failed checkpoint is skipped.
|
|
86
|
+
// Surface checkpoint-push failures at warn with a running count: a checkpoint losing
|
|
87
|
+
// a race is harmless once, but a steadily-climbing count means mid-run work is NOT
|
|
88
|
+
// being durably checkpointed, so an eviction would lose it — previously invisible at
|
|
89
|
+
// info level. Still best-effort: a failed checkpoint never fails the run.
|
|
90
|
+
let checkpointFailures = 0;
|
|
91
|
+
const checkpoint = setInterval(() => {
|
|
92
|
+
pushWorkOnce().catch((err) => {
|
|
93
|
+
checkpointFailures++;
|
|
94
|
+
logger.warn('coding-agent: checkpoint push failed', {
|
|
95
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
96
|
+
checkpointFailures,
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}, checkpointIntervalMs());
|
|
100
|
+
checkpoint.unref?.();
|
|
101
|
+
return { pushWorkOnce, inFlightPush, checkpoint };
|
|
102
|
+
}
|
|
45
103
|
export async function runCodingAgent(spec, opts = {}) {
|
|
46
104
|
const { signal } = opts;
|
|
47
105
|
// The registry already binds jobId/repo/branch; add the coding kind + the push branch
|
|
@@ -51,56 +109,17 @@ export async function runCodingAgent(spec, opts = {}) {
|
|
|
51
109
|
// Clone (or resume) the checkout, fetch any read-only reference branches, and capture the
|
|
52
110
|
// pre-run branch tip. See {@link prepareCodingCheckout} for the resume-safety invariants.
|
|
53
111
|
const { resumed, baseSha } = await prepareCodingCheckout(dir, spec, logger, opts);
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// treat as resumable work — then fail to open a PR ("no commits between base and
|
|
66
|
-
// head"). So a run that never commits leaves NO branch behind, preserving the
|
|
67
|
-
// clean no-op outcome.
|
|
68
|
-
let pushInFlight = null;
|
|
69
|
-
const pushWorkOnce = () => {
|
|
70
|
-
if (pushInFlight)
|
|
71
|
-
return pushInFlight;
|
|
72
|
-
pushInFlight = (async () => {
|
|
73
|
-
if (!(await branchHasCommitsSince(dir, baseSha, signal)))
|
|
74
|
-
return;
|
|
75
|
-
await pushBranch(dir, spec.pushBranch, spec.ghToken, signal);
|
|
76
|
-
})().finally(() => {
|
|
77
|
-
pushInFlight = null;
|
|
78
|
-
});
|
|
79
|
-
return pushInFlight;
|
|
80
|
-
};
|
|
81
|
-
// Read the in-flight push, if any. A function (with an explicit return type) so the
|
|
82
|
-
// value isn't subject to the caller's straight-line narrowing — `pushInFlight` is
|
|
83
|
-
// only ever assigned inside closures, which flow analysis can't observe.
|
|
84
|
-
const inFlightPush = () => pushInFlight;
|
|
85
|
-
// Checkpoint the agent's committed work to the branch periodically so an eviction
|
|
86
|
-
// mid-run doesn't lose it (a retry then resumes from the pushed commits). The
|
|
87
|
-
// agent commits its own work; this only PUSHES already-committed commits, so it
|
|
88
|
-
// never races the agent's staging. Best-effort: a failed checkpoint is skipped.
|
|
89
|
-
// Surface checkpoint-push failures at warn with a running count: a checkpoint losing
|
|
90
|
-
// a race is harmless once, but a steadily-climbing count means mid-run work is NOT
|
|
91
|
-
// being durably checkpointed, so an eviction would lose it — previously invisible at
|
|
92
|
-
// info level. Still best-effort: a failed checkpoint never fails the run.
|
|
93
|
-
let checkpointFailures = 0;
|
|
94
|
-
const checkpoint = setInterval(() => {
|
|
95
|
-
pushWorkOnce().catch((err) => {
|
|
96
|
-
checkpointFailures++;
|
|
97
|
-
logger.warn('coding-agent: checkpoint push failed', {
|
|
98
|
-
reason: err instanceof Error ? err.message : String(err),
|
|
99
|
-
checkpointFailures,
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
}, checkpointIntervalMs());
|
|
103
|
-
checkpoint.unref?.();
|
|
112
|
+
// The work-branch push machinery: one coalesced in-flight push plus the periodic
|
|
113
|
+
// checkpoint that keeps mid-run commits durable across an eviction. Lifted into
|
|
114
|
+
// {@link createWorkBranchPusher} so this callback stays within the per-function line budget;
|
|
115
|
+
// the invariants it upholds are documented there.
|
|
116
|
+
const { pushWorkOnce, inFlightPush, checkpoint } = createWorkBranchPusher({
|
|
117
|
+
dir,
|
|
118
|
+
spec,
|
|
119
|
+
baseSha,
|
|
120
|
+
logger,
|
|
121
|
+
signal,
|
|
122
|
+
});
|
|
104
123
|
// In a monorepo the service lives in a subdirectory: run Pi with its cwd set to
|
|
105
124
|
// that subtree (git stays rooted at `dir` so commits/pushes still cover the whole
|
|
106
125
|
// checkout). Created if missing so a coder scaffolding a brand-new service into an
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { type RunOptions } from './runner.js';
|
|
2
|
+
import type { Logger } from './logger.js';
|
|
3
|
+
/** The dependency-install phase as it arrives on the job body. */
|
|
4
|
+
export interface DependencyInstallSpec {
|
|
5
|
+
/** The shell command, run as `sh -c` in the checkout (the service directory for a monorepo). */
|
|
6
|
+
command: string;
|
|
7
|
+
}
|
|
8
|
+
/** What the install did — folded into the agent's prompt, never a verdict about the run. */
|
|
9
|
+
export interface DependencyInstallOutcome {
|
|
10
|
+
command: string;
|
|
11
|
+
exitCode: number;
|
|
12
|
+
passed: boolean;
|
|
13
|
+
/** Scrubbed, bounded tail of the combined output. Only kept for a FAILED install. */
|
|
14
|
+
outputTail?: string;
|
|
15
|
+
durationMs: number;
|
|
16
|
+
timedOut?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* How much of a failed install's output the agent is shown. Smaller than the validation loop's
|
|
20
|
+
* repair budget (16k) on purpose: a repair prompt has to carry the whole failure because fixing
|
|
21
|
+
* it IS the task, whereas this note only has to let the agent decide whether to install
|
|
22
|
+
* something itself. The tail is where a package manager puts its actual error.
|
|
23
|
+
*/
|
|
24
|
+
export declare const DEPENDENCY_INSTALL_TAIL_CHARS = 4000;
|
|
25
|
+
/**
|
|
26
|
+
* The per-install watchdog: the longest the install may run before it is killed and reported as
|
|
27
|
+
* failed. Generous (20 min at the defaults) because a cold monorepo install on a slow registry
|
|
28
|
+
* legitimately takes many minutes.
|
|
29
|
+
*
|
|
30
|
+
* DERIVED from the configured job ceiling rather than hardcoded against the default one, the same
|
|
31
|
+
* way `git.ts` derives its per-command timeout from the configured inactivity window: a constant
|
|
32
|
+
* sized against a default silently breaks its own invariant the moment an operator changes that
|
|
33
|
+
* default. An explicit `DEPENDENCY_INSTALL_TIMEOUT_MS` is honoured but still CLAMPED — the point
|
|
34
|
+
* of the share is that no configuration lets setup eat the run, and an override that could exceed
|
|
35
|
+
* the job's own ceiling would only ever be killed later by a watchdog that fails the whole job
|
|
36
|
+
* instead of degrading to a note.
|
|
37
|
+
*/
|
|
38
|
+
export declare function dependencyInstallTimeoutMs(env?: NodeJS.ProcessEnv): number;
|
|
39
|
+
/**
|
|
40
|
+
* How often the install feeds the run's inactivity watchdog. Well under `JOB_INACTIVITY_MS`
|
|
41
|
+
* (default 10 min); matches the validation loop's and the frontend stand-up's heartbeat, which
|
|
42
|
+
* exist for exactly the same reason.
|
|
43
|
+
*/
|
|
44
|
+
export declare function dependencyInstallHeartbeatMs(): number;
|
|
45
|
+
/**
|
|
46
|
+
* Parse the optional DEPENDENCY INSTALL envelope off the job body. A missing/blank command
|
|
47
|
+
* returns `undefined`, so a malformed body degrades to the exact pre-feature behaviour (no
|
|
48
|
+
* install phase, the agent starts against the bare clone) rather than failing a good run.
|
|
49
|
+
*
|
|
50
|
+
* Lives with the feature rather than in `job.ts`, following the same rule the two pre-PR
|
|
51
|
+
* verification phases do: each phase owns its own job-body parser next to the code that consumes
|
|
52
|
+
* it, and `job.ts` stays the job SHAPE plus the generic assembly.
|
|
53
|
+
*/
|
|
54
|
+
export declare function parseDependencyInstallSpec(value: unknown): DependencyInstallSpec | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Run the declared install against `cwd` and return what happened. Never throws and never fails
|
|
57
|
+
* the job: every failure shape ({@link runCapturedCommand} maps a timeout to 124, a spawn error
|
|
58
|
+
* to 127, an abort to 130) comes back as a non-zero outcome the caller turns into a prompt note.
|
|
59
|
+
*
|
|
60
|
+
* The output tail is kept ONLY for a failure. A successful install prints tens of thousands of
|
|
61
|
+
* uninteresting lines, and the agent needs to know that it succeeded, not what it resolved.
|
|
62
|
+
*/
|
|
63
|
+
export declare function runDependencyInstall(args: {
|
|
64
|
+
cwd: string;
|
|
65
|
+
spec: DependencyInstallSpec;
|
|
66
|
+
logger: Logger;
|
|
67
|
+
opts: RunOptions;
|
|
68
|
+
}): Promise<DependencyInstallOutcome>;
|
|
69
|
+
/**
|
|
70
|
+
* THE entry point: run the phase for a mode that has a checkout, and hand back the note to fold
|
|
71
|
+
* into the agent's prompt (or `undefined` when the service declared no install, which is every
|
|
72
|
+
* dispatch today that never configured one).
|
|
73
|
+
*
|
|
74
|
+
* Everything a caller could get wrong lives here rather than at six call sites: the phase marker,
|
|
75
|
+
* the best-effort run, keeping the installed tree out of the agent's commits, and naming WHERE
|
|
76
|
+
* the install ran when that is not where the agent will be standing. A mode supplies only its
|
|
77
|
+
* three directories.
|
|
78
|
+
*/
|
|
79
|
+
export declare function prepopulateDependencies(args: {
|
|
80
|
+
spec: DependencyInstallSpec | undefined;
|
|
81
|
+
/** Where the install runs: the service subtree for a monorepo, else the checkout root. */
|
|
82
|
+
installDir: string;
|
|
83
|
+
/** The git checkout whose local excludes protect the agent's commits from the installed tree. */
|
|
84
|
+
repoDir: string;
|
|
85
|
+
/** The agent's own working directory, which names the install location when the two differ. */
|
|
86
|
+
agentDir: string;
|
|
87
|
+
logger: Logger;
|
|
88
|
+
opts: RunOptions;
|
|
89
|
+
}): Promise<string | undefined>;
|
|
90
|
+
/**
|
|
91
|
+
* Fold the note into a prompt. Trivial, and deliberately not inlined: it is applied on EVERY
|
|
92
|
+
* agent pass — including the validation and reproduction REPAIR passes, which start a fresh
|
|
93
|
+
* agent that would otherwise never learn the tree is already installed and would spend a repair
|
|
94
|
+
* round reinstalling it.
|
|
95
|
+
*/
|
|
96
|
+
export declare function withDependencyNote(userPrompt: string, note: string | undefined): string;
|
|
97
|
+
/**
|
|
98
|
+
* The note folded into the agent's prompt describing the checkout it is about to work in.
|
|
99
|
+
*
|
|
100
|
+
* Stated in BOTH directions on purpose. On success the agent is told the tree is ready, which is
|
|
101
|
+
* what stops it spending turns re-running an install that already ran (and, on a repo whose
|
|
102
|
+
* install is slow, spending most of its budget there). On failure it is told plainly what failed
|
|
103
|
+
* and that it may install what it needs itself — an agent that merely finds no `node_modules` and
|
|
104
|
+
* no explanation concludes the environment is offline and works around a gap that isn't there.
|
|
105
|
+
*
|
|
106
|
+
* `scope` names the checkout the install ran in and is set ONLY when that is not the agent's own
|
|
107
|
+
* working directory — the multi-repo layout runs the agent at the workspace root while the install
|
|
108
|
+
* belongs to the primary service's sibling directory. Saying "this checkout" there would point the
|
|
109
|
+
* agent at a root that has no dependency tree of its own.
|
|
110
|
+
*/
|
|
111
|
+
export declare function buildDependencyInstallNote(outcome: DependencyInstallOutcome, scope?: string): string;
|
package/dist/effort.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** The sentinel file the agent writes its effort self-assessment to (relative to its cwd). */
|
|
2
|
+
export declare const EFFORT_REPORT_FILE = ".cat-effort.json";
|
|
3
|
+
/** A container agent's self-assessment of the work it just did. */
|
|
4
|
+
export interface EffortReport {
|
|
5
|
+
/** How hard the work was: 1 (trivial) .. 10 (extremely hard). */
|
|
6
|
+
difficulty: number;
|
|
7
|
+
/** One or two sentences on how hard/easy the work was and why. */
|
|
8
|
+
summary?: string;
|
|
9
|
+
/** What reduced the agent's effectiveness. */
|
|
10
|
+
reducedEffectiveness?: string;
|
|
11
|
+
/** The key obstacles the agent hit. */
|
|
12
|
+
obstacles?: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Read + parse + REMOVE the agent's effort sentinel file from `cwd`. Lenient: returns undefined
|
|
16
|
+
* when the file is absent (the agent wrote none), unreadable, not JSON, or carries nothing
|
|
17
|
+
* meaningful. Never throws — a malformed self-report must never fail an otherwise-good run.
|
|
18
|
+
*/
|
|
19
|
+
export declare function readEffortReport(cwd: string): Promise<EffortReport | undefined>;
|
package/dist/embed.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { PI_MAX_OUTPUT_TOKENS, writePiModelsConfig, writeAgentsContext, runPi, summarizePiRun, parsePiOutput, parseTodoProgress, terminalRunError, type PiRunOutcome, type PiRunStats, type TodoItem, type TodoProgress, } from './pi.js';
|
|
2
|
+
export { DEFAULT_PROGRESS_GUARD_LIMITS, progressGuardLimitsFromEnv, type ProgressGuardLimits, } from './progress-guard.js';
|
|
3
|
+
export { cloneRepo, createBranch, changedPathsFromPorcelain, hasAgentChanges, redactSecrets, } from './git.js';
|
|
4
|
+
export type { RepoSpec } from './job.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The structured reason a harness job failed, surfaced on the job view's `failureCause`.
|
|
3
|
+
* Covers only HARNESS-owned failures — container eviction is detected by the runtime facade
|
|
4
|
+
* (a vanished container → `(container evicted or crashed)`), never set here.
|
|
5
|
+
*
|
|
6
|
+
* - `inactivity-timeout` — the inactivity watchdog fired (no agent output for the window).
|
|
7
|
+
* - `max-duration` — the overall wall-clock cap fired.
|
|
8
|
+
* - `agent` — the agent ran but produced an unusable/failed result, or threw.
|
|
9
|
+
* - `git` — a git operation failed (clone/push/merge/PR).
|
|
10
|
+
* - `api` — an upstream API call failed (e.g. the GitHub/GitLab PR/MR REST call).
|
|
11
|
+
* - `llm-upstream` — the model provider rejected every call (auth/quota/rate-limit) and Pi
|
|
12
|
+
* exhausted its retries, so the run never produced a result.
|
|
13
|
+
* - `no-usable-output` — the agent finished but returned no usable report / structured output.
|
|
14
|
+
* - `no-changes` — a coding agent finished without producing any change to push.
|
|
15
|
+
*/
|
|
16
|
+
export type FailureCause = 'inactivity-timeout' | 'max-duration' | 'agent' | 'git' | 'api' | 'llm-upstream' | 'no-usable-output' | 'no-changes';
|
|
17
|
+
/**
|
|
18
|
+
* A thrown failure that carries a structured {@link FailureCause}, so a `git` / `api`
|
|
19
|
+
* operation that fails deep in a helper surfaces its real cause instead of being flattened
|
|
20
|
+
* to the generic `agent` in the registry's catch. The watchdog kills set their cause from
|
|
21
|
+
* `killReason` and never throw this; anything else thrown without a cause stays `agent`.
|
|
22
|
+
*/
|
|
23
|
+
export declare class HarnessFailure extends Error {
|
|
24
|
+
readonly failureCause: FailureCause;
|
|
25
|
+
constructor(failureCause: FailureCause, message: string);
|
|
26
|
+
}
|
|
27
|
+
/** The structured cause a thrown error carries, or undefined for a plain/agent error. */
|
|
28
|
+
export declare function failureCauseOf(err: unknown): FailureCause | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* The inactivity-watchdog abort message PREFIX. Human-readable only now — the backend reads the
|
|
31
|
+
* structured `inactivity-timeout` {@link FailureCause}, not this phrase (the string fallback was
|
|
32
|
+
* deleted in error-message coverage I5), so it is free to change. The caller appends a `(likely
|
|
33
|
+
* hung ...)` diagnostic clause (phase + last tool) after this, so the prefix deliberately stops
|
|
34
|
+
* before the parenthetical (see `runner.ts` drive catch).
|
|
35
|
+
*/
|
|
36
|
+
export declare function inactivityAbortMessage(inactivityMs: number): string;
|
|
37
|
+
/**
|
|
38
|
+
* The max-duration-watchdog abort message. Human-readable only now — the backend reads the
|
|
39
|
+
* structured `max-duration` {@link FailureCause}, not this phrase (the string fallback was deleted
|
|
40
|
+
* in error-message coverage I5), so it is free to change.
|
|
41
|
+
*/
|
|
42
|
+
export declare function maxDurationAbortMessage(maxDurationMs: number): string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Logger } from './logger.js';
|
|
2
|
+
/** The sentinel file the Coder appends items to, relative to its working directory. */
|
|
3
|
+
export declare const FOLLOW_UPS_FILENAME = ".cat-follow-ups.jsonl";
|
|
4
|
+
/** One streamed item the Coder surfaced. Mirrors the backend's `streamedFollowUpSchema`. */
|
|
5
|
+
export interface FollowUpLine {
|
|
6
|
+
kind: 'follow_up' | 'question';
|
|
7
|
+
title: string;
|
|
8
|
+
detail: string;
|
|
9
|
+
suggestedAction?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Tails an append-only JSONL sentinel file, yielding only the NEW complete lines on each
|
|
13
|
+
* {@link poll}. Tracks how many characters have been consumed so a partially-written
|
|
14
|
+
* trailing line (no newline yet) is held back until it completes. Tolerant: a malformed
|
|
15
|
+
* line is skipped, a missing file yields nothing — surfacing follow-ups must never
|
|
16
|
+
* disturb the coding run.
|
|
17
|
+
*/
|
|
18
|
+
export declare class FollowUpTailer {
|
|
19
|
+
private readonly filePath;
|
|
20
|
+
private readonly onItems;
|
|
21
|
+
private readonly logger;
|
|
22
|
+
private consumed;
|
|
23
|
+
/** Running count of complete-but-unparsable lines, so silent drops become visible. */
|
|
24
|
+
private skipped;
|
|
25
|
+
constructor(filePath: string, onItems: (items: FollowUpLine[]) => void, logger?: Logger);
|
|
26
|
+
/** Read any new complete lines and emit the coerced items. Best-effort; never throws. */
|
|
27
|
+
poll(): Promise<void>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ChildProcess } from 'node:child_process';
|
|
2
|
+
import type { FrontendInfraSpec, InfraSetupRecord } from './job.js';
|
|
3
|
+
import type { RunOptions } from './runner.js';
|
|
4
|
+
import { type Logger } from './logger.js';
|
|
5
|
+
export interface FrontendStandUp {
|
|
6
|
+
/** The processes to terminate on teardown (WireMock + the served app). */
|
|
7
|
+
processes: ChildProcess[];
|
|
8
|
+
/** The URL the built app is served at, when it came up. Folded into the agent prompt. */
|
|
9
|
+
serveUrl?: string;
|
|
10
|
+
/** A problem note folded into the agent prompt (a failed build / server that never bound). */
|
|
11
|
+
note?: string;
|
|
12
|
+
/** The captured (redacted, bounded) stand-up record surfaced on the Tester step. */
|
|
13
|
+
record: InfraSetupRecord;
|
|
14
|
+
}
|
|
15
|
+
/** The install command for a package manager (an explicit `install` overrides this). */
|
|
16
|
+
export declare function installCommand(spec: FrontendInfraSpec): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Build the frontend, start WireMock, serve the built app and health-check both. Best-effort,
|
|
19
|
+
* like the docker-compose stand-up: a failed build / server that never binds is surfaced to
|
|
20
|
+
* the agent as a prompt note (and captured on the record) rather than failing the job — the
|
|
21
|
+
* agent then reports the gap as a concern. Every path returns the processes to tear down.
|
|
22
|
+
*/
|
|
23
|
+
export declare function standUpFrontend(dir: string, infra: FrontendInfraSpec, run: Pick<RunOptions, 'signal' | 'onActivity' | 'agentEnv'>, logger?: Logger): Promise<FrontendStandUp>;
|
|
24
|
+
/** Terminate the frontend stand-up's processes (WireMock + the served app). Best-effort. */
|
|
25
|
+
export declare function tearDownFrontend(processes: ChildProcess[], logger?: Logger): Promise<void>;
|