pi-subagents 0.59.0 → 0.61.0
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/CHANGELOG.md +69 -0
- package/docs/agents.md +2 -2
- package/docs/configuration.md +9 -5
- package/docs/extension-api.md +14 -7
- package/docs/models.md +1 -1
- package/docs/observability.md +1 -1
- package/docs/tool-reference.md +15 -3
- package/docs/workflows.md +15 -14
- package/install.mjs +2 -1
- package/package.json +1 -1
- package/skills/council-mode/SKILL.md +48 -243
- package/skills/council-mode/references/pass-contracts.md +150 -0
- package/skills/pi-subagents/SKILL.md +89 -37
- package/skills/pi-subagents/references/constraints-and-recipes.md +30 -234
- package/skills/pi-subagents/references/execution-controls.md +79 -7
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/skills/pi-subagents/references/multi-lane-orchestration.md +13 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +35 -28
- package/skills/pi-subagents/references/review-and-validation.md +73 -0
- package/src/agents/agent-management.ts +256 -88
- package/src/agents/agents.ts +527 -221
- package/src/api/background-work.ts +7 -2
- package/src/api/external-runs.ts +67 -4
- package/src/api/preflight.ts +13 -8
- package/src/api/shared-types.ts +3 -0
- package/src/extension/index.ts +7 -4
- package/src/extension/public-execution.ts +48 -4
- package/src/extension/rpc.ts +62 -4
- package/src/extension/schemas.ts +12 -7
- package/src/extension/tool-description.ts +16 -16
- package/src/runs/background/async-execution.ts +53 -37
- package/src/runs/background/async-job-tracker.ts +65 -3
- package/src/runs/background/async-resume.ts +3 -1
- package/src/runs/background/async-status.ts +104 -11
- package/src/runs/background/auto-drain.ts +1 -1
- package/src/runs/background/control-channel.ts +3 -2
- package/src/runs/background/fleet-view.ts +1 -1
- package/src/runs/background/result-watcher.ts +1 -1
- package/src/runs/background/resume-guidance.ts +1 -1
- package/src/runs/background/run-status.ts +15 -4
- package/src/runs/background/subagent-runner.ts +6 -3
- package/src/runs/background/subagent-wait.ts +30 -23
- package/src/runs/background/wait-completions.ts +4 -1
- package/src/runs/background/wait-tool.ts +24 -18
- package/src/runs/foreground/execution.ts +72 -4
- package/src/runs/foreground/subagent-executor.ts +276 -59
- package/src/runs/shared/acceptance.ts +43 -18
- package/src/runs/shared/async-status-projection.ts +138 -4
- package/src/runs/shared/background-process-options.ts +9 -0
- package/src/runs/shared/host-step-status.ts +1 -0
- package/src/runs/shared/mcp-direct-tool-grant.ts +2 -5
- package/src/runs/shared/model-fallback.ts +61 -17
- package/src/runs/shared/mutation-evidence.ts +52 -3
- package/src/runs/shared/permissions.ts +1 -1
- package/src/runs/shared/pi-args.ts +47 -1
- package/src/runs/shared/single-output.ts +45 -18
- package/src/runs/shared/subagent-prompt-runtime.ts +20 -2
- package/src/runs/shared/tool-timeout.ts +1 -1
- package/src/runs/shared/workflow-graph.ts +16 -0
- package/src/shared/types.ts +74 -4
- package/src/shared/workflow-child-permit.ts +91 -0
- package/src/slash/prompt-template-bridge.ts +37 -1
- package/src/slash/slash-commands.ts +18 -26
- package/src/tui/fleet-status.ts +11 -3
- package/src/tui/render-helpers.ts +31 -0
- package/src/tui/render.ts +673 -145
- package/src/watchdog/change-signature.ts +40 -1
- package/src/workflows/host-command.ts +6 -1
- package/src/workflows/scripted-workflow.ts +206 -6
- package/src/workflows/workflow-child-summary.ts +1 -1
- package/src/workflows/workflow-receipt.ts +41 -4
- package/src/workflows/workflow-resources.ts +150 -0
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
type AgentDiscoveryDiagnostic,
|
|
8
8
|
type AgentScope,
|
|
9
9
|
type AgentSource,
|
|
10
|
-
BUILTIN_AGENT_NAMES,
|
|
11
10
|
defaultInheritProjectContext,
|
|
12
11
|
defaultInheritSkills,
|
|
13
12
|
defaultSystemPromptMode,
|
|
@@ -28,15 +27,16 @@ import {
|
|
|
28
27
|
buildProactiveSkillSubagentRecommendationLines,
|
|
29
28
|
} from "./proactive-skills.ts";
|
|
30
29
|
import { parseFrontmatter, parseFrontmatterList } from "./frontmatter.ts";
|
|
31
|
-
import { toModelInfo } from "../shared/model-info.ts";
|
|
30
|
+
import { resolveEffectiveThinking, toModelInfo } from "../shared/model-info.ts";
|
|
32
31
|
import { resolveSubagentModelOverride, type ParentModel } from "../runs/shared/model-fallback.ts";
|
|
33
32
|
import { validateToolBudgetConfig } from "../runs/shared/tool-budget.ts";
|
|
34
33
|
import { validateAcceptanceInput } from "../runs/shared/acceptance.ts";
|
|
35
|
-
import { CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL, isCodeOwnedExternalCliAdapterId, validateCodeOwnedProfileRunner } from "../runs/shared/external-cli-contract.ts";
|
|
36
|
-
import type { AcceptanceInput, Details, ExtensionConfig, ToolBudgetConfig } from "../shared/types.ts";
|
|
34
|
+
import { CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL, isCodeOwnedExternalCliAdapterId, resolveExternalCliRunnerStatus, validateCodeOwnedProfileRunner } from "../runs/shared/external-cli-contract.ts";
|
|
35
|
+
import type { AcceptanceInput, AgentCapabilitiesSnapshot, AgentCapabilityRow, Details, ExtensionConfig, ToolBudgetConfig } from "../shared/types.ts";
|
|
37
36
|
import { getProjectConfigDir } from "../shared/utils.ts";
|
|
37
|
+
import { previewDisplayText } from "../shared/display-text.ts";
|
|
38
38
|
import { capabilityCeilingAgentRestrictionSources, isAgentAllowedByCapabilityCeiling, resolveCurrentSubagentCapabilityCeiling } from "../runs/shared/capability-ceiling.ts";
|
|
39
|
-
import {
|
|
39
|
+
import { mergeRuntimeAgents, type RuntimeAgentOwner } from "./runtime-agent-registry.ts";
|
|
40
40
|
import { listExternalJobProviders } from "../api/external-job-provider.ts";
|
|
41
41
|
|
|
42
42
|
type ManagementAction = "list" | "get" | "models" | "create" | "update" | "delete" | "eject" | "disable" | "enable" | "reset";
|
|
@@ -47,11 +47,21 @@ interface ManagementParams {
|
|
|
47
47
|
action?: string;
|
|
48
48
|
agent?: string;
|
|
49
49
|
agentScope?: unknown;
|
|
50
|
+
capabilities?: unknown;
|
|
50
51
|
config?: unknown;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
function result(text: string, isError = false): AgentToolResult<Details> {
|
|
54
|
-
return { content: [{ type: "text", text }], isError, details: { mode: "management", results: [] } };
|
|
54
|
+
function result(text: string, isError = false, details?: Partial<Details>): AgentToolResult<Details> {
|
|
55
|
+
return { content: [{ type: "text", text }], isError, details: { mode: "management", results: [], ...details } };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function jsonDetails<T>(value: T): T {
|
|
59
|
+
return JSON.parse(JSON.stringify(value)) as T;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function presentDetails<T extends Record<string, unknown>>(value: T): T | undefined {
|
|
63
|
+
const cleaned = jsonDetails(value);
|
|
64
|
+
return Object.keys(cleaned).length > 0 ? cleaned : undefined;
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
function parseCsv(value: string): string[] {
|
|
@@ -118,18 +128,39 @@ function allAgents(d: { builtin: AgentConfig[]; package: AgentConfig[]; user: Ag
|
|
|
118
128
|
return [...d.builtin, ...d.package, ...d.user, ...d.project];
|
|
119
129
|
}
|
|
120
130
|
|
|
121
|
-
function
|
|
122
|
-
|
|
131
|
+
function effectiveAgentsForScope(
|
|
132
|
+
scope: AgentScope,
|
|
133
|
+
d: { builtin: AgentConfig[]; package: AgentConfig[]; user: AgentConfig[]; project: AgentConfig[] },
|
|
134
|
+
runtimeAgentOwner?: RuntimeAgentOwner,
|
|
135
|
+
): AgentConfig[] {
|
|
136
|
+
let agents = mergeAgentsForScope(scope, d.user, d.project, d.builtin, d.package);
|
|
137
|
+
if (runtimeAgentOwner) {
|
|
138
|
+
agents = mergeRuntimeAgents(runtimeAgentOwner, { agents }, allAgents(d)).agents;
|
|
139
|
+
}
|
|
140
|
+
return agents;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function availableAgentNamesFromDiscovery(
|
|
144
|
+
d: { builtin: AgentConfig[]; package: AgentConfig[]; user: AgentConfig[]; project: AgentConfig[] },
|
|
145
|
+
runtimeAgentOwner?: RuntimeAgentOwner,
|
|
146
|
+
): string[] {
|
|
147
|
+
const agents = runtimeAgentOwner ? effectiveAgentsForScope("both", d, runtimeAgentOwner) : allAgents(d);
|
|
148
|
+
return [...new Set(agents.map((agent) => agent.name))].sort((a, b) => a.localeCompare(b));
|
|
123
149
|
}
|
|
124
150
|
|
|
125
151
|
function availableAgentNames(cwd: string): string[] {
|
|
126
152
|
return availableAgentNamesFromDiscovery(discoverAgentsAll(cwd));
|
|
127
153
|
}
|
|
128
154
|
|
|
129
|
-
function findAgentsInDiscovery(
|
|
155
|
+
function findAgentsInDiscovery(
|
|
156
|
+
name: string,
|
|
157
|
+
d: { builtin: AgentConfig[]; package: AgentConfig[]; user: AgentConfig[]; project: AgentConfig[] },
|
|
158
|
+
scope: AgentScope = "both",
|
|
159
|
+
runtimeAgentOwner?: RuntimeAgentOwner,
|
|
160
|
+
): AgentConfig[] {
|
|
130
161
|
const raw = name.trim();
|
|
131
162
|
const sanitized = sanitizeName(raw);
|
|
132
|
-
const scoped =
|
|
163
|
+
const scoped = effectiveAgentsForScope(scope, d, runtimeAgentOwner);
|
|
133
164
|
let resolved = resolveAgentName(raw, scoped);
|
|
134
165
|
if (!resolved.agent && !resolved.error && sanitized !== raw) resolved = resolveAgentName(sanitized, scoped);
|
|
135
166
|
if (resolved.agent) return scoped.filter((agent) => agent.name === resolved.agent!.name).sort((a, b) => a.source.localeCompare(b.source));
|
|
@@ -645,18 +676,135 @@ function runnerListBadge(agent: AgentConfig, providerNames: Set<string> | undefi
|
|
|
645
676
|
return undefined;
|
|
646
677
|
}
|
|
647
678
|
|
|
648
|
-
function
|
|
679
|
+
function agentListMetadata(agent: AgentConfig, providerNames: Set<string> | undefined): string {
|
|
649
680
|
const source = agent.source === "package" ? packageSourceLabel(agent) : agent.source;
|
|
650
|
-
|
|
681
|
+
return [
|
|
651
682
|
source,
|
|
652
683
|
runnerListBadge(agent, providerNames),
|
|
653
684
|
agent.defaultContext ? `context: ${agent.defaultContext}` : undefined,
|
|
654
685
|
agent.aliases?.length ? `aliases: ${agent.aliases.join(", ")}` : undefined,
|
|
655
|
-
].filter((part): part is string => Boolean(part));
|
|
656
|
-
|
|
686
|
+
].filter((part): part is string => Boolean(part)).join(", ");
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function formatAgentListLine(agent: AgentConfig, providerNames: Set<string> | undefined): string {
|
|
690
|
+
return `- ${agent.name} (${agentListMetadata(agent, providerNames)}): ${agent.description}`;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function formatAgentCapabilitiesLine(agent: AgentConfig, providerNames: Set<string> | undefined): string {
|
|
694
|
+
const declaredTools = [
|
|
695
|
+
...(agent.tools ?? []),
|
|
696
|
+
...(agent.mcpDirectTools ?? []).map((tool) => `mcp:${tool}`),
|
|
697
|
+
];
|
|
698
|
+
let tools = "none";
|
|
699
|
+
if (agent.tools === undefined && agent.mcpDirectTools === undefined) {
|
|
700
|
+
tools = "default/ambient";
|
|
701
|
+
} else if (declaredTools.length > 0) {
|
|
702
|
+
tools = declaredTools.join(", ");
|
|
703
|
+
}
|
|
704
|
+
let model = "inherits current session";
|
|
705
|
+
if (agent.model !== undefined) {
|
|
706
|
+
model = agent.model;
|
|
707
|
+
if (agent.modelProvider && !agent.model.includes("/")) model = `${agent.modelProvider}/${agent.model}`;
|
|
708
|
+
}
|
|
709
|
+
const thinking = agent.thinking === false ? "off" : agent.thinking ?? "default";
|
|
710
|
+
return `- ${agent.name} (${agentListMetadata(agent, providerNames)}): Description: ${previewDisplayText(agent.description, 240)}; Tools: ${tools}; Model: ${model}; Thinking: ${thinking}`;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
const EXTERNAL_JOB_CAPABILITIES = { stop: false, steer: false, resume: false, structuredOutput: false, toolEvents: false } as const;
|
|
714
|
+
const PI_AGENT_RUNNER = { type: "pi" } as const;
|
|
715
|
+
|
|
716
|
+
function listOrEmpty<T>(values: T[] | undefined): T[] {
|
|
717
|
+
return values ?? [];
|
|
657
718
|
}
|
|
658
719
|
|
|
659
|
-
function
|
|
720
|
+
function agentCapabilityRunner(agent: AgentConfig, providerNames: Set<string> | undefined): AgentCapabilityRow["runner"] {
|
|
721
|
+
const runner = agent.runner;
|
|
722
|
+
if (!runner || runner.type === "pi") return PI_AGENT_RUNNER;
|
|
723
|
+
if (runner.type === "external-cli") return { type: "external-cli", adapter: runner.adapter, capabilities: resolveExternalCliRunnerStatus(runner).capabilities };
|
|
724
|
+
return { type: "external-job", provider: runner.provider, available: providerNames?.has(runner.provider), capabilities: EXTERNAL_JOB_CAPABILITIES };
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function agentCapabilityTools(agent: AgentConfig): AgentCapabilityRow["tools"] {
|
|
728
|
+
return {
|
|
729
|
+
ambient: agent.tools === undefined && agent.mcpDirectTools === undefined,
|
|
730
|
+
names: listOrEmpty(agent.tools),
|
|
731
|
+
mcpDirectTools: listOrEmpty(agent.mcpDirectTools),
|
|
732
|
+
mutationTools: agent.mutationTools,
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function agentCapabilityRow(agent: AgentConfig, options: { executable: boolean; providerNames?: Set<string>; restrictionSources?: string[] }): AgentCapabilityRow {
|
|
737
|
+
return {
|
|
738
|
+
name: agent.name,
|
|
739
|
+
description: previewDisplayText(agent.description, 1000),
|
|
740
|
+
source: agent.source,
|
|
741
|
+
executable: options.executable,
|
|
742
|
+
restrictionSources: options.executable ? undefined : options.restrictionSources ?? [],
|
|
743
|
+
aliases: agent.aliases ? [...agent.aliases] : undefined,
|
|
744
|
+
runner: agentCapabilityRunner(agent, options.providerNames),
|
|
745
|
+
tools: agentCapabilityTools(agent),
|
|
746
|
+
model: presentDetails({ value: agent.model, fallbackModels: agent.fallbackModels, thinking: agent.thinking }),
|
|
747
|
+
execution: presentDetails({ defaultAsync: agent.defaultAsync, timeoutMs: agent.defaultTimeoutMs }),
|
|
748
|
+
output: presentDetails({ path: agent.output, mode: agent.outputMode }),
|
|
749
|
+
extensions: presentDetails({ names: agent.extensions, subagentOnly: agent.subagentOnlyExtensions, skills: agent.skills }),
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
function agentCapabilitiesSnapshot(input: { agents: AgentConfig[]; restrictedAgents: AgentConfig[]; providerNames?: Set<string>; restrictedSources?: string[] }): AgentCapabilitiesSnapshot {
|
|
754
|
+
return {
|
|
755
|
+
agents: [
|
|
756
|
+
...input.agents.map((agent) => agentCapabilityRow(agent, { executable: true, providerNames: input.providerNames })),
|
|
757
|
+
...input.restrictedAgents.map((agent) => agentCapabilityRow(agent, { executable: false, providerNames: input.providerNames, restrictionSources: input.restrictedSources })),
|
|
758
|
+
],
|
|
759
|
+
restrictedCount: input.restrictedAgents.length,
|
|
760
|
+
...(input.restrictedSources?.length ? { capabilityCeilingSources: [...input.restrictedSources] } : {}),
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function providerNames(status: ExternalJobProviderStatus): Set<string> | undefined {
|
|
765
|
+
return status.ok ? status.names : undefined;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
function appendRestrictedAgentLines(input: { lines: string[]; agents: AgentConfig[]; sources?: string[]; providerNames?: Set<string>; formatLine: (agent: AgentConfig, providerNames: Set<string> | undefined) => string }): void {
|
|
769
|
+
if (input.agents.length === 0) return;
|
|
770
|
+
input.lines.push(
|
|
771
|
+
"",
|
|
772
|
+
`Restricted agents (not executable in this session${input.sources?.length ? `; capability ceiling: ${input.sources.join(", ")}` : ""}):`,
|
|
773
|
+
...input.agents.map((agent) => input.formatLine(agent, input.providerNames)),
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
function appendExternalJobRegistryLine(lines: string[], agents: AgentConfig[], status: ExternalJobProviderStatus): void {
|
|
778
|
+
if (status.ok || !agents.some((agent) => agent.runner?.type === "external-job")) return;
|
|
779
|
+
lines.push("", `External-job provider registry unavailable: ${status.error}`);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
function appendAgentDiagnosticLines(lines: string[], diagnostics: AgentDiscoveryDiagnostic[] | undefined): void {
|
|
783
|
+
if (!diagnostics?.length) return;
|
|
784
|
+
lines.push(
|
|
785
|
+
"",
|
|
786
|
+
"Invalid agent definitions:",
|
|
787
|
+
...diagnostics.map((diagnostic) => `- ${diagnostic.name ?? diagnostic.filePath} (${diagnostic.source}): ${diagnostic.error}`),
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
function agentCapabilityDetails(input: { capabilityMode: boolean; agents: AgentConfig[]; restrictedAgents: AgentConfig[]; providerNames?: Set<string>; restrictedSources?: string[] }): Partial<Details> | undefined {
|
|
792
|
+
if (!input.capabilityMode) return undefined;
|
|
793
|
+
return {
|
|
794
|
+
agentCapabilities: jsonDetails(agentCapabilitiesSnapshot({
|
|
795
|
+
agents: input.agents,
|
|
796
|
+
restrictedAgents: input.restrictedAgents,
|
|
797
|
+
providerNames: input.providerNames,
|
|
798
|
+
restrictedSources: input.restrictedSources,
|
|
799
|
+
})),
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
function formatAgentListSections(
|
|
804
|
+
agents: AgentConfig[],
|
|
805
|
+
providerNames: Set<string> | undefined,
|
|
806
|
+
formatLine: (agent: AgentConfig, providerNames: Set<string> | undefined) => string = formatAgentListLine,
|
|
807
|
+
): string[] {
|
|
660
808
|
if (agents.length === 0) return ["- (none)"];
|
|
661
809
|
const sections: Array<[AgentSource, string]> = [
|
|
662
810
|
["package", "Package agents"],
|
|
@@ -670,7 +818,7 @@ function formatAgentListSections(agents: AgentConfig[], providerNames: Set<strin
|
|
|
670
818
|
const matches = agents.filter((agent) => agent.source === source);
|
|
671
819
|
if (matches.length === 0) continue;
|
|
672
820
|
if (lines.length > 0) lines.push("");
|
|
673
|
-
lines.push(label, ...matches.map((agent) =>
|
|
821
|
+
lines.push(label, ...matches.map((agent) => formatLine(agent, providerNames)));
|
|
674
822
|
}
|
|
675
823
|
return lines;
|
|
676
824
|
}
|
|
@@ -735,16 +883,7 @@ function formatAgentDetail(agent: AgentConfig): string {
|
|
|
735
883
|
export function handleList(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
|
|
736
884
|
const scope = normalizeListScope(params.agentScope) ?? "both";
|
|
737
885
|
const d = discoverAgentsAll(ctx.cwd, ctx.model?.provider);
|
|
738
|
-
let scopedAgents =
|
|
739
|
-
if (ctx.runtimeAgentOwner && listRuntimeAgentConfigs(ctx.runtimeAgentOwner).length > 0) {
|
|
740
|
-
const configuredAgents: AgentConfig[] = [
|
|
741
|
-
...d.builtin,
|
|
742
|
-
...d.package,
|
|
743
|
-
...d.user,
|
|
744
|
-
...d.project,
|
|
745
|
-
];
|
|
746
|
-
scopedAgents = mergeRuntimeAgents(ctx.runtimeAgentOwner, { agents: scopedAgents }, configuredAgents).agents;
|
|
747
|
-
}
|
|
886
|
+
let scopedAgents = effectiveAgentsForScope(scope, d, ctx.runtimeAgentOwner);
|
|
748
887
|
scopedAgents = scopedAgents
|
|
749
888
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
750
889
|
const capabilityCeiling = resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId);
|
|
@@ -758,23 +897,24 @@ export function handleList(params: ManagementParams, ctx: ManagementContext): Ag
|
|
|
758
897
|
discoverAvailableSkills: () => discoverAvailableSkills(ctx.cwd),
|
|
759
898
|
});
|
|
760
899
|
const providerStatus = registeredExternalJobProviderStatus();
|
|
900
|
+
const providerNameSet = providerNames(providerStatus);
|
|
901
|
+
const capabilityMode = params.capabilities === true;
|
|
902
|
+
const formatLine = capabilityMode ? formatAgentCapabilitiesLine : formatAgentListLine;
|
|
761
903
|
const lines = [
|
|
762
|
-
"Executable agents:",
|
|
763
|
-
...formatAgentListSections(agents,
|
|
764
|
-
...(restrictedAgents.length ? [
|
|
765
|
-
"",
|
|
766
|
-
`Restricted agents (not executable in this session${restrictedSources?.length ? `; capability ceiling: ${restrictedSources.join(", ")}` : ""}):`,
|
|
767
|
-
...restrictedAgents.map((a) => formatAgentListLine(a, providerStatus.ok ? providerStatus.names : undefined)),
|
|
768
|
-
] : []),
|
|
769
|
-
...(!providerStatus.ok && [...agents, ...restrictedAgents].some((agent) => agent.runner?.type === "external-job") ? ["", `External-job provider registry unavailable: ${providerStatus.error}`] : []),
|
|
770
|
-
...(d.agentDiagnostics?.length ? [
|
|
771
|
-
"",
|
|
772
|
-
"Invalid agent definitions:",
|
|
773
|
-
...d.agentDiagnostics.map((diagnostic) => `- ${diagnostic.name ?? diagnostic.filePath} (${diagnostic.source}): ${diagnostic.error}`),
|
|
774
|
-
] : []),
|
|
775
|
-
...(proactiveSuggestions.length ? ["", ...proactiveSuggestions] : []),
|
|
904
|
+
capabilityMode ? "Executable agents (capabilities):" : "Executable agents:",
|
|
905
|
+
...formatAgentListSections(agents, providerNameSet, formatLine),
|
|
776
906
|
];
|
|
777
|
-
|
|
907
|
+
appendRestrictedAgentLines({ lines, agents: restrictedAgents, sources: restrictedSources, providerNames: providerNameSet, formatLine });
|
|
908
|
+
appendExternalJobRegistryLine(lines, [...agents, ...restrictedAgents], providerStatus);
|
|
909
|
+
appendAgentDiagnosticLines(lines, d.agentDiagnostics);
|
|
910
|
+
if (proactiveSuggestions.length) lines.push("", ...proactiveSuggestions);
|
|
911
|
+
return result(lines.join("\n"), false, agentCapabilityDetails({
|
|
912
|
+
capabilityMode,
|
|
913
|
+
agents,
|
|
914
|
+
restrictedAgents,
|
|
915
|
+
providerNames: providerNameSet,
|
|
916
|
+
restrictedSources,
|
|
917
|
+
}));
|
|
778
918
|
}
|
|
779
919
|
|
|
780
920
|
function formatModelSource(agent: AgentConfig, currentModel: ParentModel | undefined): string {
|
|
@@ -784,80 +924,108 @@ function formatModelSource(agent: AgentConfig, currentModel: ParentModel | undef
|
|
|
784
924
|
if (agent.modelSource?.type === "subagents.defaultModel" && agent.model === agent.modelSource.model) {
|
|
785
925
|
return `${agent.modelSource.scope} defaultModel`;
|
|
786
926
|
}
|
|
787
|
-
if (agent.model) return
|
|
927
|
+
if (agent.model) return `${agent.source} agent config`;
|
|
788
928
|
if (currentModel) return "inherits current session model";
|
|
789
929
|
return "inherit requested, but no current session model is available";
|
|
790
930
|
}
|
|
791
931
|
|
|
792
932
|
function handleModels(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
|
|
793
933
|
const requestedAgent = params.agent?.trim();
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
}
|
|
934
|
+
const scope = normalizeListScope(params.agentScope);
|
|
935
|
+
if (!scope) return result("agentScope must be 'user', 'project', or 'both' for models.", true);
|
|
797
936
|
|
|
798
937
|
const discovered = discoverAgentsAll(ctx.cwd, ctx.model?.provider);
|
|
799
|
-
const
|
|
800
|
-
|
|
938
|
+
const effectiveAgents = effectiveAgentsForScope(scope, discovered, ctx.runtimeAgentOwner)
|
|
939
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
801
940
|
const availableModels = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
802
941
|
const currentModel = ctx.model ? { provider: ctx.model.provider, id: ctx.model.id } : undefined;
|
|
803
942
|
const preferredProvider = ctx.model?.provider;
|
|
804
|
-
const
|
|
943
|
+
const capabilityCeiling = resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId);
|
|
805
944
|
|
|
945
|
+
let selectedAgents = effectiveAgents;
|
|
806
946
|
if (requestedAgent) {
|
|
807
|
-
const
|
|
808
|
-
|
|
809
|
-
const
|
|
810
|
-
const
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
`
|
|
817
|
-
];
|
|
818
|
-
if (agent.override) {
|
|
819
|
-
lines.push("Override file:");
|
|
820
|
-
lines.push(` ${agent.override.path}`);
|
|
821
|
-
}
|
|
822
|
-
if (agent.model && resolvedModel && agent.model !== resolvedModel) {
|
|
823
|
-
lines.push("Requested model setting:");
|
|
824
|
-
lines.push(` ${agent.model}`);
|
|
947
|
+
const matches = findAgentsInDiscovery(requestedAgent, discovered, scope, ctx.runtimeAgentOwner);
|
|
948
|
+
const diagnostics = diagnosticsForScope(discovered.agentDiagnostics, scope);
|
|
949
|
+
const normalizedName = sanitizeName(requestedAgent);
|
|
950
|
+
const diagnostic = findBlockingAgentDiagnostic(requestedAgent, matches, diagnostics)
|
|
951
|
+
?? (normalizedName !== requestedAgent ? findBlockingAgentDiagnostic(normalizedName, matches, diagnostics) : undefined);
|
|
952
|
+
if (diagnostic) return result(`Agent '${params.agent}' has invalid configuration: ${diagnostic.error}`, true);
|
|
953
|
+
const distinctNames = [...new Set(matches.map((agent) => agent.name))];
|
|
954
|
+
if (distinctNames.length > 1) return result(`Ambiguous agent alias or name '${params.agent}': ${distinctNames.sort((a, b) => a.localeCompare(b)).join(", ")}`, true);
|
|
955
|
+
if (!matches.length) {
|
|
956
|
+
return result(`Agent '${params.agent}' not found. Available: ${availableAgentNamesFromDiscovery(discovered, ctx.runtimeAgentOwner).join(", ") || "none"}.`, true);
|
|
825
957
|
}
|
|
826
|
-
|
|
827
|
-
lines.push("Current session model:");
|
|
828
|
-
lines.push(` ${currentModel ? `${currentModel.provider}/${currentModel.id}` : "(unavailable)"}`);
|
|
829
|
-
return result(lines.join("\n"));
|
|
958
|
+
selectedAgents = [matches[0]!];
|
|
830
959
|
}
|
|
831
960
|
|
|
832
961
|
const lines = [
|
|
833
|
-
"
|
|
834
|
-
"",
|
|
835
|
-
"Current session model:",
|
|
836
|
-
` ${currentModel ? `${currentModel.provider}/${currentModel.id}` : "(unavailable)"}`,
|
|
962
|
+
requestedAgent ? "Subagent model" : "Subagent models",
|
|
837
963
|
"",
|
|
964
|
+
...(requestedAgent ? [] : [
|
|
965
|
+
"Current session model:",
|
|
966
|
+
` ${currentModel ? `${currentModel.provider}/${currentModel.id}` : "(unavailable)"}`,
|
|
967
|
+
"",
|
|
968
|
+
]),
|
|
838
969
|
];
|
|
839
970
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
971
|
+
const modelEntries = selectedAgents.flatMap((agent) => requestedAgent
|
|
972
|
+
? [{ agent, name: requestedAgent }]
|
|
973
|
+
: [{ agent, name: agent.name }, ...(agent.aliases ?? []).map((name) => ({ agent, name }))]);
|
|
974
|
+
for (const { agent, name } of modelEntries) {
|
|
975
|
+
const resolvedModel = resolveSubagentModelOverride(agent.model, currentModel, availableModels, agent.modelProvider ?? preferredProvider);
|
|
976
|
+
const effectiveThinking = resolveEffectiveThinking(resolvedModel, agent.thinking)
|
|
977
|
+
?? (agent.thinking === false ? "off" : undefined);
|
|
978
|
+
const source = `${formatModelSource(agent, currentModel)}${agent.disabled ? "; disabled" : ""}${isAgentAllowedByCapabilityCeiling(agent.name, capabilityCeiling) ? "" : "; restricted"}`;
|
|
979
|
+
if (requestedAgent) {
|
|
980
|
+
lines.push(`Agent: ${requestedAgent}`);
|
|
981
|
+
lines.push("Effective model:");
|
|
982
|
+
lines.push(` ${resolvedModel ?? "(unresolved)"}`);
|
|
983
|
+
lines.push(`Source: ${source}`);
|
|
984
|
+
lines.push(`Thinking: ${effectiveThinking ?? "default"}`);
|
|
985
|
+
if (agent.fallbackModels?.length) {
|
|
986
|
+
lines.push("Fallback models:");
|
|
987
|
+
for (const fallback of agent.fallbackModels) {
|
|
988
|
+
lines.push(` ${resolveSubagentModelOverride(fallback, currentModel, availableModels, agent.modelProvider ?? preferredProvider) ?? fallback}`);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
if (agent.override) {
|
|
992
|
+
lines.push("Override file:");
|
|
993
|
+
lines.push(` ${agent.override.path}`);
|
|
994
|
+
}
|
|
995
|
+
if (agent.model && resolvedModel && agent.model !== resolvedModel) {
|
|
996
|
+
lines.push("Requested model setting:");
|
|
997
|
+
lines.push(` ${agent.model}`);
|
|
998
|
+
}
|
|
999
|
+
if (agent.disabled) lines.push("Disabled: true");
|
|
1000
|
+
if (!isAgentAllowedByCapabilityCeiling(agent.name, capabilityCeiling)) lines.push("Restricted: true");
|
|
1001
|
+
lines.push("Current session model:");
|
|
1002
|
+
lines.push(` ${currentModel ? `${currentModel.provider}/${currentModel.id}` : "(unavailable)"}`);
|
|
1003
|
+
break;
|
|
849
1004
|
}
|
|
850
|
-
const resolvedModel = resolveSubagentModelOverride(agent.model, currentModel, availableModels, preferredProvider);
|
|
851
|
-
const source = `${formatModelSource(agent, currentModel)}${agent.disabled ? "; disabled" : ""}`;
|
|
852
1005
|
lines.push(name);
|
|
853
1006
|
lines.push(" model:");
|
|
854
1007
|
lines.push(` ${resolvedModel ?? "(unresolved)"}`);
|
|
855
1008
|
lines.push(` source: ${source}`);
|
|
1009
|
+
lines.push(` thinking: ${effectiveThinking ?? "default"}`);
|
|
1010
|
+
if (agent.fallbackModels?.length) {
|
|
1011
|
+
lines.push(" fallback models:");
|
|
1012
|
+
for (const fallback of agent.fallbackModels) {
|
|
1013
|
+
lines.push(` ${resolveSubagentModelOverride(fallback, currentModel, availableModels, agent.modelProvider ?? preferredProvider) ?? fallback}`);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
if (agent.override) {
|
|
1017
|
+
lines.push(" override file:");
|
|
1018
|
+
lines.push(` ${agent.override.path}`);
|
|
1019
|
+
}
|
|
1020
|
+
if (agent.model && resolvedModel && agent.model !== resolvedModel) {
|
|
1021
|
+
lines.push(" requested model setting:");
|
|
1022
|
+
lines.push(` ${agent.model}`);
|
|
1023
|
+
}
|
|
856
1024
|
lines.push("");
|
|
857
1025
|
}
|
|
858
1026
|
|
|
859
1027
|
const availableFullIds = availableModels.map((m) => m.fullId).sort();
|
|
860
|
-
if (availableFullIds.length > 0) {
|
|
1028
|
+
if (!requestedAgent && availableFullIds.length > 0) {
|
|
861
1029
|
lines.push("Available models in this session's registry (copy an exact provider/id when passing model):");
|
|
862
1030
|
lines.push("");
|
|
863
1031
|
const shown = availableFullIds.slice(0, 80);
|
|
@@ -866,7 +1034,7 @@ function handleModels(params: ManagementParams, ctx: ManagementContext): AgentTo
|
|
|
866
1034
|
lines.push("");
|
|
867
1035
|
lines.push("Use an exact provider/id from this list when you pass model; bare ids resolve only when unique in the registry.");
|
|
868
1036
|
}
|
|
869
|
-
|
|
1037
|
+
if (!requestedAgent) appendAgentDiagnosticLines(lines, diagnosticsForScope(discovered.agentDiagnostics, scope));
|
|
870
1038
|
return result(lines.join("\n"));
|
|
871
1039
|
}
|
|
872
1040
|
|