@gaia-ai/core 0.6.0 → 0.6.1

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 (41) hide show
  1. package/README.md +2 -2
  2. package/dist/src/cli/load-gaia-config.js +8 -1
  3. package/dist/src/core/exec.d.ts +1 -1
  4. package/dist/src/core/exec.js +1 -1
  5. package/dist/src/index.d.ts +1 -9
  6. package/dist/src/index.js +12 -6
  7. package/dist/src/plugins/discover-addons.d.ts +19 -8
  8. package/dist/src/plugins/discover-addons.js +13 -3
  9. package/dist/src/plugins/preset.d.ts +30 -12
  10. package/dist/src/plugins/preset.js +10 -0
  11. package/package.json +4 -5
  12. package/dist/src/plugins/agent/agent.d.ts +0 -61
  13. package/dist/src/plugins/agent/agent.js +0 -11
  14. package/dist/src/plugins/auth/basic.d.ts +0 -12
  15. package/dist/src/plugins/auth/basic.js +0 -37
  16. package/dist/src/plugins/builtins-preset.d.ts +0 -5
  17. package/dist/src/plugins/builtins-preset.js +0 -32
  18. package/dist/src/plugins/executor/executor.d.ts +0 -104
  19. package/dist/src/plugins/executor/executor.js +0 -1
  20. package/dist/src/plugins/plugins.d.ts +0 -60
  21. package/dist/src/plugins/plugins.js +0 -42
  22. package/dist/src/plugins/registry-exports.d.ts +0 -6
  23. package/dist/src/plugins/registry-exports.js +0 -6
  24. package/dist/src/plugins/remote/drupal.d.ts +0 -40
  25. package/dist/src/plugins/remote/drupal.js +0 -393
  26. package/dist/src/plugins/remote/fake.d.ts +0 -113
  27. package/dist/src/plugins/remote/fake.js +0 -247
  28. package/dist/src/plugins/remote/remote.d.ts +0 -203
  29. package/dist/src/plugins/remote/remote.js +0 -1
  30. package/dist/src/plugins/workspace/fake.d.ts +0 -6
  31. package/dist/src/plugins/workspace/fake.js +0 -16
  32. package/dist/src/plugins/workspace/git.d.ts +0 -37
  33. package/dist/src/plugins/workspace/git.js +0 -89
  34. package/dist/src/plugins/workspace/instructions.d.ts +0 -6
  35. package/dist/src/plugins/workspace/instructions.js +0 -16
  36. package/dist/src/plugins/workspace/workspace.d.ts +0 -35
  37. package/dist/src/plugins/workspace/workspace.js +0 -1
  38. package/dist/src/plugins-index.d.ts +0 -1
  39. package/dist/src/plugins-index.js +0 -1
  40. package/dist/src/types.d.ts +0 -65
  41. package/dist/src/types.js +0 -1
