fix: preserve subagent target precedence

This commit is contained in:
Peter Steinberger
2026-05-10 01:37:16 +01:00
parent c09f790732
commit f7a6b9b5a1
4 changed files with 31 additions and 10 deletions

View File

@@ -0,0 +1,11 @@
import { describe, expect, it } from "vitest";
import { createSubagentsTool } from "./subagents-tool.js";
describe("subagents tool", () => {
it("does not advertise sessions_yield as unconditionally available", () => {
const tool = createSubagentsTool();
expect(tool.description).toContain("If sessions_yield is available");
expect(tool.description).not.toContain("Use sessions_yield to wait");
});
});

View File

@@ -35,7 +35,7 @@ export function createSubagentsTool(opts?: { agentSessionKey?: string }): AnyAge
label: "Subagents",
name: "subagents",
description:
"On-demand list, kill, or steer spawned sub-agents for this requester session. Use sessions_yield to wait for completion events; do not poll this tool in wait loops.",
"On-demand list, kill, or steer spawned sub-agents for this requester session. If sessions_yield is available, use it to wait for completion events; do not poll this tool in wait loops.",
parameters: SubagentsToolSchema,
execute: async (_toolCallId, args) => {
const params = args as Record<string, unknown>;

View File

@@ -152,6 +152,16 @@ describe("subagents utils", () => {
expectResolvedRunId(runs, "copy_", "run-review-2");
});
it("preserves exact label targets before taskName prefix aliases", () => {
const runs = [
makeRun({ runId: "run-review-label", label: "review" }),
makeRun({ runId: "run-review-docs", label: "docs", taskName: "review_docs" }),
];
expectResolvedRunId(runs, "review", "run-review-label");
expectResolvedRunId(runs, "review_", "run-review-docs");
});
it("ignores stale duplicate taskName aliases when a current run reuses the handle", () => {
vi.spyOn(Date, "now").mockReturnValue(NOW_MS);
const runs = [

View File

@@ -107,15 +107,6 @@ export function resolveSubagentTargetFromRuns(params: {
if (byExactAlias.length > 1) {
return { error: params.errors.ambiguousLabel(trimmed) };
}
const byAliasPrefix = numericOrder.filter((entry) =>
aliases(entry).some((alias) => normalizeLowercaseStringOrEmpty(alias).startsWith(lowered)),
);
if (byAliasPrefix.length === 1) {
return { entry: byAliasPrefix[0] };
}
if (byAliasPrefix.length > 1) {
return { error: params.errors.ambiguousLabelPrefix(trimmed) };
}
const byExactLabel = deduped.filter(
(entry) => normalizeLowercaseStringOrEmpty(params.label(entry)) === lowered,
);
@@ -125,6 +116,15 @@ export function resolveSubagentTargetFromRuns(params: {
if (byExactLabel.length > 1) {
return { error: params.errors.ambiguousLabel(trimmed) };
}
const byAliasPrefix = numericOrder.filter((entry) =>
aliases(entry).some((alias) => normalizeLowercaseStringOrEmpty(alias).startsWith(lowered)),
);
if (byAliasPrefix.length === 1) {
return { entry: byAliasPrefix[0] };
}
if (byAliasPrefix.length > 1) {
return { error: params.errors.ambiguousLabelPrefix(trimmed) };
}
const byLabelPrefix = deduped.filter((entry) =>
normalizeLowercaseStringOrEmpty(params.label(entry)).startsWith(lowered),
);