@cat-factory/executor-harness 1.78.0 → 1.82.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 +15 -1
- 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/claude-stream.js +23 -0
- package/dist/coding-agent.d.ts +263 -0
- 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-workspace.js +4 -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/runner.js +3 -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/subagents.js +74 -4
- 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 +21 -2
- package/src/claude-call-aggregator.ts +181 -32
- package/src/claude-stream.ts +21 -0
- package/src/pi-workspace.ts +4 -0
- package/src/runner.ts +24 -0
- package/src/subagents.ts +57 -3
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type Fields = Record<string, unknown>;
|
|
2
|
+
/** The logging surface: the four levels plus `child` to bind correlation fields once. */
|
|
3
|
+
export interface Logger {
|
|
4
|
+
debug: (msg: string, fields?: Fields) => void;
|
|
5
|
+
info: (msg: string, fields?: Fields) => void;
|
|
6
|
+
warn: (msg: string, fields?: Fields) => void;
|
|
7
|
+
error: (msg: string, fields?: Fields) => void;
|
|
8
|
+
/**
|
|
9
|
+
* Return a logger that merges `bound` into every line (e.g. `{ jobId, repo, branch }`),
|
|
10
|
+
* so a per-job logger carries its correlation context without each call site re-spreading
|
|
11
|
+
* it. Nestable — the returned logger's own `child` accumulates onto these bound fields.
|
|
12
|
+
*/
|
|
13
|
+
child: (bound: Fields) => Logger;
|
|
14
|
+
}
|
|
15
|
+
export declare const log: Logger;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Logger } from './logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* The onboarding gates we pre-accept in a fresh config home. Kept as a single constant so
|
|
4
|
+
* the write and the assertion below can never drift, and so a new gate is added in exactly
|
|
5
|
+
* one place. If the CLI renames/adds a key, this is where the fix lands.
|
|
6
|
+
*/
|
|
7
|
+
export declare const ONBOARDING_PRESEED_KEYS: {
|
|
8
|
+
readonly hasCompletedOnboarding: true;
|
|
9
|
+
readonly bypassPermissionsModeAccepted: true;
|
|
10
|
+
readonly hasTrustDialogAccepted: true;
|
|
11
|
+
};
|
|
12
|
+
/** Write the onboarding pre-seed into `<configHome>/.claude.json`. Best-effort; never throws. */
|
|
13
|
+
export declare function writeOnboardingPreseed(configHome: string): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Verify the pre-seed actually landed and log the pinned onboarding keys alongside the
|
|
16
|
+
* installed CLI version — the "one-line assertion after the pre-seed" from D4. It cannot
|
|
17
|
+
* introspect the CLI's true first-run gate set (the CLI never exposes it), so it does the
|
|
18
|
+
* two things it CAN do cheaply and deterministically: confirm every key we intended is
|
|
19
|
+
* present + truthy in the written file (catching a botched write), and emit a structured
|
|
20
|
+
* record pairing the keys with the CLI version so a future onboarding regression — surfaced
|
|
21
|
+
* by the cold-start watchdog as a silent, output-less start — is diffable against a new gate.
|
|
22
|
+
* Best-effort; never throws.
|
|
23
|
+
*/
|
|
24
|
+
export declare function assertOnboardingKeysCurrent(configHome: string, cliVersion: string | undefined, log: Logger | undefined): Promise<void>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { PackageRegistrySpec } from './job.js';
|
|
2
|
+
/** Where the per-job npm auth lands in a container (the user npmrc, outside any checkout). */
|
|
3
|
+
export declare function npmrcPath(): string;
|
|
4
|
+
/**
|
|
5
|
+
* Per-job isolation for the rendered npmrc. Set `isolatedDir` when the harness process is
|
|
6
|
+
* SHARED across concurrent jobs and its HOME is the developer's own — i.e. the local native
|
|
7
|
+
* host-process transport, which is exactly the set of jobs carrying `ambientAuth`. Absent ⇒
|
|
8
|
+
* the container default (`~/.npmrc`).
|
|
9
|
+
*/
|
|
10
|
+
export interface PackageRegistryScope {
|
|
11
|
+
/** A per-job directory (removed with the job) to hold this job's npmrc. */
|
|
12
|
+
isolatedDir?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Render the job's registry entries as npmrc lines: each scope routed to its
|
|
16
|
+
* registry, plus one `_authToken` credential line per distinct host.
|
|
17
|
+
*/
|
|
18
|
+
export declare function renderNpmrc(entries: readonly PackageRegistrySpec[]): string;
|
|
19
|
+
/**
|
|
20
|
+
* Write (or clear) the job's npmrc before the agent runs, and return the env the agent's child
|
|
21
|
+
* process needs to find it (empty for the container default, which npm picks up from HOME).
|
|
22
|
+
* Tokens are registered for output redaction so a token echoed in an npm error never reaches
|
|
23
|
+
* logs or stored output.
|
|
24
|
+
*/
|
|
25
|
+
export declare function configurePackageRegistries(entries: readonly PackageRegistrySpec[] | undefined, scope?: PackageRegistryScope): Promise<Record<string, string>>;
|
|
26
|
+
/**
|
|
27
|
+
* The credential VALUES in npmrc content: the three keys npm accepts a secret under, on any host
|
|
28
|
+
* line. Used to register a seeded (developer-owned) file's tokens for redaction. An `${ENV_VAR}`
|
|
29
|
+
* reference is not itself a secret — npm expands it at read time — so it is skipped rather than
|
|
30
|
+
* registered as a literal to scrub.
|
|
31
|
+
*/
|
|
32
|
+
export declare function npmrcCredentials(content: string): string[];
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import type { RepoSpec } from './job.js';
|
|
2
|
+
import type { McpServerSpec, SkillSpec } from './agent-capabilities.js';
|
|
3
|
+
import { type ContextFileInfo, type PiRunOutcome, type PiRunStats, type RunDiagnostics } from './pi.js';
|
|
4
|
+
import { type ProgressGuardLimits } from './progress-guard.js';
|
|
5
|
+
import type { RunOptions } from './runner.js';
|
|
6
|
+
import { type SubscriptionHarness } from './agent-runner.js';
|
|
7
|
+
/** Which container harness runs an agent (the default Pi, or a subscription CLI). */
|
|
8
|
+
export type HarnessKind = 'pi' | SubscriptionHarness;
|
|
9
|
+
/**
|
|
10
|
+
* Run `fn` against a fresh temp working directory, always removing it afterwards
|
|
11
|
+
* (even on throw). `prefix` labels the directory (e.g. 'impl', 'merge').
|
|
12
|
+
*
|
|
13
|
+
* Teardown is **best-effort**: on Windows (native local mode) a just-exited child —
|
|
14
|
+
* git, or the developer's own `claude`/`codex` CLI — can still hold a transient handle
|
|
15
|
+
* on a file in the checkout, so a straight `rm` throws `EBUSY`/`EPERM` and, running in
|
|
16
|
+
* the `finally`, would fail an otherwise-successful run. We lean on `fs.rm`'s Windows
|
|
17
|
+
* backoff (`maxRetries`/`retryDelay`) and, if it STILL can't remove the dir, log and
|
|
18
|
+
* swallow: a leaked temp dir is harmless (the OS reclaims the temp root), a failed run
|
|
19
|
+
* is not.
|
|
20
|
+
*/
|
|
21
|
+
export declare function withWorkspace<T>(prefix: string, fn: (dir: string) => Promise<T>): Promise<T>;
|
|
22
|
+
/**
|
|
23
|
+
* Run `fn` against a STABLE per-repo working directory (`<root>/<owner>/<repo>`) that is
|
|
24
|
+
* NOT removed afterwards — the persistent-checkout analogue of {@link withWorkspace}. The
|
|
25
|
+
* caller (via `prepareExistingCheckout`) clean-sweeps + fetches the dir into the right
|
|
26
|
+
* state before use; serialised per dir so concurrent jobs can't corrupt the shared tree.
|
|
27
|
+
*/
|
|
28
|
+
export declare function withPersistentWorkspace<T>(repo: RepoSpec, fn: (dir: string) => Promise<T>): Promise<T>;
|
|
29
|
+
/**
|
|
30
|
+
* Acquire a working directory for a run: a STABLE, reused per-repo checkout when the job
|
|
31
|
+
* opted into persistent checkout (the warm-pool path), else a fresh ephemeral temp dir
|
|
32
|
+
* (every other runtime). The two flows differ ONLY in dir lifecycle — the caller populates
|
|
33
|
+
* the dir (clone vs `prepareExistingCheckout`) itself, so it can keep its flow-specific
|
|
34
|
+
* resume / full-clone / branch logic.
|
|
35
|
+
*/
|
|
36
|
+
export declare function acquireRepoCheckout<T>(opts: {
|
|
37
|
+
persistent: boolean;
|
|
38
|
+
prefix: string;
|
|
39
|
+
repo: RepoSpec;
|
|
40
|
+
}, fn: (dir: string) => Promise<T>): Promise<T>;
|
|
41
|
+
/** What every agent needs to drive Pi against an already-prepared directory. */
|
|
42
|
+
export interface AgentRunSpec {
|
|
43
|
+
/** The prepared working directory (cloned/scaffolded by the caller). */
|
|
44
|
+
dir: string;
|
|
45
|
+
/** Composed role + best-practice fragments; written to Pi's global AGENTS.md context. */
|
|
46
|
+
systemPrompt: string;
|
|
47
|
+
/** The concrete task prompt handed to Pi. */
|
|
48
|
+
userPrompt: string;
|
|
49
|
+
model: string;
|
|
50
|
+
/**
|
|
51
|
+
* Which harness runs the agent. Absent ⇒ the default Pi harness (proxy +
|
|
52
|
+
* sessionToken). For `claude-code` / `codex`, `subscriptionToken` carries the
|
|
53
|
+
* leased credential and the proxy fields are unused (the CLI talks direct).
|
|
54
|
+
*/
|
|
55
|
+
harness?: HarnessKind;
|
|
56
|
+
/** The leased subscription credential for `claude-code` / `codex`. */
|
|
57
|
+
subscriptionToken?: string;
|
|
58
|
+
/** Anthropic-compatible base URL for a non-Anthropic Claude-Code vendor (GLM/Kimi). */
|
|
59
|
+
subscriptionBaseUrl?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Native local execution: run the developer's installed `claude` / `codex` with its
|
|
62
|
+
* OWN ambient login instead of a leased credential. Set only by the local native
|
|
63
|
+
* transport; a no-op for the Pi harness.
|
|
64
|
+
*/
|
|
65
|
+
ambientAuth?: boolean;
|
|
66
|
+
/** Pi proxy base URL (Pi harness only). */
|
|
67
|
+
proxyBaseUrl?: string;
|
|
68
|
+
/**
|
|
69
|
+
* The backend serves the phase-tagged completions route, so this pass may tag the URL it
|
|
70
|
+
* points Pi at with the phase it is running under (see {@link HarnessAuthFields.proxyPhasePath}
|
|
71
|
+
* and `phasedProxyBaseUrl`). Absent ⇒ the plain path.
|
|
72
|
+
*/
|
|
73
|
+
proxyPhasePath?: boolean;
|
|
74
|
+
/** Pi proxy session token (Pi harness only). */
|
|
75
|
+
sessionToken?: string;
|
|
76
|
+
/**
|
|
77
|
+
* For a monorepo service, the subdirectory (relative to the repo root) this run
|
|
78
|
+
* operates within — `spec.dir` already points there. Surfaced to the agent in
|
|
79
|
+
* AGENTS.md so it knows it's in a monorepo and where its service lives. Absent ⇒
|
|
80
|
+
* whole-repo run (no monorepo note).
|
|
81
|
+
*/
|
|
82
|
+
serviceDirectory?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Whether this run is expected to edit files. Defaults to true; set false for
|
|
85
|
+
* assess-only runs (the merger) so the no-progress guard's no-edit bound — which
|
|
86
|
+
* would otherwise fire on a run that correctly makes zero edits — is skipped.
|
|
87
|
+
*/
|
|
88
|
+
expectsEdits?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Per-knob overrides for the progress guard, set by the backend per agent kind (it
|
|
91
|
+
* only LOOSENS limits, never tightens). Each present knob overrides the env/default;
|
|
92
|
+
* absent knobs keep {@link progressGuardLimitsFromEnv}. Absent ⇒ env/default for all.
|
|
93
|
+
*/
|
|
94
|
+
guardLimits?: Partial<ProgressGuardLimits>;
|
|
95
|
+
/**
|
|
96
|
+
* Per-kind web-search guidance composed by the backend (so it can speak to what
|
|
97
|
+
* this agent kind does). Surfaced in AGENTS.md only when web search is configured;
|
|
98
|
+
* absent ⇒ the generic blurb is used. See `writeAgentsContext`.
|
|
99
|
+
*/
|
|
100
|
+
webToolsGuidance?: string;
|
|
101
|
+
/**
|
|
102
|
+
* Linked-context files the backend prepared (requirements / RFCs / PRDs / tracker
|
|
103
|
+
* issues). Materialised into CONTEXT_DIR in the checkout before the run and pointed at
|
|
104
|
+
* from AGENTS.md, so the agent reads them on demand. Absent ⇒ none.
|
|
105
|
+
*/
|
|
106
|
+
contextFiles?: ContextFileInfo[];
|
|
107
|
+
/**
|
|
108
|
+
* The skills to make available for this run — a `skill` step's picked skill and/or the playbooks
|
|
109
|
+
* the running agent kind declares. Installed HARNESS-AWARE: the claude-code runner writes them
|
|
110
|
+
* natively into the config dir's `skills/`; for Pi/codex the resource files are materialised
|
|
111
|
+
* under `.cat-context/skill/<name>/` (their prompt already carries the folded-in instructions).
|
|
112
|
+
* Absent ⇒ no skills.
|
|
113
|
+
*/
|
|
114
|
+
skills?: SkillSpec[];
|
|
115
|
+
/**
|
|
116
|
+
* Tool servers (MCP) to wire into the agent CLI. Served by the subscription harnesses only —
|
|
117
|
+
* Pi has no MCP client, and the BACKEND is what decides that (it drops an unservable server and
|
|
118
|
+
* tells the agent so), which is why this path simply forwards whatever it is given rather than
|
|
119
|
+
* re-deciding. Absent ⇒ the CLI's built-in tools only.
|
|
120
|
+
*/
|
|
121
|
+
mcpServers?: McpServerSpec[];
|
|
122
|
+
/**
|
|
123
|
+
* Enable proxy-backed web search: point the rpiv-web-tools SearXNG provider at the
|
|
124
|
+
* backend's search proxy (`${proxyBaseUrl}/web-search`) with the session token as
|
|
125
|
+
* the bearer — so the search runs server-side under the deployment's key and no
|
|
126
|
+
* provider secret reaches the sandbox. Off ⇒ web search is on only if a provider key
|
|
127
|
+
* is present directly in the container env (the self-hosted runner-pool path).
|
|
128
|
+
*/
|
|
129
|
+
webSearchProxy?: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Multi-repo run (service-connections phase 3): the cwd (`dir`) is the WORKSPACE ROOT with
|
|
132
|
+
* every involved repo checked out as a sibling under it. Suppresses the single-repo monorepo
|
|
133
|
+
* note in AGENTS.md and adds the multi-repo mechanics note instead. Absent ⇒ single-repo.
|
|
134
|
+
*/
|
|
135
|
+
multiRepo?: boolean;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Whether the run's checkout actually ships a `blueprints/` folder — what gates the blueprint
|
|
139
|
+
* orientation note in AGENTS.md (an external repo has none, so the note would be ~10 lines of
|
|
140
|
+
* dead guidance pointing at files that don't exist, re-sent on every turn).
|
|
141
|
+
*
|
|
142
|
+
* A MULTI-REPO run's `dir` is the workspace ROOT with each repo checked out as a sibling under
|
|
143
|
+
* it, so the root itself never holds `blueprints/`: the legs are checked too, and the note is
|
|
144
|
+
* included when ANY leg ships one (it orients the agent to the concept, and the agent finds the
|
|
145
|
+
* per-repo folder from there). Best-effort throughout — any stat/readdir failure simply omits
|
|
146
|
+
* the note rather than failing the dispatch.
|
|
147
|
+
*/
|
|
148
|
+
export declare function checkoutHasBlueprints(dir: string, multiRepo: boolean): Promise<boolean>;
|
|
149
|
+
/**
|
|
150
|
+
* Write Pi's global agent context (`~/.pi/agent/AGENTS.md`) + provider config,
|
|
151
|
+
* then run Pi once in `spec.dir` and return its summary/stats/stderr. The context
|
|
152
|
+
* lives outside the checkout so it never lands in a commit; the shared middle of
|
|
153
|
+
* every container agent.
|
|
154
|
+
*/
|
|
155
|
+
export declare function runAgentInWorkspace(spec: AgentRunSpec, opts?: RunOptions): Promise<PiRunOutcome>;
|
|
156
|
+
/**
|
|
157
|
+
* Whether the claude-code runner will install this run's skills natively (into the CLI's config
|
|
158
|
+
* dir) rather than the caller materialising them into the checkout. True ONLY for a
|
|
159
|
+
* leased-credential claude-code run, which gets a throwaway per-run config home. An AMBIENT run
|
|
160
|
+
* uses the developer's own `~/.claude`, which the runner will not write a skill into — it would
|
|
161
|
+
* outlive the run in their personal setup, and two concurrent jobs carrying same-named skills
|
|
162
|
+
* would overwrite each other's.
|
|
163
|
+
*/
|
|
164
|
+
export declare function installsSkillNatively(spec: Pick<AgentRunSpec, 'harness' | 'ambientAuth'>): boolean;
|
|
165
|
+
/**
|
|
166
|
+
* True when Pi exited cleanly without a single tool call or token of output — the
|
|
167
|
+
* signature of a run where it never reached the model. Used by every agent's
|
|
168
|
+
* no-op reason to point at the most likely cause (an unreachable proxy / rejected
|
|
169
|
+
* model) rather than a genuine "nothing to do".
|
|
170
|
+
*/
|
|
171
|
+
export declare function agentNeverActed(stats: PiRunStats): boolean;
|
|
172
|
+
/** The full-sentence "never acted" cause shared by the structured no-op reasons. */
|
|
173
|
+
export declare const NEVER_ACTED_CAUSE = " The agent never acted (no tool calls, no model output) \u2014 it most likely could not reach the model.";
|
|
174
|
+
/**
|
|
175
|
+
* A human-readable cause when the agent's FINAL answer is unusable — its last turn was
|
|
176
|
+
* cut off at the output ceiling, or carried no text at all (an empty completion) — or
|
|
177
|
+
* `undefined` when the final answer looks fine.
|
|
178
|
+
*
|
|
179
|
+
* This is OPT-IN per agent, never a blanket harness rule. Only agents whose work
|
|
180
|
+
* product is a final text/document the pipeline hands ONWARD to be reviewed or parsed
|
|
181
|
+
* (the spec-writer, the blueprinter) should treat a non-undefined result as a hard
|
|
182
|
+
* failure — for them an empty/cut-off final turn means there is nothing trustworthy to
|
|
183
|
+
* review, which is exactly what drove the spec-writer ⇄ companion rework loop. Agents
|
|
184
|
+
* whose product is a side effect (a pushed PR/commit from the coder or ci-fixer, a
|
|
185
|
+
* self-contained validation) legitimately end with no final text and MUST NOT call this.
|
|
186
|
+
*/
|
|
187
|
+
export declare function unusableFinalAnswerCause(diagnostics: RunDiagnostics | undefined): string | undefined;
|
|
188
|
+
/**
|
|
189
|
+
* The credential-scrubbed tail where a no-op's real cause shows up: a slice of Pi's
|
|
190
|
+
* stderr, or — when stderr is empty — a slice of its summary. Empty when neither is
|
|
191
|
+
* present. Shared by every agent's no-op reason so the cause is always diagnosable
|
|
192
|
+
* without shelling into the (ephemeral) container.
|
|
193
|
+
*/
|
|
194
|
+
export declare function agentOutputTail(stderrTail: string | undefined, summary?: string): string;
|
package/dist/pi-workspace.js
CHANGED
|
@@ -182,6 +182,10 @@ export async function runAgentInWorkspace(spec, opts = {}) {
|
|
|
182
182
|
expectsEdits: spec.expectsEdits ?? true,
|
|
183
183
|
onActivity: opts.onActivity,
|
|
184
184
|
onProgress: opts.onProgress,
|
|
185
|
+
// Per-slice review capture, so a parallel review's finished slices are persisted as they
|
|
186
|
+
// land rather than only in the terminal output. Only the subscription runners fan work out
|
|
187
|
+
// across subagents, so this is the only path that can produce it.
|
|
188
|
+
onSliceReviews: opts.onSliceReviews,
|
|
185
189
|
// Stream this run's per-call telemetry to the job's live drain. The subscription
|
|
186
190
|
// harnesses are the only producers of `callMetrics` (Pi's calls are metered by the LLM
|
|
187
191
|
// proxy as they happen), so this is the only path that needs the hook.
|