Skip to content

Commit 36f3493

Browse files
committed
extensions: align subagent install semantics and add regressions
1 parent 347902f commit 36f3493

3 files changed

Lines changed: 89 additions & 2 deletions

File tree

bin/subagents.test.sh

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
161161
server.bind(sock_path)
162162
server.listen(1)
163163
164-
end = time.time() + 60
164+
end = time.time() + 10
165165
while time.time() < end:
166166
server.settimeout(1)
167167
try:
@@ -318,13 +318,53 @@ test_start_rejects_injected_cwd() {
318318
)
319319
}
320320

321+
test_start_handles_single_quote_path() {
322+
(
323+
set -euo pipefail
324+
local tmp agent_home fakebin control_dir socket_path alias_path sock_pid real_user manifest quoted_cwd output_file
325+
tmp="$(mktemp -d /tmp/baudbot-subagents-test.XXXXXX)"
326+
trap 'kill "$sock_pid" 2>/dev/null || true; rm -rf "$tmp"' EXIT
327+
328+
agent_home="$(setup_fixture "$tmp")"
329+
fakebin="$tmp/fakebin"
330+
real_user="$(/usr/bin/id -un)"
331+
control_dir="$agent_home/.pi/session-control"
332+
socket_path="$control_dir/sentry-agent.sock"
333+
alias_path="$control_dir/sentry-agent.alias"
334+
manifest="$agent_home/.pi/agent/subagents/sentry-agent/subagent.json"
335+
quoted_cwd="$tmp/cwd-with-quote's"
336+
output_file="$tmp/start.out"
337+
338+
mkdir -p "$quoted_cwd"
339+
jq --arg cwd "$quoted_cwd" '.cwd = $cwd' "$manifest" > "$manifest.tmp"
340+
mv "$manifest.tmp" "$manifest"
341+
342+
sock_pid="$(start_unix_socket "$socket_path")"
343+
for _i in $(seq 1 20); do
344+
[ -S "$socket_path" ] && break
345+
sleep 0.1
346+
done
347+
ln -sf "$(basename "$socket_path")" "$alias_path"
348+
349+
export PATH="$fakebin:$PATH"
350+
export BAUDBOT_TEST_ID_U="0"
351+
export BAUDBOT_AGENT_USER="$real_user"
352+
export BAUDBOT_AGENT_HOME="$agent_home"
353+
export BAUDBOT_TEST_TMUX_FILE="$tmp/tmux-sessions"
354+
355+
bash "$SCRIPT" start sentry-agent >"$output_file" 2>&1
356+
grep -q "started sentry-agent" "$output_file"
357+
)
358+
}
359+
321360
echo "=== subagents cli tests ==="
322361
echo ""
323362

324363
run_test "requires root guard" test_requires_root
325364
run_test "list/install/enable/autostart state" test_list_and_state_toggles
326365
run_test "reconcile/status/stop lifecycle" test_reconcile_status_stop
327366
run_test "start rejects injected cwd payload" test_start_rejects_injected_cwd
367+
run_test "start handles single-quote cwd path" test_start_handles_single_quote_path
328368

329369
echo ""
330370
echo "=== $PASSED/$TOTAL passed, $FAILED failed ==="

pi/extensions/subagent-manager.test.mjs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,52 @@ describe("subagent_manage extension tool", () => {
151151
expect(state.agents["sentry-agent"].enabled).toBe(true);
152152
});
153153

154+
it("install action reenables and clears autostart to match shell behavior", async () => {
155+
const root = mkdtempSync(path.join(tmpdir(), "subagent-manager-test-"));
156+
tempDirs.push(root);
157+
158+
const subagentsDir = path.join(root, "subagents");
159+
const statePath = path.join(root, "subagents-state.json");
160+
mkdirSync(subagentsDir, { recursive: true });
161+
162+
writeManifest(subagentsDir, {
163+
id: "sentry-agent",
164+
name: "Sentry Agent",
165+
description: "Incident triage agent",
166+
session_name: "sentry-agent",
167+
model_profile: "cheap_tier",
168+
});
169+
170+
writeFileSync(statePath, JSON.stringify({
171+
version: 1,
172+
agents: {
173+
"sentry-agent": {
174+
installed: false,
175+
enabled: false,
176+
autostart: true,
177+
},
178+
},
179+
}, null, 2), "utf-8");
180+
181+
process.env[SUBAGENTS_DIR_ENV] = subagentsDir;
182+
process.env[SUBAGENTS_STATE_FILE_ENV] = statePath;
183+
184+
const tool = createHarness(async () => ({ stdout: "", stderr: "", code: 0, killed: false }));
185+
const result = await tool.execute(
186+
"tool-call",
187+
{ action: "install", id: "sentry-agent" },
188+
undefined,
189+
undefined,
190+
{},
191+
);
192+
193+
expect(result.isError).not.toBe(true);
194+
const state = JSON.parse(readFileSync(statePath, "utf-8"));
195+
expect(state.agents["sentry-agent"].installed).toBe(true);
196+
expect(state.agents["sentry-agent"].enabled).toBe(true);
197+
expect(state.agents["sentry-agent"].autostart).toBe(false);
198+
});
199+
154200
it("reconcile starts missing autostart-enabled subagent", async () => {
155201
const root = mkdtempSync(path.join(tmpdir(), "subagent-manager-test-"));
156202
tempDirs.push(root);

pi/extensions/subagent-manager.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,8 @@ export default function subagentManagerExtension(pi: ExtensionAPI): void {
485485

486486
case "install": {
487487
stateEntry.installed = true;
488-
if (stateEntry.enabled === undefined) stateEntry.enabled = true;
488+
stateEntry.enabled = true;
489+
stateEntry.autostart = false;
489490
writeSubagentState(state);
490491
return {
491492
content: [{ type: "text", text: `Installed ${pkg.id}.` }],

0 commit comments

Comments
 (0)