@gaia-ai/core 0.5.3 → 0.5.5
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/core/exec.js +7 -1
- package/dist/src/plugins/agent/agent.d.ts +11 -2
- package/dist/src/plugins/agent/agent.js +1 -0
- package/dist/src/plugins/executor/executor.d.ts +23 -11
- package/dist/src/plugins/remote/drupal.d.ts +1 -0
- package/dist/src/plugins/remote/drupal.js +22 -10
- package/dist/src/plugins/remote/fake.d.ts +6 -2
- package/dist/src/plugins/remote/fake.js +10 -2
- package/dist/src/plugins/remote/remote.d.ts +15 -8
- package/package.json +1 -1
package/dist/src/core/exec.js
CHANGED
|
@@ -7,7 +7,13 @@ export class ExecError extends Error {
|
|
|
7
7
|
exitCode;
|
|
8
8
|
stderr;
|
|
9
9
|
constructor(file, args, exitCode, stderr, options) {
|
|
10
|
-
|
|
10
|
+
// Include exitCode + stderr in the message so a stringified ExecError
|
|
11
|
+
// (e.g. gaia_run.error_log = String(err)) carries the ACTUAL cause, not a
|
|
12
|
+
// bare "exec failed: herdr". Args are omitted on purpose — they can embed
|
|
13
|
+
// secrets (herdr's `KEY='value' <agent>` pane-run string, GAIA-99); stderr
|
|
14
|
+
// is the tool's own diagnostic output and is trimmed.
|
|
15
|
+
const detail = stderr.trim();
|
|
16
|
+
super(`exec failed: ${file} (exit ${exitCode ?? 'null'})${detail ? `: ${detail}` : ''}`, options);
|
|
11
17
|
this.file = file;
|
|
12
18
|
this.args = args;
|
|
13
19
|
this.exitCode = exitCode;
|
|
@@ -3,12 +3,21 @@
|
|
|
3
3
|
* (GAIA-132). The metrics are transcript-derived and therefore
|
|
4
4
|
* **agent-specific** (each agent's transcript has its own format), so parsing
|
|
5
5
|
* lives behind the agent abstraction (see {@link GaiaAgent.parseFootprint}) —
|
|
6
|
-
* the conductor stays agent-agnostic and
|
|
7
|
-
*
|
|
6
|
+
* the conductor stays agent-agnostic and writes the footprint verbatim.
|
|
7
|
+
* `duration_s` is likewise derived from the transcript (its first→last entry
|
|
8
|
+
* timestamps), NOT recomputed by the conductor from a re-read `started_at`
|
|
9
|
+
* (GAIA-151): the log is the single source of the run's wall-clock length, so
|
|
10
|
+
* there is no timestamp round-trip through JSON:API to mis-parse.
|
|
8
11
|
*/
|
|
9
12
|
export interface AgentFootprint {
|
|
10
13
|
/** Total tokens across every usage bucket of every assistant turn. */
|
|
11
14
|
tokens: number;
|
|
15
|
+
/**
|
|
16
|
+
* Wall-clock run length in seconds, derived from the transcript's first→last
|
|
17
|
+
* entry timestamps (GAIA-151). 0 when the transcript has fewer than two
|
|
18
|
+
* timestamped entries (empty/absent log).
|
|
19
|
+
*/
|
|
20
|
+
duration_s: number;
|
|
12
21
|
/** Number of assistant turns in the transcript. */
|
|
13
22
|
agent_turns: number;
|
|
14
23
|
/** Number of tool-use calls across all assistant turns. */
|
|
@@ -55,19 +55,31 @@ export interface GaiaExecutor {
|
|
|
55
55
|
*/
|
|
56
56
|
runHook(name: HookName, cwd: string, ctx: HookContext, env?: Record<string, string>): Promise<void>;
|
|
57
57
|
startRun(input: SpawnRunInput): Promise<SpawnedSession>;
|
|
58
|
-
/** Send C-c to every open (non-`(done)`) tab in the branch workspace and
|
|
59
|
-
* append ` (done)` to its label. Best-effort, non-destructive: tabs stay open
|
|
60
|
-
* for the human to close. */
|
|
61
|
-
retire(branch: string): Promise<void>;
|
|
62
58
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
59
|
+
* Signal the run's agent to stop. Called by the conductor's run-finalise pass
|
|
60
|
+
* BEFORE it captures the agent transcript (GAIA-132), so a stop must not
|
|
61
|
+
* destroy the log. For herdr this is a NO-OP: at finalise time the agent is
|
|
62
|
+
* idle and its jsonl transcript is already fully written to disk, and the
|
|
63
|
+
* subsequent {@link cleanupRun} tab-close kills the PTY (the agent dies with
|
|
64
|
+
* it). The seam exists for executors whose agent outlives its UI surface.
|
|
65
|
+
* Best-effort; gated by the conductor on `capabilities().persistent`.
|
|
69
66
|
*/
|
|
70
|
-
|
|
67
|
+
stopRun(branch: string): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Tear down ONE run's hosted UI surface — for herdr, close only the tab whose
|
|
70
|
+
* label carries the `#<runId>` token (`startRun` labels every tab
|
|
71
|
+
* `<identifier> · <state> #<run.id>`), via `herdr tab close <tab_id>` (killing
|
|
72
|
+
* that PTY). Run-scoped by design (GAIA-183): a sibling run's tab in the same
|
|
73
|
+
* branch workspace — a still-open prior-state run or a concurrent re-claim —
|
|
74
|
+
* and any non-run tab are left untouched. Called as the LAST finalisation step,
|
|
75
|
+
* strictly after the transcript is captured, and again at dispatch to clear a
|
|
76
|
+
* reused workspace's leftover tab for the run being (re-)started. No matching
|
|
77
|
+
* tab → a quiet no-op. Does NOT touch the branch worktree (that is the
|
|
78
|
+
* reap/{@link removeWorktree} lifecycle). Best-effort: a failing tab-close is
|
|
79
|
+
* swallowed and never aborts finalisation or dispatch. A no-op for
|
|
80
|
+
* non-persistent executors.
|
|
81
|
+
*/
|
|
82
|
+
cleanupRun(branch: string, runId: number): Promise<void>;
|
|
71
83
|
/**
|
|
72
84
|
* Tear down the branch's entire worktree (git worktree + hosted workspace),
|
|
73
85
|
* reclaiming its disk. Called by the conductor's ticket-cleanup pass once a
|
|
@@ -17,6 +17,7 @@ export declare class DrupalGaiaRemote implements GaiaRemote {
|
|
|
17
17
|
setConductorStatus(id: string, status: string): Promise<void>;
|
|
18
18
|
listConductors(owner?: 'me'): Promise<ConductorStatus[]>;
|
|
19
19
|
markRunning(uuid: string, attrs?: RunWriteAttributes): Promise<void>;
|
|
20
|
+
markFailed(uuid: string, errorLog: string): Promise<void>;
|
|
20
21
|
fetchFinalizableRuns(id: string): Promise<FinalizableRun[]>;
|
|
21
22
|
finalizeRun(uuid: string, log: string, metrics?: RunMetrics): Promise<void>;
|
|
22
23
|
fetchUncleanedTickets(id: string): Promise<UncleanTicket[]>;
|
|
@@ -220,24 +220,36 @@ export class DrupalGaiaRemote {
|
|
|
220
220
|
},
|
|
221
221
|
});
|
|
222
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
|
+
}
|
|
223
234
|
async fetchFinalizableRuns(id) {
|
|
224
235
|
const rows = await this.api
|
|
225
236
|
.collection('gaia_run')
|
|
226
237
|
.where('conductor_id.machine_id', '=', id)
|
|
227
238
|
.where('state', '=', 'done')
|
|
228
239
|
.where('closed', '=', '0')
|
|
229
|
-
.fields(['worktree_path', '
|
|
240
|
+
.fields(['worktree_path', 'agent', 'drupal_internal__id'])
|
|
230
241
|
.page(100)
|
|
231
242
|
.list();
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
+
}));
|
|
241
253
|
}
|
|
242
254
|
async finalizeRun(uuid, log, metrics) {
|
|
243
255
|
const t = Math.floor(Date.now() / 1000);
|
|
@@ -11,8 +11,6 @@ export interface FakeSeedRun {
|
|
|
11
11
|
handler?: string;
|
|
12
12
|
worktreePath?: string;
|
|
13
13
|
closed?: boolean;
|
|
14
|
-
/** Unix timestamp (seconds) the run started — drives footprint duration_s. */
|
|
15
|
-
startedAt?: number;
|
|
16
14
|
/** Id of the agent that ran (GAIA-144) — routes footprint parsing at finalize. */
|
|
17
15
|
agent?: string;
|
|
18
16
|
}
|
|
@@ -61,9 +59,14 @@ export interface FinalizeRunCall {
|
|
|
61
59
|
log: string;
|
|
62
60
|
metrics?: RunMetrics;
|
|
63
61
|
}
|
|
62
|
+
export interface MarkFailedCall {
|
|
63
|
+
runUuid: string;
|
|
64
|
+
errorLog: string;
|
|
65
|
+
}
|
|
64
66
|
export declare class FakeGaiaRemote implements GaiaRemote {
|
|
65
67
|
readonly calls: {
|
|
66
68
|
markRunning: MarkRunningCall[];
|
|
69
|
+
markFailed: MarkFailedCall[];
|
|
67
70
|
finalizeRun: FinalizeRunCall[];
|
|
68
71
|
closeTicket: string[];
|
|
69
72
|
markCleanedUp: string[];
|
|
@@ -93,6 +96,7 @@ export declare class FakeGaiaRemote implements GaiaRemote {
|
|
|
93
96
|
getRunTicketIdentifier(runUuid: string): Promise<string>;
|
|
94
97
|
getRunTicketBranchName(runUuid: string): Promise<string>;
|
|
95
98
|
markRunning(runUuid: string, attrs?: RunWriteAttributes): Promise<void>;
|
|
99
|
+
markFailed(runUuid: string, errorLog: string): Promise<void>;
|
|
96
100
|
fetchFinalizableRuns(_conductorId: string): Promise<FinalizableRun[]>;
|
|
97
101
|
finalizeRun(runUuid: string, log: string, metrics?: RunMetrics): Promise<void>;
|
|
98
102
|
fetchUncleanedTickets(conductorId: string): Promise<UncleanTicket[]>;
|
|
@@ -2,6 +2,7 @@ const ACTIVE = ['claimed', 'running'];
|
|
|
2
2
|
export class FakeGaiaRemote {
|
|
3
3
|
calls = {
|
|
4
4
|
markRunning: [],
|
|
5
|
+
markFailed: [],
|
|
5
6
|
finalizeRun: [],
|
|
6
7
|
closeTicket: [],
|
|
7
8
|
markCleanedUp: [],
|
|
@@ -33,7 +34,6 @@ export class FakeGaiaRemote {
|
|
|
33
34
|
...(r.worktreePath !== undefined
|
|
34
35
|
? { worktreePath: r.worktreePath }
|
|
35
36
|
: {}),
|
|
36
|
-
...(r.startedAt !== undefined ? { startedAt: r.startedAt } : {}),
|
|
37
37
|
...(r.agent !== undefined ? { agent: r.agent } : {}),
|
|
38
38
|
...(r.closed !== undefined ? { closed: r.closed } : {}),
|
|
39
39
|
});
|
|
@@ -155,13 +155,21 @@ export class FakeGaiaRemote {
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
|
+
async markFailed(runUuid, errorLog) {
|
|
159
|
+
this.calls.markFailed.push({ runUuid, errorLog });
|
|
160
|
+
const run = this.runs.get(runUuid);
|
|
161
|
+
if (run) {
|
|
162
|
+
run.state = 'failed';
|
|
163
|
+
run.closed = true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
158
166
|
async fetchFinalizableRuns(_conductorId) {
|
|
159
167
|
return [...this.runs.values()]
|
|
160
168
|
.filter((r) => r.state === 'done' && !r.closed)
|
|
161
169
|
.map((r) => ({
|
|
162
170
|
runUuid: r.runUuid,
|
|
171
|
+
runId: r.runId,
|
|
163
172
|
worktreePath: r.worktreePath ?? '',
|
|
164
|
-
...(r.startedAt !== undefined ? { startedAt: r.startedAt } : {}),
|
|
165
173
|
agent: r.agent ?? '',
|
|
166
174
|
}));
|
|
167
175
|
}
|
|
@@ -62,21 +62,18 @@ export interface ConductorStatus {
|
|
|
62
62
|
}
|
|
63
63
|
export interface FinalizableRun {
|
|
64
64
|
runUuid: string;
|
|
65
|
+
/** Numeric run id (the `#<runId>` tab-label token); scopes the run's tab-close at finalise (GAIA-183). */
|
|
66
|
+
runId: number;
|
|
65
67
|
/** Absolute path of the per-run git worktree, or '' when unset. */
|
|
66
68
|
worktreePath: string;
|
|
67
|
-
/**
|
|
68
|
-
* Unix timestamp (seconds) the run started, or undefined when unset. The
|
|
69
|
-
* conductor derives the footprint `duration_s` as `now − startedAt` at close
|
|
70
|
-
* (GAIA-132).
|
|
71
|
-
*/
|
|
72
|
-
startedAt?: number;
|
|
73
69
|
/** Id of the agent that ran (GAIA-144); routes footprint parsing at finalize. '' / undefined when unset. */
|
|
74
70
|
agent?: string;
|
|
75
71
|
}
|
|
76
72
|
/**
|
|
77
73
|
* Per-run effort footprint the conductor writes onto `gaia_run` at close
|
|
78
|
-
* (GAIA-132). Six integer metrics
|
|
79
|
-
*
|
|
74
|
+
* (GAIA-132). Six integer metrics, all parsed from the agent transcript —
|
|
75
|
+
* including `duration_s`, the run's wall-clock length derived from the
|
|
76
|
+
* transcript's first→last timestamps (GAIA-151), not a re-read `started_at`.
|
|
80
77
|
*/
|
|
81
78
|
export interface RunMetrics {
|
|
82
79
|
tokens: number;
|
|
@@ -147,6 +144,16 @@ export interface GaiaRemote {
|
|
|
147
144
|
*/
|
|
148
145
|
getRunTicketBranchName(runUuid: string): Promise<string>;
|
|
149
146
|
markRunning(runUuid: string, attrs?: RunWriteAttributes): Promise<void>;
|
|
147
|
+
/**
|
|
148
|
+
* Terminalise a run to the `failed` state on a dispatch/setup error (GAIA-149).
|
|
149
|
+
* PATCHes state=failed, records the failure detail in `error_log`, and closes
|
|
150
|
+
* the run (closed + closed_date). `failed` is a terminal sibling of `expired`
|
|
151
|
+
* (dispatch error vs lease lapse), so the run stops counting toward capacity
|
|
152
|
+
* and its ticket is no longer blocked by the single-active-run gate — instead
|
|
153
|
+
* of the old "leave it `claimed` to expire after ~300s" silent stall. The
|
|
154
|
+
* failure is visible immediately and its cause is recorded.
|
|
155
|
+
*/
|
|
156
|
+
markFailed(runUuid: string, errorLog: string): Promise<void>;
|
|
150
157
|
/**
|
|
151
158
|
* Runs this conductor owns that are state=done but not yet closed — the
|
|
152
159
|
* conductor's one-shot finalisation work list.
|