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

402 lines
11 KiB
TypeScript

import type { AgentUsageQuota } from "../../../shared/contracts/dto";
import {
agentGuiScheduler,
type AgentGuiScheduledTask
} from "../agentGuiScheduler";
export type AgentStatusSectionState = "available" | "unavailable" | "error";
export type AgentStatusRequestReason =
| "slash-status"
| "agent-info"
| "agent-config";
export interface AgentStatusQuery {
/** Exact host-owned execution target identity. AgentGUI treats it as opaque. */
scopeKey: string;
agentSessionId?: string | null;
reason: AgentStatusRequestReason;
forceRefresh?: boolean;
}
export interface AgentStatusValue {
agentSessionId?: string | null;
/** Host-projected account or billing label for the active provider mode. */
accountLabel?: string | null;
contextWindow?: {
usedTokens?: number | null;
totalTokens?: number | null;
} | null;
contextState: AgentStatusSectionState;
quotas: readonly AgentUsageQuota[];
limitsState: AgentStatusSectionState;
/** Stable host error code for the limits section. Raw provider text is forbidden. */
limitsErrorCode?: string | null;
limitsCapturedAtUnixMs?: number | null;
limitsStale?: boolean;
}
export interface AgentStatusFrame {
kind: "snapshot" | "refreshed";
value: AgentStatusValue;
}
export interface AgentStatusStreamObserver {
onFrame(frame: AgentStatusFrame): void;
onError(error: AgentStatusSourceError): void;
onComplete(): void;
}
export interface AgentStatusSourceError {
/** Structured host error code. Raw provider errors must not cross this port. */
code: string;
}
export interface AgentStatusSource {
open(
query: AgentStatusQuery,
observer: AgentStatusStreamObserver
): () => void;
}
export type AgentStatusRequestPhase = "idle" | "loading" | "ready" | "error";
export interface AgentStatusControllerSnapshot {
query: AgentStatusQuery | null;
value: AgentStatusValue | null;
phase: AgentStatusRequestPhase;
isRefreshing: boolean;
errorCode: string | null;
}
export interface AgentStatusController {
getSnapshot(): AgentStatusControllerSnapshot;
subscribe(listener: () => void): () => void;
open(query: AgentStatusQuery): void;
close(): void;
invalidate(scopeKey?: string): void;
}
export interface AgentStatusControllerOptions {
source: AgentStatusSource;
now?: () => number;
requestTimeoutMs?: number;
retainedSnapshotMs?: number;
forcedRefreshDebounceMs?: number;
}
export interface AgentStatusSelectionKey {
scopeKey: string;
agentSessionId?: string | null;
reasons?: readonly AgentStatusRequestReason[];
}
const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
const DEFAULT_RETAINED_SNAPSHOT_MS = 60 * 60_000;
const DEFAULT_FORCED_REFRESH_DEBOUNCE_MS = 5_000;
const IDLE_SNAPSHOT: AgentStatusControllerSnapshot = {
query: null,
value: null,
phase: "idle",
isRefreshing: false,
errorCode: null
};
interface RetainedStatusValue {
value: AgentStatusValue;
receivedAtUnixMs: number;
}
interface ActiveRequest {
id: number;
cacheKey: string;
frameCount: number;
lastFrameKind: AgentStatusFrame["kind"] | null;
receivedFrame: boolean;
timeout: AgentGuiScheduledTask | null;
unsubscribe: (() => void) | null;
}
function normalizeQuery(query: AgentStatusQuery): AgentStatusQuery | null {
const scopeKey = query.scopeKey.trim();
if (!scopeKey) {
return null;
}
return {
scopeKey,
agentSessionId: query.agentSessionId?.trim() || null,
reason: query.reason,
forceRefresh: query.forceRefresh === true
};
}
function cacheKeyFor(query: AgentStatusQuery): string {
return `${query.scopeKey}\u0000${query.agentSessionId ?? ""}`;
}
/** Selects status only for the exact target and caller-visible Session. */
export function selectAgentStatusControllerSnapshot(
snapshot: AgentStatusControllerSnapshot,
key: AgentStatusSelectionKey
): AgentStatusControllerSnapshot {
const scopeKey = key.scopeKey.trim();
const agentSessionId = key.agentSessionId?.trim() || null;
if (
snapshot.query?.scopeKey !== scopeKey ||
(snapshot.query.agentSessionId?.trim() || null) !== agentSessionId ||
(key.reasons !== undefined && !key.reasons.includes(snapshot.query.reason))
) {
return IDLE_SNAPSHOT;
}
return snapshot;
}
/**
* Creates the shared AgentGUI interaction controller for bounded status reads.
* The host owns transport, authorization and provider probing. The controller
* owns only request visibility, a bounded presentation snapshot, timeout and
* stale-response fencing.
*/
export function createAgentStatusController(
options: AgentStatusControllerOptions
): AgentStatusController {
const now = options.now ?? Date.now;
const requestTimeoutMs =
options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
const retainedSnapshotMs =
options.retainedSnapshotMs ?? DEFAULT_RETAINED_SNAPSHOT_MS;
const forcedRefreshDebounceMs =
options.forcedRefreshDebounceMs ?? DEFAULT_FORCED_REFRESH_DEBOUNCE_MS;
const listeners = new Set<() => void>();
const retained = new Map<string, RetainedStatusValue>();
const lastForcedRefreshAt = new Map<string, number>();
let snapshot = IDLE_SNAPSHOT;
let active: ActiveRequest | null = null;
let nextRequestId = 1;
const publish = (next: AgentStatusControllerSnapshot): void => {
if (snapshot === next) {
return;
}
snapshot = next;
for (const listener of listeners) {
listener();
}
};
const stopActive = (): void => {
const current = active;
active = null;
if (!current) {
return;
}
if (current.timeout !== null) {
current.timeout.cancel();
}
current.unsubscribe?.();
};
const finishRequest = (request: ActiveRequest): boolean => {
if (active?.id !== request.id) {
return false;
}
active = null;
if (request.timeout !== null) {
request.timeout.cancel();
request.timeout = null;
}
const unsubscribe = request.unsubscribe;
request.unsubscribe = null;
unsubscribe?.();
return true;
};
const controller: AgentStatusController = {
getSnapshot: () => snapshot,
subscribe: (listener) => {
listeners.add(listener);
return () => listeners.delete(listener);
},
open: (requestedQuery) => {
const query = normalizeQuery(requestedQuery);
if (!query) {
stopActive();
publish({
query: null,
value: null,
phase: "error",
isRefreshing: false,
errorCode: "invalid_target"
});
return;
}
const cacheKey = cacheKeyFor(query);
const requestedAt = now();
if (query.forceRefresh) {
const previousForcedRefreshAt = lastForcedRefreshAt.get(cacheKey);
if (
previousForcedRefreshAt !== undefined &&
requestedAt - previousForcedRefreshAt < forcedRefreshDebounceMs
) {
return;
}
lastForcedRefreshAt.set(cacheKey, requestedAt);
}
stopActive();
const retainedEntry = retained.get(cacheKey);
const retainedValue =
retainedEntry &&
requestedAt - retainedEntry.receivedAtUnixMs <= retainedSnapshotMs
? retainedEntry.value
: null;
if (retainedEntry && retainedValue === null) {
retained.delete(cacheKey);
}
const request: ActiveRequest = {
id: nextRequestId++,
cacheKey,
frameCount: 0,
lastFrameKind: null,
receivedFrame: false,
timeout: null,
unsubscribe: null
};
active = request;
publish({
query,
value: retainedValue,
phase: retainedValue ? "ready" : "loading",
isRefreshing: true,
errorCode: null
});
request.timeout = agentGuiScheduler.schedule(requestTimeoutMs, () => {
if (active?.id !== request.id) {
return;
}
stopActive();
publish({
...snapshot,
phase: snapshot.value ? "ready" : "error",
isRefreshing: false,
errorCode: "timeout"
});
});
const isCurrent = (): boolean => active?.id === request.id;
const observer: AgentStatusStreamObserver = {
onFrame: (frame) => {
if (!isCurrent()) {
return;
}
const responseSessionId = frame.value.agentSessionId?.trim() || null;
const expectedSessionId = query.agentSessionId?.trim() || null;
const invalidSequence =
request.frameCount >= 2 ||
(request.frameCount === 1 &&
(request.lastFrameKind !== "snapshot" ||
frame.kind !== "refreshed"));
if (invalidSequence || responseSessionId !== expectedSessionId) {
if (!finishRequest(request)) return;
publish({
...snapshot,
phase: snapshot.value ? "ready" : "error",
isRefreshing: false,
errorCode: "unavailable"
});
return;
}
request.frameCount++;
request.lastFrameKind = frame.kind;
request.receivedFrame = true;
retained.set(cacheKey, {
value: frame.value,
receivedAtUnixMs: now()
});
publish({
query,
value: frame.value,
phase: "ready",
isRefreshing: true,
errorCode: null
});
},
onError: (error) => {
if (!finishRequest(request)) {
return;
}
publish({
...snapshot,
phase: snapshot.value ? "ready" : "error",
isRefreshing: false,
errorCode: error.code.trim() || "unavailable"
});
},
onComplete: () => {
if (!finishRequest(request)) {
return;
}
const errorCode = request.receivedFrame
? snapshot.errorCode
: "unavailable";
publish({
...snapshot,
phase: snapshot.value ? "ready" : "error",
isRefreshing: false,
errorCode
});
}
};
try {
const unsubscribe = options.source.open(query, observer);
if (active?.id === request.id) {
request.unsubscribe = unsubscribe;
} else {
unsubscribe();
}
} catch {
observer.onError({ code: "unavailable" });
}
},
close: () => {
stopActive();
if (snapshot.isRefreshing) {
publish({ ...snapshot, isRefreshing: false });
}
},
invalidate: (scopeKey) => {
const normalizedScopeKey = scopeKey?.trim() ?? "";
if (!normalizedScopeKey) {
retained.clear();
lastForcedRefreshAt.clear();
} else {
for (const key of retained.keys()) {
if (key.startsWith(`${normalizedScopeKey}\u0000`)) {
retained.delete(key);
}
}
for (const key of lastForcedRefreshAt.keys()) {
if (key.startsWith(`${normalizedScopeKey}\u0000`)) {
lastForcedRefreshAt.delete(key);
}
}
}
if (
snapshot.query &&
(!normalizedScopeKey || snapshot.query.scopeKey === normalizedScopeKey)
) {
stopActive();
publish(IDLE_SNAPSHOT);
}
}
};
return controller;
}