@gaia-ai/addon-herdr 0.6.3 → 0.6.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/agents.js +6 -2
- package/dist/src/index.d.ts +8 -2
- package/dist/src/index.js +26 -5
- package/dist/src/pane-layout.d.ts +8 -0
- package/dist/src/pane-layout.js +9 -0
- package/dist/src/panes.d.ts +12 -0
- package/dist/src/panes.js +14 -0
- package/dist/src/workspace.d.ts +35 -5
- package/dist/src/workspace.js +69 -31
- package/package.json +4 -4
package/dist/src/agents.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { envArgs, findTabIdByLabel, parsePaneInfo, parsePanesInTab, parseTabCreate, } from './panes.js';
|
|
1
|
+
import { AGENT_SHELL_ENV, envArgs, findTabIdByLabel, parsePaneInfo, parsePanesInTab, parseTabCreate, } from './panes.js';
|
|
2
2
|
/** The single tab that holds a group's agent panes. */
|
|
3
3
|
const groupTabLabel = (group) => `gaia-agents:${group}`;
|
|
4
4
|
export function herdrAgentHost(execHerdr) {
|
|
@@ -31,7 +31,11 @@ export function herdrAgentHost(execHerdr) {
|
|
|
31
31
|
return {
|
|
32
32
|
async launch({ group, cwd, command, env, label }) {
|
|
33
33
|
await captureHome();
|
|
34
|
-
|
|
34
|
+
// Every pane this host opens holds an agent, so all of them carry the
|
|
35
|
+
// agent-shell marker (GAIA-64) — this is the pane a `gaia ui` user actually
|
|
36
|
+
// attaches to. The caller's own env wins on a key clash; the marker is
|
|
37
|
+
// merged in, never substituted for it.
|
|
38
|
+
const e = { ...AGENT_SHELL_ENV, ...(env ?? {}) };
|
|
35
39
|
// Find the group's tab and split off a LIVE pane (newest-first; a stale
|
|
36
40
|
// pane — a previous agent died/was closed — is skipped). None survive, or
|
|
37
41
|
// no tab yet → create the group tab (its root pane is the agent).
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExecutorCapabilities, ExecutorPlugin, GaiaExecutor, HookContext, HookName, SpawnedSession, SpawnRunInput } from '@gaia-ai/conductor/contract';
|
|
1
|
+
import type { ExecutorCapabilities, ExecutorPlugin, GaiaExecutor, HookContext, HookName, HookResult, SpawnedSession, SpawnRunInput } from '@gaia-ai/conductor/contract';
|
|
2
2
|
import type { ConductorLogger } from '@gaia-ai/core';
|
|
3
3
|
import { type HerdrExecutorOptions } from './config.js';
|
|
4
4
|
import { type HerdrExec } from './pane-layout.js';
|
|
@@ -26,8 +26,14 @@ export declare class HerdrExecutor implements GaiaExecutor {
|
|
|
26
26
|
* silent no-op. A failing command → log loudly (hook + worktree + ticket +
|
|
27
27
|
* err) and return. NEVER throws, so a hook failure can't abort dispatch or
|
|
28
28
|
* wedge a run.
|
|
29
|
+
*
|
|
30
|
+
* GAIA-237: it also RETURNS what happened ({@link HookResult}). Not throwing
|
|
31
|
+
* kept a hook from wedging a run, but swallowing the outcome entirely left the
|
|
32
|
+
* caller unable to tell a provisioned worktree from a broken one — so an agent
|
|
33
|
+
* was dispatched into a worktree whose bootstrap had aborted, with nothing but
|
|
34
|
+
* a log line (which, on a TTY, went nowhere) to say so.
|
|
29
35
|
*/
|
|
30
|
-
runHook(name: HookName, cwd: string, ctx: HookContext, env?: Record<string, string>): Promise<
|
|
36
|
+
runHook(name: HookName, cwd: string, ctx: HookContext, env?: Record<string, string>): Promise<HookResult>;
|
|
31
37
|
/**
|
|
32
38
|
* List worktrees, ALWAYS anchored to the configured parent repo root
|
|
33
39
|
* (GAIA-211). `herdr worktree list` is machine-global, but run WITHOUT
|
package/dist/src/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { exec, shellQuote, slugify } from '@gaia-ai/core';
|
|
|
3
3
|
import { resolveStateConfig, } from './config.js';
|
|
4
4
|
import { forceRemoveDir, restoreWritable } from './fs.js';
|
|
5
5
|
import { applyPaneLayout, isRecord, parseJson, } from './pane-layout.js';
|
|
6
|
+
import { AGENT_SHELL_ENV, envArgs } from './panes.js';
|
|
6
7
|
import { deriveHerdrRoot } from './root-anchor.js';
|
|
7
8
|
export { herdrAgentHost } from './agents.js';
|
|
8
9
|
export { envArgs, findTabIdByLabel, parsePaneInfo, parsePanesInTab, parseTabCreate, } from './panes.js';
|
|
@@ -217,17 +218,25 @@ export class HerdrExecutor {
|
|
|
217
218
|
* silent no-op. A failing command → log loudly (hook + worktree + ticket +
|
|
218
219
|
* err) and return. NEVER throws, so a hook failure can't abort dispatch or
|
|
219
220
|
* wedge a run.
|
|
221
|
+
*
|
|
222
|
+
* GAIA-237: it also RETURNS what happened ({@link HookResult}). Not throwing
|
|
223
|
+
* kept a hook from wedging a run, but swallowing the outcome entirely left the
|
|
224
|
+
* caller unable to tell a provisioned worktree from a broken one — so an agent
|
|
225
|
+
* was dispatched into a worktree whose bootstrap had aborted, with nothing but
|
|
226
|
+
* a log line (which, on a TTY, went nowhere) to say so.
|
|
220
227
|
*/
|
|
221
228
|
async runHook(name, cwd, ctx, env) {
|
|
222
229
|
const command = this.options.hooks?.[name];
|
|
223
230
|
if (!command) {
|
|
224
|
-
return;
|
|
231
|
+
return { hook: name, ran: false, ok: true };
|
|
225
232
|
}
|
|
226
233
|
try {
|
|
227
234
|
await this.runShell(command, cwd, env);
|
|
235
|
+
return { hook: name, ran: true, ok: true };
|
|
228
236
|
}
|
|
229
237
|
catch (err) {
|
|
230
238
|
this.logger.error({ hook: name, worktree: cwd, ticket: ctx.ticket, err: String(err) }, 'lifecycle hook failed');
|
|
239
|
+
return { hook: name, ran: true, ok: false, error: String(err) };
|
|
231
240
|
}
|
|
232
241
|
}
|
|
233
242
|
/**
|
|
@@ -579,7 +588,12 @@ export class HerdrExecutor {
|
|
|
579
588
|
runUuid: input.run.uuid,
|
|
580
589
|
workspacePath: input.workspacePath,
|
|
581
590
|
};
|
|
582
|
-
// 3. Create tab — NO --json flag; label includes the run-id token
|
|
591
|
+
// 3. Create tab — NO --json flag; label includes the run-id token. The tab's
|
|
592
|
+
// root pane IS the agent pane, so it is created carrying the agent-shell
|
|
593
|
+
// marker: the interactive shell must see it at startup for the user's opt-in
|
|
594
|
+
// history isolation to fire (GAIA-64). The run's own env (GAIA-99) stays out
|
|
595
|
+
// of here — it travels as inline `KEY='value'` prefixes on the command, so no
|
|
596
|
+
// secret moves into the pane environment.
|
|
583
597
|
const { paneId: rootPaneId, tabId } = parseTabCreate(await this.execHerdr([
|
|
584
598
|
'tab',
|
|
585
599
|
'create',
|
|
@@ -588,6 +602,7 @@ export class HerdrExecutor {
|
|
|
588
602
|
'--label',
|
|
589
603
|
`${input.ticket.identifier} · ${input.ticket.state} #${input.run.id}`,
|
|
590
604
|
'--no-focus',
|
|
605
|
+
...envArgs(AGENT_SHELL_ENV),
|
|
591
606
|
]));
|
|
592
607
|
try {
|
|
593
608
|
// 4+5. Run the agent command in the root pane, then split each configured
|
|
@@ -595,10 +610,16 @@ export class HerdrExecutor {
|
|
|
595
610
|
// open pane). The root command embeds `KEY='value'` env prefixes whose
|
|
596
611
|
// values may be secret (GAIA-99); pass a redactor so a failed pane-run
|
|
597
612
|
// exec-log carries key names, never values (only when env is present).
|
|
613
|
+
// The layout panes belong to the run's tab too, so they are marked as
|
|
614
|
+
// agent shells as well — the executor opts in, the workspace's open-layout
|
|
615
|
+
// caller does not (GAIA-64).
|
|
598
616
|
const hasEnv = Object.keys(input.env ?? {}).length > 0;
|
|
599
|
-
await applyPaneLayout(this.execHerdr, rootPaneId, { command: this.commandFor(input), panes: cfg.panes }, input.workspacePath, vars,
|
|
600
|
-
|
|
601
|
-
|
|
617
|
+
await applyPaneLayout(this.execHerdr, rootPaneId, { command: this.commandFor(input), panes: cfg.panes }, input.workspacePath, vars, {
|
|
618
|
+
env: AGENT_SHELL_ENV,
|
|
619
|
+
...(hasEnv
|
|
620
|
+
? { redactCommand: () => this.redactedCommandFor(input) }
|
|
621
|
+
: {}),
|
|
622
|
+
});
|
|
602
623
|
// 6. Return branch as sessionRef (for logging)
|
|
603
624
|
return { sessionRef: branchName };
|
|
604
625
|
}
|
|
@@ -31,7 +31,15 @@ export interface PaneLayout {
|
|
|
31
31
|
* `redactArgs` scrubber is passed so a failed `pane run` exec-log never carries a
|
|
32
32
|
* secret, while the command herdr actually runs stays real. An empty layout
|
|
33
33
|
* (`{}`) issues no calls at all — the caller's unconfigured default is preserved.
|
|
34
|
+
*
|
|
35
|
+
* `opts.env` is forwarded to each split pane as `--env` args (GAIA_* only, see
|
|
36
|
+
* {@link envArgs}). It is deliberately per-CALLER rather than baked in: this
|
|
37
|
+
* primitive serves both the executor's agent tab and the workspace's own open
|
|
38
|
+
* pane, and only the former's panes are agent-owned (GAIA-64). The ROOT pane is
|
|
39
|
+
* not touched — it already exists when we get here, so its environment came from
|
|
40
|
+
* whoever created it. Omitted ⇒ no `--env` at all.
|
|
34
41
|
*/
|
|
35
42
|
export declare function applyPaneLayout(execHerdr: HerdrExec, rootPaneId: string, layout: PaneLayout, cwd: string, vars: Record<string, string>, opts?: {
|
|
36
43
|
redactCommand?: (command: string) => string;
|
|
44
|
+
env?: Record<string, string | undefined>;
|
|
37
45
|
}): Promise<void>;
|
package/dist/src/pane-layout.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { renderTemplate } from './config.js';
|
|
2
|
+
import { envArgs } from './panes.js';
|
|
2
3
|
export function isRecord(value) {
|
|
3
4
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
4
5
|
}
|
|
@@ -33,6 +34,13 @@ export function parsePaneSplit(output) {
|
|
|
33
34
|
* `redactArgs` scrubber is passed so a failed `pane run` exec-log never carries a
|
|
34
35
|
* secret, while the command herdr actually runs stays real. An empty layout
|
|
35
36
|
* (`{}`) issues no calls at all — the caller's unconfigured default is preserved.
|
|
37
|
+
*
|
|
38
|
+
* `opts.env` is forwarded to each split pane as `--env` args (GAIA_* only, see
|
|
39
|
+
* {@link envArgs}). It is deliberately per-CALLER rather than baked in: this
|
|
40
|
+
* primitive serves both the executor's agent tab and the workspace's own open
|
|
41
|
+
* pane, and only the former's panes are agent-owned (GAIA-64). The ROOT pane is
|
|
42
|
+
* not touched — it already exists when we get here, so its environment came from
|
|
43
|
+
* whoever created it. Omitted ⇒ no `--env` at all.
|
|
36
44
|
*/
|
|
37
45
|
export async function applyPaneLayout(execHerdr, rootPaneId, layout, cwd, vars, opts) {
|
|
38
46
|
if (layout.command) {
|
|
@@ -58,6 +66,7 @@ export async function applyPaneLayout(execHerdr, rootPaneId, layout, cwd, vars,
|
|
|
58
66
|
'--cwd',
|
|
59
67
|
cwd,
|
|
60
68
|
spec.focus ? '--focus' : '--no-focus',
|
|
69
|
+
...envArgs(opts?.env ?? {}),
|
|
61
70
|
]));
|
|
62
71
|
await execHerdr([
|
|
63
72
|
'pane',
|
package/dist/src/panes.d.ts
CHANGED
|
@@ -7,6 +7,18 @@ export interface HerdrPaneRef {
|
|
|
7
7
|
export interface HerdrPaneInfo extends HerdrPaneRef {
|
|
8
8
|
label: string;
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Marker exported into an AGENT pane's interactive shell, so a user's opt-in
|
|
12
|
+
* shell init can recognise an agent-run pane at startup and isolate its history
|
|
13
|
+
* (GAIA-64). It is a plain `GAIA_*` env entry on purpose: it rides {@link envArgs}
|
|
14
|
+
* like every other pane var instead of adding a second mechanism.
|
|
15
|
+
*
|
|
16
|
+
* AGENT panes only — the executor's per-run tab and its layout panes, plus the
|
|
17
|
+
* TUI-hosted agents. NOT the workspace's own open-layout pane: that one is the
|
|
18
|
+
* human's landing pane, and the opt-in unsets `HISTFILE`. See
|
|
19
|
+
* `docs/conductor/node.md` § Agent-pane shell marker.
|
|
20
|
+
*/
|
|
21
|
+
export declare const AGENT_SHELL_ENV: Record<string, string>;
|
|
10
22
|
/** GAIA_* env vars as `--env KEY=VALUE` args (never dump the full environment). */
|
|
11
23
|
export declare function envArgs(env: Record<string, string | undefined>): string[];
|
|
12
24
|
/** `{ paneId, tabId }` from a `herdr tab create` envelope. */
|
package/dist/src/panes.js
CHANGED
|
@@ -4,6 +4,20 @@
|
|
|
4
4
|
function isRecord(v) {
|
|
5
5
|
return typeof v === 'object' && v !== null;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Marker exported into an AGENT pane's interactive shell, so a user's opt-in
|
|
9
|
+
* shell init can recognise an agent-run pane at startup and isolate its history
|
|
10
|
+
* (GAIA-64). It is a plain `GAIA_*` env entry on purpose: it rides {@link envArgs}
|
|
11
|
+
* like every other pane var instead of adding a second mechanism.
|
|
12
|
+
*
|
|
13
|
+
* AGENT panes only — the executor's per-run tab and its layout panes, plus the
|
|
14
|
+
* TUI-hosted agents. NOT the workspace's own open-layout pane: that one is the
|
|
15
|
+
* human's landing pane, and the opt-in unsets `HISTFILE`. See
|
|
16
|
+
* `docs/conductor/node.md` § Agent-pane shell marker.
|
|
17
|
+
*/
|
|
18
|
+
export const AGENT_SHELL_ENV = {
|
|
19
|
+
GAIA_AGENT_SHELL: '1',
|
|
20
|
+
};
|
|
7
21
|
/** GAIA_* env vars as `--env KEY=VALUE` args (never dump the full environment). */
|
|
8
22
|
export function envArgs(env) {
|
|
9
23
|
const out = [];
|
package/dist/src/workspace.d.ts
CHANGED
|
@@ -71,6 +71,11 @@ export declare class HerdrWorkspace implements GaiaWorkspace {
|
|
|
71
71
|
private readonly worktreeDir;
|
|
72
72
|
private readonly open;
|
|
73
73
|
private readonly logger;
|
|
74
|
+
/**
|
|
75
|
+
* Root pane ids captured by a FRESH `ensure` create, keyed by worktree path,
|
|
76
|
+
* awaiting {@link applyOpenLayout} (GAIA-237). One-shot: consumed on apply.
|
|
77
|
+
*/
|
|
78
|
+
private readonly pendingRootPanes;
|
|
74
79
|
/** Herdr-backed intent-level agent host for `gaia ui` (GAIA-190). */
|
|
75
80
|
readonly agentHost: AgentLaunchHost;
|
|
76
81
|
constructor(options: HerdrWorkspaceOptions);
|
|
@@ -92,12 +97,37 @@ export declare class HerdrWorkspace implements GaiaWorkspace {
|
|
|
92
97
|
*/
|
|
93
98
|
private verifyRemoteRef;
|
|
94
99
|
/**
|
|
95
|
-
* Ensure the branch's worktree + herdr workspace exist.
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
100
|
+
* Ensure the branch's worktree + herdr workspace exist.
|
|
101
|
+
*
|
|
102
|
+
* `_identifier` is unused HERE: the worktree is located by branch, and since
|
|
103
|
+
* GAIA-237 the open layout — the one thing that consumed the identifier, to
|
|
104
|
+
* render `{identifier}` (GAIA-234) — is applied by {@link applyOpenLayout},
|
|
105
|
+
* which takes it on its own `ctx`. The parameter stays because it is the
|
|
106
|
+
* `GaiaWorkspace.ensure` contract position (the git workspace does use it).
|
|
107
|
+
* It is still taken as-passed rather than parsed back out of `branch`, which
|
|
108
|
+
* remains the reason the contract carries it at all.
|
|
109
|
+
*/
|
|
110
|
+
ensure(_identifier: string, branch?: string, baseRefOverride?: string): Promise<EnsuredWorkspace>;
|
|
111
|
+
/**
|
|
112
|
+
* Apply the open layout to the root pane captured by the most recent
|
|
113
|
+
* {@link ensure} create for `path` — by default (GAIA-234) the ticket's
|
|
114
|
+
* `gaia ui` deep link, so the tab shows the ticket instead of a bare shell.
|
|
115
|
+
* The pane's cwd is the worktree checkout, passed explicitly on the splits
|
|
116
|
+
* (the root pane's cwd is already the checkout).
|
|
117
|
+
*
|
|
118
|
+
* NO stored pane ⇒ silent no-op. That is the reused-worktree case: `ensure`
|
|
119
|
+
* took the reuse branch, there is no fresh root pane, and re-applying would
|
|
120
|
+
* start a duplicate editor process on reattach (GAIA-139). The capture is
|
|
121
|
+
* one-shot — consumed here — so a repeat call cannot double-apply and the map
|
|
122
|
+
* cannot grow without bound.
|
|
123
|
+
*
|
|
124
|
+
* Best-effort — mirrors the `fetchBaseRef` pattern: a broken layout is logged
|
|
125
|
+
* and swallowed so a startup-command failure can NEVER wedge dispatch.
|
|
99
126
|
*/
|
|
100
|
-
|
|
127
|
+
applyOpenLayout(path: string, ctx: {
|
|
128
|
+
identifier: string;
|
|
129
|
+
branch: string;
|
|
130
|
+
}): Promise<void>;
|
|
101
131
|
}
|
|
102
132
|
export declare function herdrWorkspace(opts?: {
|
|
103
133
|
/**
|
package/dist/src/workspace.js
CHANGED
|
@@ -90,6 +90,11 @@ export class HerdrWorkspace {
|
|
|
90
90
|
worktreeDir;
|
|
91
91
|
open;
|
|
92
92
|
logger;
|
|
93
|
+
/**
|
|
94
|
+
* Root pane ids captured by a FRESH `ensure` create, keyed by worktree path,
|
|
95
|
+
* awaiting {@link applyOpenLayout} (GAIA-237). One-shot: consumed on apply.
|
|
96
|
+
*/
|
|
97
|
+
pendingRootPanes = new Map();
|
|
93
98
|
/** Herdr-backed intent-level agent host for `gaia ui` (GAIA-190). */
|
|
94
99
|
agentHost;
|
|
95
100
|
constructor(options) {
|
|
@@ -168,12 +173,17 @@ export class HerdrWorkspace {
|
|
|
168
173
|
}
|
|
169
174
|
}
|
|
170
175
|
/**
|
|
171
|
-
* Ensure the branch's worktree + herdr workspace exist.
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
176
|
+
* Ensure the branch's worktree + herdr workspace exist.
|
|
177
|
+
*
|
|
178
|
+
* `_identifier` is unused HERE: the worktree is located by branch, and since
|
|
179
|
+
* GAIA-237 the open layout — the one thing that consumed the identifier, to
|
|
180
|
+
* render `{identifier}` (GAIA-234) — is applied by {@link applyOpenLayout},
|
|
181
|
+
* which takes it on its own `ctx`. The parameter stays because it is the
|
|
182
|
+
* `GaiaWorkspace.ensure` contract position (the git workspace does use it).
|
|
183
|
+
* It is still taken as-passed rather than parsed back out of `branch`, which
|
|
184
|
+
* remains the reason the contract carries it at all.
|
|
175
185
|
*/
|
|
176
|
-
async ensure(
|
|
186
|
+
async ensure(_identifier, branch, baseRefOverride) {
|
|
177
187
|
if (branch === undefined) {
|
|
178
188
|
throw new Error('herdr workspace requires a branch name');
|
|
179
189
|
}
|
|
@@ -237,37 +247,65 @@ export class HerdrWorkspace {
|
|
|
237
247
|
'--json',
|
|
238
248
|
]);
|
|
239
249
|
const path = parseWorktreePath(createOutput, 'worktree create');
|
|
240
|
-
// GAIA-
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
// Render the ROOT command here (spec D2). `applyPaneLayout` types its
|
|
251
|
-
// root command verbatim for the executor's brace-bearing env-prefixed
|
|
252
|
-
// agent command, so templating it there would corrupt that; the split
|
|
253
|
-
// commands it already renders from the same `vars`.
|
|
254
|
-
const vars = { identifier, branch, worktreePath: path };
|
|
255
|
-
const layout = {
|
|
256
|
-
...this.open,
|
|
257
|
-
...(this.open.command !== undefined
|
|
258
|
-
? { command: renderTemplate(this.open.command, vars) }
|
|
259
|
-
: {}),
|
|
260
|
-
};
|
|
261
|
-
await applyPaneLayout(this.execHerdr, rootPaneId, layout, path, vars);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
catch (err) {
|
|
265
|
-
this.logger.warn({ branch, worktree: path, err: String(err) }, 'herdr open-layout failed (best-effort, ignored)');
|
|
250
|
+
// GAIA-237: CAPTURE the fresh root pane here, but do NOT lay it out yet.
|
|
251
|
+
// The layout's startup command names the gaia CLI, which the `after_create`
|
|
252
|
+
// hook builds — and that hook runs after `ensure()` returns. Applying the
|
|
253
|
+
// layout here fired the command before its binary existed, which is why the
|
|
254
|
+
// pane command needed a sentinel wait loop. The conductor now calls
|
|
255
|
+
// `applyOpenLayout` after the hook; a create with no root pane in its output
|
|
256
|
+
// stores nothing, so the layout is a no-op exactly as before.
|
|
257
|
+
const rootPaneId = parseWorktreeRootPane(createOutput);
|
|
258
|
+
if (rootPaneId) {
|
|
259
|
+
this.pendingRootPanes.set(path, rootPaneId);
|
|
266
260
|
}
|
|
267
261
|
// The `after_create` hook is run by the executor (GAIA-84), not here — the
|
|
268
262
|
// workspace only reports that it created a fresh worktree.
|
|
269
263
|
return { path, instructions: loadInstructions(path), created: true };
|
|
270
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* Apply the open layout to the root pane captured by the most recent
|
|
267
|
+
* {@link ensure} create for `path` — by default (GAIA-234) the ticket's
|
|
268
|
+
* `gaia ui` deep link, so the tab shows the ticket instead of a bare shell.
|
|
269
|
+
* The pane's cwd is the worktree checkout, passed explicitly on the splits
|
|
270
|
+
* (the root pane's cwd is already the checkout).
|
|
271
|
+
*
|
|
272
|
+
* NO stored pane ⇒ silent no-op. That is the reused-worktree case: `ensure`
|
|
273
|
+
* took the reuse branch, there is no fresh root pane, and re-applying would
|
|
274
|
+
* start a duplicate editor process on reattach (GAIA-139). The capture is
|
|
275
|
+
* one-shot — consumed here — so a repeat call cannot double-apply and the map
|
|
276
|
+
* cannot grow without bound.
|
|
277
|
+
*
|
|
278
|
+
* Best-effort — mirrors the `fetchBaseRef` pattern: a broken layout is logged
|
|
279
|
+
* and swallowed so a startup-command failure can NEVER wedge dispatch.
|
|
280
|
+
*/
|
|
281
|
+
async applyOpenLayout(path, ctx) {
|
|
282
|
+
const rootPaneId = this.pendingRootPanes.get(path);
|
|
283
|
+
if (!rootPaneId) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
this.pendingRootPanes.delete(path);
|
|
287
|
+
try {
|
|
288
|
+
// Render the ROOT command here (spec D2). `applyPaneLayout` types its
|
|
289
|
+
// root command verbatim for the executor's brace-bearing env-prefixed
|
|
290
|
+
// agent command, so templating it there would corrupt that; the split
|
|
291
|
+
// commands it already renders from the same `vars`.
|
|
292
|
+
const vars = {
|
|
293
|
+
identifier: ctx.identifier,
|
|
294
|
+
branch: ctx.branch,
|
|
295
|
+
worktreePath: path,
|
|
296
|
+
};
|
|
297
|
+
const layout = {
|
|
298
|
+
...this.open,
|
|
299
|
+
...(this.open.command !== undefined
|
|
300
|
+
? { command: renderTemplate(this.open.command, vars) }
|
|
301
|
+
: {}),
|
|
302
|
+
};
|
|
303
|
+
await applyPaneLayout(this.execHerdr, rootPaneId, layout, path, vars);
|
|
304
|
+
}
|
|
305
|
+
catch (err) {
|
|
306
|
+
this.logger.warn({ branch: ctx.branch, worktree: path, err: String(err) }, 'herdr open-layout failed (best-effort, ignored)');
|
|
307
|
+
}
|
|
308
|
+
}
|
|
271
309
|
}
|
|
272
310
|
export function herdrWorkspace(opts = {}) {
|
|
273
311
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gaia-ai/addon-herdr",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"description": "GAIA herdr integration: spawn executor + per-ticket worktree workspace.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
"directory": "gaia-cli/addons/herdr"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@gaia-ai/addon-workspace-git": "^0.6.
|
|
23
|
+
"@gaia-ai/addon-workspace-git": "^0.6.5"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@gaia-ai/conductor": "^0.6.
|
|
27
|
-
"@gaia-ai/core": "^0.6.
|
|
26
|
+
"@gaia-ai/conductor": "^0.6.5",
|
|
27
|
+
"@gaia-ai/core": "^0.6.5"
|
|
28
28
|
}
|
|
29
29
|
}
|