@cat-factory/server 0.325.1 → 0.326.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/dist/agents/CompositeAgentExecutor.d.ts +22 -3
- package/dist/agents/CompositeAgentExecutor.d.ts.map +1 -1
- package/dist/agents/CompositeAgentExecutor.js +91 -5
- package/dist/agents/CompositeAgentExecutor.js.map +1 -1
- package/dist/agents/DelegatedAgentExecutor.d.ts +147 -0
- package/dist/agents/DelegatedAgentExecutor.d.ts.map +1 -0
- package/dist/agents/DelegatedAgentExecutor.js +537 -0
- package/dist/agents/DelegatedAgentExecutor.js.map +1 -0
- package/dist/agents/brief.d.ts +61 -0
- package/dist/agents/brief.d.ts.map +1 -0
- package/dist/agents/brief.js +94 -0
- package/dist/agents/brief.js.map +1 -0
- package/dist/agents/containerJobAddressing.d.ts +1 -20
- package/dist/agents/containerJobAddressing.d.ts.map +1 -1
- package/dist/agents/containerJobAddressing.js +5 -23
- package/dist/agents/containerJobAddressing.js.map +1 -1
- package/dist/agents/delegatedExecutorHost.d.ts +90 -0
- package/dist/agents/delegatedExecutorHost.d.ts.map +1 -0
- package/dist/agents/delegatedExecutorHost.js +124 -0
- package/dist/agents/delegatedExecutorHost.js.map +1 -0
- package/dist/agents/delegationCredentials.d.ts +20 -0
- package/dist/agents/delegationCredentials.d.ts.map +1 -0
- package/dist/agents/delegationCredentials.js +63 -0
- package/dist/agents/delegationCredentials.js.map +1 -0
- package/dist/agents/delegationWorkBranch.d.ts +31 -0
- package/dist/agents/delegationWorkBranch.d.ts.map +1 -0
- package/dist/agents/delegationWorkBranch.js +103 -0
- package/dist/agents/delegationWorkBranch.js.map +1 -0
- package/dist/agents/jobBody.d.ts.map +1 -1
- package/dist/agents/jobBody.js +8 -9
- package/dist/agents/jobBody.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/publicApi/openapiDocument.generated.d.ts +1 -1
- package/dist/modules/publicApi/openapiDocument.generated.d.ts.map +1 -1
- package/dist/modules/publicApi/openapiDocument.generated.js +1 -1
- package/dist/modules/publicApi/openapiDocument.generated.js.map +1 -1
- package/dist/modules/workspaces/WorkspaceController.d.ts.map +1 -1
- package/dist/modules/workspaces/WorkspaceController.js +42 -1
- package/dist/modules/workspaces/WorkspaceController.js.map +1 -1
- package/package.json +14 -14
|
@@ -1,12 +1,24 @@
|
|
|
1
|
-
import { type AgentExecutor, type AgentJobHandle, type AgentJobUpdate, type AgentRunContext, type AgentRunResult, type AsyncAgentExecutor, type RunReclaimTarget } from '@cat-factory/kernel';
|
|
1
|
+
import { type AgentExecutor, type AgentJobHandle, type AgentJobUpdate, type AgentRunContext, type AgentRunResult, type AsyncAgentExecutor, type Logger, type RunReclaimReport, type RunReclaimTarget } from '@cat-factory/kernel';
|
|
2
2
|
import type { DispatchToolServers } from '@cat-factory/contracts';
|
|
3
3
|
import { type AgentKindRegistry } from '@cat-factory/agents';
|
|
4
4
|
export declare class CompositeAgentExecutor implements AsyncAgentExecutor {
|
|
5
5
|
private readonly inline;
|
|
6
6
|
private readonly container;
|
|
7
|
+
private readonly delegated;
|
|
7
8
|
/** The app-owned agent-kind registry: decides whether a registered custom kind needs a container. */
|
|
8
9
|
private readonly registry;
|
|
9
|
-
|
|
10
|
+
/** Normalised once, so the one best-effort site below can log unconditionally (CLAUDE.md). */
|
|
11
|
+
private readonly log;
|
|
12
|
+
constructor(inline: AgentExecutor, container: AgentExecutor | null, registry?: AgentKindRegistry, delegated?: AgentExecutor | null, logger?: Logger);
|
|
13
|
+
/**
|
|
14
|
+
* The delegated executor for this kind, or undefined when the kind does not run on one.
|
|
15
|
+
*
|
|
16
|
+
* Refuses LOUDLY rather than falling through when a delegated kind's arm is unwired, for exactly
|
|
17
|
+
* the reason the container arm does: the fallback is an inline LLM call over an implementer's
|
|
18
|
+
* prompt, which produces confident prose and no branch, and the run then advances into a `ci`
|
|
19
|
+
* gate with nothing to check.
|
|
20
|
+
*/
|
|
21
|
+
private delegatedFor;
|
|
10
22
|
/**
|
|
11
23
|
* The executor that handles a given step's kind. Container kinds REQUIRE a real
|
|
12
24
|
* sandbox: with none wired we throw rather than fall back to the inline executor,
|
|
@@ -44,7 +56,14 @@ export declare class CompositeAgentExecutor implements AsyncAgentExecutor {
|
|
|
44
56
|
* container executor) when stopping a run, so the composite must forward the reclaim
|
|
45
57
|
* to the container — otherwise the Layer-2 reclaim silently no-ops and leaks a
|
|
46
58
|
* warm instance. Delegates only when a container that supports it is wired.
|
|
59
|
+
*
|
|
60
|
+
* The two arms are INDEPENDENT resources, so the second must not be gated on the first
|
|
61
|
+
* succeeding. A container reclaim that throws (a DO/EKS API error, a runner-pool timeout: what
|
|
62
|
+
* "best-effort" here was written for) would otherwise propagate out before the delegated arm
|
|
63
|
+
* runs, and `applyDelegationCancellation` would then mark every live delegation "could not stop
|
|
64
|
+
* the external work" while the executor that COULD stop it was never asked. The external run
|
|
65
|
+
* carries on, opens its pull request and bills its tokens.
|
|
47
66
|
*/
|
|
48
|
-
reclaimRun(target: RunReclaimTarget): Promise<void>;
|
|
67
|
+
reclaimRun(target: RunReclaimTarget): Promise<RunReclaimReport | void>;
|
|
49
68
|
}
|
|
50
69
|
//# sourceMappingURL=CompositeAgentExecutor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CompositeAgentExecutor.d.ts","sourceRoot":"","sources":["../../src/agents/CompositeAgentExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EAEvB,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AACjE,OAAO,EACL,KAAK,iBAAiB,
|
|
1
|
+
{"version":3,"file":"CompositeAgentExecutor.d.ts","sourceRoot":"","sources":["../../src/agents/CompositeAgentExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EAEvB,KAAK,MAAM,EAGX,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AACjE,OAAO,EACL,KAAK,iBAAiB,EAIvB,MAAM,qBAAqB,CAAA;AA8B5B,qBAAa,sBAAuB,YAAW,kBAAkB;IAO7D,OAAO,CAAC,QAAQ,CAAC,MAAM;IAGvB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAQ1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAjB5B,qGAAqG;IACrG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,8FAA8F;IAC9F,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAE5B,YACmB,MAAM,EAAE,aAAa,EAGrB,SAAS,EAAE,aAAa,GAAG,IAAI,EAGhD,QAAQ,GAAE,iBAA8C,EAKvC,SAAS,GAAE,aAAa,GAAG,IAAW,EACvD,MAAM,CAAC,EAAE,MAAM,EAIhB;IAED;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAYpB;;;;OAIG;IACH,OAAO,CAAC,IAAI;IA4BZ,GAAG,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAErD;IAED;;;;;OAKG;IACH,YAAY,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAGlE;IAED;;;;;OAKG;IACH,kBAAkB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAGrF;IAED;;;;;OAKG;IACH,YAAY,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAQvD;IAED,+EAA+E;IAC/E,SAAS,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAG3C;IAED,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAM1D;IAED,OAAO,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAkBvD;IAED;;;;;;;;;;;;OAYG;IACG,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAyB3E;CACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { isAsyncAgentExecutor, } from '@cat-factory/kernel';
|
|
2
|
-
import { defaultAgentKindRegistry, runsInContainer, } from '@cat-factory/agents';
|
|
1
|
+
import { isAsyncAgentExecutor, noopLogger, runBestEffort, } from '@cat-factory/kernel';
|
|
2
|
+
import { defaultAgentKindRegistry, runsDelegated, runsInContainer, } from '@cat-factory/agents';
|
|
3
3
|
// Routes each pipeline step to the right executor by agent kind. The kinds that
|
|
4
4
|
// produce and commit files against a real checkout — implementation (`coder`),
|
|
5
5
|
// the external-dependency mock builder (`mocker`), the Playwright e2e test
|
|
@@ -21,21 +21,55 @@ import { defaultAgentKindRegistry, runsInContainer, } from '@cat-factory/agents'
|
|
|
21
21
|
// Runtime-neutral: both the Cloudflare Worker and the Node service wire this
|
|
22
22
|
// composite (inline `AiAgentExecutor` + a container executor backed by a
|
|
23
23
|
// per-run Cloudflare Container or an org's self-hosted runner pool).
|
|
24
|
+
//
|
|
25
|
+
// A THIRD arm routes a `delegated` kind to the deployment's own external executor (see
|
|
26
|
+
// `DelegatedAgentExecutor`). It is keyed off the agent-kind registry exactly as the container arm
|
|
27
|
+
// is, and it is the reason `pollJob` no longer hard-routes to the container: a poll rebuilds its
|
|
28
|
+
// handle from the step alone, so the arm has to be re-derived from the SAME declaration the
|
|
29
|
+
// dispatch routed on rather than assumed.
|
|
24
30
|
export class CompositeAgentExecutor {
|
|
25
31
|
inline;
|
|
26
32
|
container;
|
|
33
|
+
delegated;
|
|
27
34
|
/** The app-owned agent-kind registry: decides whether a registered custom kind needs a container. */
|
|
28
35
|
registry;
|
|
36
|
+
/** Normalised once, so the one best-effort site below can log unconditionally (CLAUDE.md). */
|
|
37
|
+
log;
|
|
29
38
|
constructor(inline,
|
|
30
39
|
// null when no sandbox is wired — container kinds then fail loudly (see below)
|
|
31
40
|
// rather than silently degrading to a useless one-shot inline call.
|
|
32
41
|
container,
|
|
33
42
|
// The app-owned agent-kind registry; defaults to the built-ins-only registry when a
|
|
34
43
|
// facade doesn't inject the shared instance (tests / no custom kinds).
|
|
35
|
-
registry = defaultAgentKindRegistry()
|
|
44
|
+
registry = defaultAgentKindRegistry(),
|
|
45
|
+
// The deployment's external executors, or null when the facade wired none. Null is the
|
|
46
|
+
// ordinary state: the platform ships no delegated kind, so nothing reaches this arm unless a
|
|
47
|
+
// deployment registered one, and a kind that does with no executor wired fails loudly for the
|
|
48
|
+
// reason an unwired container kind does.
|
|
49
|
+
delegated = null, logger) {
|
|
36
50
|
this.inline = inline;
|
|
37
51
|
this.container = container;
|
|
52
|
+
this.delegated = delegated;
|
|
38
53
|
this.registry = registry;
|
|
54
|
+
this.log = logger ?? noopLogger;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The delegated executor for this kind, or undefined when the kind does not run on one.
|
|
58
|
+
*
|
|
59
|
+
* Refuses LOUDLY rather than falling through when a delegated kind's arm is unwired, for exactly
|
|
60
|
+
* the reason the container arm does: the fallback is an inline LLM call over an implementer's
|
|
61
|
+
* prompt, which produces confident prose and no branch, and the run then advances into a `ci`
|
|
62
|
+
* gate with nothing to check.
|
|
63
|
+
*/
|
|
64
|
+
delegatedFor(agentKind) {
|
|
65
|
+
if (!runsDelegated(agentKind, this.registry))
|
|
66
|
+
return undefined;
|
|
67
|
+
if (!this.delegated) {
|
|
68
|
+
throw new Error(`Agent kind '${agentKind}' runs on an external (delegated) executor, and this ` +
|
|
69
|
+
'deployment wired none. Register the executor on the delegated-executor registry and ' +
|
|
70
|
+
'pass it to the facade entry point.');
|
|
71
|
+
}
|
|
72
|
+
return this.delegated;
|
|
39
73
|
}
|
|
40
74
|
/**
|
|
41
75
|
* The executor that handles a given step's kind. Container kinds REQUIRE a real
|
|
@@ -43,6 +77,14 @@ export class CompositeAgentExecutor {
|
|
|
43
77
|
* because a one-shot LLM call cannot operate on repo contents.
|
|
44
78
|
*/
|
|
45
79
|
pick(context) {
|
|
80
|
+
// A DELEGATED kind's work happens in a system the deployment already runs. Asked FIRST,
|
|
81
|
+
// because the two predicates below both answer "no" for it and the inline arm is what it would
|
|
82
|
+
// otherwise fall through to: a one-shot LLM call, over a prompt written for an implementer,
|
|
83
|
+
// producing plausible text and no branch. Routed off the same registry declaration the engine
|
|
84
|
+
// reads, so what the pipeline builder labelled as leaving the platform is what leaves it.
|
|
85
|
+
const delegated = this.delegatedFor(context.agentKind);
|
|
86
|
+
if (delegated)
|
|
87
|
+
return delegated;
|
|
46
88
|
// Built-in container kinds, plus any custom kind a deployment registered with
|
|
47
89
|
// `requiresContainer: true` (e.g. a proprietary org package contributing a
|
|
48
90
|
// repo-operating agent) and the container-backed companions, need a real checkout;
|
|
@@ -91,6 +133,11 @@ export class CompositeAgentExecutor {
|
|
|
91
133
|
* without the capability all report false (budget-metered, the prior behaviour).
|
|
92
134
|
*/
|
|
93
135
|
isQuotaBased(context) {
|
|
136
|
+
// A DELEGATED step spends nothing of ours: no pooled token is leased and no proxy call is
|
|
137
|
+
// metered. Answered before the container check below, which would otherwise read its `false`
|
|
138
|
+
// as "budget-metered" and let the spend gate account for tokens nobody here can see.
|
|
139
|
+
if (runsDelegated(context.agentKind, this.registry))
|
|
140
|
+
return Promise.resolve(false);
|
|
94
141
|
if (!this.container)
|
|
95
142
|
return Promise.resolve(false);
|
|
96
143
|
if (!runsInContainer(context.agentKind, this.registry))
|
|
@@ -110,7 +157,17 @@ export class CompositeAgentExecutor {
|
|
|
110
157
|
return executor.startJob(context);
|
|
111
158
|
}
|
|
112
159
|
pollJob(handle) {
|
|
113
|
-
//
|
|
160
|
+
// ROUTED, not assumed. A poll rebuilds its handle from the persisted step, and a delegated
|
|
161
|
+
// step's `delegated` record is what says the work is somewhere else. Read it here rather than
|
|
162
|
+
// hard-routing to the container, which would poll a container that was never started and
|
|
163
|
+
// settle the step against nothing.
|
|
164
|
+
if (handle.delegated) {
|
|
165
|
+
if (!this.delegated || !isAsyncAgentExecutor(this.delegated)) {
|
|
166
|
+
throw new Error(`This run has work on the external executor '${handle.delegated.executor}' and this ` +
|
|
167
|
+
'deployment wired no delegated executor to poll it with.');
|
|
168
|
+
}
|
|
169
|
+
return this.delegated.pollJob(handle);
|
|
170
|
+
}
|
|
114
171
|
if (!this.container || !isAsyncAgentExecutor(this.container)) {
|
|
115
172
|
throw new Error('Container executor does not support async jobs');
|
|
116
173
|
}
|
|
@@ -121,11 +178,40 @@ export class CompositeAgentExecutor {
|
|
|
121
178
|
* container executor) when stopping a run, so the composite must forward the reclaim
|
|
122
179
|
* to the container — otherwise the Layer-2 reclaim silently no-ops and leaks a
|
|
123
180
|
* warm instance. Delegates only when a container that supports it is wired.
|
|
181
|
+
*
|
|
182
|
+
* The two arms are INDEPENDENT resources, so the second must not be gated on the first
|
|
183
|
+
* succeeding. A container reclaim that throws (a DO/EKS API error, a runner-pool timeout: what
|
|
184
|
+
* "best-effort" here was written for) would otherwise propagate out before the delegated arm
|
|
185
|
+
* runs, and `applyDelegationCancellation` would then mark every live delegation "could not stop
|
|
186
|
+
* the external work" while the executor that COULD stop it was never asked. The external run
|
|
187
|
+
* carries on, opens its pull request and bills its tokens.
|
|
124
188
|
*/
|
|
125
189
|
async reclaimRun(target) {
|
|
126
190
|
if (this.container && isAsyncAgentExecutor(this.container) && this.container.reclaimRun) {
|
|
127
|
-
|
|
191
|
+
const reclaim = this.container.reclaimRun.bind(this.container);
|
|
192
|
+
await runBestEffort(this.log, 'composite.reclaimContainer', () => reclaim(target), {
|
|
193
|
+
runId: target.runId,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
// BOTH arms, always, and the second one ANSWERS. A run can hold a container and external work
|
|
197
|
+
// at once (a delegated implementer followed by a container fixer), so reclaiming one is not
|
|
198
|
+
// reclaiming the run; and whether the external half actually stopped is a fact only its
|
|
199
|
+
// executor knows, which is what the report carries back to the record.
|
|
200
|
+
if (!target.delegations?.length)
|
|
201
|
+
return;
|
|
202
|
+
if (this.delegated && isAsyncAgentExecutor(this.delegated) && this.delegated.reclaimRun) {
|
|
203
|
+
return this.delegated.reclaimRun(target);
|
|
128
204
|
}
|
|
205
|
+
// Named rather than dropped: the run is being torn down with external work still running and
|
|
206
|
+
// nothing here able to stop it, which is precisely the state the record must not render as a
|
|
207
|
+
// clean teardown.
|
|
208
|
+
return {
|
|
209
|
+
delegations: target.delegations.map((handle) => ({
|
|
210
|
+
correlationKey: handle.correlationKey,
|
|
211
|
+
cancelled: false,
|
|
212
|
+
note: 'This deployment wired no delegated executor, so the external work was left running.',
|
|
213
|
+
})),
|
|
214
|
+
};
|
|
129
215
|
}
|
|
130
216
|
}
|
|
131
217
|
//# sourceMappingURL=CompositeAgentExecutor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CompositeAgentExecutor.js","sourceRoot":"","sources":["../../src/agents/CompositeAgentExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,oBAAoB,
|
|
1
|
+
{"version":3,"file":"CompositeAgentExecutor.js","sourceRoot":"","sources":["../../src/agents/CompositeAgentExecutor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { type AgentContextRecorder, type AgentJobHandle, type AgentJobUpdate, type AgentRunContext, type AgentRunResult, type AsyncAgentExecutor, type Clock, type DelegatedExecutorDeps, type DelegatedExecutorRegistry, type DelegatedRepoFilesResolver, type Logger, type RunReclaimReport, type RunReclaimTarget, type TaskRepository, type ToolSecretResolver } from '@cat-factory/kernel';
|
|
2
|
+
import type { AgentKindRegistry } from '@cat-factory/agents';
|
|
3
|
+
import type { ResolveRepoTarget, ResolveRepoOrigin } from './repoTargeting.js';
|
|
4
|
+
/** What the facade wires for the delegated path. */
|
|
5
|
+
export interface DelegatedAgentExecutorDependencies {
|
|
6
|
+
/** The app-owned registry of the deployment's external executors. */
|
|
7
|
+
delegatedExecutorRegistry: DelegatedExecutorRegistry;
|
|
8
|
+
/** The app-owned agent-kind registry: which kinds are delegated, and to which executor. */
|
|
9
|
+
agentKindRegistry: AgentKindRegistry;
|
|
10
|
+
/** The run's repo target (the service↔repo projection), the same resolver the container uses. */
|
|
11
|
+
resolveRepoTarget: ResolveRepoTarget;
|
|
12
|
+
/**
|
|
13
|
+
* Where the repo is reached: the clone URL plus the VCS provider.
|
|
14
|
+
*
|
|
15
|
+
* REQUIRED, with no GitHub default. A default is invisible when it is wrong: a GitLab-hosted
|
|
16
|
+
* deployment whose facade forgot to wire this handed every brief a `https://github.com/...`
|
|
17
|
+
* clone URL for a repository that does not live there, and the provider-mismatch refusal that
|
|
18
|
+
* exists to catch exactly that never ran. A facade with genuinely nothing else to resolve
|
|
19
|
+
* passes `githubRepoOrigin` by name, which says so.
|
|
20
|
+
*/
|
|
21
|
+
resolveRepoOrigin: ResolveRepoOrigin;
|
|
22
|
+
/**
|
|
23
|
+
* Where the work came from, when it came from a tracker. One read per dispatch off the block's
|
|
24
|
+
* linked issues, never a per-issue loop, and never re-read on a poll.
|
|
25
|
+
*/
|
|
26
|
+
taskRepository?: TaskRepository;
|
|
27
|
+
/**
|
|
28
|
+
* Resolves an executor's declared credentials for this workspace. Absent ⇒ every executor is
|
|
29
|
+
* called with an empty bag, which is exactly what a deployment that declared none expects and
|
|
30
|
+
* what one that declared some will see as its own system refusing the call.
|
|
31
|
+
*/
|
|
32
|
+
resolveToolSecrets?: ToolSecretResolver;
|
|
33
|
+
/** Files the dispatch's agent-context snapshot; absent ⇒ nothing is recorded. */
|
|
34
|
+
agentContextObservability?: AgentContextRecorder;
|
|
35
|
+
/** The bound deps every registered executor is BUILT over. */
|
|
36
|
+
executorDeps: DelegatedExecutorDeps;
|
|
37
|
+
/**
|
|
38
|
+
* The checkout-free repo binding the engine writes a `platform-creates` work branch through.
|
|
39
|
+
*
|
|
40
|
+
* The SAME resolver `executorDeps.repoFiles` carries, named here as its own dependency because
|
|
41
|
+
* the engine's use of it is not the executor's: reaching into another collaborator's bundle is
|
|
42
|
+
* how the two come to disagree about which binding is live. Absent ⇒ this deployment configured
|
|
43
|
+
* no VCS provider, which refuses a `platform-creates` DISPATCH and nothing else.
|
|
44
|
+
*/
|
|
45
|
+
resolveRepoFiles?: DelegatedRepoFilesResolver;
|
|
46
|
+
logger: Logger;
|
|
47
|
+
clock: Clock;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Runs a `delegated`-surface step on a registered external executor.
|
|
51
|
+
*
|
|
52
|
+
* Async by construction: an external run is polled, never awaited, which is the same shape the
|
|
53
|
+
* container executor has and the reason both durable drivers already know how to drive it.
|
|
54
|
+
*/
|
|
55
|
+
export declare class DelegatedAgentExecutor implements AsyncAgentExecutor {
|
|
56
|
+
private readonly deps;
|
|
57
|
+
private readonly log;
|
|
58
|
+
/**
|
|
59
|
+
* Built executors, memoised per id for the life of this instance.
|
|
60
|
+
*
|
|
61
|
+
* A definition's `create` may build a client, and this executor is constructed once per app
|
|
62
|
+
* (per isolate on the Worker), so building per dispatch would discard whatever an executor
|
|
63
|
+
* warmed. Memoising on the INSTANCE rather than a module map is what keeps it out of the
|
|
64
|
+
* module-global trap every registry in this codebase is built to avoid.
|
|
65
|
+
*/
|
|
66
|
+
private readonly built;
|
|
67
|
+
constructor(deps: DelegatedAgentExecutorDependencies);
|
|
68
|
+
/** Every delegated step is polled: an external run outlives any request of ours. */
|
|
69
|
+
runsAsync(_context: AgentRunContext): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* A delegated step resolves NO model, and that is the honest answer rather than a gap.
|
|
72
|
+
*
|
|
73
|
+
* The external executor picks its own, through its own credentials, and the platform never sees
|
|
74
|
+
* it. Returning a guess would put a model on the board that nothing ran, and every reader
|
|
75
|
+
* downstream (the run card, the spend rollup, the debug overview) would treat it as the thing
|
|
76
|
+
* that spent the tokens it also cannot see.
|
|
77
|
+
*/
|
|
78
|
+
resolveModel(_context: AgentRunContext): Promise<string | undefined>;
|
|
79
|
+
/**
|
|
80
|
+
* Not quota-based. A delegated run spends nothing of OURS: no pooled subscription token is
|
|
81
|
+
* leased and no proxy call is metered, so it is neither quota nor budget.
|
|
82
|
+
*/
|
|
83
|
+
isQuotaBased(_context: AgentRunContext): Promise<boolean>;
|
|
84
|
+
/**
|
|
85
|
+
* Start (or RE-ATTACH to) this step's external work.
|
|
86
|
+
*
|
|
87
|
+
* Idempotent per correlation key, which is what the port asks of every executor and what the
|
|
88
|
+
* engine's claim-before-effect makes possible on this side: the key is derived from the run, the
|
|
89
|
+
* kind and the dispatch epoch, so a replayed dispatch produces the same key and the executor is
|
|
90
|
+
* asked to recognise its own run rather than start another.
|
|
91
|
+
*/
|
|
92
|
+
startJob(context: AgentRunContext): Promise<AgentJobHandle>;
|
|
93
|
+
/**
|
|
94
|
+
* Poll the external work, re-resolving the executor's credentials FIRST.
|
|
95
|
+
*
|
|
96
|
+
* Re-resolved rather than carried on the handle, and that is not a caching miss: a delegated
|
|
97
|
+
* poll can run hours after the dispatch, a GitHub App token lives one hour, and a credential
|
|
98
|
+
* frozen at dispatch would be dead exactly on the long runs this executor class exists for.
|
|
99
|
+
*/
|
|
100
|
+
pollJob(handle: AgentJobHandle): Promise<AgentJobUpdate>;
|
|
101
|
+
/**
|
|
102
|
+
* Stop every delegated unit this run holds, and REPORT what that achieved.
|
|
103
|
+
*
|
|
104
|
+
* The report is the point. An executor that declares no `cancel` leaves its run alive: it will
|
|
105
|
+
* finish, open its pull request and bill its tokens long after the platform recorded this run as
|
|
106
|
+
* stopped, and the person who stopped it is the one who needs to know to go and stop it there.
|
|
107
|
+
* Best-effort throughout (a teardown must never propagate) but never SILENT: a failure to
|
|
108
|
+
* cancel is reported as "not cancelled" with its cause, which is a different fact from a clean
|
|
109
|
+
* stop and renders differently on the step.
|
|
110
|
+
*/
|
|
111
|
+
reclaimRun(target: RunReclaimTarget): Promise<RunReclaimReport | void>;
|
|
112
|
+
/**
|
|
113
|
+
* A delegated step cannot be run SYNCHRONOUSLY: `run()` exists for non-durable callers and
|
|
114
|
+
* tests, and an external system that answers in hours is exactly what that shape cannot serve.
|
|
115
|
+
* Refused loudly rather than looped in-process, which would hold a request open for the duration
|
|
116
|
+
* of somebody else's CI.
|
|
117
|
+
*/
|
|
118
|
+
run(context: AgentRunContext): Promise<AgentRunResult>;
|
|
119
|
+
private cancelOne;
|
|
120
|
+
/** The registered definition for a kind, refusing the two ways it can be missing. */
|
|
121
|
+
private requireDefinition;
|
|
122
|
+
private executorFor;
|
|
123
|
+
/**
|
|
124
|
+
* Honour the executor's {@link DelegatedExecutorDefinition.workBranch} declaration.
|
|
125
|
+
*
|
|
126
|
+
* `executor-creates` writes nothing and probes nothing, which is what keeps a run whose external
|
|
127
|
+
* work never landed from leaving an empty ref behind.
|
|
128
|
+
*/
|
|
129
|
+
private prepareWorkBranch;
|
|
130
|
+
/**
|
|
131
|
+
* Resolve the repo + branches this dispatch targets, then compose the brief over them.
|
|
132
|
+
*
|
|
133
|
+
* `aprioriWork` rides back out because the brief carries the branch NAME and not who is allowed
|
|
134
|
+
* to create it, and only this method knows which of the two answers produced the name.
|
|
135
|
+
*/
|
|
136
|
+
private composeBrief;
|
|
137
|
+
/**
|
|
138
|
+
* Where the work came from, when it came from a tracker: ONE batch read of the block's linked
|
|
139
|
+
* issues, and the first live one.
|
|
140
|
+
*
|
|
141
|
+
* First rather than all, because the brief names the ticket the work is FOR and a block carries
|
|
142
|
+
* one such link by construction (`claimBlockLink` makes "one task per ticket" an invariant). Any
|
|
143
|
+
* further attachments are reference material, and they already reach the agent as context files.
|
|
144
|
+
*/
|
|
145
|
+
private resolveTrackerRef;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=DelegatedAgentExecutor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DelegatedAgentExecutor.d.ts","sourceRoot":"","sources":["../../src/agents/DelegatedAgentExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,KAAK,EAGV,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAI/B,KAAK,MAAM,EACX,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAO5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAoB9E,oDAAoD;AACpD,MAAM,WAAW,kCAAkC;IACjD,qEAAqE;IACrE,yBAAyB,EAAE,yBAAyB,CAAA;IACpD,2FAA2F;IAC3F,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,iGAAiG;IACjG,iBAAiB,EAAE,iBAAiB,CAAA;IACpC;;;;;;;;OAQG;IACH,iBAAiB,EAAE,iBAAiB,CAAA;IACpC;;;OAGG;IACH,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAA;IACvC,iFAAiF;IACjF,yBAAyB,CAAC,EAAE,oBAAoB,CAAA;IAChD,8DAA8D;IAC9D,YAAY,EAAE,qBAAqB,CAAA;IACnC;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,0BAA0B,CAAA;IAC7C,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,KAAK,CAAA;CACb;AAED;;;;;GAKG;AACH,qBAAa,sBAAuB,YAAW,kBAAkB;IAYnD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAC5B;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuC;IAE7D,YAA6B,IAAI,EAAE,kCAAkC,EAEpE;IAED,oFAAoF;IACpF,SAAS,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAE5C;IAED;;;;;;;OAOG;IACH,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAEnE;IAED;;;OAGG;IACH,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAExD;IAED;;;;;;;OAOG;IACG,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAuGhE;IAED;;;;;;OAMG;IACG,OAAO,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CA4C7D;IAED;;;;;;;;;OASG;IACG,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAK3E;IAED;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAWrD;YAEa,SAAS;IAiDvB,qFAAqF;IACrF,OAAO,CAAC,iBAAiB;IAyBzB,OAAO,CAAC,WAAW;IAQnB;;;;;OAKG;YACW,iBAAiB;IAc/B;;;;;OAKG;YACW,YAAY;IAkD1B;;;;;;;OAOG;YACW,iBAAiB;CAehC"}
|