@@ -1,42 +0,0 @@
1
- /** Resolves the remote from its named config slot. */
2
- export async function selectRemote(config) {
3
- return config.remote.createRemote(config);
4
- }
5
- /** Resolves the executor from its named config slot, injecting the logger. */
6
- export async function selectExecutor(config, logger) {
7
- return config.executor.createExecutor(config, { logger });
8
- }
9
- /** Resolves the workspace from its named config slot. */
10
- export async function selectWorkspace(config) {
11
- return config.workspace.createWorkspace(config);
12
- }
13
- /** A missing `priority` scores strictly below any real number (AC-4). */
14
- const DEFAULT_PRIORITY = Number.NEGATIVE_INFINITY;
15
- /**
16
- * Pick the agent for a ticket: highest `priority(ticket)` wins; a candidate with
17
- * no `priority` scores lowest; ties resolve by config array order (stable sort).
18
- */
19
- export function selectAgent(candidates, ticket) {
20
- const best = candidates
21
- .map((c, index) => ({
22
- c,
23
- index,
24
- score: c.priority ? c.priority(ticket) : DEFAULT_PRIORITY,
25
- }))
26
- .sort((a, b) => b.score - a.score || a.index - b.index)[0];
27
- if (!best) {
28
- throw new Error('selectAgent: no agent candidates configured');
29
- }
30
- return best.c;
31
- }
32
- /** Constructs every agent candidate from the resolved config slot (single or array). */
33
- export async function selectAgents(config) {
34
- const candidates = Array.isArray(config.agent)
35
- ? config.agent
36
- : [config.agent];
37
- return Promise.all(candidates.map(async (c) => ({
38
- id: c.agent.id,
39
- agent: await c.agent.createAgent(config),
40
- ...(c.priority ? { priority: c.priority } : {}),
41
- })));
42
- }
@@ -1,6 +0,0 @@
1
- export { basicAuthProvider } from './auth/basic.js';
2
- export { type AgentCandidate, type ResolvedAgent, selectAgent, selectAgents, selectExecutor, selectRemote, selectWorkspace, } from './plugins.js';
3
- export { DrupalGaiaRemote, drupalRemote } from './remote/drupal.js';
4
- export { FakeGaiaRemote, type FakeRemoteSeed, fakeRemote, } from './remote/fake.js';
5
- export { FakeWorkspace, fakeWorkspace } from './workspace/fake.js';
6
- export { GitWorkspace, gitWorkspace } from './workspace/git.js';
@@ -1,6 +0,0 @@
1
- export { basicAuthProvider } from './auth/basic.js';
2
- export { selectAgent, selectAgents, selectExecutor, selectRemote, selectWorkspace, } from './plugins.js';
3
- export { DrupalGaiaRemote, drupalRemote } from './remote/drupal.js';
4
- export { FakeGaiaRemote, fakeRemote, } from './remote/fake.js';
5
- export { FakeWorkspace, fakeWorkspace } from './workspace/fake.js';
6
- export { GitWorkspace, gitWorkspace } from './workspace/git.js';
@@ -1,40 +0,0 @@
1
- import type { JsonApiClient } from 'dropsh/plugin';
2
- import type { RemotePlugin } from '../plugins.js';
3
- import type { ActiveRun, ClaimedRun, ClaimOptions, ConductorRegistration, ConductorStatus, FinalizableRun, GaiaRemote, RunMetrics, RunWriteAttributes, Ticket, UncleanTicket } from './remote.js';
4
- export declare class DrupalGaiaRemote implements GaiaRemote {
5
- private readonly api;
6
- constructor(api: JsonApiClient);
7
- fetchActiveRuns(id: string): Promise<ActiveRun[]>;
8
- activeRunCount(id: string): Promise<number>;
9
- claimNext(c: ClaimOptions): Promise<ClaimedRun | null>;
10
- getTicket(uuid: string): Promise<Ticket>;
11
- getRunWorktree(uuid: string): Promise<string>;
12
- getRunTicketIdentifier(uuid: string): Promise<string>;
13
- getRunTicketBranchName(uuid: string): Promise<string>;
14
- registerConductor(reg: ConductorRegistration): Promise<string>;
15
- heartbeat(reg: ConductorRegistration, load: number, lease?: number): Promise<string>;
16
- getConductorStatus(id: string): Promise<string | null>;
17
- setConductorStatus(id: string, status: string): Promise<void>;
18
- listConductors(owner?: 'me'): Promise<ConductorStatus[]>;
19
- markRunning(uuid: string, attrs?: RunWriteAttributes): Promise<void>;
20
- markFailed(uuid: string, errorLog: string): Promise<void>;
21
- fetchFinalizableRuns(id: string): Promise<FinalizableRun[]>;
22
- finalizeRun(uuid: string, log: string, metrics?: RunMetrics): Promise<void>;
23
- fetchUncleanedTickets(id: string): Promise<UncleanTicket[]>;
24
- closeTicket(uuid: string): Promise<void>;
25
- markTicketCleanedUp(uuid: string): Promise<void>;
26
- /**
27
- * Absolute worktree path of the ticket's latest run (highest run id with a
28
- * non-empty worktree_path), or '' when none — the cwd the cleanup command
29
- * runs in. The conductor persists worktree_path on markRunning, so a done
30
- * ticket's run carries the path even after the run closed.
31
- */
32
- private latestRunWorktree;
33
- resolveTicketByIdentifier(project: string, identifier: string): Promise<{
34
- uuid: string;
35
- title: string;
36
- } | null>;
37
- private readonly projectUuidCache;
38
- private projectUuid;
39
- }
40
- export declare function drupalRemote(): RemotePlugin;
@@ -1,393 +0,0 @@
1
- import { resolveAuth } from 'dropsh';
2
- import { createHttpClient, createJsonApiClient } from 'dropsh/plugin';
3
- const ACTIVE = ['claimed', 'running'];
4
- export class DrupalGaiaRemote {
5
- api;
6
- constructor(api) {
7
- this.api = api;
8
- }
9
- async fetchActiveRuns(id) {
10
- // The conductor identity `id` is the machine_id (hash of hostname+path).
11
- // gaia_run links to its owning conductor via the `conductor_id` entity-ref;
12
- // filter on the related conductor's `machine_id` (nested relationship filter).
13
- const rows = await this.api
14
- .collection('gaia_run')
15
- .where('conductor_id.machine_id', '=', id)
16
- .whereIn('state', ACTIVE)
17
- .fields(['state', 'state_at_start', 'worktree_path'])
18
- .page(100)
19
- .list();
20
- return Promise.all(rows.map(async (r) => ({
21
- runUuid: r.id,
22
- ticketUuid: r.rel('ticket_id') ?? '',
23
- ticketIdentifier: await this.getRunTicketIdentifier(r.id),
24
- branchName: await this.getRunTicketBranchName(r.id),
25
- state: r.attr('state') ?? '',
26
- stateAtStart: r.attr('state_at_start') ?? '',
27
- worktreePath: r.attr('worktree_path') ?? '',
28
- })));
29
- }
30
- async activeRunCount(id) {
31
- // Filter on the related conductor's `machine_id` (nested relationship filter).
32
- return this.api
33
- .collection('gaia_run')
34
- .where('conductor_id.machine_id', '=', id)
35
- .whereIn('state', ACTIVE)
36
- .fields(['drupal_internal__id'])
37
- .page(100)
38
- .count();
39
- }
40
- async claimNext(c) {
41
- const res = (await this.api.post('gaia/claim-next', {
42
- data: {
43
- attributes: {
44
- lease_seconds: c.leaseSeconds,
45
- // Server (`ConductorResolverTrait`) narrows to the caller's conductor
46
- // by the `machine_id` attribute; `c.conductorId` IS the machine_id.
47
- machine_id: c.conductorId,
48
- },
49
- },
50
- }));
51
- if (!res?.data) {
52
- return null;
53
- }
54
- const d = res.data;
55
- const rawId = d.attributes?.drupal_internal__id;
56
- if (rawId === undefined || rawId === null) {
57
- throw new Error(`claimNext: missing drupal_internal__id on run ${d.id}`);
58
- }
59
- return {
60
- runUuid: d.id,
61
- runId: Number(rawId),
62
- ticketUuid: d.relationships?.ticket_id?.data?.id ?? '',
63
- stateAtStart: String(d.attributes?.state_at_start ?? ''),
64
- // handler_id was dropped; the handler is the work the run does, i.e. the
65
- // ticket state the run started in.
66
- handler: String(d.attributes?.state_at_start ?? ''),
67
- };
68
- }
69
- async getTicket(uuid) {
70
- // The agent self-reads the ticket + its comments at run start (GAIA-112),
71
- // so getTicket needs no ?include=comments — but it DOES sideload labels +
72
- // environments (GAIA-144) so a config-side priority(ticket) can read them
73
- // without fetching (AC-5).
74
- const doc = (await this.api.get(`gaia_ticket/gaia_ticket/${uuid}?include=labels,environments`));
75
- const attrs = doc.data?.attributes ?? {};
76
- const issueUrl = typeof attrs.origin === 'string' ? attrs.origin : undefined;
77
- const included = Array.isArray(doc.included) ? doc.included : [];
78
- const byId = new Map(included.map((r) => [`${r.type}:${r.id}`, r]));
79
- const refs = (name) => {
80
- const data = doc.data?.relationships?.[name]?.data;
81
- return Array.isArray(data) ? data : [];
82
- };
83
- const labels = refs('labels')
84
- .map((ref) => byId.get(`${ref.type}:${ref.id}`)?.attributes?.name)
85
- .filter((n) => typeof n === 'string');
86
- const environments = refs('environments')
87
- .map((ref) => {
88
- const a = byId.get(`${ref.type}:${ref.id}`)?.attributes ?? {};
89
- return {
90
- name: typeof a.name === 'string' ? a.name : '',
91
- tier: typeof a.tier === 'string' ? a.tier : '',
92
- };
93
- })
94
- .filter((e) => e.name !== '');
95
- return {
96
- uuid: doc.data?.id ?? uuid,
97
- identifier: typeof attrs.identifier === 'string' ? attrs.identifier : uuid,
98
- title: typeof attrs.title === 'string' ? attrs.title : '',
99
- state: typeof attrs.state === 'string' ? attrs.state : '',
100
- branchName: typeof attrs.branch_name === 'string' ? attrs.branch_name : '',
101
- ...(typeof attrs.base_branch === 'string' && attrs.base_branch !== ''
102
- ? { baseBranch: attrs.base_branch }
103
- : {}),
104
- ...(typeof attrs.effective_env_vars === 'string' &&
105
- attrs.effective_env_vars !== ''
106
- ? { effectiveEnvVars: attrs.effective_env_vars }
107
- : {}),
108
- ...(issueUrl ? { issueUrl } : {}),
109
- labels,
110
- environments,
111
- };
112
- }
113
- async getRunWorktree(uuid) {
114
- const r = await this.api.resource('gaia_run', uuid);
115
- return r.attr('worktree_path') ?? '';
116
- }
117
- async getRunTicketIdentifier(uuid) {
118
- const run = await this.api.resource('gaia_run', uuid);
119
- const ticketUuid = run.rel('ticket_id');
120
- if (!ticketUuid) {
121
- return '';
122
- }
123
- const ticket = await this.api.resource('gaia_ticket', ticketUuid);
124
- return ticket.attr('identifier') ?? '';
125
- }
126
- async getRunTicketBranchName(uuid) {
127
- const run = await this.api.resource('gaia_run', uuid);
128
- const ticketUuid = run.rel('ticket_id');
129
- if (!ticketUuid) {
130
- return '';
131
- }
132
- const ticket = await this.api.resource('gaia_ticket', ticketUuid);
133
- return ticket.attr('branch_name') ?? '';
134
- }
135
- async registerConductor(reg) {
136
- const r = await this.api.upsert('gaia_conductor', { path: 'machine_id', value: reg.id }, {
137
- attributes: {
138
- machine_id: reg.id,
139
- status: 'online',
140
- workspace_root: reg.workspace,
141
- label: reg.label,
142
- states: reg.states,
143
- max_parallel: reg.max_parallel,
144
- },
145
- relationships: {
146
- owner_user_id: {
147
- data: { type: 'user--user', id: await this.api.me() },
148
- },
149
- project_id: {
150
- data: {
151
- type: 'gaia_project--gaia_project',
152
- id: await this.projectUuid(reg.project),
153
- },
154
- },
155
- },
156
- });
157
- return r.id; // drupal uuid
158
- }
159
- async heartbeat(reg, load, lease = 300) {
160
- // Server-side upsert by machine_id (see HeartbeatResource): never 404s,
161
- // heals a vanished registration, and returns the resulting status. Sending
162
- // the full registration lets the server recreate the entity when missing.
163
- const res = (await this.api.post('gaia/heartbeat', {
164
- data: {
165
- attributes: {
166
- machine_id: reg.id,
167
- project: reg.project,
168
- states: reg.states,
169
- workspace_root: reg.workspace,
170
- label: reg.label,
171
- max_parallel: reg.max_parallel,
172
- current_load: load,
173
- lease_seconds: lease,
174
- },
175
- },
176
- }));
177
- const status = res?.data?.attributes?.status;
178
- return typeof status === 'string' ? status : 'online';
179
- }
180
- async getConductorStatus(id) {
181
- const c = await this.api
182
- .collection('gaia_conductor')
183
- .where('machine_id', '=', id)
184
- .fields(['status'])
185
- .first();
186
- return c ? (c.attr('status') ?? null) : null;
187
- }
188
- async setConductorStatus(id, status) {
189
- const c = await this.api
190
- .collection('gaia_conductor')
191
- .where('machine_id', '=', id)
192
- .first();
193
- if (c) {
194
- await this.api.update('gaia_conductor', c.id, { attributes: { status } });
195
- }
196
- }
197
- async listConductors(owner) {
198
- let col = this.api.collection('gaia_conductor');
199
- if (owner === 'me') {
200
- col = col.where('owner_user_id.id', '=', await this.api.me());
201
- }
202
- const rows = await col.page(200).list();
203
- return rows.map((r) => ({
204
- id: r.attr('machine_id') ?? r.id,
205
- project: '',
206
- label: r.attr('label') ?? '',
207
- status: r.attr('status') ?? '',
208
- lastSeen: r.attr('last_seen') ?? 0,
209
- load: r.attr('current_load') ?? 0,
210
- }));
211
- }
212
- async markRunning(uuid, attrs) {
213
- const t = Math.floor(Date.now() / 1000);
214
- await this.api.update('gaia_run', uuid, {
215
- attributes: {
216
- state: 'running',
217
- heartbeat: t,
218
- ...(attrs?.worktree_path ? { worktree_path: attrs.worktree_path } : {}),
219
- ...(attrs?.agent ? { agent: attrs.agent } : {}),
220
- },
221
- });
222
- }
223
- async markFailed(uuid, errorLog) {
224
- const t = Math.floor(Date.now() / 1000);
225
- await this.api.update('gaia_run', uuid, {
226
- attributes: {
227
- state: 'failed',
228
- error_log: errorLog,
229
- closed: true,
230
- closed_date: t,
231
- },
232
- });
233
- }
234
- async fetchFinalizableRuns(id) {
235
- const rows = await this.api
236
- .collection('gaia_run')
237
- .where('conductor_id.machine_id', '=', id)
238
- .where('state', '=', 'done')
239
- .where('closed', '=', '0')
240
- .fields(['worktree_path', 'agent', 'drupal_internal__id'])
241
- .page(100)
242
- .list();
243
- // No `started_at` read: the run's duration_s is derived from the agent
244
- // transcript at finalize (GAIA-151), so the conductor no longer round-trips
245
- // the timestamp back through JSON:API.
246
- return rows.map((r) => ({
247
- runUuid: r.id,
248
- // The `#<runId>` tab-label token: scopes the run's tab-close (GAIA-183).
249
- runId: Number(r.attr('drupal_internal__id')),
250
- worktreePath: r.attr('worktree_path') ?? '',
251
- agent: r.attr('agent') ?? '',
252
- }));
253
- }
254
- async finalizeRun(uuid, log, metrics) {
255
- const t = Math.floor(Date.now() / 1000);
256
- await this.api.update('gaia_run', uuid, {
257
- attributes: {
258
- closed: true,
259
- closed_date: t,
260
- ...(log ? { log } : {}),
261
- ...(metrics ?? {}),
262
- },
263
- }); // NO state — the run is already done.
264
- }
265
- async fetchUncleanedTickets(id) {
266
- // Finished tickets whose worktree is not yet torn down (cleaned_up=0),
267
- // driven by the durable `cleaned_up` flag (GAIA-89) AND scoped to this
268
- // conductor (GAIA-121): only tickets assigned to `id` are loaded, so a
269
- // ticket owned by another conductor never reaches the reaper loop. "Finished"
270
- // = reached `done` OR already `closed`; the collection builder has no OR, so
271
- // this is two AND-queries merged + de-duped by uuid. The `conductor_id`
272
- // relationship carries the assigned conductor's `machine_id` — the same
273
- // nested filter the sibling reaper queries (`fetchFinalizableRuns` etc.) use.
274
- const fields = ['branch_name', 'state', 'closed'];
275
- const done = await this.api
276
- .collection('gaia_ticket')
277
- .where('conductor_id.machine_id', '=', id)
278
- .where('state', '=', 'done')
279
- .where('cleaned_up', '=', '0')
280
- .fields(fields)
281
- .page(100)
282
- .list();
283
- const closed = await this.api
284
- .collection('gaia_ticket')
285
- .where('conductor_id.machine_id', '=', id)
286
- .where('closed', '=', '1')
287
- .where('cleaned_up', '=', '0')
288
- .fields(fields)
289
- .page(100)
290
- .list();
291
- const byUuid = new Map();
292
- for (const r of [...done, ...closed]) {
293
- byUuid.set(r.id, r);
294
- }
295
- return Promise.all([...byUuid.values()].map(async (r) => ({
296
- ticketUuid: r.id,
297
- branchName: r.attr('branch_name') ?? '',
298
- state: r.attr('state') ?? '',
299
- closed: r.attr('closed') ?? false,
300
- worktreePath: await this.latestRunWorktree(r.id),
301
- })));
302
- }
303
- async closeTicket(uuid) {
304
- const t = Math.floor(Date.now() / 1000);
305
- await this.api.update('gaia_ticket', uuid, {
306
- attributes: { closed: true, closed_date: t },
307
- }); // NO state — the ticket is already done.
308
- }
309
- async markTicketCleanedUp(uuid) {
310
- await this.api.update('gaia_ticket', uuid, {
311
- attributes: { cleaned_up: true },
312
- }); // NO state/closed — teardown flag only, decoupled from the lifecycle.
313
- }
314
- /**
315
- * Absolute worktree path of the ticket's latest run (highest run id with a
316
- * non-empty worktree_path), or '' when none — the cwd the cleanup command
317
- * runs in. The conductor persists worktree_path on markRunning, so a done
318
- * ticket's run carries the path even after the run closed.
319
- */
320
- async latestRunWorktree(ticketUuid) {
321
- const rows = await this.api
322
- .collection('gaia_run')
323
- .where('ticket_id.id', '=', ticketUuid)
324
- .fields(['worktree_path', 'drupal_internal__id'])
325
- .sort('-drupal_internal__id')
326
- .page(100)
327
- .list();
328
- for (const r of rows) {
329
- const path = r.attr('worktree_path');
330
- if (path) {
331
- return path;
332
- }
333
- }
334
- return '';
335
- }
336
- async resolveTicketByIdentifier(project, identifier) {
337
- // Scope by the project uuid: identifiers (GAIA-nnn) are unique per project,
338
- // not globally, so a bare identifier filter could match another project's
339
- // ticket in a multi-project instance.
340
- const projectId = await this.projectUuid(project);
341
- const t = await this.api
342
- .collection('gaia_ticket')
343
- .where('identifier', '=', identifier)
344
- .where('project_id.id', '=', projectId)
345
- .first();
346
- if (!t) {
347
- return null;
348
- }
349
- return { uuid: t.id, title: t.attr('title') ?? '' };
350
- }
351
- projectUuidCache = new Map();
352
- async projectUuid(name) {
353
- // A project's name→uuid never changes, so cache it: gatherBatch resolves
354
- // many identifiers per `gaia deployment tickets` call, each of which would
355
- // otherwise re-fetch the same project.
356
- const cached = this.projectUuidCache.get(name);
357
- if (cached) {
358
- return cached;
359
- }
360
- const p = await this.api
361
- .collection('gaia_project')
362
- .where('name', '=', name)
363
- .first();
364
- if (!p) {
365
- throw new Error(`gaia project "${name}" not found`);
366
- }
367
- this.projectUuidCache.set(name, p.id);
368
- return p.id;
369
- }
370
- }
371
- export function drupalRemote() {
372
- return {
373
- kind: 'remote',
374
- id: 'drupal',
375
- requiredModules: [],
376
- async createRemote(config) {
377
- const http = createHttpClient();
378
- const auth = await resolveAuth({
379
- baseUrl: config.site.base_url,
380
- plugins: config.plugins ?? [],
381
- http,
382
- now: Date.now,
383
- // stateDir omitted → dropsh defaultStateDir() (~/.config/dropsh)
384
- });
385
- return new DrupalGaiaRemote(createJsonApiClient({
386
- baseUrl: config.site.base_url,
387
- prefix: config.site.jsonapi_prefix,
388
- http,
389
- auth: auth,
390
- }));
391
- },
392
- };
393
- }
@@ -1,113 +0,0 @@
1
- import type { RemotePlugin } from '../plugins.js';
2
- import type { ActiveRun, ClaimedRun, ClaimOptions, ConductorRegistration, ConductorStatus, FinalizableRun, GaiaRemote, RunMetrics, RunWriteAttributes, Ticket, UncleanTicket } from './remote.js';
3
- export interface FakeSeedRun {
4
- runUuid: string;
5
- /** Numeric (drupal_internal__id) run id. Defaults to a stable 1-based counter when omitted. */
6
- id?: number;
7
- ticketUuid: string;
8
- stateAtStart: string;
9
- /** Override the initial state (defaults to stateAtStart). */
10
- state?: string;
11
- handler?: string;
12
- worktreePath?: string;
13
- closed?: boolean;
14
- /** Id of the agent that ran (GAIA-144) — routes footprint parsing at finalize. */
15
- agent?: string;
16
- }
17
- export interface FakeSeedTicket {
18
- identifier: string;
19
- title: string;
20
- state: string;
21
- branchName?: string;
22
- /** Computed base branch (parent branch / project default / 'main'). */
23
- baseBranch?: string;
24
- /** Effective env vars (parent-chain resolved) as `.env`-style lines. */
25
- effectiveEnvVars?: string;
26
- url?: string;
27
- workflow?: string;
28
- /** Label term names sideloaded for agent selection (GAIA-144). */
29
- labels?: string[];
30
- /** Environments sideloaded for agent selection (GAIA-144). */
31
- environments?: {
32
- name: string;
33
- tier: string;
34
- }[];
35
- /** Whether the ticket is already closed (its lifecycle wound up). */
36
- closed?: boolean;
37
- /** Whether the ticket's worktree is already torn down (cleaned_up flag). */
38
- cleanedUp?: boolean;
39
- /**
40
- * machine_id of the conductor this ticket is assigned to (GAIA-121). Omit to
41
- * mean "belongs to the querying conductor" (the common case for reaper tests);
42
- * set an explicit value to test conductor-scoping, or `''` for unassigned.
43
- */
44
- conductorId?: string;
45
- }
46
- export interface FakeRemoteSeed {
47
- runs?: FakeSeedRun[];
48
- tickets?: Record<string, FakeSeedTicket>;
49
- }
50
- export interface MarkRunningCall {
51
- runUuid: string;
52
- attrs?: {
53
- worktree_path?: string;
54
- agent?: string;
55
- };
56
- }
57
- export interface FinalizeRunCall {
58
- runUuid: string;
59
- log: string;
60
- metrics?: RunMetrics;
61
- }
62
- export interface MarkFailedCall {
63
- runUuid: string;
64
- errorLog: string;
65
- }
66
- export declare class FakeGaiaRemote implements GaiaRemote {
67
- readonly calls: {
68
- markRunning: MarkRunningCall[];
69
- markFailed: MarkFailedCall[];
70
- finalizeRun: FinalizeRunCall[];
71
- closeTicket: string[];
72
- markCleanedUp: string[];
73
- };
74
- /**
75
- * Override for reconcile tests: when set, `fetchActiveRuns` returns this
76
- * list verbatim instead of deriving it from the internal runs map.
77
- */
78
- activeRuns: ActiveRun[] | null;
79
- private readonly runs;
80
- private readonly queue;
81
- private readonly tickets;
82
- private conductorStatus;
83
- /** Stable counter for assigning numeric ids to unseeded runs. */
84
- private runIdCounter;
85
- constructor(seed?: FakeRemoteSeed);
86
- registerConductor(_reg: ConductorRegistration): Promise<string>;
87
- heartbeat(): Promise<string>;
88
- getConductorStatus(_conductorId: string): Promise<string | null>;
89
- setConductorStatus(_conductorId: string, status: 'offline' | 'online'): Promise<void>;
90
- listConductors(_owner?: 'me'): Promise<ConductorStatus[]>;
91
- activeRunCount(_conductorId: string): Promise<number>;
92
- fetchActiveRuns(_conductorId: string): Promise<ActiveRun[]>;
93
- claimNext(_claim: ClaimOptions): Promise<ClaimedRun | null>;
94
- getTicket(ticketUuid: string): Promise<Ticket>;
95
- getRunWorktree(runUuid: string): Promise<string>;
96
- getRunTicketIdentifier(runUuid: string): Promise<string>;
97
- getRunTicketBranchName(runUuid: string): Promise<string>;
98
- markRunning(runUuid: string, attrs?: RunWriteAttributes): Promise<void>;
99
- markFailed(runUuid: string, errorLog: string): Promise<void>;
100
- fetchFinalizableRuns(_conductorId: string): Promise<FinalizableRun[]>;
101
- finalizeRun(runUuid: string, log: string, metrics?: RunMetrics): Promise<void>;
102
- fetchUncleanedTickets(conductorId: string): Promise<UncleanTicket[]>;
103
- closeTicket(uuid: string): Promise<void>;
104
- markTicketCleanedUp(uuid: string): Promise<void>;
105
- resolveTicketByIdentifier(_project: string, identifier: string): Promise<{
106
- uuid: string;
107
- title: string;
108
- } | null>;
109
- /** Worktree path of the ticket's most recently seeded run, or '' when none. */
110
- private latestRunWorktree;
111
- private internalActiveRuns;
112
- }
113
- export declare function fakeRemote(seed?: FakeRemoteSeed): RemotePlugin;