Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

176 lines
5.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
projectAgentGUIComposerGateControls,
resolveAgentGUIComposerGate,
type ResolveAgentGUIComposerGateInput
} from "./agentGuiComposerGate";
const readyInput: ResolveAgentGUIComposerGateInput = {
activeConversationBusy: false,
activeConversationId: "session-1",
activeEngineHasPendingInteractions: false,
activeLiveState: "active",
activeConversationResumeUnavailable: false,
agentTargetsLoading: false,
authBlocked: false,
hasNonRetryableRecoveryFailure: false,
isCollaboratorConversation: false,
isCreatingConversation: false,
isInterrupting: false,
isAwaitingTurnStart: false,
isSubmitting: false,
pendingApproval: false,
pendingInteractivePrompt: false,
providerReadinessGate: null,
selectedAgentTargetUnavailable: false,
settingsUpdatePending: false,
sessionRuntimeBlockedReason: null,
targetConnectionBlocked: false
};
describe("resolveAgentGUIComposerGate", () => {
it("atomically unblocks a shared target when its connection becomes ready", () => {
const connecting = resolveAgentGUIComposerGate({
...readyInput,
targetConnectionBlocked: true
});
expect(connecting).toEqual({
conversationBusy: false,
isAwaitingTurnStart: false,
runtime: {
status: "blocked",
reason: "target_connection",
sessionRuntimeReason: null
},
editor: { status: "blocked", reason: "runtime_blocked" },
submission: { status: "blocked", reason: "runtime_blocked" }
});
const ready = resolveAgentGUIComposerGate(readyInput);
expect(ready).toEqual({
conversationBusy: false,
isAwaitingTurnStart: false,
runtime: {
status: "ready",
reason: null,
sessionRuntimeReason: null
},
editor: { status: "editable", reason: null },
submission: { status: "ready", reason: null }
});
const controls = projectAgentGUIComposerGateControls({
gate: ready,
presentationEditorDisabled: false,
presentationSubmitDisabled: false
});
expect(controls.editorDisabled).toBe(false);
const editor = document.createElement("div");
editor.contentEditable = String(!controls.editorDisabled);
editor.tabIndex = 0;
document.body.append(editor);
editor.focus();
expect(editor.contentEditable).toBe("true");
expect(document.activeElement).toBe(editor);
editor.remove();
});
it("blocks submission while a session settings update is pending", () => {
expect(
resolveAgentGUIComposerGate({
...readyInput,
settingsUpdatePending: true
})
).toMatchObject({
runtime: { status: "ready", reason: null },
editor: { status: "editable", reason: null },
submission: { status: "blocked", reason: "settings_update_pending" }
});
});
it("cannot expose ready submission with a stale runtime connection block", () => {
for (const targetConnectionBlocked of [false, true]) {
const gate = resolveAgentGUIComposerGate({
...readyInput,
targetConnectionBlocked
});
if (gate.submission.status === "ready") {
expect(gate.runtime.status).toBe("ready");
expect(gate.editor.status).toBe("editable");
}
}
});
it("blocks editing and submission when the selected Home target is unavailable", () => {
expect(
resolveAgentGUIComposerGate({
...readyInput,
activeConversationId: null,
activeLiveState: "inactive",
selectedAgentTargetUnavailable: true
})
).toMatchObject({
runtime: { status: "ready", reason: null },
editor: { status: "blocked", reason: "provider_readiness" },
submission: { status: "blocked", reason: "provider_readiness" }
});
});
it("keeps the editor editable and projects queue submission while busy", () => {
expect(
resolveAgentGUIComposerGate({
...readyInput,
activeConversationBusy: true
})
).toMatchObject({
conversationBusy: true,
runtime: { status: "ready" },
editor: { status: "editable", reason: null },
submission: { status: "queue", reason: "conversation_busy" }
});
});
it("blocks a prompt awaiting Turn start without exposing queue semantics", () => {
expect(
resolveAgentGUIComposerGate({
...readyInput,
isAwaitingTurnStart: true,
isSubmitting: true
})
).toMatchObject({
conversationBusy: false,
isAwaitingTurnStart: true,
runtime: { status: "ready" },
editor: { status: "blocked", reason: "submitting" },
submission: { status: "blocked", reason: "submitting" }
});
});
it("blocks both editing and submission for a collaborator-owned session", () => {
expect(
resolveAgentGUIComposerGate({
...readyInput,
isCollaboratorConversation: true
})
).toMatchObject({
runtime: { status: "ready" },
editor: { status: "blocked", reason: "collaborator_read_only" },
submission: { status: "blocked", reason: "collaborator_read_only" }
});
});
it("keeps approval queue semantics while runtime commands are available", () => {
expect(
resolveAgentGUIComposerGate({
...readyInput,
activeEngineHasPendingInteractions: true,
pendingApproval: true
})
).toMatchObject({
runtime: { status: "ready" },
editor: { status: "editable", reason: null },
submission: { status: "queue", reason: "conversation_busy" }
});
});
});