117 lines
6.0 KiB
Swift
117 lines
6.0 KiB
Swift
// SPDX-FileCopyrightText: 2026 Kaede Akino
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
import AgentKit
|
|
import Foundation
|
|
import TermindApplication
|
|
import TermindPresentation
|
|
import TermindRemoteInfrastructure
|
|
import Testing
|
|
|
|
@Suite
|
|
struct AgentToolPresentationProfileTests {
|
|
private let english = Locale(identifier: "en")
|
|
|
|
@Test
|
|
func injectedProfileControlsPhasesPendingDetailsApprovalAndTruncation() throws {
|
|
var profile = AgentToolPresentationProfile()
|
|
profile.phaseSummary = { phase, activity, target, _ in
|
|
phase.rawValue == "uploading" ? "Uploading \(activity) to \(target ?? "destination")" : nil
|
|
}
|
|
profile.detail = { card, _, _ in
|
|
card.result == nil ? .content(.init(items: [.message("Pending upload", .secondary)])) : .hidden
|
|
}
|
|
profile.requiresArgumentProjection = true
|
|
profile.hidesTruncationNotice = true
|
|
let configured = profile
|
|
let renderer = AgentToolPresentation(profile: { _ in configured })
|
|
var card = card(name: "unrelated_name", target: .local)
|
|
card.descriptor.presentation = .init(
|
|
symbol: "arrow.up", activity: .label("artifacts"), presenterID: "example.upload",
|
|
actionKind: .push, phaseKinds: ["running": .init("uploading")])
|
|
card.state = .running
|
|
#expect(renderer.summary(for: card, hostName: "server", locale: english) == "Uploading artifacts to server")
|
|
#expect(renderer.detail(for: card)?.items == [.message("Pending upload", .secondary)])
|
|
#expect(AgentToolPresentation().summary(for: card, locale: english) == "Pushing artifacts")
|
|
#expect(AgentToolPresentation().detail(for: card) == nil)
|
|
|
|
let request = AgentApprovalRequest(
|
|
invocation: card.invocation, descriptor: card.descriptor, userIntent: "Upload")
|
|
#expect(renderer.approval(for: request, locale: english).detail.items.isEmpty)
|
|
#expect(!AgentToolPresentation().approval(for: request, locale: english).detail.items.isEmpty)
|
|
|
|
card.result = .init(callID: "call", content: "private payload")
|
|
card.result?.truncateMarker = "truncated"
|
|
#expect(renderer.detail(for: card) == nil)
|
|
#expect(renderer.truncationNotice(for: card) == nil)
|
|
#expect(AgentToolPresentation().truncationNotice(for: card) == "truncated")
|
|
}
|
|
|
|
@Test
|
|
func injectedVocabularyCountsUnknownActivitiesWithoutToolNameRules() {
|
|
var vocabulary = AgentToolPresentationVocabulary()
|
|
vocabulary.activity = { _, _ in "a widget" }
|
|
vocabulary.countedActivity = { _, count, _ in "\(count) widgets" }
|
|
let renderer = AgentToolPresentation(vocabulary: vocabulary)
|
|
var card = card(name: "custom_tool", target: .local)
|
|
card.descriptor.presentation = .init(
|
|
symbol: "shippingbox", activity: .semanticLabel(.init("widget")), actionKind: .create)
|
|
card.state = .completed
|
|
#expect(renderer.summary(for: card, locale: english) == "Created a widget")
|
|
#expect(renderer.batchSummary(for: [card, card], locale: english) == "Created 2 widgets")
|
|
card.state = .failed
|
|
#expect(renderer.batchSummary(for: [card, card], locale: english) == "Failed to create 2 widgets")
|
|
}
|
|
|
|
@Test
|
|
func explicitMCPPresenterIsUsedButRemoteMetadataCannotSelectLocalCode() throws {
|
|
var card = card(name: "ssh_exec_run", target: .mcp)
|
|
card.descriptor.presentation = .init(
|
|
symbol: "terminal", activity: .label("remote"), output: .json,
|
|
presenterID: SSHExecRunTool.presenter.id)
|
|
card.result = .init(callID: "call", content: "{\"value\":42}")
|
|
#expect(TermindAgentTools.presenter(for: card.descriptor) == nil)
|
|
let presenter = AgentToolDetailPresenter(
|
|
id: "example.remote", layout: .init(singleLineHeader: true),
|
|
present: { input in
|
|
[
|
|
.field(
|
|
.init(label: "Projected", value: input.result.objectValue?["value"]?.encodedString ?? "missing")
|
|
)
|
|
]
|
|
})
|
|
let detail = try #require(TermindToolPresentationProfiles.renderer.detail(for: card, presenter: presenter))
|
|
#expect(detail.items == [.field(.init(label: "Projected", value: "42"))])
|
|
#expect(detail.layout.singleLineHeader)
|
|
let fallback = try #require(TermindToolPresentationProfiles.renderer.detail(for: card))
|
|
#expect(fallback.items != detail.items)
|
|
#expect(fallback.items == [.field(.init(label: "Value", value: "42"))])
|
|
}
|
|
|
|
@Test
|
|
func registeredProfileFollowsItsIDNotTheToolName() {
|
|
var card = card(name: "renamed_command", target: .host)
|
|
card.descriptor.presentation = .init(
|
|
symbol: "terminal", activity: .label("command"), presenterID: SSHExecRunTool.presenter.id,
|
|
actionKind: .run, phaseKinds: ["running": .sshConnecting])
|
|
card.state = .running
|
|
#expect(TermindToolPresentationProfiles.renderer.summary(for: card, locale: english) == "Connecting")
|
|
card.descriptor.presentation?.presenterID = "unknown.command"
|
|
#expect(TermindToolPresentationProfiles.renderer.summary(for: card, locale: english) == "Running command")
|
|
#expect(TermindToolPresentationProfiles.renderer.detail(for: card) == nil)
|
|
card.descriptor.name = "ssh_exec_run"
|
|
card.descriptor.presentation = nil
|
|
#expect(TermindAgentTools.presenter(for: card.descriptor) == nil)
|
|
#expect(TermindToolPresentationProfiles.renderer.summary(for: card, locale: english) == "Running ssh exec run")
|
|
#expect(TermindToolPresentationProfiles.renderer.detail(for: card) == nil)
|
|
}
|
|
|
|
private func card(name: String, target: AgentToolDescriptor.Target) -> AgentToolCard {
|
|
.init(
|
|
invocation: .init(
|
|
runID: UUID(), call: .init(id: "call", name: name, arguments: .object(["secret": .string("private")]))),
|
|
descriptor: .init(
|
|
name: name, summary: name, inputSchema: .object([:]), target: target, approvalPolicy: .ask))
|
|
}
|
|
}
|