Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions PROTOCOL.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@ Fields:
| `initialValues` | `array` | No | Default selected values for multiselect |
| `active` | `string` | No | Label for "true" in confirm (default: "Yes") |
| `inactive` | `string` | No | Label for "false" in confirm (default: "No") |
| `sensitive` | `boolean` | No | Marks prompt as sensitive. Sensitive values must not be sent in OSC `resolve` payloads. |

### Resolve

Emitted in two scenarios:

1. **By the terminal host** into PTY stdin when the user interacts with the host's native UI
2. **By the application** to stdout when the TUI prompt resolves normally
2. **By the application** to stdout when the TUI prompt resolves normally (except `sensitive: true` prompts)

```
ESC ] 7770 ; <json> BEL
Expand Down Expand Up @@ -92,8 +93,8 @@ The reference implementation provides higher-level prompt types that map to exis

| Variant | Wire Type | Description |
|---|---|---|
| `password` | `input` | Text input with masked TUI display. The OSC payload is identical to `input`. The masking is purely a TUI rendering concern. |
| `number` | `input` | Numeric input with validation, min/max bounds, and up/down stepping. The OSC payload is identical to `input`. Numeric constraints are enforced application-side. |
| `password` | `input` | Text input with masked TUI display. Should set `sensitive: true`; secret values should return via normal stdin keystrokes, not OSC `resolve`. |
| `number` | `input` | Numeric input with validation, min/max bounds, and up/down stepping. Numeric resolve values may be string or number. |
| `search` | `select` | Filterable select with a text query. The OSC payload is identical to `select` (all options included). Filtering is a TUI-side behavior. |

### Group
Expand Down Expand Up @@ -194,9 +195,13 @@ Terminal hosts can render these as toast notifications, status bar updates, or s
1. Register an OSC handler for code 7770
2. Parse the JSON payload
3. If `type` is not `"resolve"`, display a native UI (modal, panel, buttons)
4. When the user makes a selection, write the resolve sequence into PTY stdin
4. When the user makes a selection, write the resolve sequence into PTY stdin (except `sensitive: true`, where host should inject keystrokes)
5. The application's stdin handler detects the resolve and completes the prompt

For prompts with `sensitive: true`, hosts should not send secret values in
OSC resolve payloads. If intercepting, hosts should inject normal stdin
keystrokes instead.

The resolve sequence is written as raw bytes into the PTY's stdin file descriptor. The application's prompt library parses it.

### xterm.js Example
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ Keys: `Space` to toggle, `a` to toggle all, `Enter` to submit.
### password

Masked text input.
Password prompts are marked as sensitive: secret values are not sent in OSC
`resolve` payloads.

```typescript
const secret = await password({
Expand Down Expand Up @@ -421,6 +423,9 @@ ESC ] 7770 ; {"v":1,"type":"select","id":"...","message":"Pick a framework","opt

Terminal hosts (web terminals, IDE terminals, multiplexers) can register an OSC handler for code `7770`, intercept the payload, and render native UI (dropdowns, modals, checkboxes, progress bars) instead of the TUI. When the user makes a selection, the host writes a resolve message back to PTY stdin.

For sensitive prompts (for example `password`), hosts should return values as
normal PTY keystrokes instead of OSC `resolve` payload values.

Terminals that don't support OSC 7770 silently ignore the sequences per ECMA-48. The TUI works exactly as it would without the protocol.

Your code doesn't change. No feature flags. No configuration. The library handles everything.
Expand Down
86 changes: 56 additions & 30 deletions SPEC.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# OSC 7770: Structured Terminal Prompts

**Version:** 1.1.0
**Date:** 2026-03-07
**Version:** 1.1.1
**Date:** 2026-03-09
**Status:** Living Standard
**Authors:** Termprompt Contributors
**Authors:** Zlatko Fedor
**Canonical URL:** https://github.com/seeden/termprompt/blob/main/SPEC.md
**License:** MIT

Expand Down Expand Up @@ -249,11 +249,12 @@ type. See [Section 8](#8-prompt-types) for per-type field definitions.
| Field | Type | Applicable Types | Description |
|----------------|------------|--------------------|-------------|
| `options` | `array` | select, multiselect | Array of option objects. See below. |
| `placeholder` | `string` | input | Placeholder text shown when value is empty. |
| `placeholder` | `string` | input, select (search variant) | Placeholder text shown when value is empty. |
| `initialValue` | `any` | select, confirm, input | Default value. |
| `initialValues`| `array` | multiselect | Default selected values. |
| `active` | `string` | confirm | Label for the affirmative choice. Default: `"Yes"`. |
| `inactive` | `string` | confirm | Label for the negative choice. Default: `"No"`. |
| `sensitive` | `boolean` | input | If `true`, resolved value MUST NOT be transmitted in OSC `resolve` payloads. |

#### Option Object

Expand Down Expand Up @@ -302,7 +303,8 @@ in two distinct scenarios:

2. **Application -> Terminal host (via stdout).** When the user interacts
with the TUI fallback normally (no host interception), the application
emits a resolve message to stdout after the prompt completes. This
emits a resolve message to stdout after the prompt completes (except for
prompts marked `sensitive: true`). This
allows the terminal host to track prompt state even when it chose not
to intercept.

Expand Down Expand Up @@ -626,23 +628,30 @@ The `options` array MUST contain at least one non-disabled option. The
An application-level variant of `input` that masks user input in the TUI.
The OSC wire type is `"input"`. Terminal hosts that intercept an `input`
prompt cannot distinguish a password prompt from a regular text input based
on the OSC payload alone. The masking is purely a TUI rendering concern.
on the OSC payload alone unless the optional `sensitive` field is present.
The masking is primarily a TUI rendering concern.

**Wire type:** `"input"`
**Required fields:** `message`
**Optional fields:** `placeholder`
**Optional fields:** `placeholder`, `sensitive` (SHOULD be `true`)

The application renders each typed character as a mask symbol (e.g., `*`)
in the TUI. The `message` and `placeholder` fields are visible in the OSC
payload, but the user's typed response is never transmitted via OSC 7770
(it travels through normal stdin).
payload.

**Resolve value type:** `string`

#### Security Note

See Section 12.2. The prompt message is transmitted in cleartext. The
actual password value is never included in an OSC sequence.
For password prompts, applications SHOULD set `sensitive: true`. For
`sensitive` prompts:

- The application MUST NOT emit a stdout OSC `resolve` containing the user's value.
- The terminal host MUST NOT send a stdin OSC `resolve` containing the user's value.
- If the terminal host intercepts, it SHOULD inject normal stdin keystrokes
(as if the user typed) rather than a resolve payload.

See Section 12.2.

### 8.6. number

Expand All @@ -658,8 +667,8 @@ The TUI restricts keystroke input to numeric characters (`0-9`, `-`, `.`)
and supports up/down arrow keys for incrementing and decrementing by a
configurable step value.

**Resolve value type:** `string` (the terminal host sends a string; the
application parses it as a number)
**Resolve value type:** `number` or numeric `string` (the terminal host MAY
send either; the application parses and validates as number)

#### Terminal Host Rendering Suggestions

Expand Down Expand Up @@ -732,22 +741,30 @@ When a terminal host receives a prompt announcement (type is `"select"`,

1. **Intercept:** Suppress the TUI rendering and display a native UI
component. When the user makes a selection, write a resolve message
(Section 7.2) into the PTY's stdin file descriptor as raw bytes.
(Section 7.2) into the PTY's stdin file descriptor as raw bytes. For
prompts with `sensitive: true`, do not send value-carrying resolve
payloads; inject normal stdin keystrokes instead.

2. **Observe:** Record the prompt metadata without intercepting. Allow
the TUI fallback to proceed normally. Optionally listen for the
application's resolve message on stdout.

3. **Ignore:** Discard the message entirely.

A terminal host that intercepts a prompt MUST write the resolve message
before the application's TUI times out or the user interacts with the TUI
fallback. Race conditions between host-initiated and TUI-initiated input
are resolved by the application accepting whichever completes first.
A terminal host that intercepts a prompt MUST complete the interaction
(resolve message for non-sensitive prompts, or stdin keystroke injection
for sensitive prompts) before the application's TUI times out or the user
interacts with the TUI fallback. Race conditions between host-initiated and
TUI-initiated input are resolved by the application accepting whichever
completes first.

For prompts with `sensitive: true`, terminal hosts MUST NOT send the secret
value in an OSC `resolve` payload. If intercepting, hosts SHOULD inject
normal stdin keystrokes instead.

### 9.3. Non-Interactive Events

For spinner and log messages, the terminal host MAY render enhanced UI
For spinner, progress, tasks, and log messages, the terminal host MAY render enhanced UI
(progress bars, toast notifications, structured log panels). The terminal
host MUST NOT write any data to PTY stdin in response to these messages.

Expand Down Expand Up @@ -802,6 +819,9 @@ prefix (`ESC ] 7770 ;`) and extract the JSON payload. If the parsed
message is a valid resolve for the active prompt, the application MUST
complete the prompt with the provided value and clean up TUI state.

For prompts marked `sensitive: true`, applications MUST ignore OSC resolve
payloads that carry user values and rely on normal stdin keystrokes.

Non-matching data on stdin MUST be processed as normal keystroke input.

---
Expand Down Expand Up @@ -843,10 +863,8 @@ resolved values against expected types and ranges.
Prompt messages and option values are transmitted as cleartext in the OSC
sequence. Applications SHOULD NOT include secrets, credentials, or
personally identifiable information in prompt payloads. For password-type
prompts, the `input` type may be used with the understanding that the
`placeholder` and `message` fields are visible in the escape sequence, but
the user's typed response is not transmitted via OSC 7770 (it travels
through normal stdin).
prompts, applications SHOULD set `sensitive: true` and MUST NOT transport
the secret value in OSC resolve payloads in either direction.

### 12.3. JSON Parsing

Expand Down Expand Up @@ -964,19 +982,19 @@ New message types or optional fields do not require a version increment.

### Plain Text

> OSC 7770: Structured Terminal Prompts, Version 1.0.0, 2026.
> OSC 7770: Structured Terminal Prompts, Version 1.1.1, 2026.
> https://github.com/seeden/termprompt/blob/main/SPEC.md

### BibTeX

```bibtex
@techreport{osc7770,
title = {{OSC} 7770: Structured Terminal Prompts},
author = {{Termprompt Contributors}},
author = {Zlatko Fedor},
year = {2026},
month = mar,
type = {Living Standard},
version = {1.0.0},
version = {1.1.1},
url = {https://github.com/seeden/termprompt/blob/main/SPEC.md},
note = {Specification for structured interactive prompt
announcements over terminal escape sequences}
Expand All @@ -985,20 +1003,28 @@ New message types or optional fields do not require a version increment.

### APA

Termprompt Contributors. (2026). *OSC 7770: Structured terminal prompts*
(Version 1.0.0) [Living Standard].
Fedor, Z. (2026). *OSC 7770: Structured terminal prompts*
(Version 1.1.1) [Living Standard].
https://github.com/seeden/termprompt/blob/main/SPEC.md

### Chicago

Termprompt Contributors. "OSC 7770: Structured Terminal Prompts."
Version 1.0.0. Living Standard, March 2026.
Fedor, Zlatko. "OSC 7770: Structured Terminal Prompts."
Version 1.1.1. Living Standard, March 2026.
https://github.com/seeden/termprompt/blob/main/SPEC.md.

---

## 17. Changelog

### Version 1.1.1 (2026-03-09)

- Added optional `sensitive` prompt field for input-style prompts.
- Defined secure handling for `sensitive` prompts: no OSC resolve values.
- Clarified `number` resolve value typing (numeric string or number).
- Clarified placeholder applicability for search (`select` wire type).
- Clarified non-interactive handling for `progress` and `tasks`.

### Version 1.1.0 (2026-03-07)

- Added `progress` message type for determinate progress bars.
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"private": true,
"version": "0.2.1",
"version": "0.2.2",
"type": "module",
"packageManager": "pnpm@10.29.3",
"scripts": {
"build": "pnpm -r build",
Expand Down
6 changes: 5 additions & 1 deletion packages/protocol/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ for (const msg of messages) {
case 'confirm':
case 'input':
case 'multiselect':
// Interactive prompt - show native UI, then resolve
// Interactive prompt - show native UI, then resolve.
// If payload.sensitive === true, return via stdin keystrokes instead.
break;
case 'spinner':
case 'progress':
Expand All @@ -57,6 +58,9 @@ const data = encodeResolve(promptId, selectedValue);
pty.write(data);
```

For prompts with `sensitive: true`, do not send secret values in OSC
`resolve` payloads. Inject normal stdin keystrokes instead.

### Encode a prompt announcement

```typescript
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@termprompt/protocol",
"version": "0.2.1",
"version": "0.2.2",
"type": "module",
"description": "OSC 7770 protocol parser and encoder for termprompt",
"license": "MIT",
Expand Down
1 change: 1 addition & 0 deletions packages/protocol/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export type OscPromptPayload = {
type: "select" | "confirm" | "input" | "multiselect";
id: string;
message: string;
sensitive?: boolean;
options?: Array<{
value: unknown;
label: string;
Expand Down
55 changes: 0 additions & 55 deletions packages/termprompt/README.md

This file was deleted.

1 change: 1 addition & 0 deletions packages/termprompt/README.md
2 changes: 1 addition & 1 deletion packages/termprompt/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "termprompt",
"version": "0.2.1",
"version": "0.2.2",
"type": "module",
"description": "Beautiful terminal prompts with OSC 7770 structured output for rich terminal hosts",
"license": "MIT",
Expand Down
Loading