@gaia-ai/core 0.4.7 → 0.5.3
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/src/index.d.ts +2 -1
- package/dist/src/index.js +1 -0
- package/dist/src/plugins/plugins.d.ts +19 -3
- package/dist/src/plugins/plugins.js +29 -3
- package/dist/src/plugins/registry-exports.d.ts +1 -1
- package/dist/src/plugins/registry-exports.js +1 -1
- package/dist/src/plugins/remote/drupal.js +27 -4
- package/dist/src/plugins/remote/fake.d.ts +10 -0
- package/dist/src/plugins/remote/fake.js +8 -0
- package/dist/src/plugins/remote/remote.d.ts +11 -0
- package/dist/src/types.d.ts +3 -2
- package/package.json +1 -1
package/dist/src/index.d.ts
CHANGED
|
@@ -5,7 +5,8 @@ export { shellQuote } from './core/shell.js';
|
|
|
5
5
|
export { DEFAULT_SLUG_MAX_LENGTH, slugify } from './core/slug.js';
|
|
6
6
|
export { type AgentFootprint, emptyAgentFootprint, type GaiaAgent, } from './plugins/agent/agent.js';
|
|
7
7
|
export type * from './plugins/executor/executor.js';
|
|
8
|
-
export type { AgentPlugin, ExecutorDeps, ExecutorPlugin, RemotePlugin, WorkspacePlugin, } from './plugins/plugins.js';
|
|
8
|
+
export type { AgentCandidate, AgentPlugin, ExecutorDeps, ExecutorPlugin, RemotePlugin, ResolvedAgent, WorkspacePlugin, } from './plugins/plugins.js';
|
|
9
|
+
export { selectAgent, selectAgents } from './plugins/plugins.js';
|
|
9
10
|
export type * from './plugins/remote/remote.js';
|
|
10
11
|
export { loadInstructions } from './plugins/workspace/instructions.js';
|
|
11
12
|
export type { EnsuredWorkspace, GaiaWorkspace, } from './plugins/workspace/workspace.js';
|
package/dist/src/index.js
CHANGED
|
@@ -4,4 +4,5 @@ export { createLogger } from './core/logger.js';
|
|
|
4
4
|
export { shellQuote } from './core/shell.js';
|
|
5
5
|
export { DEFAULT_SLUG_MAX_LENGTH, slugify } from './core/slug.js';
|
|
6
6
|
export { emptyAgentFootprint, } from './plugins/agent/agent.js';
|
|
7
|
+
export { selectAgent, selectAgents } from './plugins/plugins.js';
|
|
7
8
|
export { loadInstructions } from './plugins/workspace/instructions.js';
|
|
@@ -3,7 +3,7 @@ import type { ConductorLogger } from '../core/logger.js';
|
|
|
3
3
|
import type { ConductorFileConfig } from '../types.js';
|
|
4
4
|
import type { GaiaAgent } from './agent/agent.js';
|
|
5
5
|
import type { GaiaExecutor } from './executor/executor.js';
|
|
6
|
-
import type { GaiaRemote } from './remote/remote.js';
|
|
6
|
+
import type { GaiaRemote, Ticket } from './remote/remote.js';
|
|
7
7
|
import type { GaiaWorkspace } from './workspace/workspace.js';
|
|
8
8
|
/** Runtime dependencies handed to a plugin factory at selection time. */
|
|
9
9
|
export interface ExecutorDeps {
|
|
@@ -40,5 +40,21 @@ export declare function selectRemote(config: ConductorFileConfig): Promise<GaiaR
|
|
|
40
40
|
export declare function selectExecutor(config: ConductorFileConfig, logger: ConductorLogger): Promise<GaiaExecutor>;
|
|
41
41
|
/** Resolves the workspace from its named config slot. */
|
|
42
42
|
export declare function selectWorkspace(config: ConductorFileConfig): Promise<GaiaWorkspace>;
|
|
43
|
-
/**
|
|
44
|
-
export
|
|
43
|
+
/** A config-side agent choice: an agent plugin + an optional static priority over the ticket. */
|
|
44
|
+
export interface AgentCandidate {
|
|
45
|
+
agent: AgentPlugin;
|
|
46
|
+
priority?: (ticket: Ticket) => number;
|
|
47
|
+
}
|
|
48
|
+
/** A constructed candidate: the agent id, its GaiaAgent, and the config-side priority. */
|
|
49
|
+
export interface ResolvedAgent {
|
|
50
|
+
id: string;
|
|
51
|
+
agent: GaiaAgent;
|
|
52
|
+
priority?: (ticket: Ticket) => number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Pick the agent for a ticket: highest `priority(ticket)` wins; a candidate with
|
|
56
|
+
* no `priority` scores lowest; ties resolve by config array order (stable sort).
|
|
57
|
+
*/
|
|
58
|
+
export declare function selectAgent(candidates: ResolvedAgent[], ticket: Ticket): ResolvedAgent;
|
|
59
|
+
/** Constructs every agent candidate from the resolved config slot (single or array). */
|
|
60
|
+
export declare function selectAgents(config: ConductorFileConfig): Promise<ResolvedAgent[]>;
|
|
@@ -10,7 +10,33 @@ export async function selectExecutor(config, logger) {
|
|
|
10
10
|
export async function selectWorkspace(config) {
|
|
11
11
|
return config.workspace.createWorkspace(config);
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
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
|
+
})));
|
|
16
42
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { basicAuthProvider } from './auth/basic.js';
|
|
2
|
-
export { selectAgent, selectExecutor, selectRemote, selectWorkspace, } from './plugins.js';
|
|
2
|
+
export { type AgentCandidate, type ResolvedAgent, selectAgent, selectAgents, selectExecutor, selectRemote, selectWorkspace, } from './plugins.js';
|
|
3
3
|
export { DrupalGaiaRemote, drupalRemote } from './remote/drupal.js';
|
|
4
4
|
export { FakeGaiaRemote, type FakeRemoteSeed, fakeRemote, } from './remote/fake.js';
|
|
5
5
|
export { FakeWorkspace, fakeWorkspace } from './workspace/fake.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { basicAuthProvider } from './auth/basic.js';
|
|
2
|
-
export { selectAgent, selectExecutor, selectRemote, selectWorkspace, } from './plugins.js';
|
|
2
|
+
export { selectAgent, selectAgents, selectExecutor, selectRemote, selectWorkspace, } from './plugins.js';
|
|
3
3
|
export { DrupalGaiaRemote, drupalRemote } from './remote/drupal.js';
|
|
4
4
|
export { FakeGaiaRemote, fakeRemote, } from './remote/fake.js';
|
|
5
5
|
export { FakeWorkspace, fakeWorkspace } from './workspace/fake.js';
|
|
@@ -68,11 +68,30 @@ export class DrupalGaiaRemote {
|
|
|
68
68
|
}
|
|
69
69
|
async getTicket(uuid) {
|
|
70
70
|
// The agent self-reads the ticket + its comments at run start (GAIA-112),
|
|
71
|
-
// so getTicket
|
|
72
|
-
//
|
|
73
|
-
|
|
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`));
|
|
74
75
|
const attrs = doc.data?.attributes ?? {};
|
|
75
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 !== '');
|
|
76
95
|
return {
|
|
77
96
|
uuid: doc.data?.id ?? uuid,
|
|
78
97
|
identifier: typeof attrs.identifier === 'string' ? attrs.identifier : uuid,
|
|
@@ -87,6 +106,8 @@ export class DrupalGaiaRemote {
|
|
|
87
106
|
? { effectiveEnvVars: attrs.effective_env_vars }
|
|
88
107
|
: {}),
|
|
89
108
|
...(issueUrl ? { issueUrl } : {}),
|
|
109
|
+
labels,
|
|
110
|
+
environments,
|
|
90
111
|
};
|
|
91
112
|
}
|
|
92
113
|
async getRunWorktree(uuid) {
|
|
@@ -195,6 +216,7 @@ export class DrupalGaiaRemote {
|
|
|
195
216
|
state: 'running',
|
|
196
217
|
heartbeat: t,
|
|
197
218
|
...(attrs?.worktree_path ? { worktree_path: attrs.worktree_path } : {}),
|
|
219
|
+
...(attrs?.agent ? { agent: attrs.agent } : {}),
|
|
198
220
|
},
|
|
199
221
|
});
|
|
200
222
|
}
|
|
@@ -204,7 +226,7 @@ export class DrupalGaiaRemote {
|
|
|
204
226
|
.where('conductor_id.machine_id', '=', id)
|
|
205
227
|
.where('state', '=', 'done')
|
|
206
228
|
.where('closed', '=', '0')
|
|
207
|
-
.fields(['worktree_path', 'started_at'])
|
|
229
|
+
.fields(['worktree_path', 'started_at', 'agent'])
|
|
208
230
|
.page(100)
|
|
209
231
|
.list();
|
|
210
232
|
return rows.map((r) => {
|
|
@@ -213,6 +235,7 @@ export class DrupalGaiaRemote {
|
|
|
213
235
|
runUuid: r.id,
|
|
214
236
|
worktreePath: r.attr('worktree_path') ?? '',
|
|
215
237
|
...(typeof startedAt === 'number' ? { startedAt } : {}),
|
|
238
|
+
agent: r.attr('agent') ?? '',
|
|
216
239
|
};
|
|
217
240
|
});
|
|
218
241
|
}
|
|
@@ -13,6 +13,8 @@ export interface FakeSeedRun {
|
|
|
13
13
|
closed?: boolean;
|
|
14
14
|
/** Unix timestamp (seconds) the run started — drives footprint duration_s. */
|
|
15
15
|
startedAt?: number;
|
|
16
|
+
/** Id of the agent that ran (GAIA-144) — routes footprint parsing at finalize. */
|
|
17
|
+
agent?: string;
|
|
16
18
|
}
|
|
17
19
|
export interface FakeSeedTicket {
|
|
18
20
|
identifier: string;
|
|
@@ -25,6 +27,13 @@ export interface FakeSeedTicket {
|
|
|
25
27
|
effectiveEnvVars?: string;
|
|
26
28
|
url?: string;
|
|
27
29
|
workflow?: string;
|
|
30
|
+
/** Label term names sideloaded for agent selection (GAIA-144). */
|
|
31
|
+
labels?: string[];
|
|
32
|
+
/** Environments sideloaded for agent selection (GAIA-144). */
|
|
33
|
+
environments?: {
|
|
34
|
+
name: string;
|
|
35
|
+
tier: string;
|
|
36
|
+
}[];
|
|
28
37
|
/** Whether the ticket is already closed (its lifecycle wound up). */
|
|
29
38
|
closed?: boolean;
|
|
30
39
|
/** Whether the ticket's worktree is already torn down (cleaned_up flag). */
|
|
@@ -44,6 +53,7 @@ export interface MarkRunningCall {
|
|
|
44
53
|
runUuid: string;
|
|
45
54
|
attrs?: {
|
|
46
55
|
worktree_path?: string;
|
|
56
|
+
agent?: string;
|
|
47
57
|
};
|
|
48
58
|
}
|
|
49
59
|
export interface FinalizeRunCall {
|
|
@@ -34,6 +34,7 @@ export class FakeGaiaRemote {
|
|
|
34
34
|
? { worktreePath: r.worktreePath }
|
|
35
35
|
: {}),
|
|
36
36
|
...(r.startedAt !== undefined ? { startedAt: r.startedAt } : {}),
|
|
37
|
+
...(r.agent !== undefined ? { agent: r.agent } : {}),
|
|
37
38
|
...(r.closed !== undefined ? { closed: r.closed } : {}),
|
|
38
39
|
});
|
|
39
40
|
this.queue.push({
|
|
@@ -104,6 +105,8 @@ export class FakeGaiaRemote {
|
|
|
104
105
|
...(t.baseBranch ? { baseBranch: t.baseBranch } : {}),
|
|
105
106
|
...(t.effectiveEnvVars ? { effectiveEnvVars: t.effectiveEnvVars } : {}),
|
|
106
107
|
...(t.url ? { issueUrl: t.url } : {}),
|
|
108
|
+
labels: t.labels ?? [],
|
|
109
|
+
environments: t.environments ?? [],
|
|
107
110
|
};
|
|
108
111
|
}
|
|
109
112
|
async getRunWorktree(runUuid) {
|
|
@@ -136,6 +139,7 @@ export class FakeGaiaRemote {
|
|
|
136
139
|
...(attrs.worktree_path
|
|
137
140
|
? { worktree_path: attrs.worktree_path }
|
|
138
141
|
: {}),
|
|
142
|
+
...(attrs.agent ? { agent: attrs.agent } : {}),
|
|
139
143
|
},
|
|
140
144
|
}
|
|
141
145
|
: {}),
|
|
@@ -146,6 +150,9 @@ export class FakeGaiaRemote {
|
|
|
146
150
|
if (attrs?.worktree_path) {
|
|
147
151
|
run.worktreePath = attrs.worktree_path;
|
|
148
152
|
}
|
|
153
|
+
if (attrs?.agent) {
|
|
154
|
+
run.agent = attrs.agent;
|
|
155
|
+
}
|
|
149
156
|
}
|
|
150
157
|
}
|
|
151
158
|
async fetchFinalizableRuns(_conductorId) {
|
|
@@ -155,6 +162,7 @@ export class FakeGaiaRemote {
|
|
|
155
162
|
runUuid: r.runUuid,
|
|
156
163
|
worktreePath: r.worktreePath ?? '',
|
|
157
164
|
...(r.startedAt !== undefined ? { startedAt: r.startedAt } : {}),
|
|
165
|
+
agent: r.agent ?? '',
|
|
158
166
|
}));
|
|
159
167
|
}
|
|
160
168
|
async finalizeRun(runUuid, log, metrics) {
|
|
@@ -34,6 +34,13 @@ export interface Ticket {
|
|
|
34
34
|
*/
|
|
35
35
|
effectiveEnvVars?: string;
|
|
36
36
|
issueUrl?: string;
|
|
37
|
+
/** Ticket label term names (gaia_labels vocab), sideloaded for agent selection. Empty when none. */
|
|
38
|
+
labels: string[];
|
|
39
|
+
/** Ticket environments (gaia_environment refs), each as name + tier, sideloaded for agent selection. Empty when none. */
|
|
40
|
+
environments: {
|
|
41
|
+
name: string;
|
|
42
|
+
tier: string;
|
|
43
|
+
}[];
|
|
37
44
|
}
|
|
38
45
|
export interface ActiveRun {
|
|
39
46
|
runUuid: string;
|
|
@@ -63,6 +70,8 @@ export interface FinalizableRun {
|
|
|
63
70
|
* (GAIA-132).
|
|
64
71
|
*/
|
|
65
72
|
startedAt?: number;
|
|
73
|
+
/** Id of the agent that ran (GAIA-144); routes footprint parsing at finalize. '' / undefined when unset. */
|
|
74
|
+
agent?: string;
|
|
66
75
|
}
|
|
67
76
|
/**
|
|
68
77
|
* Per-run effort footprint the conductor writes onto `gaia_run` at close
|
|
@@ -103,6 +112,8 @@ export interface UncleanTicket {
|
|
|
103
112
|
export interface RunWriteAttributes {
|
|
104
113
|
/** Absolute path of the per-run git worktree (set by the conductor). */
|
|
105
114
|
worktree_path?: string;
|
|
115
|
+
/** Id of the agent the conductor chose for this run (GAIA-144), for footprint routing. */
|
|
116
|
+
agent?: string;
|
|
106
117
|
}
|
|
107
118
|
export interface GaiaRemote {
|
|
108
119
|
/** Upserts by machine_id; sets status=online. Returns the Drupal entity uuid. */
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DropSHPlugin } from 'dropsh/plugin';
|
|
2
|
-
import type {
|
|
2
|
+
import type { AgentCandidate, ExecutorPlugin, RemotePlugin, WorkspacePlugin } from './plugins/plugins.js';
|
|
3
3
|
/** Normalized single conductor. */
|
|
4
4
|
export interface ConductorSettings {
|
|
5
5
|
/** Default: `${project} @ ${checkoutRoot}`. */
|
|
@@ -44,7 +44,8 @@ export interface ConductorSettings {
|
|
|
44
44
|
export interface ConductorFileConfig extends ConductorSettings {
|
|
45
45
|
remote: RemotePlugin;
|
|
46
46
|
executor: ExecutorPlugin;
|
|
47
|
-
agent:
|
|
47
|
+
/** The agent slot: a single candidate or an array of candidates (shape preserved as authored). Selection normalizes + picks one per dispatch. */
|
|
48
|
+
agent: AgentCandidate | AgentCandidate[];
|
|
48
49
|
workspace: WorkspacePlugin;
|
|
49
50
|
/** dropsh-layer plugins (auth etc.); separate from the GAIA slots. */
|
|
50
51
|
plugins?: DropSHPlugin[];
|