@agimon-ai/po-core 0.0.0-alpha.95 → 0.0.0-alpha.97
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/schemas/headless.cjs.map +1 -1
- package/dist/src/schemas/headless.d.mts +2 -0
- package/dist/src/schemas/headless.d.mts.map +1 -1
- package/dist/src/schemas/headless.mjs.map +1 -1
- package/dist/src/services/headlessClient/index.cjs +4 -0
- package/dist/src/services/headlessClient/index.cjs.map +1 -1
- package/dist/src/services/headlessClient/index.mjs +4 -0
- package/dist/src/services/headlessClient/index.mjs.map +1 -1
- package/dist/src/systems/main/adapters/headlessSessionHost.cjs +1 -0
- package/dist/src/systems/main/adapters/headlessSessionHost.cjs.map +1 -1
- package/dist/src/systems/main/adapters/headlessSessionHost.mjs +1 -0
- package/dist/src/systems/main/adapters/headlessSessionHost.mjs.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless.cjs","names":[],"sources":["../../../src/schemas/headless.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport type { CallToolResult, Tool } from '@modelcontextprotocol/server';\nimport type { Static, TSchema } from 'typebox';\n\nimport type { SessionToolSurface } from '../types/server/sessionToolSurface';\nimport type { PoApprovalPolicy, PoApprovalPrompt } from './approval';\nimport type { PoSessionContext } from './hubChannel';\nimport type { PoNotificationRequest } from './notification';\nimport type { PoServerBundleEntry } from './serverBundle';\n\n/** Session-only contributions. This is not a Pi ExtensionAPI or a TUI adapter. */\nexport const PO_HEADLESS_HOST_SERVICE = 'po/headless-host';\nexport const PO_HEADLESS_OWNER = Symbol.for('po/headless-owner');\n\nconst PROMPT_ADMISSION_ERROR = 'PoHeadlessPromptAdmissionError';\n\n/** Admission is a host boundary, not an advisory package-hook failure. */\nexport class PoHeadlessPromptAdmissionError extends Error {\n constructor(cause: unknown) {\n super(cause instanceof Error ? cause.message : String(cause), { cause });\n this.name = PROMPT_ADMISSION_ERROR;\n }\n}\n\nconst SESSION_SETUP_ERROR = 'PoSessionSetupError';\n\n/**\n * A session cannot start until the user fixes their setup, such as configuring a model.\n *\n * Its message is written for the user and says what to do, so a server returns it as is\n * rather than hiding it behind a generic internal error.\n */\nexport class PoSessionSetupError extends Error {\n constructor(message: string) {\n super(message);\n this.name = SESSION_SETUP_ERROR;\n }\n}\n\n/** Error identity must survive independently bundled server and extension graphs. */\nexport function isPoSessionSetupError(error: unknown): error is Error {\n return error instanceof Error && error.name === SESSION_SETUP_ERROR;\n}\n\n/** Error identity must survive independently bundled server and extension graphs. */\nexport function isPoHeadlessPromptAdmissionError(error: unknown): boolean {\n return error instanceof Error && error.name === PROMPT_ADMISSION_ERROR;\n}\nconst TOOL_BUSY_ERROR = 'PoHeadlessToolBusyError';\n\n/** The tool and its hooks have not started. */\nexport class PoHeadlessToolBusyError extends Error {\n constructor() {\n super('An operation is already running');\n this.name = TOOL_BUSY_ERROR;\n }\n}\n\nexport function isPoHeadlessToolBusyError(error: unknown): boolean {\n return error instanceof Error && error.name === TOOL_BUSY_ERROR;\n}\n\nexport interface PoHeadlessSelection {\n readonly majorMode: string;\n readonly activeLayers: readonly string[];\n readonly domains: readonly string[];\n readonly profile?: string;\n /** Feature-owned activation sets, keyed by the contributing capability. */\n readonly state?: Readonly<Record<string, readonly string[]>>;\n}\n\n/** One package-owned selection transition. Server packages change exactly one axis per operation. */\nexport type PoHeadlessSelectionChange =\n | { readonly axis: 'majorMode'; readonly majorMode: string }\n | { readonly axis: 'domains'; readonly domains: readonly string[] }\n | { readonly axis: 'profile'; readonly profile?: string }\n | {\n readonly axis: 'state';\n readonly key: string;\n readonly values: readonly string[];\n };\n\n/** A selection axis a listener re-applies from its config even when its value did not change. */\nexport type PoHeadlessRefreshAxis = 'majorMode' | 'domains' | 'profile';\n\n/** How a selection was applied. `refresh` names axes whose config changed on disk. */\nexport interface PoHeadlessSelectionApplied {\n readonly refresh?: readonly PoHeadlessRefreshAxis[];\n}\n\nexport function readPoHeadlessOwner(context: Context): PoServerBundleEntry | undefined {\n return (context as Context & { [PO_HEADLESS_OWNER]?: PoServerBundleEntry })[PO_HEADLESS_OWNER];\n}\n\nexport interface PoHeadlessRegistration {\n dispose(): void;\n}\n\nexport type PoHeadlessContent = { type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string };\n\nexport interface PoHeadlessToolResult {\n content: PoHeadlessContent[];\n /** Public MCP result data. Internal renderer details are not a remote result contract. */\n structuredContent?: Record<string, unknown>;\n /** Component-only MCP metadata, never inferred from internal renderer details. */\n _meta?: CallToolResult['_meta'];\n details?: unknown;\n isError?: boolean;\n}\n\nlet crcTable: Uint32Array | undefined;\n\nfunction crc32(bytes: Uint8Array, start: number, end: number): number {\n if (crcTable === undefined) {\n crcTable = new Uint32Array(256);\n for (let n = 0; n < 256; n++) {\n let c = n;\n for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n crcTable[n] = c >>> 0;\n }\n }\n let crc = 0xffffffff;\n for (let i = start; i < end; i++) crc = crcTable[(crc ^ bytes[i]!) & 0xff]! ^ (crc >>> 8);\n return (crc ^ 0xffffffff) >>> 0;\n}\n\nconst PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\nfunction startsWith(bytes: Uint8Array, prefix: readonly number[], offset = 0): boolean {\n return prefix.every((byte, index) => bytes[offset + index] === byte);\n}\n\n/** Why the model API would reject this image, or undefined when it looks decodable. Only png, jpeg, gif and webp are checked. */\nexport function invalidImageReason(data: string, mimeType: string): string | undefined {\n let bytes: Uint8Array;\n try {\n bytes = Uint8Array.from(atob(data), (char) => char.charCodeAt(0));\n } catch {\n return 'the data is not valid base64';\n }\n if (mimeType === 'image/png') {\n if (!startsWith(bytes, PNG_SIGNATURE)) return 'the data is not a PNG';\n const view = new DataView(bytes.buffer);\n let offset = 8;\n while (offset + 12 <= bytes.length) {\n const length = view.getUint32(offset);\n const end = offset + 8 + length;\n if (end + 4 > bytes.length) break;\n if (crc32(bytes, offset + 4, end) !== view.getUint32(end)) return 'a PNG chunk failed its checksum';\n if (startsWith(bytes, [0x49, 0x45, 0x4e, 0x44], offset + 4)) return undefined;\n offset = end + 4;\n }\n return 'the PNG is truncated';\n }\n if (mimeType === 'image/jpeg') return startsWith(bytes, [0xff, 0xd8]) ? undefined : 'the data is not a JPEG';\n if (mimeType === 'image/gif')\n return startsWith(bytes, [0x47, 0x49, 0x46, 0x38]) ? undefined : 'the data is not a GIF';\n if (mimeType === 'image/webp')\n return startsWith(bytes, [0x52, 0x49, 0x46, 0x46]) && startsWith(bytes, [0x57, 0x45, 0x42, 0x50], 8)\n ? undefined\n : 'the data is not a WebP';\n return undefined;\n}\n\n/** Keeps valid images and swaps an undecodable one for a text note, since one bad image fails the whole turn and every later one. */\nexport function dropInvalidImages(content: readonly PoHeadlessContent[]): PoHeadlessContent[] {\n return content.map((part) => {\n if (part.type !== 'image') return part;\n const reason = invalidImageReason(part.data, part.mimeType);\n return reason === undefined ? part : { type: 'text', text: `[image omitted: ${reason}]` };\n });\n}\n\n/**\n * Narrows a Pi tool result to the headless contract. Pi types `structuredContent` as any JSON\n * value, while MCP only accepts an object, so anything else is dropped rather than forwarded.\n */\nexport function toPoHeadlessToolResult(result: {\n readonly content: PoHeadlessContent[];\n readonly details?: unknown;\n readonly structuredContent?: unknown;\n readonly _meta?: CallToolResult['_meta'];\n readonly isError?: boolean;\n}): PoHeadlessToolResult {\n const { structuredContent } = result;\n return {\n content: result.content,\n details: result.details,\n ...(result._meta === undefined ? {} : { _meta: result._meta }),\n ...(typeof structuredContent === 'object' && structuredContent !== null && !Array.isArray(structuredContent)\n ? { structuredContent: structuredContent as Record<string, unknown> }\n : {}),\n ...(result.isError === undefined ? {} : { isError: result.isError }),\n };\n}\n\nexport interface PoHeadlessClientRequest {\n kind: 'confirm' | 'select' | 'input';\n purpose?: 'feedback';\n title: string;\n message?: string;\n options?: readonly { label: string; description?: string; value: string }[];\n initialValue?: string;\n multiline?: boolean;\n /** Set only by po-approval: a `select` of Approve/Deny the app draws as an approval card. */\n approval?: PoApprovalPrompt;\n}\n\nexport interface PoHeadlessClient {\n /**\n * Operator-facing surface only: the body is flattened to one line and capped at 4096 characters\n * by schemas/notification.ts:73-83, and the model never sees it. Use session.admitPrompt to\n * reach the model.\n */\n notify(request: PoNotificationRequest): void | Promise<void>;\n request(request: PoHeadlessClientRequest, signal?: AbortSignal): Promise<unknown>;\n /** Only the currently visible feedback question, never an approval or queued request. */\n pendingFeedback?():\n | {\n id: string;\n kind: 'select' | 'input';\n title: string;\n message?: string;\n labels?: string[];\n }\n | undefined;\n /** Returns false for stale ids, queued requests and approvals. */\n answerFeedback?(id: string, value: string): boolean;\n /** Only the visible approval at the head of the dialog queue. */\n pendingApproval?(): PoApprovalPrompt | undefined;\n /** Refuses stale, queued, expired and duplicate answers. Voice cannot answer a human approval. */\n answerApproval?(id: string, decision: 'approved' | 'denied', channel: 'voice' | 'app'): boolean;\n /** Ignored once the session has closed, so a package can clear its status while it unwinds. */\n setStatus(source: string, text: string | undefined): void;\n appendComposerText?(text: string): void;\n}\n\nexport interface PoHeadlessModelSettings {\n model?: { provider: string; id: string };\n thinkingLevel: ThinkingLevel;\n}\n\nexport interface PoHeadlessSession {\n /** Live host close intent, set synchronously before interruption. Absent for a person's stop or remove. */\n readonly closeReason?: 'released' | 'host_shutdown';\n readModelSettings?(): Promise<PoHeadlessModelSettings>;\n setFastMode?(enabled: boolean): Promise<void>;\n setModelSettings?(settings: Partial<PoHeadlessModelSettings>): Promise<void>;\n /** Query durable history on demand; hosts do not retain the entire transcript. */\n entries(query?: {\n type?: 'custom' | 'message' | 'compaction';\n customType?: string;\n limit?: number;\n }): readonly Record<string, unknown>[] | Promise<readonly Record<string, unknown>[]>;\n /** Stream matching history newest first. Stop iteration to stop storage reads. */\n entriesNewestFirst(query?: {\n type?: 'custom' | 'message' | 'compaction';\n customType?: string;\n limit?: number;\n }): AsyncIterable<Record<string, unknown>>;\n /** Write-maintained latest metadata. Missing legacy coverage stays unavailable, with no history fallback. */\n customSnapshot?(\n customType: string,\n validator?: { schemaId: string; accepts: (data: unknown) => boolean },\n ): Promise<{ available: boolean; entry?: Record<string, unknown> }>;\n /** Branch assistant tokens plus requested custom-entry usage, unavailable when historical measurements are incomplete. */\n tokenSnapshot?(customTypes?: readonly string[]): Promise<{ available: boolean; totalTokens?: number }>;\n appendCustomEntry(type: string, data: unknown): Promise<void>;\n /** Append a displayable informational message to history and model context, without admitting input or starting a turn. */\n appendMessage?(message: { type: string; text: string; display?: boolean; details?: unknown }): Promise<string | void>;\n /**\n * Admit one custom message without awaiting its turn, using environmental input policy.\n * Defaults to followUp. Reusing requestId deduplicates normal Durable admission retries;\n * locked-agent appends and deferred operator admission do not provide that guarantee.\n * The receipt's settled resolves when the run that consumed the message ends; a locked agent\n * resolves it at once. Hosts without tracking return nothing.\n */\n admitMessage?(\n message: {\n type: string;\n text: string;\n display?: boolean;\n details?: unknown;\n },\n delivery?: 'steer' | 'followUp',\n requestId?: string,\n ): Promise<{ requestId: string; settled: Promise<void> } | void>;\n /**\n * Register a checkpoint flush before the next tool-turn request. Idle writes return undefined instead of queueing when busy.\n * `resetContext` starts native context at the message, which stands for itself followed by `retainedMessages`.\n */\n registerCheckpointBoundary?(\n callback: (boundary: {\n signal?: AbortSignal;\n appendMessage(\n message: {\n type: string;\n text: string;\n display?: boolean;\n details?: unknown;\n },\n options?: { resetContext?: { retainedMessages: readonly Record<string, unknown>[] } },\n ): Promise<string | undefined>;\n }) => Promise<void>,\n ): {\n appendMessage(\n message: {\n type: string;\n text: string;\n display?: boolean;\n details?: unknown;\n },\n options?: { resetContext?: { retainedMessages: readonly Record<string, unknown>[] } },\n ): Promise<string | undefined>;\n dispose(): void;\n };\n /** Detached canonical model context before package transformations, with context edits applied and no system message. */\n contextMessages?(): Promise<readonly Record<string, unknown>[]>;\n /** The latest measured assistant usage and the selected model's context window. */\n contextUsage?(): Promise<{ tokens: number | null; contextWindow: number } | undefined>;\n /** Label a durable entry without admitting input or starting a turn. */\n setLabel?(entryId: string, label: string | undefined): Promise<void>;\n /**\n * Delivers operator input through prompt preflight and awaits its turn.\n * Environmental input uses admitPrompt with origin omitted instead.\n * An idle agent is woken; busy delivery uses Durable steering or follow-up.\n */\n prompt(text: string, delivery?: 'prompt' | 'steer' | 'followUp'): Promise<void>;\n /**\n * Admits input without awaiting its turn. Environmental input omits origin and never\n * changes the operator queue. Operator origin uses prompt preflight, including voice\n * capture; 'interrupt' cancels the active run and admits operator input while preserving\n * retained queued work. Environmental callers may reuse requestId when retrying the same input.\n */\n admitPrompt?(\n text: string,\n delivery?: 'prompt' | 'steer' | 'followUp' | 'interrupt',\n origin?: 'operator',\n requestId?: string,\n ): Promise<void>;\n /** Stops the current run. With expectedOperationId, a different or finished operation is left alone. */\n abort(expectedOperationId?: string): Promise<void>;\n compact(instructions?: string): Promise<void>;\n /** operationId names the current run, absent while idle or settling. */\n activity(): Promise<{ hasPendingMessages: boolean; isIdle: boolean; operationId?: string }>;\n /** Renames the durable session and publishes the updated title to presentation clients. */\n setName?(name: string): Promise<void>;\n /** The current branch's newest entry, for a child that starts from this session's history. */\n forkSource?(): Promise<{\n kind: 'fork';\n entryId?: string;\n }>;\n}\n\n/** A grant-filtered skill view available only during one remote MCP tool invocation. */\nexport interface PoMcpSkillAccess {\n list(): Promise<readonly { name: string; description: string }[]>;\n read(name: string): Promise<string>;\n}\n\n/** A stateless auxiliary request. These tools are private to this request, never registered on the agent. */\nexport interface PoHeadlessToolCompletionRequest {\n systemPrompt: string;\n input: string;\n maxTokens: number;\n cacheRetention?: 'none' | 'short' | 'long';\n signal?: AbortSignal;\n /** Record auxiliary token usage in this session, including rejected results. */\n recordUsage?: boolean;\n tools: readonly Pick<PoHeadlessTool, 'name' | 'description' | 'parameters'>[];\n}\n\nexport interface PoHeadlessToolCompletionResult {\n toolCalls: readonly { id: string; name: string; arguments: unknown }[];\n usage: { totalTokens: number };\n}\n\n/** Credential-free catalog metadata. Packages cannot obtain auth, headers, or a mutable registry. */\nexport interface PoHeadlessModelMetadata {\n readonly provider: string;\n readonly id: string;\n readonly api?: string;\n readonly baseUrl?: string;\n readonly name?: string;\n readonly compat?: Readonly<Record<string, boolean | string>>;\n}\n\n/** Host policy retained by a supervised tool sequence after its original call returns. */\nexport interface PoHeadlessToolContinuation {\n admit(request: {\n toolCallId: string;\n toolName: string;\n args: Record<string, unknown>;\n signal?: AbortSignal;\n }): Promise<Record<string, unknown>>;\n finalize(request: {\n toolCallId: string;\n toolName: string;\n args: Record<string, unknown>;\n result: PoHeadlessToolResult;\n signal?: AbortSignal;\n }): Promise<PoHeadlessToolResult>;\n}\n\nexport interface PoHeadlessExecutionContext {\n /** Present only on an admitted tool invocation. Retained callbacks recheck current host policy. */\n readonly toolContinuation?: PoHeadlessToolContinuation;\n readonly cwd: string;\n /** The checkout the session runs in, distinct from the tool execution directory. */\n readonly repoRoot: string;\n readonly sessionId: string;\n /** Who invoked the tool: the session's own agent, or a subagent borrowing it. Absent when the host cannot tell. */\n readonly caller?: 'main' | 'child';\n /** The session's baseline context, when its host knows its workspace. */\n readonly sessionContext?: PoSessionContext;\n /** Host-owned presence, also supplied when the session has no workspace context. */\n readonly interaction?: 'interactive' | 'unattended';\n /** Immutable session configuration. Packages must not read or mutate process.env. */\n readonly environment: Readonly<Record<string, string | undefined>>;\n readonly client: PoHeadlessClient;\n readonly session: PoHeadlessSession;\n readonly model?: PoHeadlessModelMetadata;\n /** Lookup in this session's catalog, including physical models named by finalized messages. */\n readonly getModel?: (provider: string, id: string) => PoHeadlessModelMetadata | undefined;\n /** Detached credential-free authenticated chat catalog, read after refreshing provider availability. */\n readonly getAvailableModels?: () => Promise<\n readonly {\n readonly provider: string;\n readonly id: string;\n }[]\n >;\n /** Operation cancellation, supplied only for an active invocation. */\n readonly signal?: AbortSignal;\n readonly textCompletion?: {\n available(reference: string): boolean;\n /** Tool-less conversation summary through the host's guarded session provider. */\n summarize?(\n reference: string,\n request: {\n messages: readonly Record<string, unknown>[];\n instructions: string;\n previousSummary?: string;\n thinkingLevel?: ThinkingLevel;\n signal?: AbortSignal;\n },\n ): Promise<string>;\n complete(\n reference: string,\n request: {\n systemPrompt: string;\n input: string;\n maxTokens: number;\n cacheRetention?: 'none' | 'short' | 'long';\n signal?: AbortSignal;\n /** Record auxiliary usage and reject truncated text rather than returning partial output. */\n recordUsage?: boolean;\n },\n ): Promise<string>;\n };\n readonly toolCompletion?: {\n complete(reference: string, request: PoHeadlessToolCompletionRequest): Promise<PoHeadlessToolCompletionResult>;\n };\n /** Guarded image operation. Credentials and transport configuration remain host-owned. */\n readonly imageGeneration?: {\n available(): boolean;\n generate(request: {\n prompt: string;\n transparentBackground?: boolean;\n images?: readonly { data: string; mimeType: string }[];\n correlationId: string;\n signal?: AbortSignal;\n }): Promise<{ data: string; mimeType: 'image/png'; requestId?: string; generationId?: string }>;\n };\n /**\n * Guarded web search. Credentials, endpoint, model choice, session identity and transport remain host-owned.\n * `commands` follows a schema owned by the calling package; the host only bounds it.\n */\n readonly webSearch?: {\n available(): boolean;\n search(request: {\n commands: Readonly<Record<string, unknown>>;\n /** Optional plain-text excerpt, oldest first. Omit to send no conversation content. */\n conversation?: readonly { role: 'user' | 'assistant'; text: string }[];\n access: 'cached' | 'indexed' | 'live';\n signal?: AbortSignal;\n }): Promise<{ output: string; results?: readonly Record<string, unknown>[] }>;\n };\n readonly selection: PoHeadlessSelection;\n /** Present only while an authenticated remote MCP tool is running. */\n readonly mcpSkills?: PoMcpSkillAccess;\n shutdown(): void;\n}\n\n/** Conditions narrow an eligible owner's contributions; they can never restore a disabled owner. */\nexport interface PoHeadlessCondition {\n state?: Readonly<Record<string, string>>;\n attribution?: { kind: 'minor' | 'domain' | 'shared'; mode: string; label?: string };\n domain?: string;\n /** Any selected domain must match. Absent is unrestricted; empty never matches. */\n domains?: readonly string[];\n}\n\nexport interface PoHeadlessToolRestriction {\n when?: PoHeadlessCondition;\n /** Optional capability ceiling. An empty list disables every tool. */\n allowedTools?: readonly string[];\n /**\n * Narrow exclusions without disabling unrelated tools. Exclude a tool your package owns, or a\n * specific foreign tool the mode exists to withdraw. Never enumerate an allowlist here.\n */\n excludedTools?: readonly string[];\n}\n\nexport interface PoHeadlessTool<TParameters extends TSchema = TSchema> {\n when?: PoHeadlessCondition;\n name: string;\n label?: string;\n description: string;\n parameters: TParameters;\n annotations?: Tool['annotations'];\n outputSchema?: Tool['outputSchema'];\n /** Remote presentation only. Component tool access must be explicitly opted into. */\n _meta?: Tool['_meta'] & {\n ui?: { resourceUri?: string; visibility?: ('model' | 'app')[] };\n };\n promptSnippet?: string;\n promptGuidelines?: readonly string[];\n executionMode?: 'parallel' | 'serial';\n /**\n * Keeps the tool from subagents, which otherwise borrow the parent's tools. Set it when the tool needs\n * the person to answer, since nobody can answer a subagent, or when it acts for the main agent itself.\n */\n mainAgentOnly?: boolean;\n /** Asks the person before each call; see PoApprovalPolicy for who can answer. */\n approval?: PoApprovalPolicy;\n execute(\n toolCallId: string,\n parameters: Static<TParameters>,\n signal: AbortSignal | undefined,\n onUpdate: ((result: PoHeadlessToolResult) => void) | undefined,\n context: PoHeadlessExecutionContext,\n ): Promise<PoHeadlessToolResult>;\n}\n\n/**\n * One package contribution to the session's resource surface.\n *\n * `kind` decides what the model is billed for, and the difference is large:\n * - `context` is EAGER. `read()` runs on every prompt build and the text is pasted\n * into the system prompt verbatim. Reserve it for small live session state that\n * exists nowhere on disk. Never use it for a file the package ships: a README\n * registered this way costs its full length on every single turn.\n * - `skill` is LAZY. Only `name`, `description` and `path` reach the prompt; the\n * agent reads the file itself when the description matches the task.\n * - `prompt` is inert until the user invokes it by name.\n *\n * The rule in one line: if it is a file the package ships, it is a `skill`.\n */\nexport interface PoHeadlessResource {\n when?: PoHeadlessCondition;\n name: string;\n /** Model-visible selection signal. A `skill` without one tells the agent nothing. */\n description?: string;\n /** Absolute path of the file `read()` returns, so an advertised skill is one the agent can open. */\n path?: string;\n /**\n * A `skill` listed in the system prompt itself, kept out of the `load_skill` catalog and\n * `search_skills`. For a few always-relevant guides (the code-mode how-tos) that search would miss.\n */\n pinned?: boolean;\n kind: 'prompt' | 'skill' | 'context';\n read(context: PoHeadlessExecutionContext): string | Promise<string>;\n}\n\nexport interface PoHeadlessCommand {\n when?: PoHeadlessCondition;\n name: string;\n description: string;\n /** Selection-only control allowed after a coherent selection fails admission. */\n selectionRecovery?: boolean;\n execute(args: string, context: PoHeadlessExecutionContext): void | Promise<void>;\n}\n\nexport type PoHeadlessEventName =\n | 'session_start'\n | 'session_shutdown'\n | 'session_tree'\n | 'before_agent_start'\n | 'agent_start'\n | 'agent_end'\n | 'agent_settled'\n | 'turn_start'\n | 'turn_end'\n | 'context'\n | 'before_provider_request'\n | 'message_start'\n | 'message_update'\n | 'message_end'\n | 'tool_call'\n | 'tool_execution_start'\n | 'tool_execution_update'\n | 'tool_execution_end'\n | 'tool_result'\n | 'model_select'\n | 'session_before_compact'\n | 'session_compact';\n\nexport type PoHeadlessHookEvent<E extends PoHeadlessEventName> = E extends 'before_provider_request'\n ? {\n lane: string;\n runId: string;\n model: { provider: string; id: string; api?: string };\n payload: unknown;\n }\n : E extends 'session_before_compact'\n ? {\n reason: string;\n preparation: unknown;\n instructions?: string;\n customInstructions?: string;\n }\n : E extends 'model_select'\n ? { model: { provider: string; id: string } }\n : Readonly<Record<string, unknown>>;\n\nexport interface PoHeadlessCompactionResult {\n summary: string;\n tokensBefore: number;\n retainedTail: Record<string, unknown>[];\n usage?: Record<string, unknown>;\n details?: unknown;\n}\n\nexport type PoHeadlessHookResult<E extends PoHeadlessEventName> = E extends 'before_provider_request'\n ? { payload: unknown } | void\n : E extends 'session_before_compact'\n ? {\n cancel?: boolean;\n decline?: boolean;\n compaction?: PoHeadlessCompactionResult;\n } | void\n : E extends 'context'\n ? { messages?: unknown[]; systemPrompt?: string } | void\n : E extends 'before_agent_start'\n ? { systemPrompt?: string } | void\n : E extends 'tool_call'\n ? {\n args?: Record<string, unknown>;\n block?: { reason: string; terminate?: boolean };\n } | void\n : E extends 'tool_result'\n ? {\n content?: PoHeadlessContent[];\n details?: unknown;\n isError?: boolean;\n usage?: Record<string, unknown>;\n terminate?: boolean;\n } | void\n : unknown;\n\nexport type PoHeadlessHook<E extends PoHeadlessEventName = PoHeadlessEventName> = E extends PoHeadlessEventName\n ? {\n when?: PoHeadlessCondition;\n event: E;\n handle(\n event: Readonly<PoHeadlessHookEvent<E>>,\n context: PoHeadlessExecutionContext,\n ): PoHeadlessHookResult<E> | Promise<PoHeadlessHookResult<E>>;\n }\n : never;\n\n/** A retained package can register activity without starting disabled watchers or services. */\nexport interface PoHeadlessActivity {\n when?: PoHeadlessCondition;\n name: string;\n start(context: PoHeadlessExecutionContext): (() => void | Promise<void>) | Promise<() => void | Promise<void>>;\n}\n\n/** Read-only metadata for the currently applied session, never executable declarations or resource bodies. */\nexport interface PoHeadlessCapability {\n readonly source: string;\n readonly name: string;\n readonly kind: 'tool' | 'skill';\n readonly when?: PoHeadlessCondition;\n readonly active: boolean;\n readonly discoverable: boolean;\n readonly reason?: 'inactive' | 'restricted' | 'shadowed' | 'unavailable' | 'not-discoverable';\n}\n\n/** Visibility controls model discovery only, never admission for external execution. */\nexport function isPoHeadlessToolModelVisible(tool: Pick<PoHeadlessTool, '_meta'>): boolean {\n const visibility = tool._meta?.ui?.visibility;\n return visibility === undefined || visibility.includes('model');\n}\n\nexport interface PoHeadlessCapabilitySnapshot {\n readonly revision: number;\n readonly ready: boolean;\n readonly capabilities: readonly PoHeadlessCapability[];\n}\n\n/** Session skill discovery tools, adapted to the size of the active catalog. */\nexport const PO_LOAD_SKILL_TOOL = 'load_skill';\nexport const PO_SEARCH_SKILLS_TOOL = 'search_skills';\nexport interface PoHeadlessHostService {\n readonly context: PoHeadlessExecutionContext;\n /** Admitted external execution, sharing native hooks, restrictions and session lifetime. */\n readonly toolSurface?: Pick<SessionToolSurface, 'readSurface' | 'invokeTool'>;\n /** The instructions of an applied skill by exact name, or undefined when none is applied. */\n readSkill(name: string): string | undefined;\n /** Names and descriptions of searchable applied skills, excluding pinned guides and instruction bodies. */\n listSkills(): readonly { readonly name: string; readonly description: string }[];\n /** atToolBoundary defers the change to the next tool boundary, as slash commands do; idle sessions apply at once. */\n changeSelection(change: PoHeadlessSelectionChange, options?: { atToolBoundary?: boolean }): Promise<void>;\n assertActive(source?: string): void;\n /** Whether a package owns contributions in the currently applied selection. */\n isSourceActive(source: string): boolean;\n /**\n * Normalize selection before the first successful application only. Disposed with the registering context.\n * Resolvers run in registration order and each may be asynchronous. A resolver that changes the profile\n * decides it for the session, so turn-admission inheritance no longer overrides it; an explicit\n * `changeSelection` still can.\n */\n registerInitialSelectionResolver(\n resolve: (selection: PoHeadlessSelection) => PoHeadlessSelection | Promise<PoHeadlessSelection>,\n ): () => void;\n subscribeSelection(\n listener: (selection: PoHeadlessSelection, applied: PoHeadlessSelectionApplied) => void | Promise<void>,\n ): () => void;\n /** Inspect only a coherently applied generation. A transition returns ready=false and no partial inventory. */\n inspectCapabilities(): PoHeadlessCapabilitySnapshot;\n registerToolRestriction(restriction: PoHeadlessToolRestriction): PoHeadlessRegistration;\n registerTool<TParameters extends TSchema>(tool: PoHeadlessTool<TParameters>): PoHeadlessRegistration;\n registerResource(resource: PoHeadlessResource): PoHeadlessRegistration;\n registerCommand(command: PoHeadlessCommand): PoHeadlessRegistration;\n registerHook(hook: PoHeadlessHook): PoHeadlessRegistration;\n registerActivity(activity: PoHeadlessActivity): PoHeadlessRegistration;\n}\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/headless-host': PoHeadlessHostService;\n }\n}\n\nexport function readPoHeadlessHost(context: Context): PoHeadlessHostService | undefined {\n return context.get(PO_HEADLESS_HOST_SERVICE) as PoHeadlessHostService | undefined;\n}\n\nexport function requirePoHeadlessHost(context: Context): PoHeadlessHostService {\n const host = readPoHeadlessHost(context);\n if (!host) throw new Error('This contribution requires the Po headless session host.');\n return host;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"headless.cjs","names":[],"sources":["../../../src/schemas/headless.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport type { CallToolResult, Tool } from '@modelcontextprotocol/server';\nimport type { Static, TSchema } from 'typebox';\n\nimport type { SessionToolSurface } from '../types/server/sessionToolSurface';\nimport type { PoApprovalPolicy, PoApprovalPrompt } from './approval';\nimport type { PoSessionContext } from './hubChannel';\nimport type { PoNotificationRequest } from './notification';\nimport type { PoServerBundleEntry } from './serverBundle';\n\n/** Session-only contributions. This is not a Pi ExtensionAPI or a TUI adapter. */\nexport const PO_HEADLESS_HOST_SERVICE = 'po/headless-host';\nexport const PO_HEADLESS_OWNER = Symbol.for('po/headless-owner');\n\nconst PROMPT_ADMISSION_ERROR = 'PoHeadlessPromptAdmissionError';\n\n/** Admission is a host boundary, not an advisory package-hook failure. */\nexport class PoHeadlessPromptAdmissionError extends Error {\n constructor(cause: unknown) {\n super(cause instanceof Error ? cause.message : String(cause), { cause });\n this.name = PROMPT_ADMISSION_ERROR;\n }\n}\n\nconst SESSION_SETUP_ERROR = 'PoSessionSetupError';\n\n/**\n * A session cannot start until the user fixes their setup, such as configuring a model.\n *\n * Its message is written for the user and says what to do, so a server returns it as is\n * rather than hiding it behind a generic internal error.\n */\nexport class PoSessionSetupError extends Error {\n constructor(message: string) {\n super(message);\n this.name = SESSION_SETUP_ERROR;\n }\n}\n\n/** Error identity must survive independently bundled server and extension graphs. */\nexport function isPoSessionSetupError(error: unknown): error is Error {\n return error instanceof Error && error.name === SESSION_SETUP_ERROR;\n}\n\n/** Error identity must survive independently bundled server and extension graphs. */\nexport function isPoHeadlessPromptAdmissionError(error: unknown): boolean {\n return error instanceof Error && error.name === PROMPT_ADMISSION_ERROR;\n}\nconst TOOL_BUSY_ERROR = 'PoHeadlessToolBusyError';\n\n/** The tool and its hooks have not started. */\nexport class PoHeadlessToolBusyError extends Error {\n constructor() {\n super('An operation is already running');\n this.name = TOOL_BUSY_ERROR;\n }\n}\n\nexport function isPoHeadlessToolBusyError(error: unknown): boolean {\n return error instanceof Error && error.name === TOOL_BUSY_ERROR;\n}\n\nexport interface PoHeadlessSelection {\n readonly majorMode: string;\n readonly activeLayers: readonly string[];\n readonly domains: readonly string[];\n readonly profile?: string;\n /** Feature-owned activation sets, keyed by the contributing capability. */\n readonly state?: Readonly<Record<string, readonly string[]>>;\n}\n\n/** One package-owned selection transition. Server packages change exactly one axis per operation. */\nexport type PoHeadlessSelectionChange =\n | { readonly axis: 'majorMode'; readonly majorMode: string }\n | { readonly axis: 'domains'; readonly domains: readonly string[] }\n | { readonly axis: 'profile'; readonly profile?: string }\n | {\n readonly axis: 'state';\n readonly key: string;\n readonly values: readonly string[];\n };\n\n/** A selection axis a listener re-applies from its config even when its value did not change. */\nexport type PoHeadlessRefreshAxis = 'majorMode' | 'domains' | 'profile';\n\n/** How a selection was applied. `refresh` names axes whose config changed on disk. */\nexport interface PoHeadlessSelectionApplied {\n readonly refresh?: readonly PoHeadlessRefreshAxis[];\n}\n\nexport function readPoHeadlessOwner(context: Context): PoServerBundleEntry | undefined {\n return (context as Context & { [PO_HEADLESS_OWNER]?: PoServerBundleEntry })[PO_HEADLESS_OWNER];\n}\n\nexport interface PoHeadlessRegistration {\n dispose(): void;\n}\n\nexport type PoHeadlessContent = { type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string };\n\nexport interface PoHeadlessToolResult {\n content: PoHeadlessContent[];\n /** Public MCP result data. Internal renderer details are not a remote result contract. */\n structuredContent?: Record<string, unknown>;\n /** Component-only MCP metadata, never inferred from internal renderer details. */\n _meta?: CallToolResult['_meta'];\n details?: unknown;\n isError?: boolean;\n}\n\nlet crcTable: Uint32Array | undefined;\n\nfunction crc32(bytes: Uint8Array, start: number, end: number): number {\n if (crcTable === undefined) {\n crcTable = new Uint32Array(256);\n for (let n = 0; n < 256; n++) {\n let c = n;\n for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n crcTable[n] = c >>> 0;\n }\n }\n let crc = 0xffffffff;\n for (let i = start; i < end; i++) crc = crcTable[(crc ^ bytes[i]!) & 0xff]! ^ (crc >>> 8);\n return (crc ^ 0xffffffff) >>> 0;\n}\n\nconst PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\nfunction startsWith(bytes: Uint8Array, prefix: readonly number[], offset = 0): boolean {\n return prefix.every((byte, index) => bytes[offset + index] === byte);\n}\n\n/** Why the model API would reject this image, or undefined when it looks decodable. Only png, jpeg, gif and webp are checked. */\nexport function invalidImageReason(data: string, mimeType: string): string | undefined {\n let bytes: Uint8Array;\n try {\n bytes = Uint8Array.from(atob(data), (char) => char.charCodeAt(0));\n } catch {\n return 'the data is not valid base64';\n }\n if (mimeType === 'image/png') {\n if (!startsWith(bytes, PNG_SIGNATURE)) return 'the data is not a PNG';\n const view = new DataView(bytes.buffer);\n let offset = 8;\n while (offset + 12 <= bytes.length) {\n const length = view.getUint32(offset);\n const end = offset + 8 + length;\n if (end + 4 > bytes.length) break;\n if (crc32(bytes, offset + 4, end) !== view.getUint32(end)) return 'a PNG chunk failed its checksum';\n if (startsWith(bytes, [0x49, 0x45, 0x4e, 0x44], offset + 4)) return undefined;\n offset = end + 4;\n }\n return 'the PNG is truncated';\n }\n if (mimeType === 'image/jpeg') return startsWith(bytes, [0xff, 0xd8]) ? undefined : 'the data is not a JPEG';\n if (mimeType === 'image/gif')\n return startsWith(bytes, [0x47, 0x49, 0x46, 0x38]) ? undefined : 'the data is not a GIF';\n if (mimeType === 'image/webp')\n return startsWith(bytes, [0x52, 0x49, 0x46, 0x46]) && startsWith(bytes, [0x57, 0x45, 0x42, 0x50], 8)\n ? undefined\n : 'the data is not a WebP';\n return undefined;\n}\n\n/** Keeps valid images and swaps an undecodable one for a text note, since one bad image fails the whole turn and every later one. */\nexport function dropInvalidImages(content: readonly PoHeadlessContent[]): PoHeadlessContent[] {\n return content.map((part) => {\n if (part.type !== 'image') return part;\n const reason = invalidImageReason(part.data, part.mimeType);\n return reason === undefined ? part : { type: 'text', text: `[image omitted: ${reason}]` };\n });\n}\n\n/**\n * Narrows a Pi tool result to the headless contract. Pi types `structuredContent` as any JSON\n * value, while MCP only accepts an object, so anything else is dropped rather than forwarded.\n */\nexport function toPoHeadlessToolResult(result: {\n readonly content: PoHeadlessContent[];\n readonly details?: unknown;\n readonly structuredContent?: unknown;\n readonly _meta?: CallToolResult['_meta'];\n readonly isError?: boolean;\n}): PoHeadlessToolResult {\n const { structuredContent } = result;\n return {\n content: result.content,\n details: result.details,\n ...(result._meta === undefined ? {} : { _meta: result._meta }),\n ...(typeof structuredContent === 'object' && structuredContent !== null && !Array.isArray(structuredContent)\n ? { structuredContent: structuredContent as Record<string, unknown> }\n : {}),\n ...(result.isError === undefined ? {} : { isError: result.isError }),\n };\n}\n\nexport interface PoHeadlessClientRequest {\n kind: 'confirm' | 'select' | 'input';\n purpose?: 'feedback';\n title: string;\n message?: string;\n /** `icon` is a bounded image `data:` URL, as in a profile identity; a client may draw it beside the label. */\n options?: readonly { label: string; description?: string; icon?: string; value: string }[];\n initialValue?: string;\n multiline?: boolean;\n /** Set only by po-approval: a `select` of Approve/Deny the app draws as an approval card. */\n approval?: PoApprovalPrompt;\n}\n\nexport interface PoHeadlessClient {\n /**\n * Operator-facing surface only: the body is flattened to one line and capped at 4096 characters\n * by schemas/notification.ts:73-83, and the model never sees it. Use session.admitPrompt to\n * reach the model.\n */\n notify(request: PoNotificationRequest): void | Promise<void>;\n request(request: PoHeadlessClientRequest, signal?: AbortSignal): Promise<unknown>;\n /** Only the currently visible feedback question, never an approval or queued request. */\n pendingFeedback?():\n | {\n id: string;\n kind: 'select' | 'input';\n title: string;\n message?: string;\n labels?: string[];\n }\n | undefined;\n /** Returns false for stale ids, queued requests and approvals. */\n answerFeedback?(id: string, value: string): boolean;\n /** Only the visible approval at the head of the dialog queue. */\n pendingApproval?(): PoApprovalPrompt | undefined;\n /** Refuses stale, queued, expired and duplicate answers. Voice cannot answer a human approval. */\n answerApproval?(id: string, decision: 'approved' | 'denied', channel: 'voice' | 'app'): boolean;\n /** Ignored once the session has closed, so a package can clear its status while it unwinds. */\n setStatus(source: string, text: string | undefined): void;\n appendComposerText?(text: string): void;\n}\n\nexport interface PoHeadlessModelSettings {\n model?: { provider: string; id: string };\n thinkingLevel: ThinkingLevel;\n}\n\nexport interface PoHeadlessSession {\n /** Live host close intent, set synchronously before interruption. Absent for a person's stop or remove. */\n readonly closeReason?: 'released' | 'host_shutdown';\n readModelSettings?(): Promise<PoHeadlessModelSettings>;\n setFastMode?(enabled: boolean): Promise<void>;\n setModelSettings?(settings: Partial<PoHeadlessModelSettings>): Promise<void>;\n /** Query durable history on demand; hosts do not retain the entire transcript. */\n entries(query?: {\n type?: 'custom' | 'message' | 'compaction';\n customType?: string;\n limit?: number;\n }): readonly Record<string, unknown>[] | Promise<readonly Record<string, unknown>[]>;\n /** Stream matching history newest first. Stop iteration to stop storage reads. */\n entriesNewestFirst(query?: {\n type?: 'custom' | 'message' | 'compaction';\n customType?: string;\n limit?: number;\n }): AsyncIterable<Record<string, unknown>>;\n /** Write-maintained latest metadata. Missing legacy coverage stays unavailable, with no history fallback. */\n customSnapshot?(\n customType: string,\n validator?: { schemaId: string; accepts: (data: unknown) => boolean },\n ): Promise<{ available: boolean; entry?: Record<string, unknown> }>;\n /** Branch assistant tokens plus requested custom-entry usage, unavailable when historical measurements are incomplete. */\n tokenSnapshot?(customTypes?: readonly string[]): Promise<{ available: boolean; totalTokens?: number }>;\n appendCustomEntry(type: string, data: unknown): Promise<void>;\n /** Append a displayable informational message to history and model context, without admitting input or starting a turn. */\n appendMessage?(message: { type: string; text: string; display?: boolean; details?: unknown }): Promise<string | void>;\n /**\n * Admit one custom message without awaiting its turn, using environmental input policy.\n * Defaults to followUp. Reusing requestId deduplicates normal Durable admission retries;\n * locked-agent appends and deferred operator admission do not provide that guarantee.\n * The receipt's settled resolves when the run that consumed the message ends; a locked agent\n * resolves it at once. Hosts without tracking return nothing.\n */\n admitMessage?(\n message: {\n type: string;\n text: string;\n display?: boolean;\n details?: unknown;\n },\n delivery?: 'steer' | 'followUp',\n requestId?: string,\n ): Promise<{ requestId: string; settled: Promise<void> } | void>;\n /**\n * Register a checkpoint flush before the next tool-turn request. Idle writes return undefined instead of queueing when busy.\n * `resetContext` starts native context at the message, which stands for itself followed by `retainedMessages`.\n */\n registerCheckpointBoundary?(\n callback: (boundary: {\n signal?: AbortSignal;\n appendMessage(\n message: {\n type: string;\n text: string;\n display?: boolean;\n details?: unknown;\n },\n options?: { resetContext?: { retainedMessages: readonly Record<string, unknown>[] } },\n ): Promise<string | undefined>;\n }) => Promise<void>,\n ): {\n appendMessage(\n message: {\n type: string;\n text: string;\n display?: boolean;\n details?: unknown;\n },\n options?: { resetContext?: { retainedMessages: readonly Record<string, unknown>[] } },\n ): Promise<string | undefined>;\n dispose(): void;\n };\n /** Detached canonical model context before package transformations, with context edits applied and no system message. */\n contextMessages?(): Promise<readonly Record<string, unknown>[]>;\n /** The latest measured assistant usage and the selected model's context window. */\n contextUsage?(): Promise<{ tokens: number | null; contextWindow: number } | undefined>;\n /** Label a durable entry without admitting input or starting a turn. */\n setLabel?(entryId: string, label: string | undefined): Promise<void>;\n /**\n * Delivers operator input through prompt preflight and awaits its turn.\n * Environmental input uses admitPrompt with origin omitted instead.\n * An idle agent is woken; busy delivery uses Durable steering or follow-up.\n */\n prompt(text: string, delivery?: 'prompt' | 'steer' | 'followUp'): Promise<void>;\n /**\n * Admits input without awaiting its turn. Environmental input omits origin and never\n * changes the operator queue. Operator origin uses prompt preflight, including voice\n * capture; 'interrupt' cancels the active run and admits operator input while preserving\n * retained queued work. Environmental callers may reuse requestId when retrying the same input.\n */\n admitPrompt?(\n text: string,\n delivery?: 'prompt' | 'steer' | 'followUp' | 'interrupt',\n origin?: 'operator',\n requestId?: string,\n ): Promise<void>;\n /** Stops the current run. With expectedOperationId, a different or finished operation is left alone. */\n abort(expectedOperationId?: string): Promise<void>;\n compact(instructions?: string): Promise<void>;\n /** operationId names the current run, absent while idle or settling. */\n activity(): Promise<{ hasPendingMessages: boolean; isIdle: boolean; operationId?: string }>;\n /** Renames the durable session and publishes the updated title to presentation clients. */\n setName?(name: string): Promise<void>;\n /** The current branch's newest entry, for a child that starts from this session's history. */\n forkSource?(): Promise<{\n kind: 'fork';\n entryId?: string;\n }>;\n}\n\n/** A grant-filtered skill view available only during one remote MCP tool invocation. */\nexport interface PoMcpSkillAccess {\n list(): Promise<readonly { name: string; description: string }[]>;\n read(name: string): Promise<string>;\n}\n\n/** A stateless auxiliary request. These tools are private to this request, never registered on the agent. */\nexport interface PoHeadlessToolCompletionRequest {\n systemPrompt: string;\n input: string;\n maxTokens: number;\n cacheRetention?: 'none' | 'short' | 'long';\n signal?: AbortSignal;\n /** Record auxiliary token usage in this session, including rejected results. */\n recordUsage?: boolean;\n tools: readonly Pick<PoHeadlessTool, 'name' | 'description' | 'parameters'>[];\n}\n\nexport interface PoHeadlessToolCompletionResult {\n toolCalls: readonly { id: string; name: string; arguments: unknown }[];\n usage: { totalTokens: number };\n}\n\n/** Credential-free catalog metadata. Packages cannot obtain auth, headers, or a mutable registry. */\nexport interface PoHeadlessModelMetadata {\n readonly provider: string;\n readonly id: string;\n readonly api?: string;\n readonly baseUrl?: string;\n readonly name?: string;\n readonly compat?: Readonly<Record<string, boolean | string>>;\n}\n\n/** Host policy retained by a supervised tool sequence after its original call returns. */\nexport interface PoHeadlessToolContinuation {\n admit(request: {\n toolCallId: string;\n toolName: string;\n args: Record<string, unknown>;\n signal?: AbortSignal;\n }): Promise<Record<string, unknown>>;\n finalize(request: {\n toolCallId: string;\n toolName: string;\n args: Record<string, unknown>;\n result: PoHeadlessToolResult;\n signal?: AbortSignal;\n }): Promise<PoHeadlessToolResult>;\n}\n\nexport interface PoHeadlessExecutionContext {\n /** Present only on an admitted tool invocation. Retained callbacks recheck current host policy. */\n readonly toolContinuation?: PoHeadlessToolContinuation;\n readonly cwd: string;\n /** The checkout the session runs in, distinct from the tool execution directory. */\n readonly repoRoot: string;\n readonly sessionId: string;\n /** Who invoked the tool: the session's own agent, or a subagent borrowing it. Absent when the host cannot tell. */\n readonly caller?: 'main' | 'child';\n /** The session's baseline context, when its host knows its workspace. */\n readonly sessionContext?: PoSessionContext;\n /** Host-owned presence, also supplied when the session has no workspace context. */\n readonly interaction?: 'interactive' | 'unattended';\n /** Immutable session configuration. Packages must not read or mutate process.env. */\n readonly environment: Readonly<Record<string, string | undefined>>;\n readonly client: PoHeadlessClient;\n readonly session: PoHeadlessSession;\n readonly model?: PoHeadlessModelMetadata;\n /** Lookup in this session's catalog, including physical models named by finalized messages. */\n readonly getModel?: (provider: string, id: string) => PoHeadlessModelMetadata | undefined;\n /** Detached credential-free authenticated chat catalog, read after refreshing provider availability. */\n readonly getAvailableModels?: () => Promise<\n readonly {\n readonly provider: string;\n readonly id: string;\n }[]\n >;\n /** Operation cancellation, supplied only for an active invocation. */\n readonly signal?: AbortSignal;\n readonly textCompletion?: {\n available(reference: string): boolean;\n /** Tool-less conversation summary through the host's guarded session provider. */\n summarize?(\n reference: string,\n request: {\n messages: readonly Record<string, unknown>[];\n instructions: string;\n previousSummary?: string;\n thinkingLevel?: ThinkingLevel;\n signal?: AbortSignal;\n },\n ): Promise<string>;\n complete(\n reference: string,\n request: {\n systemPrompt: string;\n input: string;\n maxTokens: number;\n cacheRetention?: 'none' | 'short' | 'long';\n signal?: AbortSignal;\n /** Record auxiliary usage and reject truncated text rather than returning partial output. */\n recordUsage?: boolean;\n },\n ): Promise<string>;\n };\n readonly toolCompletion?: {\n complete(reference: string, request: PoHeadlessToolCompletionRequest): Promise<PoHeadlessToolCompletionResult>;\n };\n /** Guarded image operation. Credentials and transport configuration remain host-owned. */\n readonly imageGeneration?: {\n available(): boolean;\n generate(request: {\n prompt: string;\n transparentBackground?: boolean;\n images?: readonly { data: string; mimeType: string }[];\n correlationId: string;\n signal?: AbortSignal;\n }): Promise<{ data: string; mimeType: 'image/png'; requestId?: string; generationId?: string }>;\n };\n /**\n * Guarded web search. Credentials, endpoint, model choice, session identity and transport remain host-owned.\n * `commands` follows a schema owned by the calling package; the host only bounds it.\n */\n readonly webSearch?: {\n available(): boolean;\n search(request: {\n commands: Readonly<Record<string, unknown>>;\n /** Optional plain-text excerpt, oldest first. Omit to send no conversation content. */\n conversation?: readonly { role: 'user' | 'assistant'; text: string }[];\n access: 'cached' | 'indexed' | 'live';\n signal?: AbortSignal;\n }): Promise<{ output: string; results?: readonly Record<string, unknown>[] }>;\n };\n readonly selection: PoHeadlessSelection;\n /** Present only while an authenticated remote MCP tool is running. */\n readonly mcpSkills?: PoMcpSkillAccess;\n shutdown(): void;\n}\n\n/** Conditions narrow an eligible owner's contributions; they can never restore a disabled owner. */\nexport interface PoHeadlessCondition {\n state?: Readonly<Record<string, string>>;\n attribution?: { kind: 'minor' | 'domain' | 'shared'; mode: string; label?: string };\n domain?: string;\n /** Any selected domain must match. Absent is unrestricted; empty never matches. */\n domains?: readonly string[];\n}\n\nexport interface PoHeadlessToolRestriction {\n when?: PoHeadlessCondition;\n /** Optional capability ceiling. An empty list disables every tool. */\n allowedTools?: readonly string[];\n /**\n * Narrow exclusions without disabling unrelated tools. Exclude a tool your package owns, or a\n * specific foreign tool the mode exists to withdraw. Never enumerate an allowlist here.\n */\n excludedTools?: readonly string[];\n}\n\nexport interface PoHeadlessTool<TParameters extends TSchema = TSchema> {\n when?: PoHeadlessCondition;\n name: string;\n label?: string;\n description: string;\n parameters: TParameters;\n annotations?: Tool['annotations'];\n outputSchema?: Tool['outputSchema'];\n /** Remote presentation only. Component tool access must be explicitly opted into. */\n _meta?: Tool['_meta'] & {\n ui?: { resourceUri?: string; visibility?: ('model' | 'app')[] };\n };\n promptSnippet?: string;\n promptGuidelines?: readonly string[];\n executionMode?: 'parallel' | 'serial';\n /**\n * Keeps the tool from subagents, which otherwise borrow the parent's tools. Set it when the tool needs\n * the person to answer, since nobody can answer a subagent, or when it acts for the main agent itself.\n */\n mainAgentOnly?: boolean;\n /** Asks the person before each call; see PoApprovalPolicy for who can answer. */\n approval?: PoApprovalPolicy;\n execute(\n toolCallId: string,\n parameters: Static<TParameters>,\n signal: AbortSignal | undefined,\n onUpdate: ((result: PoHeadlessToolResult) => void) | undefined,\n context: PoHeadlessExecutionContext,\n ): Promise<PoHeadlessToolResult>;\n}\n\n/**\n * One package contribution to the session's resource surface.\n *\n * `kind` decides what the model is billed for, and the difference is large:\n * - `context` is EAGER. `read()` runs on every prompt build and the text is pasted\n * into the system prompt verbatim. Reserve it for small live session state that\n * exists nowhere on disk. Never use it for a file the package ships: a README\n * registered this way costs its full length on every single turn.\n * - `skill` is LAZY. Only `name`, `description` and `path` reach the prompt; the\n * agent reads the file itself when the description matches the task.\n * - `prompt` is inert until the user invokes it by name.\n *\n * The rule in one line: if it is a file the package ships, it is a `skill`.\n */\nexport interface PoHeadlessResource {\n when?: PoHeadlessCondition;\n name: string;\n /** Model-visible selection signal. A `skill` without one tells the agent nothing. */\n description?: string;\n /** Absolute path of the file `read()` returns, so an advertised skill is one the agent can open. */\n path?: string;\n /**\n * A `skill` listed in the system prompt itself, kept out of the `load_skill` catalog and\n * `search_skills`. For a few always-relevant guides (the code-mode how-tos) that search would miss.\n */\n pinned?: boolean;\n kind: 'prompt' | 'skill' | 'context';\n read(context: PoHeadlessExecutionContext): string | Promise<string>;\n}\n\nexport interface PoHeadlessCommand {\n when?: PoHeadlessCondition;\n name: string;\n description: string;\n /** Selection-only control allowed after a coherent selection fails admission. */\n selectionRecovery?: boolean;\n execute(args: string, context: PoHeadlessExecutionContext): void | Promise<void>;\n}\n\nexport type PoHeadlessEventName =\n | 'session_start'\n | 'session_shutdown'\n | 'session_tree'\n | 'before_agent_start'\n | 'agent_start'\n | 'agent_end'\n | 'agent_settled'\n | 'turn_start'\n | 'turn_end'\n | 'context'\n | 'before_provider_request'\n | 'message_start'\n | 'message_update'\n | 'message_end'\n | 'tool_call'\n | 'tool_execution_start'\n | 'tool_execution_update'\n | 'tool_execution_end'\n | 'tool_result'\n | 'model_select'\n | 'session_before_compact'\n | 'session_compact';\n\nexport type PoHeadlessHookEvent<E extends PoHeadlessEventName> = E extends 'before_provider_request'\n ? {\n lane: string;\n runId: string;\n model: { provider: string; id: string; api?: string };\n payload: unknown;\n }\n : E extends 'session_before_compact'\n ? {\n reason: string;\n preparation: unknown;\n instructions?: string;\n customInstructions?: string;\n }\n : E extends 'model_select'\n ? { model: { provider: string; id: string } }\n : Readonly<Record<string, unknown>>;\n\nexport interface PoHeadlessCompactionResult {\n summary: string;\n tokensBefore: number;\n retainedTail: Record<string, unknown>[];\n usage?: Record<string, unknown>;\n details?: unknown;\n}\n\nexport type PoHeadlessHookResult<E extends PoHeadlessEventName> = E extends 'before_provider_request'\n ? { payload: unknown } | void\n : E extends 'session_before_compact'\n ? {\n cancel?: boolean;\n decline?: boolean;\n compaction?: PoHeadlessCompactionResult;\n } | void\n : E extends 'context'\n ? { messages?: unknown[]; systemPrompt?: string } | void\n : E extends 'before_agent_start'\n ? { systemPrompt?: string } | void\n : E extends 'tool_call'\n ? {\n args?: Record<string, unknown>;\n block?: { reason: string; terminate?: boolean };\n } | void\n : E extends 'tool_result'\n ? {\n content?: PoHeadlessContent[];\n details?: unknown;\n isError?: boolean;\n usage?: Record<string, unknown>;\n terminate?: boolean;\n } | void\n : unknown;\n\nexport type PoHeadlessHook<E extends PoHeadlessEventName = PoHeadlessEventName> = E extends PoHeadlessEventName\n ? {\n when?: PoHeadlessCondition;\n event: E;\n handle(\n event: Readonly<PoHeadlessHookEvent<E>>,\n context: PoHeadlessExecutionContext,\n ): PoHeadlessHookResult<E> | Promise<PoHeadlessHookResult<E>>;\n }\n : never;\n\n/** A retained package can register activity without starting disabled watchers or services. */\nexport interface PoHeadlessActivity {\n when?: PoHeadlessCondition;\n name: string;\n start(context: PoHeadlessExecutionContext): (() => void | Promise<void>) | Promise<() => void | Promise<void>>;\n}\n\n/** Read-only metadata for the currently applied session, never executable declarations or resource bodies. */\nexport interface PoHeadlessCapability {\n readonly source: string;\n readonly name: string;\n readonly kind: 'tool' | 'skill';\n readonly when?: PoHeadlessCondition;\n readonly active: boolean;\n readonly discoverable: boolean;\n readonly reason?: 'inactive' | 'restricted' | 'shadowed' | 'unavailable' | 'not-discoverable';\n}\n\n/** Visibility controls model discovery only, never admission for external execution. */\nexport function isPoHeadlessToolModelVisible(tool: Pick<PoHeadlessTool, '_meta'>): boolean {\n const visibility = tool._meta?.ui?.visibility;\n return visibility === undefined || visibility.includes('model');\n}\n\nexport interface PoHeadlessCapabilitySnapshot {\n readonly revision: number;\n readonly ready: boolean;\n readonly capabilities: readonly PoHeadlessCapability[];\n}\n\n/** Session skill discovery tools, adapted to the size of the active catalog. */\nexport const PO_LOAD_SKILL_TOOL = 'load_skill';\nexport const PO_SEARCH_SKILLS_TOOL = 'search_skills';\nexport interface PoHeadlessHostService {\n readonly context: PoHeadlessExecutionContext;\n /** Admitted external execution, sharing native hooks, restrictions and session lifetime. */\n readonly toolSurface?: Pick<SessionToolSurface, 'readSurface' | 'invokeTool'>;\n /** The instructions of an applied skill by exact name, or undefined when none is applied. */\n readSkill(name: string): string | undefined;\n /** Names and descriptions of searchable applied skills, excluding pinned guides and instruction bodies. */\n listSkills(): readonly { readonly name: string; readonly description: string }[];\n /** atToolBoundary defers the change to the next tool boundary, as slash commands do; idle sessions apply at once. */\n changeSelection(change: PoHeadlessSelectionChange, options?: { atToolBoundary?: boolean }): Promise<void>;\n assertActive(source?: string): void;\n /** Whether a package owns contributions in the currently applied selection. */\n isSourceActive(source: string): boolean;\n /**\n * Normalize selection before the first successful application only. Disposed with the registering context.\n * Resolvers run in registration order and each may be asynchronous. A resolver that changes the profile\n * decides it for the session, so turn-admission inheritance no longer overrides it; an explicit\n * `changeSelection` still can.\n */\n registerInitialSelectionResolver(\n resolve: (selection: PoHeadlessSelection) => PoHeadlessSelection | Promise<PoHeadlessSelection>,\n ): () => void;\n subscribeSelection(\n listener: (selection: PoHeadlessSelection, applied: PoHeadlessSelectionApplied) => void | Promise<void>,\n ): () => void;\n /** Inspect only a coherently applied generation. A transition returns ready=false and no partial inventory. */\n inspectCapabilities(): PoHeadlessCapabilitySnapshot;\n registerToolRestriction(restriction: PoHeadlessToolRestriction): PoHeadlessRegistration;\n registerTool<TParameters extends TSchema>(tool: PoHeadlessTool<TParameters>): PoHeadlessRegistration;\n registerResource(resource: PoHeadlessResource): PoHeadlessRegistration;\n registerCommand(command: PoHeadlessCommand): PoHeadlessRegistration;\n registerHook(hook: PoHeadlessHook): PoHeadlessRegistration;\n registerActivity(activity: PoHeadlessActivity): PoHeadlessRegistration;\n}\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/headless-host': PoHeadlessHostService;\n }\n}\n\nexport function readPoHeadlessHost(context: Context): PoHeadlessHostService | undefined {\n return context.get(PO_HEADLESS_HOST_SERVICE) as PoHeadlessHostService | undefined;\n}\n\nexport function requirePoHeadlessHost(context: Context): PoHeadlessHostService {\n const host = readPoHeadlessHost(context);\n if (!host) throw new Error('This contribution requires the Po headless session host.');\n return host;\n}\n"],"mappings":"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"}
|
|
@@ -104,9 +104,11 @@ export interface PoHeadlessClientRequest {
|
|
|
104
104
|
purpose?: 'feedback';
|
|
105
105
|
title: string;
|
|
106
106
|
message?: string;
|
|
107
|
+
/** `icon` is a bounded image `data:` URL, as in a profile identity; a client may draw it beside the label. */
|
|
107
108
|
options?: readonly {
|
|
108
109
|
label: string;
|
|
109
110
|
description?: string;
|
|
111
|
+
icon?: string;
|
|
110
112
|
value: string;
|
|
111
113
|
}[];
|
|
112
114
|
initialValue?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless.d.mts","names":[],"sources":["../../../src/schemas/headless.ts"],"mappings":";;;;;;;;;;;;qBAYa;qBACA;;qBAKA,uCAAuC;EAClD,YAAY;;;;;;;;qBAcD,4BAA4B;EACvC,YAAY;;;wBAOE,sBAAsB,iBAAiB,SAAS;;wBAKhD,iCAAiC;;qBAMpC,gCAAgC;EAC3C;;wBAMc,0BAA0B;iBAIzB;WACN;WACA;WACA;WACA;;WAEA,QAAQ,SAAS;;;YAIhB;WACG;WAA4B;;WAC5B;WAA0B;;WAC1B;WAA0B;;WAE1B;WACA;WACA;;;YAIH;;iBAGK;WACN,mBAAmB;;wBAGd,oBAAoB,SAAS,UAAU;iBAItC;EACf;;YAGU;EAAsB;EAAc;;EAAmB;EAAe;EAAc;;iBAE/E;EACf,SAAS;;EAET,oBAAoB;;EAEpB,QAAQ;EACR;EACA;;;wBA0Bc,mBAAmB,cAAc;;wBAgCjC,kBAAkB,kBAAkB,sBAAsB;;;;;wBAY1D,uBAAuB;WAC5B,SAAS;WACT;WACA;WACA,QAAQ;WACR;IACP;iBAaa;EACf;EACA;EACA;EACA;
|
|
1
|
+
{"version":3,"file":"headless.d.mts","names":[],"sources":["../../../src/schemas/headless.ts"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless.mjs","names":[],"sources":["../../../src/schemas/headless.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport type { CallToolResult, Tool } from '@modelcontextprotocol/server';\nimport type { Static, TSchema } from 'typebox';\n\nimport type { SessionToolSurface } from '../types/server/sessionToolSurface';\nimport type { PoApprovalPolicy, PoApprovalPrompt } from './approval';\nimport type { PoSessionContext } from './hubChannel';\nimport type { PoNotificationRequest } from './notification';\nimport type { PoServerBundleEntry } from './serverBundle';\n\n/** Session-only contributions. This is not a Pi ExtensionAPI or a TUI adapter. */\nexport const PO_HEADLESS_HOST_SERVICE = 'po/headless-host';\nexport const PO_HEADLESS_OWNER = Symbol.for('po/headless-owner');\n\nconst PROMPT_ADMISSION_ERROR = 'PoHeadlessPromptAdmissionError';\n\n/** Admission is a host boundary, not an advisory package-hook failure. */\nexport class PoHeadlessPromptAdmissionError extends Error {\n constructor(cause: unknown) {\n super(cause instanceof Error ? cause.message : String(cause), { cause });\n this.name = PROMPT_ADMISSION_ERROR;\n }\n}\n\nconst SESSION_SETUP_ERROR = 'PoSessionSetupError';\n\n/**\n * A session cannot start until the user fixes their setup, such as configuring a model.\n *\n * Its message is written for the user and says what to do, so a server returns it as is\n * rather than hiding it behind a generic internal error.\n */\nexport class PoSessionSetupError extends Error {\n constructor(message: string) {\n super(message);\n this.name = SESSION_SETUP_ERROR;\n }\n}\n\n/** Error identity must survive independently bundled server and extension graphs. */\nexport function isPoSessionSetupError(error: unknown): error is Error {\n return error instanceof Error && error.name === SESSION_SETUP_ERROR;\n}\n\n/** Error identity must survive independently bundled server and extension graphs. */\nexport function isPoHeadlessPromptAdmissionError(error: unknown): boolean {\n return error instanceof Error && error.name === PROMPT_ADMISSION_ERROR;\n}\nconst TOOL_BUSY_ERROR = 'PoHeadlessToolBusyError';\n\n/** The tool and its hooks have not started. */\nexport class PoHeadlessToolBusyError extends Error {\n constructor() {\n super('An operation is already running');\n this.name = TOOL_BUSY_ERROR;\n }\n}\n\nexport function isPoHeadlessToolBusyError(error: unknown): boolean {\n return error instanceof Error && error.name === TOOL_BUSY_ERROR;\n}\n\nexport interface PoHeadlessSelection {\n readonly majorMode: string;\n readonly activeLayers: readonly string[];\n readonly domains: readonly string[];\n readonly profile?: string;\n /** Feature-owned activation sets, keyed by the contributing capability. */\n readonly state?: Readonly<Record<string, readonly string[]>>;\n}\n\n/** One package-owned selection transition. Server packages change exactly one axis per operation. */\nexport type PoHeadlessSelectionChange =\n | { readonly axis: 'majorMode'; readonly majorMode: string }\n | { readonly axis: 'domains'; readonly domains: readonly string[] }\n | { readonly axis: 'profile'; readonly profile?: string }\n | {\n readonly axis: 'state';\n readonly key: string;\n readonly values: readonly string[];\n };\n\n/** A selection axis a listener re-applies from its config even when its value did not change. */\nexport type PoHeadlessRefreshAxis = 'majorMode' | 'domains' | 'profile';\n\n/** How a selection was applied. `refresh` names axes whose config changed on disk. */\nexport interface PoHeadlessSelectionApplied {\n readonly refresh?: readonly PoHeadlessRefreshAxis[];\n}\n\nexport function readPoHeadlessOwner(context: Context): PoServerBundleEntry | undefined {\n return (context as Context & { [PO_HEADLESS_OWNER]?: PoServerBundleEntry })[PO_HEADLESS_OWNER];\n}\n\nexport interface PoHeadlessRegistration {\n dispose(): void;\n}\n\nexport type PoHeadlessContent = { type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string };\n\nexport interface PoHeadlessToolResult {\n content: PoHeadlessContent[];\n /** Public MCP result data. Internal renderer details are not a remote result contract. */\n structuredContent?: Record<string, unknown>;\n /** Component-only MCP metadata, never inferred from internal renderer details. */\n _meta?: CallToolResult['_meta'];\n details?: unknown;\n isError?: boolean;\n}\n\nlet crcTable: Uint32Array | undefined;\n\nfunction crc32(bytes: Uint8Array, start: number, end: number): number {\n if (crcTable === undefined) {\n crcTable = new Uint32Array(256);\n for (let n = 0; n < 256; n++) {\n let c = n;\n for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n crcTable[n] = c >>> 0;\n }\n }\n let crc = 0xffffffff;\n for (let i = start; i < end; i++) crc = crcTable[(crc ^ bytes[i]!) & 0xff]! ^ (crc >>> 8);\n return (crc ^ 0xffffffff) >>> 0;\n}\n\nconst PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\nfunction startsWith(bytes: Uint8Array, prefix: readonly number[], offset = 0): boolean {\n return prefix.every((byte, index) => bytes[offset + index] === byte);\n}\n\n/** Why the model API would reject this image, or undefined when it looks decodable. Only png, jpeg, gif and webp are checked. */\nexport function invalidImageReason(data: string, mimeType: string): string | undefined {\n let bytes: Uint8Array;\n try {\n bytes = Uint8Array.from(atob(data), (char) => char.charCodeAt(0));\n } catch {\n return 'the data is not valid base64';\n }\n if (mimeType === 'image/png') {\n if (!startsWith(bytes, PNG_SIGNATURE)) return 'the data is not a PNG';\n const view = new DataView(bytes.buffer);\n let offset = 8;\n while (offset + 12 <= bytes.length) {\n const length = view.getUint32(offset);\n const end = offset + 8 + length;\n if (end + 4 > bytes.length) break;\n if (crc32(bytes, offset + 4, end) !== view.getUint32(end)) return 'a PNG chunk failed its checksum';\n if (startsWith(bytes, [0x49, 0x45, 0x4e, 0x44], offset + 4)) return undefined;\n offset = end + 4;\n }\n return 'the PNG is truncated';\n }\n if (mimeType === 'image/jpeg') return startsWith(bytes, [0xff, 0xd8]) ? undefined : 'the data is not a JPEG';\n if (mimeType === 'image/gif')\n return startsWith(bytes, [0x47, 0x49, 0x46, 0x38]) ? undefined : 'the data is not a GIF';\n if (mimeType === 'image/webp')\n return startsWith(bytes, [0x52, 0x49, 0x46, 0x46]) && startsWith(bytes, [0x57, 0x45, 0x42, 0x50], 8)\n ? undefined\n : 'the data is not a WebP';\n return undefined;\n}\n\n/** Keeps valid images and swaps an undecodable one for a text note, since one bad image fails the whole turn and every later one. */\nexport function dropInvalidImages(content: readonly PoHeadlessContent[]): PoHeadlessContent[] {\n return content.map((part) => {\n if (part.type !== 'image') return part;\n const reason = invalidImageReason(part.data, part.mimeType);\n return reason === undefined ? part : { type: 'text', text: `[image omitted: ${reason}]` };\n });\n}\n\n/**\n * Narrows a Pi tool result to the headless contract. Pi types `structuredContent` as any JSON\n * value, while MCP only accepts an object, so anything else is dropped rather than forwarded.\n */\nexport function toPoHeadlessToolResult(result: {\n readonly content: PoHeadlessContent[];\n readonly details?: unknown;\n readonly structuredContent?: unknown;\n readonly _meta?: CallToolResult['_meta'];\n readonly isError?: boolean;\n}): PoHeadlessToolResult {\n const { structuredContent } = result;\n return {\n content: result.content,\n details: result.details,\n ...(result._meta === undefined ? {} : { _meta: result._meta }),\n ...(typeof structuredContent === 'object' && structuredContent !== null && !Array.isArray(structuredContent)\n ? { structuredContent: structuredContent as Record<string, unknown> }\n : {}),\n ...(result.isError === undefined ? {} : { isError: result.isError }),\n };\n}\n\nexport interface PoHeadlessClientRequest {\n kind: 'confirm' | 'select' | 'input';\n purpose?: 'feedback';\n title: string;\n message?: string;\n options?: readonly { label: string; description?: string; value: string }[];\n initialValue?: string;\n multiline?: boolean;\n /** Set only by po-approval: a `select` of Approve/Deny the app draws as an approval card. */\n approval?: PoApprovalPrompt;\n}\n\nexport interface PoHeadlessClient {\n /**\n * Operator-facing surface only: the body is flattened to one line and capped at 4096 characters\n * by schemas/notification.ts:73-83, and the model never sees it. Use session.admitPrompt to\n * reach the model.\n */\n notify(request: PoNotificationRequest): void | Promise<void>;\n request(request: PoHeadlessClientRequest, signal?: AbortSignal): Promise<unknown>;\n /** Only the currently visible feedback question, never an approval or queued request. */\n pendingFeedback?():\n | {\n id: string;\n kind: 'select' | 'input';\n title: string;\n message?: string;\n labels?: string[];\n }\n | undefined;\n /** Returns false for stale ids, queued requests and approvals. */\n answerFeedback?(id: string, value: string): boolean;\n /** Only the visible approval at the head of the dialog queue. */\n pendingApproval?(): PoApprovalPrompt | undefined;\n /** Refuses stale, queued, expired and duplicate answers. Voice cannot answer a human approval. */\n answerApproval?(id: string, decision: 'approved' | 'denied', channel: 'voice' | 'app'): boolean;\n /** Ignored once the session has closed, so a package can clear its status while it unwinds. */\n setStatus(source: string, text: string | undefined): void;\n appendComposerText?(text: string): void;\n}\n\nexport interface PoHeadlessModelSettings {\n model?: { provider: string; id: string };\n thinkingLevel: ThinkingLevel;\n}\n\nexport interface PoHeadlessSession {\n /** Live host close intent, set synchronously before interruption. Absent for a person's stop or remove. */\n readonly closeReason?: 'released' | 'host_shutdown';\n readModelSettings?(): Promise<PoHeadlessModelSettings>;\n setFastMode?(enabled: boolean): Promise<void>;\n setModelSettings?(settings: Partial<PoHeadlessModelSettings>): Promise<void>;\n /** Query durable history on demand; hosts do not retain the entire transcript. */\n entries(query?: {\n type?: 'custom' | 'message' | 'compaction';\n customType?: string;\n limit?: number;\n }): readonly Record<string, unknown>[] | Promise<readonly Record<string, unknown>[]>;\n /** Stream matching history newest first. Stop iteration to stop storage reads. */\n entriesNewestFirst(query?: {\n type?: 'custom' | 'message' | 'compaction';\n customType?: string;\n limit?: number;\n }): AsyncIterable<Record<string, unknown>>;\n /** Write-maintained latest metadata. Missing legacy coverage stays unavailable, with no history fallback. */\n customSnapshot?(\n customType: string,\n validator?: { schemaId: string; accepts: (data: unknown) => boolean },\n ): Promise<{ available: boolean; entry?: Record<string, unknown> }>;\n /** Branch assistant tokens plus requested custom-entry usage, unavailable when historical measurements are incomplete. */\n tokenSnapshot?(customTypes?: readonly string[]): Promise<{ available: boolean; totalTokens?: number }>;\n appendCustomEntry(type: string, data: unknown): Promise<void>;\n /** Append a displayable informational message to history and model context, without admitting input or starting a turn. */\n appendMessage?(message: { type: string; text: string; display?: boolean; details?: unknown }): Promise<string | void>;\n /**\n * Admit one custom message without awaiting its turn, using environmental input policy.\n * Defaults to followUp. Reusing requestId deduplicates normal Durable admission retries;\n * locked-agent appends and deferred operator admission do not provide that guarantee.\n * The receipt's settled resolves when the run that consumed the message ends; a locked agent\n * resolves it at once. Hosts without tracking return nothing.\n */\n admitMessage?(\n message: {\n type: string;\n text: string;\n display?: boolean;\n details?: unknown;\n },\n delivery?: 'steer' | 'followUp',\n requestId?: string,\n ): Promise<{ requestId: string; settled: Promise<void> } | void>;\n /**\n * Register a checkpoint flush before the next tool-turn request. Idle writes return undefined instead of queueing when busy.\n * `resetContext` starts native context at the message, which stands for itself followed by `retainedMessages`.\n */\n registerCheckpointBoundary?(\n callback: (boundary: {\n signal?: AbortSignal;\n appendMessage(\n message: {\n type: string;\n text: string;\n display?: boolean;\n details?: unknown;\n },\n options?: { resetContext?: { retainedMessages: readonly Record<string, unknown>[] } },\n ): Promise<string | undefined>;\n }) => Promise<void>,\n ): {\n appendMessage(\n message: {\n type: string;\n text: string;\n display?: boolean;\n details?: unknown;\n },\n options?: { resetContext?: { retainedMessages: readonly Record<string, unknown>[] } },\n ): Promise<string | undefined>;\n dispose(): void;\n };\n /** Detached canonical model context before package transformations, with context edits applied and no system message. */\n contextMessages?(): Promise<readonly Record<string, unknown>[]>;\n /** The latest measured assistant usage and the selected model's context window. */\n contextUsage?(): Promise<{ tokens: number | null; contextWindow: number } | undefined>;\n /** Label a durable entry without admitting input or starting a turn. */\n setLabel?(entryId: string, label: string | undefined): Promise<void>;\n /**\n * Delivers operator input through prompt preflight and awaits its turn.\n * Environmental input uses admitPrompt with origin omitted instead.\n * An idle agent is woken; busy delivery uses Durable steering or follow-up.\n */\n prompt(text: string, delivery?: 'prompt' | 'steer' | 'followUp'): Promise<void>;\n /**\n * Admits input without awaiting its turn. Environmental input omits origin and never\n * changes the operator queue. Operator origin uses prompt preflight, including voice\n * capture; 'interrupt' cancels the active run and admits operator input while preserving\n * retained queued work. Environmental callers may reuse requestId when retrying the same input.\n */\n admitPrompt?(\n text: string,\n delivery?: 'prompt' | 'steer' | 'followUp' | 'interrupt',\n origin?: 'operator',\n requestId?: string,\n ): Promise<void>;\n /** Stops the current run. With expectedOperationId, a different or finished operation is left alone. */\n abort(expectedOperationId?: string): Promise<void>;\n compact(instructions?: string): Promise<void>;\n /** operationId names the current run, absent while idle or settling. */\n activity(): Promise<{ hasPendingMessages: boolean; isIdle: boolean; operationId?: string }>;\n /** Renames the durable session and publishes the updated title to presentation clients. */\n setName?(name: string): Promise<void>;\n /** The current branch's newest entry, for a child that starts from this session's history. */\n forkSource?(): Promise<{\n kind: 'fork';\n entryId?: string;\n }>;\n}\n\n/** A grant-filtered skill view available only during one remote MCP tool invocation. */\nexport interface PoMcpSkillAccess {\n list(): Promise<readonly { name: string; description: string }[]>;\n read(name: string): Promise<string>;\n}\n\n/** A stateless auxiliary request. These tools are private to this request, never registered on the agent. */\nexport interface PoHeadlessToolCompletionRequest {\n systemPrompt: string;\n input: string;\n maxTokens: number;\n cacheRetention?: 'none' | 'short' | 'long';\n signal?: AbortSignal;\n /** Record auxiliary token usage in this session, including rejected results. */\n recordUsage?: boolean;\n tools: readonly Pick<PoHeadlessTool, 'name' | 'description' | 'parameters'>[];\n}\n\nexport interface PoHeadlessToolCompletionResult {\n toolCalls: readonly { id: string; name: string; arguments: unknown }[];\n usage: { totalTokens: number };\n}\n\n/** Credential-free catalog metadata. Packages cannot obtain auth, headers, or a mutable registry. */\nexport interface PoHeadlessModelMetadata {\n readonly provider: string;\n readonly id: string;\n readonly api?: string;\n readonly baseUrl?: string;\n readonly name?: string;\n readonly compat?: Readonly<Record<string, boolean | string>>;\n}\n\n/** Host policy retained by a supervised tool sequence after its original call returns. */\nexport interface PoHeadlessToolContinuation {\n admit(request: {\n toolCallId: string;\n toolName: string;\n args: Record<string, unknown>;\n signal?: AbortSignal;\n }): Promise<Record<string, unknown>>;\n finalize(request: {\n toolCallId: string;\n toolName: string;\n args: Record<string, unknown>;\n result: PoHeadlessToolResult;\n signal?: AbortSignal;\n }): Promise<PoHeadlessToolResult>;\n}\n\nexport interface PoHeadlessExecutionContext {\n /** Present only on an admitted tool invocation. Retained callbacks recheck current host policy. */\n readonly toolContinuation?: PoHeadlessToolContinuation;\n readonly cwd: string;\n /** The checkout the session runs in, distinct from the tool execution directory. */\n readonly repoRoot: string;\n readonly sessionId: string;\n /** Who invoked the tool: the session's own agent, or a subagent borrowing it. Absent when the host cannot tell. */\n readonly caller?: 'main' | 'child';\n /** The session's baseline context, when its host knows its workspace. */\n readonly sessionContext?: PoSessionContext;\n /** Host-owned presence, also supplied when the session has no workspace context. */\n readonly interaction?: 'interactive' | 'unattended';\n /** Immutable session configuration. Packages must not read or mutate process.env. */\n readonly environment: Readonly<Record<string, string | undefined>>;\n readonly client: PoHeadlessClient;\n readonly session: PoHeadlessSession;\n readonly model?: PoHeadlessModelMetadata;\n /** Lookup in this session's catalog, including physical models named by finalized messages. */\n readonly getModel?: (provider: string, id: string) => PoHeadlessModelMetadata | undefined;\n /** Detached credential-free authenticated chat catalog, read after refreshing provider availability. */\n readonly getAvailableModels?: () => Promise<\n readonly {\n readonly provider: string;\n readonly id: string;\n }[]\n >;\n /** Operation cancellation, supplied only for an active invocation. */\n readonly signal?: AbortSignal;\n readonly textCompletion?: {\n available(reference: string): boolean;\n /** Tool-less conversation summary through the host's guarded session provider. */\n summarize?(\n reference: string,\n request: {\n messages: readonly Record<string, unknown>[];\n instructions: string;\n previousSummary?: string;\n thinkingLevel?: ThinkingLevel;\n signal?: AbortSignal;\n },\n ): Promise<string>;\n complete(\n reference: string,\n request: {\n systemPrompt: string;\n input: string;\n maxTokens: number;\n cacheRetention?: 'none' | 'short' | 'long';\n signal?: AbortSignal;\n /** Record auxiliary usage and reject truncated text rather than returning partial output. */\n recordUsage?: boolean;\n },\n ): Promise<string>;\n };\n readonly toolCompletion?: {\n complete(reference: string, request: PoHeadlessToolCompletionRequest): Promise<PoHeadlessToolCompletionResult>;\n };\n /** Guarded image operation. Credentials and transport configuration remain host-owned. */\n readonly imageGeneration?: {\n available(): boolean;\n generate(request: {\n prompt: string;\n transparentBackground?: boolean;\n images?: readonly { data: string; mimeType: string }[];\n correlationId: string;\n signal?: AbortSignal;\n }): Promise<{ data: string; mimeType: 'image/png'; requestId?: string; generationId?: string }>;\n };\n /**\n * Guarded web search. Credentials, endpoint, model choice, session identity and transport remain host-owned.\n * `commands` follows a schema owned by the calling package; the host only bounds it.\n */\n readonly webSearch?: {\n available(): boolean;\n search(request: {\n commands: Readonly<Record<string, unknown>>;\n /** Optional plain-text excerpt, oldest first. Omit to send no conversation content. */\n conversation?: readonly { role: 'user' | 'assistant'; text: string }[];\n access: 'cached' | 'indexed' | 'live';\n signal?: AbortSignal;\n }): Promise<{ output: string; results?: readonly Record<string, unknown>[] }>;\n };\n readonly selection: PoHeadlessSelection;\n /** Present only while an authenticated remote MCP tool is running. */\n readonly mcpSkills?: PoMcpSkillAccess;\n shutdown(): void;\n}\n\n/** Conditions narrow an eligible owner's contributions; they can never restore a disabled owner. */\nexport interface PoHeadlessCondition {\n state?: Readonly<Record<string, string>>;\n attribution?: { kind: 'minor' | 'domain' | 'shared'; mode: string; label?: string };\n domain?: string;\n /** Any selected domain must match. Absent is unrestricted; empty never matches. */\n domains?: readonly string[];\n}\n\nexport interface PoHeadlessToolRestriction {\n when?: PoHeadlessCondition;\n /** Optional capability ceiling. An empty list disables every tool. */\n allowedTools?: readonly string[];\n /**\n * Narrow exclusions without disabling unrelated tools. Exclude a tool your package owns, or a\n * specific foreign tool the mode exists to withdraw. Never enumerate an allowlist here.\n */\n excludedTools?: readonly string[];\n}\n\nexport interface PoHeadlessTool<TParameters extends TSchema = TSchema> {\n when?: PoHeadlessCondition;\n name: string;\n label?: string;\n description: string;\n parameters: TParameters;\n annotations?: Tool['annotations'];\n outputSchema?: Tool['outputSchema'];\n /** Remote presentation only. Component tool access must be explicitly opted into. */\n _meta?: Tool['_meta'] & {\n ui?: { resourceUri?: string; visibility?: ('model' | 'app')[] };\n };\n promptSnippet?: string;\n promptGuidelines?: readonly string[];\n executionMode?: 'parallel' | 'serial';\n /**\n * Keeps the tool from subagents, which otherwise borrow the parent's tools. Set it when the tool needs\n * the person to answer, since nobody can answer a subagent, or when it acts for the main agent itself.\n */\n mainAgentOnly?: boolean;\n /** Asks the person before each call; see PoApprovalPolicy for who can answer. */\n approval?: PoApprovalPolicy;\n execute(\n toolCallId: string,\n parameters: Static<TParameters>,\n signal: AbortSignal | undefined,\n onUpdate: ((result: PoHeadlessToolResult) => void) | undefined,\n context: PoHeadlessExecutionContext,\n ): Promise<PoHeadlessToolResult>;\n}\n\n/**\n * One package contribution to the session's resource surface.\n *\n * `kind` decides what the model is billed for, and the difference is large:\n * - `context` is EAGER. `read()` runs on every prompt build and the text is pasted\n * into the system prompt verbatim. Reserve it for small live session state that\n * exists nowhere on disk. Never use it for a file the package ships: a README\n * registered this way costs its full length on every single turn.\n * - `skill` is LAZY. Only `name`, `description` and `path` reach the prompt; the\n * agent reads the file itself when the description matches the task.\n * - `prompt` is inert until the user invokes it by name.\n *\n * The rule in one line: if it is a file the package ships, it is a `skill`.\n */\nexport interface PoHeadlessResource {\n when?: PoHeadlessCondition;\n name: string;\n /** Model-visible selection signal. A `skill` without one tells the agent nothing. */\n description?: string;\n /** Absolute path of the file `read()` returns, so an advertised skill is one the agent can open. */\n path?: string;\n /**\n * A `skill` listed in the system prompt itself, kept out of the `load_skill` catalog and\n * `search_skills`. For a few always-relevant guides (the code-mode how-tos) that search would miss.\n */\n pinned?: boolean;\n kind: 'prompt' | 'skill' | 'context';\n read(context: PoHeadlessExecutionContext): string | Promise<string>;\n}\n\nexport interface PoHeadlessCommand {\n when?: PoHeadlessCondition;\n name: string;\n description: string;\n /** Selection-only control allowed after a coherent selection fails admission. */\n selectionRecovery?: boolean;\n execute(args: string, context: PoHeadlessExecutionContext): void | Promise<void>;\n}\n\nexport type PoHeadlessEventName =\n | 'session_start'\n | 'session_shutdown'\n | 'session_tree'\n | 'before_agent_start'\n | 'agent_start'\n | 'agent_end'\n | 'agent_settled'\n | 'turn_start'\n | 'turn_end'\n | 'context'\n | 'before_provider_request'\n | 'message_start'\n | 'message_update'\n | 'message_end'\n | 'tool_call'\n | 'tool_execution_start'\n | 'tool_execution_update'\n | 'tool_execution_end'\n | 'tool_result'\n | 'model_select'\n | 'session_before_compact'\n | 'session_compact';\n\nexport type PoHeadlessHookEvent<E extends PoHeadlessEventName> = E extends 'before_provider_request'\n ? {\n lane: string;\n runId: string;\n model: { provider: string; id: string; api?: string };\n payload: unknown;\n }\n : E extends 'session_before_compact'\n ? {\n reason: string;\n preparation: unknown;\n instructions?: string;\n customInstructions?: string;\n }\n : E extends 'model_select'\n ? { model: { provider: string; id: string } }\n : Readonly<Record<string, unknown>>;\n\nexport interface PoHeadlessCompactionResult {\n summary: string;\n tokensBefore: number;\n retainedTail: Record<string, unknown>[];\n usage?: Record<string, unknown>;\n details?: unknown;\n}\n\nexport type PoHeadlessHookResult<E extends PoHeadlessEventName> = E extends 'before_provider_request'\n ? { payload: unknown } | void\n : E extends 'session_before_compact'\n ? {\n cancel?: boolean;\n decline?: boolean;\n compaction?: PoHeadlessCompactionResult;\n } | void\n : E extends 'context'\n ? { messages?: unknown[]; systemPrompt?: string } | void\n : E extends 'before_agent_start'\n ? { systemPrompt?: string } | void\n : E extends 'tool_call'\n ? {\n args?: Record<string, unknown>;\n block?: { reason: string; terminate?: boolean };\n } | void\n : E extends 'tool_result'\n ? {\n content?: PoHeadlessContent[];\n details?: unknown;\n isError?: boolean;\n usage?: Record<string, unknown>;\n terminate?: boolean;\n } | void\n : unknown;\n\nexport type PoHeadlessHook<E extends PoHeadlessEventName = PoHeadlessEventName> = E extends PoHeadlessEventName\n ? {\n when?: PoHeadlessCondition;\n event: E;\n handle(\n event: Readonly<PoHeadlessHookEvent<E>>,\n context: PoHeadlessExecutionContext,\n ): PoHeadlessHookResult<E> | Promise<PoHeadlessHookResult<E>>;\n }\n : never;\n\n/** A retained package can register activity without starting disabled watchers or services. */\nexport interface PoHeadlessActivity {\n when?: PoHeadlessCondition;\n name: string;\n start(context: PoHeadlessExecutionContext): (() => void | Promise<void>) | Promise<() => void | Promise<void>>;\n}\n\n/** Read-only metadata for the currently applied session, never executable declarations or resource bodies. */\nexport interface PoHeadlessCapability {\n readonly source: string;\n readonly name: string;\n readonly kind: 'tool' | 'skill';\n readonly when?: PoHeadlessCondition;\n readonly active: boolean;\n readonly discoverable: boolean;\n readonly reason?: 'inactive' | 'restricted' | 'shadowed' | 'unavailable' | 'not-discoverable';\n}\n\n/** Visibility controls model discovery only, never admission for external execution. */\nexport function isPoHeadlessToolModelVisible(tool: Pick<PoHeadlessTool, '_meta'>): boolean {\n const visibility = tool._meta?.ui?.visibility;\n return visibility === undefined || visibility.includes('model');\n}\n\nexport interface PoHeadlessCapabilitySnapshot {\n readonly revision: number;\n readonly ready: boolean;\n readonly capabilities: readonly PoHeadlessCapability[];\n}\n\n/** Session skill discovery tools, adapted to the size of the active catalog. */\nexport const PO_LOAD_SKILL_TOOL = 'load_skill';\nexport const PO_SEARCH_SKILLS_TOOL = 'search_skills';\nexport interface PoHeadlessHostService {\n readonly context: PoHeadlessExecutionContext;\n /** Admitted external execution, sharing native hooks, restrictions and session lifetime. */\n readonly toolSurface?: Pick<SessionToolSurface, 'readSurface' | 'invokeTool'>;\n /** The instructions of an applied skill by exact name, or undefined when none is applied. */\n readSkill(name: string): string | undefined;\n /** Names and descriptions of searchable applied skills, excluding pinned guides and instruction bodies. */\n listSkills(): readonly { readonly name: string; readonly description: string }[];\n /** atToolBoundary defers the change to the next tool boundary, as slash commands do; idle sessions apply at once. */\n changeSelection(change: PoHeadlessSelectionChange, options?: { atToolBoundary?: boolean }): Promise<void>;\n assertActive(source?: string): void;\n /** Whether a package owns contributions in the currently applied selection. */\n isSourceActive(source: string): boolean;\n /**\n * Normalize selection before the first successful application only. Disposed with the registering context.\n * Resolvers run in registration order and each may be asynchronous. A resolver that changes the profile\n * decides it for the session, so turn-admission inheritance no longer overrides it; an explicit\n * `changeSelection` still can.\n */\n registerInitialSelectionResolver(\n resolve: (selection: PoHeadlessSelection) => PoHeadlessSelection | Promise<PoHeadlessSelection>,\n ): () => void;\n subscribeSelection(\n listener: (selection: PoHeadlessSelection, applied: PoHeadlessSelectionApplied) => void | Promise<void>,\n ): () => void;\n /** Inspect only a coherently applied generation. A transition returns ready=false and no partial inventory. */\n inspectCapabilities(): PoHeadlessCapabilitySnapshot;\n registerToolRestriction(restriction: PoHeadlessToolRestriction): PoHeadlessRegistration;\n registerTool<TParameters extends TSchema>(tool: PoHeadlessTool<TParameters>): PoHeadlessRegistration;\n registerResource(resource: PoHeadlessResource): PoHeadlessRegistration;\n registerCommand(command: PoHeadlessCommand): PoHeadlessRegistration;\n registerHook(hook: PoHeadlessHook): PoHeadlessRegistration;\n registerActivity(activity: PoHeadlessActivity): PoHeadlessRegistration;\n}\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/headless-host': PoHeadlessHostService;\n }\n}\n\nexport function readPoHeadlessHost(context: Context): PoHeadlessHostService | undefined {\n return context.get(PO_HEADLESS_HOST_SERVICE) as PoHeadlessHostService | undefined;\n}\n\nexport function requirePoHeadlessHost(context: Context): PoHeadlessHostService {\n const host = readPoHeadlessHost(context);\n if (!host) throw new Error('This contribution requires the Po headless session host.');\n return host;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"headless.mjs","names":[],"sources":["../../../src/schemas/headless.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport type { CallToolResult, Tool } from '@modelcontextprotocol/server';\nimport type { Static, TSchema } from 'typebox';\n\nimport type { SessionToolSurface } from '../types/server/sessionToolSurface';\nimport type { PoApprovalPolicy, PoApprovalPrompt } from './approval';\nimport type { PoSessionContext } from './hubChannel';\nimport type { PoNotificationRequest } from './notification';\nimport type { PoServerBundleEntry } from './serverBundle';\n\n/** Session-only contributions. This is not a Pi ExtensionAPI or a TUI adapter. */\nexport const PO_HEADLESS_HOST_SERVICE = 'po/headless-host';\nexport const PO_HEADLESS_OWNER = Symbol.for('po/headless-owner');\n\nconst PROMPT_ADMISSION_ERROR = 'PoHeadlessPromptAdmissionError';\n\n/** Admission is a host boundary, not an advisory package-hook failure. */\nexport class PoHeadlessPromptAdmissionError extends Error {\n constructor(cause: unknown) {\n super(cause instanceof Error ? cause.message : String(cause), { cause });\n this.name = PROMPT_ADMISSION_ERROR;\n }\n}\n\nconst SESSION_SETUP_ERROR = 'PoSessionSetupError';\n\n/**\n * A session cannot start until the user fixes their setup, such as configuring a model.\n *\n * Its message is written for the user and says what to do, so a server returns it as is\n * rather than hiding it behind a generic internal error.\n */\nexport class PoSessionSetupError extends Error {\n constructor(message: string) {\n super(message);\n this.name = SESSION_SETUP_ERROR;\n }\n}\n\n/** Error identity must survive independently bundled server and extension graphs. */\nexport function isPoSessionSetupError(error: unknown): error is Error {\n return error instanceof Error && error.name === SESSION_SETUP_ERROR;\n}\n\n/** Error identity must survive independently bundled server and extension graphs. */\nexport function isPoHeadlessPromptAdmissionError(error: unknown): boolean {\n return error instanceof Error && error.name === PROMPT_ADMISSION_ERROR;\n}\nconst TOOL_BUSY_ERROR = 'PoHeadlessToolBusyError';\n\n/** The tool and its hooks have not started. */\nexport class PoHeadlessToolBusyError extends Error {\n constructor() {\n super('An operation is already running');\n this.name = TOOL_BUSY_ERROR;\n }\n}\n\nexport function isPoHeadlessToolBusyError(error: unknown): boolean {\n return error instanceof Error && error.name === TOOL_BUSY_ERROR;\n}\n\nexport interface PoHeadlessSelection {\n readonly majorMode: string;\n readonly activeLayers: readonly string[];\n readonly domains: readonly string[];\n readonly profile?: string;\n /** Feature-owned activation sets, keyed by the contributing capability. */\n readonly state?: Readonly<Record<string, readonly string[]>>;\n}\n\n/** One package-owned selection transition. Server packages change exactly one axis per operation. */\nexport type PoHeadlessSelectionChange =\n | { readonly axis: 'majorMode'; readonly majorMode: string }\n | { readonly axis: 'domains'; readonly domains: readonly string[] }\n | { readonly axis: 'profile'; readonly profile?: string }\n | {\n readonly axis: 'state';\n readonly key: string;\n readonly values: readonly string[];\n };\n\n/** A selection axis a listener re-applies from its config even when its value did not change. */\nexport type PoHeadlessRefreshAxis = 'majorMode' | 'domains' | 'profile';\n\n/** How a selection was applied. `refresh` names axes whose config changed on disk. */\nexport interface PoHeadlessSelectionApplied {\n readonly refresh?: readonly PoHeadlessRefreshAxis[];\n}\n\nexport function readPoHeadlessOwner(context: Context): PoServerBundleEntry | undefined {\n return (context as Context & { [PO_HEADLESS_OWNER]?: PoServerBundleEntry })[PO_HEADLESS_OWNER];\n}\n\nexport interface PoHeadlessRegistration {\n dispose(): void;\n}\n\nexport type PoHeadlessContent = { type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string };\n\nexport interface PoHeadlessToolResult {\n content: PoHeadlessContent[];\n /** Public MCP result data. Internal renderer details are not a remote result contract. */\n structuredContent?: Record<string, unknown>;\n /** Component-only MCP metadata, never inferred from internal renderer details. */\n _meta?: CallToolResult['_meta'];\n details?: unknown;\n isError?: boolean;\n}\n\nlet crcTable: Uint32Array | undefined;\n\nfunction crc32(bytes: Uint8Array, start: number, end: number): number {\n if (crcTable === undefined) {\n crcTable = new Uint32Array(256);\n for (let n = 0; n < 256; n++) {\n let c = n;\n for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n crcTable[n] = c >>> 0;\n }\n }\n let crc = 0xffffffff;\n for (let i = start; i < end; i++) crc = crcTable[(crc ^ bytes[i]!) & 0xff]! ^ (crc >>> 8);\n return (crc ^ 0xffffffff) >>> 0;\n}\n\nconst PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\nfunction startsWith(bytes: Uint8Array, prefix: readonly number[], offset = 0): boolean {\n return prefix.every((byte, index) => bytes[offset + index] === byte);\n}\n\n/** Why the model API would reject this image, or undefined when it looks decodable. Only png, jpeg, gif and webp are checked. */\nexport function invalidImageReason(data: string, mimeType: string): string | undefined {\n let bytes: Uint8Array;\n try {\n bytes = Uint8Array.from(atob(data), (char) => char.charCodeAt(0));\n } catch {\n return 'the data is not valid base64';\n }\n if (mimeType === 'image/png') {\n if (!startsWith(bytes, PNG_SIGNATURE)) return 'the data is not a PNG';\n const view = new DataView(bytes.buffer);\n let offset = 8;\n while (offset + 12 <= bytes.length) {\n const length = view.getUint32(offset);\n const end = offset + 8 + length;\n if (end + 4 > bytes.length) break;\n if (crc32(bytes, offset + 4, end) !== view.getUint32(end)) return 'a PNG chunk failed its checksum';\n if (startsWith(bytes, [0x49, 0x45, 0x4e, 0x44], offset + 4)) return undefined;\n offset = end + 4;\n }\n return 'the PNG is truncated';\n }\n if (mimeType === 'image/jpeg') return startsWith(bytes, [0xff, 0xd8]) ? undefined : 'the data is not a JPEG';\n if (mimeType === 'image/gif')\n return startsWith(bytes, [0x47, 0x49, 0x46, 0x38]) ? undefined : 'the data is not a GIF';\n if (mimeType === 'image/webp')\n return startsWith(bytes, [0x52, 0x49, 0x46, 0x46]) && startsWith(bytes, [0x57, 0x45, 0x42, 0x50], 8)\n ? undefined\n : 'the data is not a WebP';\n return undefined;\n}\n\n/** Keeps valid images and swaps an undecodable one for a text note, since one bad image fails the whole turn and every later one. */\nexport function dropInvalidImages(content: readonly PoHeadlessContent[]): PoHeadlessContent[] {\n return content.map((part) => {\n if (part.type !== 'image') return part;\n const reason = invalidImageReason(part.data, part.mimeType);\n return reason === undefined ? part : { type: 'text', text: `[image omitted: ${reason}]` };\n });\n}\n\n/**\n * Narrows a Pi tool result to the headless contract. Pi types `structuredContent` as any JSON\n * value, while MCP only accepts an object, so anything else is dropped rather than forwarded.\n */\nexport function toPoHeadlessToolResult(result: {\n readonly content: PoHeadlessContent[];\n readonly details?: unknown;\n readonly structuredContent?: unknown;\n readonly _meta?: CallToolResult['_meta'];\n readonly isError?: boolean;\n}): PoHeadlessToolResult {\n const { structuredContent } = result;\n return {\n content: result.content,\n details: result.details,\n ...(result._meta === undefined ? {} : { _meta: result._meta }),\n ...(typeof structuredContent === 'object' && structuredContent !== null && !Array.isArray(structuredContent)\n ? { structuredContent: structuredContent as Record<string, unknown> }\n : {}),\n ...(result.isError === undefined ? {} : { isError: result.isError }),\n };\n}\n\nexport interface PoHeadlessClientRequest {\n kind: 'confirm' | 'select' | 'input';\n purpose?: 'feedback';\n title: string;\n message?: string;\n /** `icon` is a bounded image `data:` URL, as in a profile identity; a client may draw it beside the label. */\n options?: readonly { label: string; description?: string; icon?: string; value: string }[];\n initialValue?: string;\n multiline?: boolean;\n /** Set only by po-approval: a `select` of Approve/Deny the app draws as an approval card. */\n approval?: PoApprovalPrompt;\n}\n\nexport interface PoHeadlessClient {\n /**\n * Operator-facing surface only: the body is flattened to one line and capped at 4096 characters\n * by schemas/notification.ts:73-83, and the model never sees it. Use session.admitPrompt to\n * reach the model.\n */\n notify(request: PoNotificationRequest): void | Promise<void>;\n request(request: PoHeadlessClientRequest, signal?: AbortSignal): Promise<unknown>;\n /** Only the currently visible feedback question, never an approval or queued request. */\n pendingFeedback?():\n | {\n id: string;\n kind: 'select' | 'input';\n title: string;\n message?: string;\n labels?: string[];\n }\n | undefined;\n /** Returns false for stale ids, queued requests and approvals. */\n answerFeedback?(id: string, value: string): boolean;\n /** Only the visible approval at the head of the dialog queue. */\n pendingApproval?(): PoApprovalPrompt | undefined;\n /** Refuses stale, queued, expired and duplicate answers. Voice cannot answer a human approval. */\n answerApproval?(id: string, decision: 'approved' | 'denied', channel: 'voice' | 'app'): boolean;\n /** Ignored once the session has closed, so a package can clear its status while it unwinds. */\n setStatus(source: string, text: string | undefined): void;\n appendComposerText?(text: string): void;\n}\n\nexport interface PoHeadlessModelSettings {\n model?: { provider: string; id: string };\n thinkingLevel: ThinkingLevel;\n}\n\nexport interface PoHeadlessSession {\n /** Live host close intent, set synchronously before interruption. Absent for a person's stop or remove. */\n readonly closeReason?: 'released' | 'host_shutdown';\n readModelSettings?(): Promise<PoHeadlessModelSettings>;\n setFastMode?(enabled: boolean): Promise<void>;\n setModelSettings?(settings: Partial<PoHeadlessModelSettings>): Promise<void>;\n /** Query durable history on demand; hosts do not retain the entire transcript. */\n entries(query?: {\n type?: 'custom' | 'message' | 'compaction';\n customType?: string;\n limit?: number;\n }): readonly Record<string, unknown>[] | Promise<readonly Record<string, unknown>[]>;\n /** Stream matching history newest first. Stop iteration to stop storage reads. */\n entriesNewestFirst(query?: {\n type?: 'custom' | 'message' | 'compaction';\n customType?: string;\n limit?: number;\n }): AsyncIterable<Record<string, unknown>>;\n /** Write-maintained latest metadata. Missing legacy coverage stays unavailable, with no history fallback. */\n customSnapshot?(\n customType: string,\n validator?: { schemaId: string; accepts: (data: unknown) => boolean },\n ): Promise<{ available: boolean; entry?: Record<string, unknown> }>;\n /** Branch assistant tokens plus requested custom-entry usage, unavailable when historical measurements are incomplete. */\n tokenSnapshot?(customTypes?: readonly string[]): Promise<{ available: boolean; totalTokens?: number }>;\n appendCustomEntry(type: string, data: unknown): Promise<void>;\n /** Append a displayable informational message to history and model context, without admitting input or starting a turn. */\n appendMessage?(message: { type: string; text: string; display?: boolean; details?: unknown }): Promise<string | void>;\n /**\n * Admit one custom message without awaiting its turn, using environmental input policy.\n * Defaults to followUp. Reusing requestId deduplicates normal Durable admission retries;\n * locked-agent appends and deferred operator admission do not provide that guarantee.\n * The receipt's settled resolves when the run that consumed the message ends; a locked agent\n * resolves it at once. Hosts without tracking return nothing.\n */\n admitMessage?(\n message: {\n type: string;\n text: string;\n display?: boolean;\n details?: unknown;\n },\n delivery?: 'steer' | 'followUp',\n requestId?: string,\n ): Promise<{ requestId: string; settled: Promise<void> } | void>;\n /**\n * Register a checkpoint flush before the next tool-turn request. Idle writes return undefined instead of queueing when busy.\n * `resetContext` starts native context at the message, which stands for itself followed by `retainedMessages`.\n */\n registerCheckpointBoundary?(\n callback: (boundary: {\n signal?: AbortSignal;\n appendMessage(\n message: {\n type: string;\n text: string;\n display?: boolean;\n details?: unknown;\n },\n options?: { resetContext?: { retainedMessages: readonly Record<string, unknown>[] } },\n ): Promise<string | undefined>;\n }) => Promise<void>,\n ): {\n appendMessage(\n message: {\n type: string;\n text: string;\n display?: boolean;\n details?: unknown;\n },\n options?: { resetContext?: { retainedMessages: readonly Record<string, unknown>[] } },\n ): Promise<string | undefined>;\n dispose(): void;\n };\n /** Detached canonical model context before package transformations, with context edits applied and no system message. */\n contextMessages?(): Promise<readonly Record<string, unknown>[]>;\n /** The latest measured assistant usage and the selected model's context window. */\n contextUsage?(): Promise<{ tokens: number | null; contextWindow: number } | undefined>;\n /** Label a durable entry without admitting input or starting a turn. */\n setLabel?(entryId: string, label: string | undefined): Promise<void>;\n /**\n * Delivers operator input through prompt preflight and awaits its turn.\n * Environmental input uses admitPrompt with origin omitted instead.\n * An idle agent is woken; busy delivery uses Durable steering or follow-up.\n */\n prompt(text: string, delivery?: 'prompt' | 'steer' | 'followUp'): Promise<void>;\n /**\n * Admits input without awaiting its turn. Environmental input omits origin and never\n * changes the operator queue. Operator origin uses prompt preflight, including voice\n * capture; 'interrupt' cancels the active run and admits operator input while preserving\n * retained queued work. Environmental callers may reuse requestId when retrying the same input.\n */\n admitPrompt?(\n text: string,\n delivery?: 'prompt' | 'steer' | 'followUp' | 'interrupt',\n origin?: 'operator',\n requestId?: string,\n ): Promise<void>;\n /** Stops the current run. With expectedOperationId, a different or finished operation is left alone. */\n abort(expectedOperationId?: string): Promise<void>;\n compact(instructions?: string): Promise<void>;\n /** operationId names the current run, absent while idle or settling. */\n activity(): Promise<{ hasPendingMessages: boolean; isIdle: boolean; operationId?: string }>;\n /** Renames the durable session and publishes the updated title to presentation clients. */\n setName?(name: string): Promise<void>;\n /** The current branch's newest entry, for a child that starts from this session's history. */\n forkSource?(): Promise<{\n kind: 'fork';\n entryId?: string;\n }>;\n}\n\n/** A grant-filtered skill view available only during one remote MCP tool invocation. */\nexport interface PoMcpSkillAccess {\n list(): Promise<readonly { name: string; description: string }[]>;\n read(name: string): Promise<string>;\n}\n\n/** A stateless auxiliary request. These tools are private to this request, never registered on the agent. */\nexport interface PoHeadlessToolCompletionRequest {\n systemPrompt: string;\n input: string;\n maxTokens: number;\n cacheRetention?: 'none' | 'short' | 'long';\n signal?: AbortSignal;\n /** Record auxiliary token usage in this session, including rejected results. */\n recordUsage?: boolean;\n tools: readonly Pick<PoHeadlessTool, 'name' | 'description' | 'parameters'>[];\n}\n\nexport interface PoHeadlessToolCompletionResult {\n toolCalls: readonly { id: string; name: string; arguments: unknown }[];\n usage: { totalTokens: number };\n}\n\n/** Credential-free catalog metadata. Packages cannot obtain auth, headers, or a mutable registry. */\nexport interface PoHeadlessModelMetadata {\n readonly provider: string;\n readonly id: string;\n readonly api?: string;\n readonly baseUrl?: string;\n readonly name?: string;\n readonly compat?: Readonly<Record<string, boolean | string>>;\n}\n\n/** Host policy retained by a supervised tool sequence after its original call returns. */\nexport interface PoHeadlessToolContinuation {\n admit(request: {\n toolCallId: string;\n toolName: string;\n args: Record<string, unknown>;\n signal?: AbortSignal;\n }): Promise<Record<string, unknown>>;\n finalize(request: {\n toolCallId: string;\n toolName: string;\n args: Record<string, unknown>;\n result: PoHeadlessToolResult;\n signal?: AbortSignal;\n }): Promise<PoHeadlessToolResult>;\n}\n\nexport interface PoHeadlessExecutionContext {\n /** Present only on an admitted tool invocation. Retained callbacks recheck current host policy. */\n readonly toolContinuation?: PoHeadlessToolContinuation;\n readonly cwd: string;\n /** The checkout the session runs in, distinct from the tool execution directory. */\n readonly repoRoot: string;\n readonly sessionId: string;\n /** Who invoked the tool: the session's own agent, or a subagent borrowing it. Absent when the host cannot tell. */\n readonly caller?: 'main' | 'child';\n /** The session's baseline context, when its host knows its workspace. */\n readonly sessionContext?: PoSessionContext;\n /** Host-owned presence, also supplied when the session has no workspace context. */\n readonly interaction?: 'interactive' | 'unattended';\n /** Immutable session configuration. Packages must not read or mutate process.env. */\n readonly environment: Readonly<Record<string, string | undefined>>;\n readonly client: PoHeadlessClient;\n readonly session: PoHeadlessSession;\n readonly model?: PoHeadlessModelMetadata;\n /** Lookup in this session's catalog, including physical models named by finalized messages. */\n readonly getModel?: (provider: string, id: string) => PoHeadlessModelMetadata | undefined;\n /** Detached credential-free authenticated chat catalog, read after refreshing provider availability. */\n readonly getAvailableModels?: () => Promise<\n readonly {\n readonly provider: string;\n readonly id: string;\n }[]\n >;\n /** Operation cancellation, supplied only for an active invocation. */\n readonly signal?: AbortSignal;\n readonly textCompletion?: {\n available(reference: string): boolean;\n /** Tool-less conversation summary through the host's guarded session provider. */\n summarize?(\n reference: string,\n request: {\n messages: readonly Record<string, unknown>[];\n instructions: string;\n previousSummary?: string;\n thinkingLevel?: ThinkingLevel;\n signal?: AbortSignal;\n },\n ): Promise<string>;\n complete(\n reference: string,\n request: {\n systemPrompt: string;\n input: string;\n maxTokens: number;\n cacheRetention?: 'none' | 'short' | 'long';\n signal?: AbortSignal;\n /** Record auxiliary usage and reject truncated text rather than returning partial output. */\n recordUsage?: boolean;\n },\n ): Promise<string>;\n };\n readonly toolCompletion?: {\n complete(reference: string, request: PoHeadlessToolCompletionRequest): Promise<PoHeadlessToolCompletionResult>;\n };\n /** Guarded image operation. Credentials and transport configuration remain host-owned. */\n readonly imageGeneration?: {\n available(): boolean;\n generate(request: {\n prompt: string;\n transparentBackground?: boolean;\n images?: readonly { data: string; mimeType: string }[];\n correlationId: string;\n signal?: AbortSignal;\n }): Promise<{ data: string; mimeType: 'image/png'; requestId?: string; generationId?: string }>;\n };\n /**\n * Guarded web search. Credentials, endpoint, model choice, session identity and transport remain host-owned.\n * `commands` follows a schema owned by the calling package; the host only bounds it.\n */\n readonly webSearch?: {\n available(): boolean;\n search(request: {\n commands: Readonly<Record<string, unknown>>;\n /** Optional plain-text excerpt, oldest first. Omit to send no conversation content. */\n conversation?: readonly { role: 'user' | 'assistant'; text: string }[];\n access: 'cached' | 'indexed' | 'live';\n signal?: AbortSignal;\n }): Promise<{ output: string; results?: readonly Record<string, unknown>[] }>;\n };\n readonly selection: PoHeadlessSelection;\n /** Present only while an authenticated remote MCP tool is running. */\n readonly mcpSkills?: PoMcpSkillAccess;\n shutdown(): void;\n}\n\n/** Conditions narrow an eligible owner's contributions; they can never restore a disabled owner. */\nexport interface PoHeadlessCondition {\n state?: Readonly<Record<string, string>>;\n attribution?: { kind: 'minor' | 'domain' | 'shared'; mode: string; label?: string };\n domain?: string;\n /** Any selected domain must match. Absent is unrestricted; empty never matches. */\n domains?: readonly string[];\n}\n\nexport interface PoHeadlessToolRestriction {\n when?: PoHeadlessCondition;\n /** Optional capability ceiling. An empty list disables every tool. */\n allowedTools?: readonly string[];\n /**\n * Narrow exclusions without disabling unrelated tools. Exclude a tool your package owns, or a\n * specific foreign tool the mode exists to withdraw. Never enumerate an allowlist here.\n */\n excludedTools?: readonly string[];\n}\n\nexport interface PoHeadlessTool<TParameters extends TSchema = TSchema> {\n when?: PoHeadlessCondition;\n name: string;\n label?: string;\n description: string;\n parameters: TParameters;\n annotations?: Tool['annotations'];\n outputSchema?: Tool['outputSchema'];\n /** Remote presentation only. Component tool access must be explicitly opted into. */\n _meta?: Tool['_meta'] & {\n ui?: { resourceUri?: string; visibility?: ('model' | 'app')[] };\n };\n promptSnippet?: string;\n promptGuidelines?: readonly string[];\n executionMode?: 'parallel' | 'serial';\n /**\n * Keeps the tool from subagents, which otherwise borrow the parent's tools. Set it when the tool needs\n * the person to answer, since nobody can answer a subagent, or when it acts for the main agent itself.\n */\n mainAgentOnly?: boolean;\n /** Asks the person before each call; see PoApprovalPolicy for who can answer. */\n approval?: PoApprovalPolicy;\n execute(\n toolCallId: string,\n parameters: Static<TParameters>,\n signal: AbortSignal | undefined,\n onUpdate: ((result: PoHeadlessToolResult) => void) | undefined,\n context: PoHeadlessExecutionContext,\n ): Promise<PoHeadlessToolResult>;\n}\n\n/**\n * One package contribution to the session's resource surface.\n *\n * `kind` decides what the model is billed for, and the difference is large:\n * - `context` is EAGER. `read()` runs on every prompt build and the text is pasted\n * into the system prompt verbatim. Reserve it for small live session state that\n * exists nowhere on disk. Never use it for a file the package ships: a README\n * registered this way costs its full length on every single turn.\n * - `skill` is LAZY. Only `name`, `description` and `path` reach the prompt; the\n * agent reads the file itself when the description matches the task.\n * - `prompt` is inert until the user invokes it by name.\n *\n * The rule in one line: if it is a file the package ships, it is a `skill`.\n */\nexport interface PoHeadlessResource {\n when?: PoHeadlessCondition;\n name: string;\n /** Model-visible selection signal. A `skill` without one tells the agent nothing. */\n description?: string;\n /** Absolute path of the file `read()` returns, so an advertised skill is one the agent can open. */\n path?: string;\n /**\n * A `skill` listed in the system prompt itself, kept out of the `load_skill` catalog and\n * `search_skills`. For a few always-relevant guides (the code-mode how-tos) that search would miss.\n */\n pinned?: boolean;\n kind: 'prompt' | 'skill' | 'context';\n read(context: PoHeadlessExecutionContext): string | Promise<string>;\n}\n\nexport interface PoHeadlessCommand {\n when?: PoHeadlessCondition;\n name: string;\n description: string;\n /** Selection-only control allowed after a coherent selection fails admission. */\n selectionRecovery?: boolean;\n execute(args: string, context: PoHeadlessExecutionContext): void | Promise<void>;\n}\n\nexport type PoHeadlessEventName =\n | 'session_start'\n | 'session_shutdown'\n | 'session_tree'\n | 'before_agent_start'\n | 'agent_start'\n | 'agent_end'\n | 'agent_settled'\n | 'turn_start'\n | 'turn_end'\n | 'context'\n | 'before_provider_request'\n | 'message_start'\n | 'message_update'\n | 'message_end'\n | 'tool_call'\n | 'tool_execution_start'\n | 'tool_execution_update'\n | 'tool_execution_end'\n | 'tool_result'\n | 'model_select'\n | 'session_before_compact'\n | 'session_compact';\n\nexport type PoHeadlessHookEvent<E extends PoHeadlessEventName> = E extends 'before_provider_request'\n ? {\n lane: string;\n runId: string;\n model: { provider: string; id: string; api?: string };\n payload: unknown;\n }\n : E extends 'session_before_compact'\n ? {\n reason: string;\n preparation: unknown;\n instructions?: string;\n customInstructions?: string;\n }\n : E extends 'model_select'\n ? { model: { provider: string; id: string } }\n : Readonly<Record<string, unknown>>;\n\nexport interface PoHeadlessCompactionResult {\n summary: string;\n tokensBefore: number;\n retainedTail: Record<string, unknown>[];\n usage?: Record<string, unknown>;\n details?: unknown;\n}\n\nexport type PoHeadlessHookResult<E extends PoHeadlessEventName> = E extends 'before_provider_request'\n ? { payload: unknown } | void\n : E extends 'session_before_compact'\n ? {\n cancel?: boolean;\n decline?: boolean;\n compaction?: PoHeadlessCompactionResult;\n } | void\n : E extends 'context'\n ? { messages?: unknown[]; systemPrompt?: string } | void\n : E extends 'before_agent_start'\n ? { systemPrompt?: string } | void\n : E extends 'tool_call'\n ? {\n args?: Record<string, unknown>;\n block?: { reason: string; terminate?: boolean };\n } | void\n : E extends 'tool_result'\n ? {\n content?: PoHeadlessContent[];\n details?: unknown;\n isError?: boolean;\n usage?: Record<string, unknown>;\n terminate?: boolean;\n } | void\n : unknown;\n\nexport type PoHeadlessHook<E extends PoHeadlessEventName = PoHeadlessEventName> = E extends PoHeadlessEventName\n ? {\n when?: PoHeadlessCondition;\n event: E;\n handle(\n event: Readonly<PoHeadlessHookEvent<E>>,\n context: PoHeadlessExecutionContext,\n ): PoHeadlessHookResult<E> | Promise<PoHeadlessHookResult<E>>;\n }\n : never;\n\n/** A retained package can register activity without starting disabled watchers or services. */\nexport interface PoHeadlessActivity {\n when?: PoHeadlessCondition;\n name: string;\n start(context: PoHeadlessExecutionContext): (() => void | Promise<void>) | Promise<() => void | Promise<void>>;\n}\n\n/** Read-only metadata for the currently applied session, never executable declarations or resource bodies. */\nexport interface PoHeadlessCapability {\n readonly source: string;\n readonly name: string;\n readonly kind: 'tool' | 'skill';\n readonly when?: PoHeadlessCondition;\n readonly active: boolean;\n readonly discoverable: boolean;\n readonly reason?: 'inactive' | 'restricted' | 'shadowed' | 'unavailable' | 'not-discoverable';\n}\n\n/** Visibility controls model discovery only, never admission for external execution. */\nexport function isPoHeadlessToolModelVisible(tool: Pick<PoHeadlessTool, '_meta'>): boolean {\n const visibility = tool._meta?.ui?.visibility;\n return visibility === undefined || visibility.includes('model');\n}\n\nexport interface PoHeadlessCapabilitySnapshot {\n readonly revision: number;\n readonly ready: boolean;\n readonly capabilities: readonly PoHeadlessCapability[];\n}\n\n/** Session skill discovery tools, adapted to the size of the active catalog. */\nexport const PO_LOAD_SKILL_TOOL = 'load_skill';\nexport const PO_SEARCH_SKILLS_TOOL = 'search_skills';\nexport interface PoHeadlessHostService {\n readonly context: PoHeadlessExecutionContext;\n /** Admitted external execution, sharing native hooks, restrictions and session lifetime. */\n readonly toolSurface?: Pick<SessionToolSurface, 'readSurface' | 'invokeTool'>;\n /** The instructions of an applied skill by exact name, or undefined when none is applied. */\n readSkill(name: string): string | undefined;\n /** Names and descriptions of searchable applied skills, excluding pinned guides and instruction bodies. */\n listSkills(): readonly { readonly name: string; readonly description: string }[];\n /** atToolBoundary defers the change to the next tool boundary, as slash commands do; idle sessions apply at once. */\n changeSelection(change: PoHeadlessSelectionChange, options?: { atToolBoundary?: boolean }): Promise<void>;\n assertActive(source?: string): void;\n /** Whether a package owns contributions in the currently applied selection. */\n isSourceActive(source: string): boolean;\n /**\n * Normalize selection before the first successful application only. Disposed with the registering context.\n * Resolvers run in registration order and each may be asynchronous. A resolver that changes the profile\n * decides it for the session, so turn-admission inheritance no longer overrides it; an explicit\n * `changeSelection` still can.\n */\n registerInitialSelectionResolver(\n resolve: (selection: PoHeadlessSelection) => PoHeadlessSelection | Promise<PoHeadlessSelection>,\n ): () => void;\n subscribeSelection(\n listener: (selection: PoHeadlessSelection, applied: PoHeadlessSelectionApplied) => void | Promise<void>,\n ): () => void;\n /** Inspect only a coherently applied generation. A transition returns ready=false and no partial inventory. */\n inspectCapabilities(): PoHeadlessCapabilitySnapshot;\n registerToolRestriction(restriction: PoHeadlessToolRestriction): PoHeadlessRegistration;\n registerTool<TParameters extends TSchema>(tool: PoHeadlessTool<TParameters>): PoHeadlessRegistration;\n registerResource(resource: PoHeadlessResource): PoHeadlessRegistration;\n registerCommand(command: PoHeadlessCommand): PoHeadlessRegistration;\n registerHook(hook: PoHeadlessHook): PoHeadlessRegistration;\n registerActivity(activity: PoHeadlessActivity): PoHeadlessRegistration;\n}\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/headless-host': PoHeadlessHostService;\n }\n}\n\nexport function readPoHeadlessHost(context: Context): PoHeadlessHostService | undefined {\n return context.get(PO_HEADLESS_HOST_SERVICE) as PoHeadlessHostService | undefined;\n}\n\nexport function requirePoHeadlessHost(context: Context): PoHeadlessHostService {\n const host = readPoHeadlessHost(context);\n if (!host) throw new Error('This contribution requires the Po headless session host.');\n return host;\n}\n"],"mappings":"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"}
|
|
@@ -75,6 +75,8 @@ function createHeadlessClient(options) {
|
|
|
75
75
|
"input"
|
|
76
76
|
].includes(request.kind)) return Promise.reject(/* @__PURE__ */ new Error("Unsupported headless client request"));
|
|
77
77
|
const labels = request.options?.map((option) => option.label);
|
|
78
|
+
const descriptions = request.options?.some((option) => option.description) ? request.options.map((option) => option.description ?? "") : void 0;
|
|
79
|
+
const icons = request.options?.some((option) => option.icon) ? request.options.map((option) => option.icon ?? "") : void 0;
|
|
78
80
|
if (labels && new Set(labels).size !== labels.length) return Promise.reject(/* @__PURE__ */ new Error("Select option labels must be unique"));
|
|
79
81
|
const approval = request.approval === void 0 ? void 0 : require_approvalPrompt.parsePoApprovalPrompt(request.approval);
|
|
80
82
|
if (request.approval !== void 0 && (approval === void 0 || request.kind !== "select")) return Promise.reject(/* @__PURE__ */ new Error("Invalid approval request"));
|
|
@@ -106,6 +108,8 @@ function createHeadlessClient(options) {
|
|
|
106
108
|
message: request.message,
|
|
107
109
|
purpose: request.purpose,
|
|
108
110
|
options: labels,
|
|
111
|
+
...descriptions === void 0 ? {} : { descriptions },
|
|
112
|
+
...icons === void 0 ? {} : { icons },
|
|
109
113
|
placeholder: request.message,
|
|
110
114
|
prefill: request.initialValue,
|
|
111
115
|
...approval === void 0 ? {} : { approval }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["createPoNotificationEntryData","PO_NOTIFICATION_ENTRY_TYPE","randomUUID","parsePoApprovalPrompt"],"sources":["../../../../src/services/headlessClient/index.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { createPoNotificationEntryData, PO_NOTIFICATION_ENTRY_TYPE } from '../../exports/notification';\nimport { parsePoApprovalPrompt } from '../../schemas/approval';\nimport type {\n HeadlessClientBridge,\n HeadlessClientOptions,\n PendingClientRequest,\n} from '../../types/server/headlessClient';\n\n/** Explicit client interactions over the existing browser dialog protocol. */\nexport function createHeadlessClient(options: HeadlessClientOptions): HeadlessClientBridge {\n const pending = new Map<string, PendingClientRequest>();\n const statuses = new Map<string, string | undefined>();\n let disposed = false;\n const advance = (): void => pending.values().next().value?.start();\n\n return {\n client: {\n async notify(request) {\n // Detached callbacks can outlive their session. A closed client has no recipient.\n if (disposed) return;\n const data = createPoNotificationEntryData(request);\n if (!data) throw new Error('Invalid notification request');\n try {\n await options.appendCustomEntry(PO_NOTIFICATION_ENTRY_TYPE, data);\n } catch (error) {\n if (disposed) return;\n // Callers fire notify without awaiting, often to report a journal failure. A rejection here\n // would crash the session server, so a refused append still reaches the browser live.\n options.emitFrame({\n type: 'error',\n code: 'notification_undelivered',\n error: `${data.body} (not saved to history: ${error instanceof Error ? error.message : String(error)})`,\n });\n }\n },\n setStatus(source, text) {\n // Packages clear their status while they unwind, which is after their session shut down. A\n // throw here used to abort that teardown and leak everything after it.\n if (disposed) return;\n // Pollers republish unchanged text; every frame bumps the session's hub row for all clients.\n if (statuses.has(source) && statuses.get(source) === text) return;\n statuses.set(source, text);\n options.emitFrame({ type: 'extension_ui_request', method: 'setStatus', statusKey: source, statusText: text });\n },\n appendComposerText(text) {\n if (disposed) throw new Error('Headless client is closed');\n options.emitFrame({ type: 'extension_ui_request', method: 'append_composer_text', id: randomUUID(), text });\n },\n pendingFeedback() {\n return pending.values().next().value?.feedback();\n },\n answerFeedback(id, value) {\n const current = pending.values().next().value;\n if (current?.feedback()?.id !== id) return false;\n current.respond({ value });\n return true;\n },\n pendingApproval() {\n return parsePoApprovalPrompt(pending.values().next().value?.approval());\n },\n answerApproval(id, decision, channel) {\n const current = pending.values().next().value;\n const approval = current?.approval();\n if (\n !current ||\n !approval ||\n approval.id !== id ||\n (decision !== 'approved' && decision !== 'denied') ||\n (channel !== 'app' && channel !== 'voice') ||\n (channel === 'voice' && approval.requirement !== 'ask')\n )\n return false;\n current.respond({ value: decision === 'approved' ? 'Approve' : 'Deny' });\n return true;\n },\n request(request, signal) {\n if (disposed) return Promise.reject(new Error('Headless client is closed'));\n if (signal?.aborted) return Promise.resolve(undefined);\n if (!['confirm', 'select', 'input'].includes(request.kind)) {\n return Promise.reject(new Error('Unsupported headless client request'));\n }\n const labels = request.options?.map((option) => option.label);\n if (labels && new Set(labels).size !== labels.length) {\n return Promise.reject(new Error('Select option labels must be unique'));\n }\n const approval = request.approval === undefined ? undefined : parsePoApprovalPrompt(request.approval);\n if (request.approval !== undefined && (approval === undefined || request.kind !== 'select')) {\n return Promise.reject(new Error('Invalid approval request'));\n }\n const id = `headless-ui:${randomUUID()}`;\n return new Promise((resolve, reject) => {\n let started = false;\n const finish = (outcome: { value: unknown } | { error: unknown }): void => {\n if (!pending.delete(id)) return;\n signal?.removeEventListener('abort', abort);\n try {\n if (started) options.emitFrame({ type: 'extension_ui_answered', id });\n if ('error' in outcome) reject(outcome.error);\n else resolve(outcome.value);\n } catch (error) {\n reject(error);\n } finally {\n advance();\n }\n };\n const abort = (): void => finish({ value: undefined });\n const frame = {\n type: 'extension_ui_request',\n id,\n method: request.kind === 'input' && request.multiline ? 'editor' : request.kind,\n title: request.title,\n message: request.message,\n purpose: request.purpose,\n options: labels,\n placeholder: request.message,\n prefill: request.initialValue,\n ...(approval === undefined ? {} : { approval }),\n };\n pending.set(id, {\n feedback() {\n if (!started || approval !== undefined || request.purpose !== 'feedback' || request.kind === 'confirm')\n return undefined;\n return {\n id,\n kind: request.kind,\n title: request.title,\n ...(request.message === undefined ? {} : { message: request.message }),\n ...(labels === undefined ? {} : { labels: [...labels] }),\n };\n },\n approval: () => (started ? approval : undefined),\n isStarted: () => started,\n replay(listener) {\n if (started) listener(frame);\n },\n start() {\n if (started || disposed) return;\n started = true;\n try {\n options.emitFrame(frame);\n } catch (error) {\n finish({ error });\n }\n },\n respond(frame) {\n if (!started) return;\n if (frame.cancelled === true) return finish({ value: undefined });\n if (request.kind === 'confirm') {\n if (typeof frame.confirmed === 'boolean') finish({ value: frame.confirmed });\n } else if (typeof frame.value === 'string') {\n // Rich questionnaire clients return an encoded whole-answer envelope here.\n const selected = request.options?.find((option) => option.label === frame.value);\n finish({ value: selected?.value ?? frame.value });\n }\n },\n close: () => finish({ error: new Error('Headless client is closed') }),\n });\n signal?.addEventListener('abort', abort, { once: true });\n advance();\n });\n },\n },\n receive(frame) {\n if (frame.type !== 'extension_ui_response') return false;\n const current = typeof frame.id === 'string' ? pending.get(frame.id) : undefined;\n if (!current?.isStarted()) return false;\n current.respond(frame);\n return true;\n },\n replay(listener) {\n for (const [statusKey, statusText] of statuses) {\n listener({ type: 'extension_ui_request', method: 'setStatus', statusKey, statusText });\n }\n // Only the current request is replayed, not answered or queued dialogs.\n pending.values().next().value?.replay(listener);\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n statuses.clear();\n for (const request of pending.values()) request.close();\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["createPoNotificationEntryData","PO_NOTIFICATION_ENTRY_TYPE","randomUUID","parsePoApprovalPrompt"],"sources":["../../../../src/services/headlessClient/index.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { createPoNotificationEntryData, PO_NOTIFICATION_ENTRY_TYPE } from '../../exports/notification';\nimport { parsePoApprovalPrompt } from '../../schemas/approval';\nimport type {\n HeadlessClientBridge,\n HeadlessClientOptions,\n PendingClientRequest,\n} from '../../types/server/headlessClient';\n\n/** Explicit client interactions over the existing browser dialog protocol. */\nexport function createHeadlessClient(options: HeadlessClientOptions): HeadlessClientBridge {\n const pending = new Map<string, PendingClientRequest>();\n const statuses = new Map<string, string | undefined>();\n let disposed = false;\n const advance = (): void => pending.values().next().value?.start();\n\n return {\n client: {\n async notify(request) {\n // Detached callbacks can outlive their session. A closed client has no recipient.\n if (disposed) return;\n const data = createPoNotificationEntryData(request);\n if (!data) throw new Error('Invalid notification request');\n try {\n await options.appendCustomEntry(PO_NOTIFICATION_ENTRY_TYPE, data);\n } catch (error) {\n if (disposed) return;\n // Callers fire notify without awaiting, often to report a journal failure. A rejection here\n // would crash the session server, so a refused append still reaches the browser live.\n options.emitFrame({\n type: 'error',\n code: 'notification_undelivered',\n error: `${data.body} (not saved to history: ${error instanceof Error ? error.message : String(error)})`,\n });\n }\n },\n setStatus(source, text) {\n // Packages clear their status while they unwind, which is after their session shut down. A\n // throw here used to abort that teardown and leak everything after it.\n if (disposed) return;\n // Pollers republish unchanged text; every frame bumps the session's hub row for all clients.\n if (statuses.has(source) && statuses.get(source) === text) return;\n statuses.set(source, text);\n options.emitFrame({ type: 'extension_ui_request', method: 'setStatus', statusKey: source, statusText: text });\n },\n appendComposerText(text) {\n if (disposed) throw new Error('Headless client is closed');\n options.emitFrame({ type: 'extension_ui_request', method: 'append_composer_text', id: randomUUID(), text });\n },\n pendingFeedback() {\n return pending.values().next().value?.feedback();\n },\n answerFeedback(id, value) {\n const current = pending.values().next().value;\n if (current?.feedback()?.id !== id) return false;\n current.respond({ value });\n return true;\n },\n pendingApproval() {\n return parsePoApprovalPrompt(pending.values().next().value?.approval());\n },\n answerApproval(id, decision, channel) {\n const current = pending.values().next().value;\n const approval = current?.approval();\n if (\n !current ||\n !approval ||\n approval.id !== id ||\n (decision !== 'approved' && decision !== 'denied') ||\n (channel !== 'app' && channel !== 'voice') ||\n (channel === 'voice' && approval.requirement !== 'ask')\n )\n return false;\n current.respond({ value: decision === 'approved' ? 'Approve' : 'Deny' });\n return true;\n },\n request(request, signal) {\n if (disposed) return Promise.reject(new Error('Headless client is closed'));\n if (signal?.aborted) return Promise.resolve(undefined);\n if (!['confirm', 'select', 'input'].includes(request.kind)) {\n return Promise.reject(new Error('Unsupported headless client request'));\n }\n const labels = request.options?.map((option) => option.label);\n // Parallel to labels, so a client that reads only `options` keeps working.\n const descriptions = request.options?.some((option) => option.description)\n ? request.options.map((option) => option.description ?? '')\n : undefined;\n const icons = request.options?.some((option) => option.icon)\n ? request.options.map((option) => option.icon ?? '')\n : undefined;\n if (labels && new Set(labels).size !== labels.length) {\n return Promise.reject(new Error('Select option labels must be unique'));\n }\n const approval = request.approval === undefined ? undefined : parsePoApprovalPrompt(request.approval);\n if (request.approval !== undefined && (approval === undefined || request.kind !== 'select')) {\n return Promise.reject(new Error('Invalid approval request'));\n }\n const id = `headless-ui:${randomUUID()}`;\n return new Promise((resolve, reject) => {\n let started = false;\n const finish = (outcome: { value: unknown } | { error: unknown }): void => {\n if (!pending.delete(id)) return;\n signal?.removeEventListener('abort', abort);\n try {\n if (started) options.emitFrame({ type: 'extension_ui_answered', id });\n if ('error' in outcome) reject(outcome.error);\n else resolve(outcome.value);\n } catch (error) {\n reject(error);\n } finally {\n advance();\n }\n };\n const abort = (): void => finish({ value: undefined });\n const frame = {\n type: 'extension_ui_request',\n id,\n method: request.kind === 'input' && request.multiline ? 'editor' : request.kind,\n title: request.title,\n message: request.message,\n purpose: request.purpose,\n options: labels,\n ...(descriptions === undefined ? {} : { descriptions }),\n ...(icons === undefined ? {} : { icons }),\n placeholder: request.message,\n prefill: request.initialValue,\n ...(approval === undefined ? {} : { approval }),\n };\n pending.set(id, {\n feedback() {\n if (!started || approval !== undefined || request.purpose !== 'feedback' || request.kind === 'confirm')\n return undefined;\n return {\n id,\n kind: request.kind,\n title: request.title,\n ...(request.message === undefined ? {} : { message: request.message }),\n ...(labels === undefined ? {} : { labels: [...labels] }),\n };\n },\n approval: () => (started ? approval : undefined),\n isStarted: () => started,\n replay(listener) {\n if (started) listener(frame);\n },\n start() {\n if (started || disposed) return;\n started = true;\n try {\n options.emitFrame(frame);\n } catch (error) {\n finish({ error });\n }\n },\n respond(frame) {\n if (!started) return;\n if (frame.cancelled === true) return finish({ value: undefined });\n if (request.kind === 'confirm') {\n if (typeof frame.confirmed === 'boolean') finish({ value: frame.confirmed });\n } else if (typeof frame.value === 'string') {\n // Rich questionnaire clients return an encoded whole-answer envelope here.\n const selected = request.options?.find((option) => option.label === frame.value);\n finish({ value: selected?.value ?? frame.value });\n }\n },\n close: () => finish({ error: new Error('Headless client is closed') }),\n });\n signal?.addEventListener('abort', abort, { once: true });\n advance();\n });\n },\n },\n receive(frame) {\n if (frame.type !== 'extension_ui_response') return false;\n const current = typeof frame.id === 'string' ? pending.get(frame.id) : undefined;\n if (!current?.isStarted()) return false;\n current.respond(frame);\n return true;\n },\n replay(listener) {\n for (const [statusKey, statusText] of statuses) {\n listener({ type: 'extension_ui_request', method: 'setStatus', statusKey, statusText });\n }\n // Only the current request is replayed, not answered or queued dialogs.\n pending.values().next().value?.replay(listener);\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n statuses.clear();\n for (const request of pending.values()) request.close();\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -75,6 +75,8 @@ function createHeadlessClient(options) {
|
|
|
75
75
|
"input"
|
|
76
76
|
].includes(request.kind)) return Promise.reject(/* @__PURE__ */ new Error("Unsupported headless client request"));
|
|
77
77
|
const labels = request.options?.map((option) => option.label);
|
|
78
|
+
const descriptions = request.options?.some((option) => option.description) ? request.options.map((option) => option.description ?? "") : void 0;
|
|
79
|
+
const icons = request.options?.some((option) => option.icon) ? request.options.map((option) => option.icon ?? "") : void 0;
|
|
78
80
|
if (labels && new Set(labels).size !== labels.length) return Promise.reject(/* @__PURE__ */ new Error("Select option labels must be unique"));
|
|
79
81
|
const approval = request.approval === void 0 ? void 0 : parsePoApprovalPrompt(request.approval);
|
|
80
82
|
if (request.approval !== void 0 && (approval === void 0 || request.kind !== "select")) return Promise.reject(/* @__PURE__ */ new Error("Invalid approval request"));
|
|
@@ -106,6 +108,8 @@ function createHeadlessClient(options) {
|
|
|
106
108
|
message: request.message,
|
|
107
109
|
purpose: request.purpose,
|
|
108
110
|
options: labels,
|
|
111
|
+
...descriptions === void 0 ? {} : { descriptions },
|
|
112
|
+
...icons === void 0 ? {} : { icons },
|
|
109
113
|
placeholder: request.message,
|
|
110
114
|
prefill: request.initialValue,
|
|
111
115
|
...approval === void 0 ? {} : { approval }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/headlessClient/index.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { createPoNotificationEntryData, PO_NOTIFICATION_ENTRY_TYPE } from '../../exports/notification';\nimport { parsePoApprovalPrompt } from '../../schemas/approval';\nimport type {\n HeadlessClientBridge,\n HeadlessClientOptions,\n PendingClientRequest,\n} from '../../types/server/headlessClient';\n\n/** Explicit client interactions over the existing browser dialog protocol. */\nexport function createHeadlessClient(options: HeadlessClientOptions): HeadlessClientBridge {\n const pending = new Map<string, PendingClientRequest>();\n const statuses = new Map<string, string | undefined>();\n let disposed = false;\n const advance = (): void => pending.values().next().value?.start();\n\n return {\n client: {\n async notify(request) {\n // Detached callbacks can outlive their session. A closed client has no recipient.\n if (disposed) return;\n const data = createPoNotificationEntryData(request);\n if (!data) throw new Error('Invalid notification request');\n try {\n await options.appendCustomEntry(PO_NOTIFICATION_ENTRY_TYPE, data);\n } catch (error) {\n if (disposed) return;\n // Callers fire notify without awaiting, often to report a journal failure. A rejection here\n // would crash the session server, so a refused append still reaches the browser live.\n options.emitFrame({\n type: 'error',\n code: 'notification_undelivered',\n error: `${data.body} (not saved to history: ${error instanceof Error ? error.message : String(error)})`,\n });\n }\n },\n setStatus(source, text) {\n // Packages clear their status while they unwind, which is after their session shut down. A\n // throw here used to abort that teardown and leak everything after it.\n if (disposed) return;\n // Pollers republish unchanged text; every frame bumps the session's hub row for all clients.\n if (statuses.has(source) && statuses.get(source) === text) return;\n statuses.set(source, text);\n options.emitFrame({ type: 'extension_ui_request', method: 'setStatus', statusKey: source, statusText: text });\n },\n appendComposerText(text) {\n if (disposed) throw new Error('Headless client is closed');\n options.emitFrame({ type: 'extension_ui_request', method: 'append_composer_text', id: randomUUID(), text });\n },\n pendingFeedback() {\n return pending.values().next().value?.feedback();\n },\n answerFeedback(id, value) {\n const current = pending.values().next().value;\n if (current?.feedback()?.id !== id) return false;\n current.respond({ value });\n return true;\n },\n pendingApproval() {\n return parsePoApprovalPrompt(pending.values().next().value?.approval());\n },\n answerApproval(id, decision, channel) {\n const current = pending.values().next().value;\n const approval = current?.approval();\n if (\n !current ||\n !approval ||\n approval.id !== id ||\n (decision !== 'approved' && decision !== 'denied') ||\n (channel !== 'app' && channel !== 'voice') ||\n (channel === 'voice' && approval.requirement !== 'ask')\n )\n return false;\n current.respond({ value: decision === 'approved' ? 'Approve' : 'Deny' });\n return true;\n },\n request(request, signal) {\n if (disposed) return Promise.reject(new Error('Headless client is closed'));\n if (signal?.aborted) return Promise.resolve(undefined);\n if (!['confirm', 'select', 'input'].includes(request.kind)) {\n return Promise.reject(new Error('Unsupported headless client request'));\n }\n const labels = request.options?.map((option) => option.label);\n if (labels && new Set(labels).size !== labels.length) {\n return Promise.reject(new Error('Select option labels must be unique'));\n }\n const approval = request.approval === undefined ? undefined : parsePoApprovalPrompt(request.approval);\n if (request.approval !== undefined && (approval === undefined || request.kind !== 'select')) {\n return Promise.reject(new Error('Invalid approval request'));\n }\n const id = `headless-ui:${randomUUID()}`;\n return new Promise((resolve, reject) => {\n let started = false;\n const finish = (outcome: { value: unknown } | { error: unknown }): void => {\n if (!pending.delete(id)) return;\n signal?.removeEventListener('abort', abort);\n try {\n if (started) options.emitFrame({ type: 'extension_ui_answered', id });\n if ('error' in outcome) reject(outcome.error);\n else resolve(outcome.value);\n } catch (error) {\n reject(error);\n } finally {\n advance();\n }\n };\n const abort = (): void => finish({ value: undefined });\n const frame = {\n type: 'extension_ui_request',\n id,\n method: request.kind === 'input' && request.multiline ? 'editor' : request.kind,\n title: request.title,\n message: request.message,\n purpose: request.purpose,\n options: labels,\n placeholder: request.message,\n prefill: request.initialValue,\n ...(approval === undefined ? {} : { approval }),\n };\n pending.set(id, {\n feedback() {\n if (!started || approval !== undefined || request.purpose !== 'feedback' || request.kind === 'confirm')\n return undefined;\n return {\n id,\n kind: request.kind,\n title: request.title,\n ...(request.message === undefined ? {} : { message: request.message }),\n ...(labels === undefined ? {} : { labels: [...labels] }),\n };\n },\n approval: () => (started ? approval : undefined),\n isStarted: () => started,\n replay(listener) {\n if (started) listener(frame);\n },\n start() {\n if (started || disposed) return;\n started = true;\n try {\n options.emitFrame(frame);\n } catch (error) {\n finish({ error });\n }\n },\n respond(frame) {\n if (!started) return;\n if (frame.cancelled === true) return finish({ value: undefined });\n if (request.kind === 'confirm') {\n if (typeof frame.confirmed === 'boolean') finish({ value: frame.confirmed });\n } else if (typeof frame.value === 'string') {\n // Rich questionnaire clients return an encoded whole-answer envelope here.\n const selected = request.options?.find((option) => option.label === frame.value);\n finish({ value: selected?.value ?? frame.value });\n }\n },\n close: () => finish({ error: new Error('Headless client is closed') }),\n });\n signal?.addEventListener('abort', abort, { once: true });\n advance();\n });\n },\n },\n receive(frame) {\n if (frame.type !== 'extension_ui_response') return false;\n const current = typeof frame.id === 'string' ? pending.get(frame.id) : undefined;\n if (!current?.isStarted()) return false;\n current.respond(frame);\n return true;\n },\n replay(listener) {\n for (const [statusKey, statusText] of statuses) {\n listener({ type: 'extension_ui_request', method: 'setStatus', statusKey, statusText });\n }\n // Only the current request is replayed, not answered or queued dialogs.\n pending.values().next().value?.replay(listener);\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n statuses.clear();\n for (const request of pending.values()) request.close();\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/headlessClient/index.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { createPoNotificationEntryData, PO_NOTIFICATION_ENTRY_TYPE } from '../../exports/notification';\nimport { parsePoApprovalPrompt } from '../../schemas/approval';\nimport type {\n HeadlessClientBridge,\n HeadlessClientOptions,\n PendingClientRequest,\n} from '../../types/server/headlessClient';\n\n/** Explicit client interactions over the existing browser dialog protocol. */\nexport function createHeadlessClient(options: HeadlessClientOptions): HeadlessClientBridge {\n const pending = new Map<string, PendingClientRequest>();\n const statuses = new Map<string, string | undefined>();\n let disposed = false;\n const advance = (): void => pending.values().next().value?.start();\n\n return {\n client: {\n async notify(request) {\n // Detached callbacks can outlive their session. A closed client has no recipient.\n if (disposed) return;\n const data = createPoNotificationEntryData(request);\n if (!data) throw new Error('Invalid notification request');\n try {\n await options.appendCustomEntry(PO_NOTIFICATION_ENTRY_TYPE, data);\n } catch (error) {\n if (disposed) return;\n // Callers fire notify without awaiting, often to report a journal failure. A rejection here\n // would crash the session server, so a refused append still reaches the browser live.\n options.emitFrame({\n type: 'error',\n code: 'notification_undelivered',\n error: `${data.body} (not saved to history: ${error instanceof Error ? error.message : String(error)})`,\n });\n }\n },\n setStatus(source, text) {\n // Packages clear their status while they unwind, which is after their session shut down. A\n // throw here used to abort that teardown and leak everything after it.\n if (disposed) return;\n // Pollers republish unchanged text; every frame bumps the session's hub row for all clients.\n if (statuses.has(source) && statuses.get(source) === text) return;\n statuses.set(source, text);\n options.emitFrame({ type: 'extension_ui_request', method: 'setStatus', statusKey: source, statusText: text });\n },\n appendComposerText(text) {\n if (disposed) throw new Error('Headless client is closed');\n options.emitFrame({ type: 'extension_ui_request', method: 'append_composer_text', id: randomUUID(), text });\n },\n pendingFeedback() {\n return pending.values().next().value?.feedback();\n },\n answerFeedback(id, value) {\n const current = pending.values().next().value;\n if (current?.feedback()?.id !== id) return false;\n current.respond({ value });\n return true;\n },\n pendingApproval() {\n return parsePoApprovalPrompt(pending.values().next().value?.approval());\n },\n answerApproval(id, decision, channel) {\n const current = pending.values().next().value;\n const approval = current?.approval();\n if (\n !current ||\n !approval ||\n approval.id !== id ||\n (decision !== 'approved' && decision !== 'denied') ||\n (channel !== 'app' && channel !== 'voice') ||\n (channel === 'voice' && approval.requirement !== 'ask')\n )\n return false;\n current.respond({ value: decision === 'approved' ? 'Approve' : 'Deny' });\n return true;\n },\n request(request, signal) {\n if (disposed) return Promise.reject(new Error('Headless client is closed'));\n if (signal?.aborted) return Promise.resolve(undefined);\n if (!['confirm', 'select', 'input'].includes(request.kind)) {\n return Promise.reject(new Error('Unsupported headless client request'));\n }\n const labels = request.options?.map((option) => option.label);\n // Parallel to labels, so a client that reads only `options` keeps working.\n const descriptions = request.options?.some((option) => option.description)\n ? request.options.map((option) => option.description ?? '')\n : undefined;\n const icons = request.options?.some((option) => option.icon)\n ? request.options.map((option) => option.icon ?? '')\n : undefined;\n if (labels && new Set(labels).size !== labels.length) {\n return Promise.reject(new Error('Select option labels must be unique'));\n }\n const approval = request.approval === undefined ? undefined : parsePoApprovalPrompt(request.approval);\n if (request.approval !== undefined && (approval === undefined || request.kind !== 'select')) {\n return Promise.reject(new Error('Invalid approval request'));\n }\n const id = `headless-ui:${randomUUID()}`;\n return new Promise((resolve, reject) => {\n let started = false;\n const finish = (outcome: { value: unknown } | { error: unknown }): void => {\n if (!pending.delete(id)) return;\n signal?.removeEventListener('abort', abort);\n try {\n if (started) options.emitFrame({ type: 'extension_ui_answered', id });\n if ('error' in outcome) reject(outcome.error);\n else resolve(outcome.value);\n } catch (error) {\n reject(error);\n } finally {\n advance();\n }\n };\n const abort = (): void => finish({ value: undefined });\n const frame = {\n type: 'extension_ui_request',\n id,\n method: request.kind === 'input' && request.multiline ? 'editor' : request.kind,\n title: request.title,\n message: request.message,\n purpose: request.purpose,\n options: labels,\n ...(descriptions === undefined ? {} : { descriptions }),\n ...(icons === undefined ? {} : { icons }),\n placeholder: request.message,\n prefill: request.initialValue,\n ...(approval === undefined ? {} : { approval }),\n };\n pending.set(id, {\n feedback() {\n if (!started || approval !== undefined || request.purpose !== 'feedback' || request.kind === 'confirm')\n return undefined;\n return {\n id,\n kind: request.kind,\n title: request.title,\n ...(request.message === undefined ? {} : { message: request.message }),\n ...(labels === undefined ? {} : { labels: [...labels] }),\n };\n },\n approval: () => (started ? approval : undefined),\n isStarted: () => started,\n replay(listener) {\n if (started) listener(frame);\n },\n start() {\n if (started || disposed) return;\n started = true;\n try {\n options.emitFrame(frame);\n } catch (error) {\n finish({ error });\n }\n },\n respond(frame) {\n if (!started) return;\n if (frame.cancelled === true) return finish({ value: undefined });\n if (request.kind === 'confirm') {\n if (typeof frame.confirmed === 'boolean') finish({ value: frame.confirmed });\n } else if (typeof frame.value === 'string') {\n // Rich questionnaire clients return an encoded whole-answer envelope here.\n const selected = request.options?.find((option) => option.label === frame.value);\n finish({ value: selected?.value ?? frame.value });\n }\n },\n close: () => finish({ error: new Error('Headless client is closed') }),\n });\n signal?.addEventListener('abort', abort, { once: true });\n advance();\n });\n },\n },\n receive(frame) {\n if (frame.type !== 'extension_ui_response') return false;\n const current = typeof frame.id === 'string' ? pending.get(frame.id) : undefined;\n if (!current?.isStarted()) return false;\n current.respond(frame);\n return true;\n },\n replay(listener) {\n for (const [statusKey, statusText] of statuses) {\n listener({ type: 'extension_ui_request', method: 'setStatus', statusKey, statusText });\n }\n // Only the current request is replayed, not answered or queued dialogs.\n pending.values().next().value?.replay(listener);\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n statuses.clear();\n for (const request of pending.values()) request.close();\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -701,6 +701,7 @@ async function composeHeadlessSessionHost(options, releaseEnvironment) {
|
|
|
701
701
|
}
|
|
702
702
|
const name = match[1];
|
|
703
703
|
const args = match[2] ?? "";
|
|
704
|
+
if (!headlessHost.status.ready) await headlessHost.settled();
|
|
704
705
|
if (!headlessHost.listCommands().some((entry) => entry.name === name)) {
|
|
705
706
|
headlessHost.assertActive();
|
|
706
707
|
return false;
|