Files
grok-glance/server/src/protocol.ts
T
2026-08-09 04:00:13 +00:00

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;
}