108 lines
2.8 KiB
TypeScript
108 lines
2.8 KiB
TypeScript
/**
|
|
* Wire protocol shared between the daemon and the web app.
|
|
*
|
|
* NOTE: web/src/protocol.ts is a copy of this file. Keep the two in sync — they are
|
|
* duplicated rather than shared because the server compiles under NodeNext while the web
|
|
* app compiles under a bundler resolution, and a single rootDir cannot span both.
|
|
*/
|
|
|
|
export type EventKind =
|
|
| "session_start"
|
|
| "session_end"
|
|
| "prompt"
|
|
| "tool_start"
|
|
| "tool_end"
|
|
| "tool_fail"
|
|
| "permission_denied"
|
|
| "turn_end"
|
|
| "turn_error"
|
|
| "notification"
|
|
| "subagent_start"
|
|
| "subagent_end"
|
|
| "compact"
|
|
| "approval_request"
|
|
| "approval_allowed"
|
|
| "approval_denied"
|
|
| "approval_expired";
|
|
|
|
export type SessionState = "working" | "idle" | "waiting" | "error" | "ended";
|
|
|
|
export interface GlanceEvent {
|
|
id: number;
|
|
ts: number;
|
|
sessionId: string;
|
|
kind: EventKind;
|
|
/** Tool name, for tool-shaped events. */
|
|
tool?: string;
|
|
/** One-line human summary, already truncated and redacted. */
|
|
title: string;
|
|
/** Optional second line, e.g. a file path or an error message. */
|
|
detail?: string;
|
|
durationMs?: number;
|
|
}
|
|
|
|
export interface SessionView {
|
|
id: string;
|
|
/** Basename of the workspace root — what you actually recognise on a phone. */
|
|
label: string;
|
|
cwd: string;
|
|
state: SessionState;
|
|
startedAt: number;
|
|
lastActivity: number;
|
|
lastPrompt?: string;
|
|
currentTool?: { name: string; title: string; startedAt: number };
|
|
counts: { tools: number; failures: number; denials: number };
|
|
}
|
|
|
|
export interface PendingApproval {
|
|
id: string;
|
|
sessionId: string;
|
|
sessionLabel: string;
|
|
tool: string;
|
|
title: string;
|
|
detail?: string;
|
|
createdAt: number;
|
|
expiresAt: number;
|
|
}
|
|
|
|
export type ApprovalMode = "off" | "risky" | "all";
|
|
|
|
export interface ApprovalSettings {
|
|
mode: ApprovalMode;
|
|
riskyPattern: string;
|
|
timeoutMs: number;
|
|
/** Skip gating entirely when no browser is streaming, so an unwatched agent never stalls. */
|
|
requireWatcher: boolean;
|
|
/** What to do when nobody answers in time. Allow keeps the agent moving; deny is stricter. */
|
|
onTimeout: "allow" | "deny";
|
|
}
|
|
|
|
export interface Snapshot {
|
|
now: number;
|
|
version: string;
|
|
sessions: SessionView[];
|
|
events: GlanceEvent[];
|
|
pending: PendingApproval[];
|
|
approval: ApprovalSettings;
|
|
}
|
|
|
|
export interface DeviceInfo {
|
|
id: string;
|
|
label: string;
|
|
createdAt: number;
|
|
lastUsedAt?: number;
|
|
}
|
|
|
|
/** Everything the app needs before it knows whether you are signed in. */
|
|
export interface GateInfo {
|
|
authenticated: boolean;
|
|
/** False when no passkey has been enrolled yet — the app then asks for an enrolment code. */
|
|
enrolled: boolean;
|
|
/** True while a one-time enrolment code minted by `glance enroll` is still valid. */
|
|
enrollmentOpen: boolean;
|
|
version: string;
|
|
deviceLabel?: string;
|
|
/** The WebAuthn RP ID in force. Shown so a hostname mismatch is diagnosable from the phone. */
|
|
rpId?: string;
|
|
}
|