/** * Owner consent for a runtime's first-run Terms of Service. * * The harness never accepts third-party terms on its own. When a runtime's * Terms screen is seen (on launch, by the smoke test, or on a probe), ONE * decision card per runtime per machine goes to the owner's DM with this * machine's orc bot: "Agree, no data sharing" / "Agree + share data" / * "Don't agree", 24 h, default Don't agree. On the answer: * * - agree: launch the runtime in a dedicated PTY session, drive its setup * screens to the choice (data box verified on screen before Done), check * the prompt appears, close the session, run the runtime smoke test and * report both in the card's thread; * - don't agree (or no answer): mark the runtime "terms not accepted" so the * fallback order skips it (reason shown in Settings → Runtimes); asked * again only when the owner presses Test, re-adds it, or Accept terms…. * * specs/2026-10-01-runtime-terms-consent.md * * @module services/runtime-terms/runtime-terms-consent.service */ import type { OwnerDecision } from '../../types/decision.types.js'; import { type ComponentLogger } from '../core/logger.service.js'; import type { SystemAskInput } from '../decisions/decision.service.js'; import type { SmokeTestResult } from '../runtime-fallback/runtime-smoke-test.service.js'; import type { TermsDriveResult, TermsTerminal } from './antigravity-terms-driver.js'; import type { RuntimeTermsRecord, RuntimeTermsStore } from './runtime-terms.store.js'; /** The owner's three answers. */ export type TermsChoice = 'agree_no_data' | 'agree_share_data' | 'decline'; /** Card text of a runtime. */ export interface TermsCardText { title: string; question: string; body: string[]; } /** What Settings shows next to the inline choices. */ export interface TermsInfo { /** One plain-English sentence on what is asked */ summary: string; /** The pre-checked data-sharing item, verbatim */ dataItem: string; links: Array<{ label: string; url: string; }>; } /** What the service knows about one runtime's Terms screens. */ export interface RuntimeTermsProfile { runtime: string; label: string; info: TermsInfo; /** Card text for this machine */ card(machine: string): TermsCardText; /** Drive the setup screens of a freshly launched runtime */ drive(term: TermsTerminal, shareData: boolean): Promise; /** What a screen shows: the Terms / setup screens, the ready prompt, a refusal, or not yet known */ classify(screen: string): 'terms' | 'ready' | 'blocked' | 'unknown'; } /** Result of {@link RuntimeTermsConsentService.probe}. */ export interface TermsProbeResult { outcome: 'terms' | 'ready' | 'blocked' | 'unknown'; record: RuntimeTermsRecord | null; /** Screen text at the end (secrets redacted) */ screen: string; } /** A launched dedicated session. */ export interface TermsSession { terminal: TermsTerminal; /** Leave the runtime and kill the session (always called) */ close(): Promise; } /** The decision-card calls used. */ export interface TermsDecisionApi { askSystem(input: SystemAskInput): Promise; get(id: string): Promise; chooseFromDashboard(id: string, optionKey: string): Promise; replyInThread(id: string, text: string): Promise; } /** Dependencies. */ export interface RuntimeTermsDeps { store: RuntimeTermsStore; decisions: () => TermsDecisionApi | null; profiles: Record; /** Launch the runtime in the harness's dedicated session */ launch: (runtime: string) => Promise; /** Kill the dedicated session if it is running (Don't agree) */ closeSession?: (runtime: string) => Promise; runSmokeTest: (runtime: string) => Promise; machineName: () => string; /** A runtime's consent changed (availability caches) */ onChange?: (runtime: string) => void; now?: () => Date; sleep?: (ms: number) => Promise; /** How long a probe waits for a recognisable screen */ probeTimeoutMs?: number; logger?: ComponentLogger; } /** How a report was triggered. */ export interface TermsReport { /** launch | smoke_test | probe | settings | chain */ source: string; /** The owner asked (Test, Accept terms…, re-added): re-ask even after Don't agree */ ownerInitiated?: boolean; } /** A runtime's Terms state as the API shows it. */ export interface RuntimeTermsView extends Omit { /** `none` = never seen a Terms screen on this machine */ status: RuntimeTermsRecord['status'] | 'none'; updatedAt?: string; label: string; info: TermsInfo; /** Why the runtime is skipped, when it is */ blockedReason: string | null; /** Choices the inline Settings action offers */ choices: Array<{ choice: TermsChoice; label: string; }>; } /** Choice ↔ button label. */ export declare const TERMS_CHOICE_LABELS: Readonly>; /** * The choice an option label means. * * @param label - Button label * @returns Choice, or null */ export declare function choiceOfLabel(label: string | undefined): TermsChoice | null; /** * Screen text for a Slack code block: secrets redacted, last lines only, no * fence breakers. * * @param screen - Screen text * @returns Block text */ export declare function screenForThread(screen: string): string; /** The service. */ export declare class RuntimeTermsConsentService { private readonly deps; private readonly logger; private readonly now; /** Serialises asks per runtime (launch + smoke test can report at once) */ private readonly asking; /** Running accept / decline per runtime */ private readonly running; /** * @param deps - Dependencies */ constructor(deps: RuntimeTermsDeps); /** * Whether the service handles a runtime. * * @param runtime - Runtime id * @returns True when it has a profile */ supports(runtime: string): boolean; /** * A runtime's Terms screen was seen: ask the owner, once. * * No new card while one is open or the harness is accepting; none after * Don't agree / a failed attempt unless the owner asked * (`ownerInitiated`). * * @param runtime - Runtime id * @param report - Source * @returns The record, or null for a runtime without a profile */ reportTermsScreen(runtime: string, report: TermsReport): Promise; /** * Settings → Runtimes → Accept terms…: post the card (or return the open one). * * @param runtime - Runtime id * @returns The record * @throws Error for a runtime without Terms handling */ requestConsent(runtime: string): Promise; /** * Probe: launch the runtime in the dedicated session, read (never press * anything) until the screen is recognisable, close it. A Terms screen * asks the owner (the owner asked for the check); a ready prompt means * the Terms were accepted here already (e.g. in a terminal). * * @param runtime - Runtime id * @returns What was on screen and the record after * @throws Error while an answer is being applied, or for a runtime without a profile */ probe(runtime: string): Promise; /** * Answer inline from Settings: through the open card when there is one * (so the card updates), else directly. * * @param runtime - Runtime id * @param choice - Owner's choice * @returns The record after the answer was taken (driving continues in the background) */ answer(runtime: string, choice: TermsChoice): Promise; /** * The `runtime_terms` decision-kind handler * (`DecisionService.registerKindHandler`): acts on the answer. No agent * asked, so there is no note for one. * * @param d - Settled decision * @returns null (nothing to deliver) */ onSettled(d: OwnerDecision): Promise; /** * A decision was settled: act on runtime-terms ones. * * @param d - Settled decision */ handleSettled(d: OwnerDecision): void; /** * Why a runtime cannot be used because of its Terms (null = it can). * * @param runtime - Runtime id * @returns Owner-facing reason, or null */ blockedReason(runtime: string): string | null; /** @returns Every runtime with a Terms flow, with its record */ list(): RuntimeTermsView[]; /** * Wait for a running accept / decline (tests, the API's `?wait=1`). * * @param runtime - Runtime id */ whenIdle(runtime: string): Promise; private askOnce; private start; private decline; private accept; private reply; private save; } /** @returns The backend's instance, or null before wiring (and in tests) */ export declare function getRuntimeTermsConsentService(): RuntimeTermsConsentService | null; /** @param service - Instance to install (null clears) */ export declare function setRuntimeTermsConsentService(service: RuntimeTermsConsentService | null): void; /** * Report a Terms screen to the backend's service, when wired (fire and forget). * * @param runtime - Runtime id * @param report - Source */ export declare function reportRuntimeTermsScreen(runtime: string, report: TermsReport): void; //# sourceMappingURL=runtime-terms-consent.service.d.ts.map