@buckeyestudio/pi-wire 18.4.13
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/CHANGELOG.md +82 -0
- package/LICENSE +22 -0
- package/README.md +31 -0
- package/package.json +54 -0
- package/src/index.ts +451 -0
- package/src/skillshare.ts +323 -0
- package/src/stream.ts +169 -0
- package/src/tsp.ts +958 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## [18.4.11] - 2026-10-02
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added support for agent completion estimates in the wire protocol.
|
|
10
|
+
|
|
11
|
+
## [18.4.9] - 2026-10-01
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Added terminal scrolling support through the TSP scroll operation, allowing keyboard scrolling of the terminal scroller when supported.
|
|
16
|
+
- Added a terminal focus event for requesting keyboard focus on editors and inputs, or returning focus to the preferences sheet.
|
|
17
|
+
|
|
18
|
+
## [18.4.6] - 2026-10-01
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Added the `TspMeterMark` component for marking a position on a meter track with a custom icon and a total value that defines the full span of the track.
|
|
23
|
+
|
|
24
|
+
## [18.4.4] - 2026-09-29
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- Added the Tern Surface Protocol wire contract (`@oh-my-pi/pi-wire`): message framing constants, the component vocabulary, document ops, frames, the handshake and terminal events that let omp render natively in terminals that speak it
|
|
29
|
+
|
|
30
|
+
## [18.2.11] - 2026-09-23
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- Added public API contract and authentication schemas for the Skillshare registry.
|
|
35
|
+
|
|
36
|
+
## [18.2.5] - 2026-09-17
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- Added the `omp stream` wire contract (`@oh-my-pi/pi-wire/stream`) for pane screen updates, viewer snapshots and chat, channel metadata, and `live.omp.sh` stream routes.
|
|
41
|
+
- Added authentication support for stencil.so streams, including user identity in welcome messages, channel ownership metadata, and explicit unauthorized and forbidden close codes.
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- Restricted channel names to the Stencil-compatible alphanumeric-and-underscore format and derived host channels from authentication tokens rather than URL path segments.
|
|
46
|
+
|
|
47
|
+
## [16.3.0] - 2026-07-02
|
|
48
|
+
|
|
49
|
+
### Breaking Changes
|
|
50
|
+
|
|
51
|
+
- Upgraded the collaboration protocol to version 3. Guests using version 2 will now be rejected during the handshake with a protocol-mismatch error.
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- Added support for interactive UI request and response frames, enabling browser guests to respond to prompts initiated by the host.
|
|
56
|
+
|
|
57
|
+
## [16.1.8] - 2026-06-20
|
|
58
|
+
|
|
59
|
+
### Breaking Changes
|
|
60
|
+
|
|
61
|
+
- Bumped `COLLAB_PROTO` to `2`. The `welcome` host frame now carries metadata only (`header`, `state`, `agents`, `entryCount`, optional `readOnly`) — the transcript moves to a new `snapshot-chunk` host frame (`{ entries: SessionEntry[]; final: boolean }`) sent immediately after the welcome. Hosts split large snapshots into multiple chunks; the last chunk carries `final: true`. Old guests speaking proto v1 are rejected with the existing protocol-mismatch error. ([#3144](https://github.com/can1357/oh-my-pi/issues/3144))
|
|
62
|
+
|
|
63
|
+
## [15.12.4] - 2026-06-13
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- Changed `WireModel.contextWindow` and `ContextUsage.contextWindow` to `number | null` to allow representing unavailable context-window values
|
|
68
|
+
|
|
69
|
+
## [15.12.0] - 2026-06-12
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- Added `readOnly` flags to participant and session payload types to indicate when a guest is connected via a read-only (view) link
|
|
74
|
+
- Added `writeToken` to `GuestFrame` hello payloads and parsed collaboration links so full-access links can carry and expose a write-capability token
|
|
75
|
+
- Added `ROOM_KEY_BYTES` and `WRITE_TOKEN_BYTES` constants for room key and write-token sizing in the wire protocol
|
|
76
|
+
- Added `DEFAULT_SHARE_URL` (`https://my.omp.sh/s`), the default share viewer/upload base for `/share` links
|
|
77
|
+
|
|
78
|
+
## [15.11.8] - 2026-06-12
|
|
79
|
+
|
|
80
|
+
### Added
|
|
81
|
+
|
|
82
|
+
- Added shared collab live-session wire contracts for the host CLI and browser guest client.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Can Bölük
|
|
4
|
+
Copyright (c) 2026 Stencil Labs, Inc.
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# @buckeyestudio/pi-wire
|
|
2
|
+
|
|
3
|
+
Shared TypeScript wire contracts for omp collab live sessions.
|
|
4
|
+
|
|
5
|
+
The package contains only JSON-safe protocol shapes and constants. It has no runtime dependencies and is consumed by both the host CLI (`@buckeyestudio/pi-coding-agent`) and browser guest (`@buckeyestudio/collab-web`).
|
|
6
|
+
|
|
7
|
+
## Exports
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import type { GuestFrame, HostFrame, SessionEntry } from "@buckeyestudio/pi-wire";
|
|
11
|
+
import { COLLAB_PROTO, DEFAULT_RELAY_URL, ENVELOPE_HEADER_LENGTH } from "@buckeyestudio/pi-wire";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Key groups:
|
|
15
|
+
|
|
16
|
+
- message and transcript entry shapes rendered by collab guests,
|
|
17
|
+
- live agent event and task-subagent bus payload shapes,
|
|
18
|
+
- `GuestFrame`, `HostFrame`, and `WireFrame` unions for AES-GCM sealed payloads,
|
|
19
|
+
- relay control TEXT messages,
|
|
20
|
+
- link/envelope constants shared by host, guest, and local relay code.
|
|
21
|
+
|
|
22
|
+
## Protocol boundary
|
|
23
|
+
|
|
24
|
+
`pi-wire` does not encode, decode, validate, encrypt, or route frames. It defines the shared contract used at those boundaries:
|
|
25
|
+
|
|
26
|
+
1. callers build a `GuestFrame` or `HostFrame`,
|
|
27
|
+
2. transport code serializes it as JSON inside an encrypted payload,
|
|
28
|
+
3. relay code routes opaque envelopes using the plaintext peer-id prefix,
|
|
29
|
+
4. receivers switch on `frame.t` and tolerate unknown future fields.
|
|
30
|
+
|
|
31
|
+
Keep protocol changes backward-aware: bump `COLLAB_PROTO` only when old hosts and guests must reject each other.
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "module",
|
|
3
|
+
"name": "@buckeyestudio/pi-wire",
|
|
4
|
+
"version": "18.4.13",
|
|
5
|
+
"description": "Shared wire protocol types for omp packages",
|
|
6
|
+
"homepage": "https://omp.sh",
|
|
7
|
+
"author": { "name": "Stencil Labs, Inc.", "url": "https://stencil.so" },
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/can1357/oh-my-pi.git",
|
|
12
|
+
"directory": "packages/wire"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/can1357/oh-my-pi/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"wire",
|
|
19
|
+
"protocol",
|
|
20
|
+
"types",
|
|
21
|
+
"collab"
|
|
22
|
+
],
|
|
23
|
+
"main": "./src/index.ts",
|
|
24
|
+
"types": "./src/index.ts",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"check": "oxlint . && oxfmt --check --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts' && bun run check:types",
|
|
27
|
+
"check:types": "tsgo -p tsconfig.json --noEmit",
|
|
28
|
+
"lint": "oxlint .",
|
|
29
|
+
"fix": "oxlint --fix --fix-suggestions . && bun run fmt",
|
|
30
|
+
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/bun": "catalog:"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"bun": ">=1.3.14"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"src",
|
|
40
|
+
"README.md",
|
|
41
|
+
"CHANGELOG.md"
|
|
42
|
+
],
|
|
43
|
+
"exports": {
|
|
44
|
+
".": {
|
|
45
|
+
"types": "./src/index.ts",
|
|
46
|
+
"import": "./src/index.ts"
|
|
47
|
+
},
|
|
48
|
+
"./*": {
|
|
49
|
+
"types": "./src/*.ts",
|
|
50
|
+
"import": "./src/*.ts"
|
|
51
|
+
},
|
|
52
|
+
"./*.js": "./src/*.ts"
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared wire types for the omp collab live-session protocol.
|
|
3
|
+
*
|
|
4
|
+
* Dependency-free JSON shapes produced by `@buckeyestudio/pi-coding-agent`
|
|
5
|
+
* (`src/collab/protocol.ts` and friends). Browser and test clients import this
|
|
6
|
+
* package instead of depending on the coding-agent runtime; conformance is
|
|
7
|
+
* asserted type-only in `packages/coding-agent/test/collab/web-wire.types.ts`.
|
|
8
|
+
*
|
|
9
|
+
* Unknown entry/event variants arrive over the wire as plain JSON. The unions
|
|
10
|
+
* below cover only the variants this client renders; consumers cast at the
|
|
11
|
+
* JSON boundary and every `switch` keeps a tolerant `default:` branch.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
15
|
+
// Content blocks
|
|
16
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
17
|
+
|
|
18
|
+
export interface TextContent {
|
|
19
|
+
type: "text";
|
|
20
|
+
text: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ImageContent {
|
|
24
|
+
type: "image";
|
|
25
|
+
/** Base64-encoded image data. */
|
|
26
|
+
data: string;
|
|
27
|
+
/** e.g. "image/png". */
|
|
28
|
+
mimeType: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ThinkingContent {
|
|
32
|
+
type: "thinking";
|
|
33
|
+
thinking: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface RedactedThinkingContent {
|
|
37
|
+
type: "redactedThinking";
|
|
38
|
+
data: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ToolCallContent {
|
|
42
|
+
type: "toolCall";
|
|
43
|
+
id: string;
|
|
44
|
+
name: string;
|
|
45
|
+
arguments: Record<string, unknown>;
|
|
46
|
+
intent?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type AssistantContent = TextContent | ThinkingContent | RedactedThinkingContent | ToolCallContent;
|
|
50
|
+
|
|
51
|
+
export type StopReason = "stop" | "length" | "toolUse" | "error" | "aborted";
|
|
52
|
+
|
|
53
|
+
export interface WireUsage {
|
|
54
|
+
input: number;
|
|
55
|
+
output: number;
|
|
56
|
+
cacheRead: number;
|
|
57
|
+
cacheWrite: number;
|
|
58
|
+
totalTokens: number;
|
|
59
|
+
cost: { total: number };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
63
|
+
// Messages
|
|
64
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
65
|
+
|
|
66
|
+
export interface UserMessage {
|
|
67
|
+
role: "user";
|
|
68
|
+
content: string | (TextContent | ImageContent)[];
|
|
69
|
+
/** True if the message was injected by the system (e.g. auto-continue). */
|
|
70
|
+
synthetic?: boolean;
|
|
71
|
+
/** Unix timestamp in milliseconds. */
|
|
72
|
+
timestamp: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface DeveloperMessage {
|
|
76
|
+
role: "developer";
|
|
77
|
+
content: string | (TextContent | ImageContent)[];
|
|
78
|
+
timestamp: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface AssistantMessage {
|
|
82
|
+
role: "assistant";
|
|
83
|
+
content: AssistantContent[];
|
|
84
|
+
model: string;
|
|
85
|
+
usage: WireUsage;
|
|
86
|
+
stopReason: StopReason;
|
|
87
|
+
errorMessage?: string;
|
|
88
|
+
timestamp: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface ToolResultMessage {
|
|
92
|
+
role: "toolResult";
|
|
93
|
+
toolCallId: string;
|
|
94
|
+
toolName: string;
|
|
95
|
+
content: (TextContent | ImageContent)[];
|
|
96
|
+
details?: unknown;
|
|
97
|
+
isError: boolean;
|
|
98
|
+
timestamp: number;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export type WireMessage = UserMessage | DeveloperMessage | AssistantMessage | ToolResultMessage;
|
|
102
|
+
|
|
103
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
104
|
+
// Session entries (rendered subset; cast `as SessionEntry` at the JSON
|
|
105
|
+
// boundary and skip unknown `type`s in a tolerant `default:`)
|
|
106
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
107
|
+
|
|
108
|
+
export interface SessionHeader {
|
|
109
|
+
type: "session";
|
|
110
|
+
id: string;
|
|
111
|
+
title?: string;
|
|
112
|
+
timestamp: string;
|
|
113
|
+
cwd: string;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface EntryBase {
|
|
117
|
+
id: string;
|
|
118
|
+
parentId: string | null;
|
|
119
|
+
timestamp: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface MessageEntry extends EntryBase {
|
|
123
|
+
type: "message";
|
|
124
|
+
message: WireMessage;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface CustomMessageEntry extends EntryBase {
|
|
128
|
+
type: "custom_message";
|
|
129
|
+
customType: string;
|
|
130
|
+
content: string | (TextContent | ImageContent)[];
|
|
131
|
+
details?: unknown;
|
|
132
|
+
display: boolean;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface CompactionEntry extends EntryBase {
|
|
136
|
+
type: "compaction";
|
|
137
|
+
summary: string;
|
|
138
|
+
shortSummary?: string;
|
|
139
|
+
firstKeptEntryId: string;
|
|
140
|
+
tokensBefore: number;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface BranchSummaryEntry extends EntryBase {
|
|
144
|
+
type: "branch_summary";
|
|
145
|
+
fromId: string;
|
|
146
|
+
summary: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface ModelChangeEntry extends EntryBase {
|
|
150
|
+
type: "model_change";
|
|
151
|
+
/** Model in "provider/modelId" format. */
|
|
152
|
+
model: string;
|
|
153
|
+
role?: string;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface ThinkingLevelChangeEntry extends EntryBase {
|
|
157
|
+
type: "thinking_level_change";
|
|
158
|
+
thinkingLevel?: string | null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export type SessionEntry =
|
|
162
|
+
| MessageEntry
|
|
163
|
+
| CustomMessageEntry
|
|
164
|
+
| CompactionEntry
|
|
165
|
+
| BranchSummaryEntry
|
|
166
|
+
| ModelChangeEntry
|
|
167
|
+
| ThinkingLevelChangeEntry;
|
|
168
|
+
|
|
169
|
+
/** customType of collab guest prompts injected on the host. */
|
|
170
|
+
export const COLLAB_PROMPT_MESSAGE_TYPE = "collab-prompt";
|
|
171
|
+
|
|
172
|
+
/** `details` shape of `custom_message` entries with `customType === "collab-prompt"`. */
|
|
173
|
+
export interface CollabPromptDetails {
|
|
174
|
+
from?: string;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
178
|
+
// Events (handled subset)
|
|
179
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
180
|
+
|
|
181
|
+
export type AgentEvent =
|
|
182
|
+
| { type: "agent_start" }
|
|
183
|
+
| { type: "agent_end" }
|
|
184
|
+
| { type: "turn_start" }
|
|
185
|
+
| { type: "turn_end" }
|
|
186
|
+
| { type: "message_start"; message: WireMessage }
|
|
187
|
+
/** Carries the FULL accumulating partial message — no delta tracking needed. */
|
|
188
|
+
| { type: "message_update"; message: WireMessage }
|
|
189
|
+
| { type: "message_end"; message: WireMessage }
|
|
190
|
+
| { type: "tool_execution_start"; toolCallId: string; toolName: string; args: unknown; intent?: string }
|
|
191
|
+
| { type: "tool_execution_update"; toolCallId: string; toolName: string; args: unknown; partialResult: unknown }
|
|
192
|
+
| { type: "tool_execution_end"; toolCallId: string; toolName: string; result: unknown; isError?: boolean }
|
|
193
|
+
| { type: "notice"; level: "info" | "warning" | "error"; message: string; source?: string }
|
|
194
|
+
| { type: "auto_compaction_start"; reason: string; action: string }
|
|
195
|
+
| { type: "auto_compaction_end"; aborted: boolean; willRetry: boolean; errorMessage?: string; skipped?: boolean }
|
|
196
|
+
| { type: "auto_retry_start"; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string }
|
|
197
|
+
| { type: "auto_retry_end"; success: boolean; attempt: number; finalError?: string }
|
|
198
|
+
| { type: "thinking_level_changed"; thinkingLevel?: string };
|
|
199
|
+
|
|
200
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
201
|
+
// State & agents
|
|
202
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
203
|
+
|
|
204
|
+
export interface WireModel {
|
|
205
|
+
id: string;
|
|
206
|
+
name: string;
|
|
207
|
+
provider: string;
|
|
208
|
+
contextWindow: number | null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export interface ContextUsage {
|
|
212
|
+
tokens: number | null;
|
|
213
|
+
contextWindow: number | null;
|
|
214
|
+
percent: number | null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface Participant {
|
|
218
|
+
name: string;
|
|
219
|
+
role: "host" | "guest";
|
|
220
|
+
/** True when the guest joined through a read-only (view) link. */
|
|
221
|
+
readOnly?: boolean;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Debounced footer snapshot broadcast by the host. */
|
|
225
|
+
export interface SessionState {
|
|
226
|
+
isStreaming: boolean;
|
|
227
|
+
queuedMessageCount: number;
|
|
228
|
+
sessionName?: string;
|
|
229
|
+
/** Host cwd — display only; the guest never chdirs. */
|
|
230
|
+
cwd: string;
|
|
231
|
+
model?: WireModel;
|
|
232
|
+
thinkingLevel?: string;
|
|
233
|
+
contextUsage?: ContextUsage;
|
|
234
|
+
participants: Participant[];
|
|
235
|
+
isAborting?: boolean;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export interface AgentSnapshot {
|
|
239
|
+
id: string;
|
|
240
|
+
displayName: string;
|
|
241
|
+
kind: "main" | "sub";
|
|
242
|
+
parentId?: string;
|
|
243
|
+
status: "running" | "idle" | "parked" | "aborted";
|
|
244
|
+
/** Whether the host has a transcript file for this agent (gates remote transcript fetch). */
|
|
245
|
+
hasSessionFile: boolean;
|
|
246
|
+
createdAt: number;
|
|
247
|
+
lastActivity: number;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
251
|
+
// Bus payloads (task subagent lifecycle/progress channels)
|
|
252
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
253
|
+
|
|
254
|
+
export interface AgentProgress {
|
|
255
|
+
index: number;
|
|
256
|
+
id: string;
|
|
257
|
+
agent: string;
|
|
258
|
+
status: "pending" | "running" | "completed" | "failed" | "aborted";
|
|
259
|
+
task: string;
|
|
260
|
+
description?: string;
|
|
261
|
+
lastIntent?: string;
|
|
262
|
+
currentTool?: string;
|
|
263
|
+
currentToolArgs?: string;
|
|
264
|
+
/** Argument key selected for the display preview, when known. */
|
|
265
|
+
currentToolArgsKey?: string;
|
|
266
|
+
/** Intent the model attached to the current call; undefined when that call carried none. */
|
|
267
|
+
currentToolIntent?: string;
|
|
268
|
+
currentToolStartMs?: number;
|
|
269
|
+
recentTools: { tool: string; args: string; argsKey?: string; intent?: string; isError?: boolean; endMs: number }[];
|
|
270
|
+
recentOutput: string[];
|
|
271
|
+
toolCount: number;
|
|
272
|
+
requests: number;
|
|
273
|
+
tokens: number;
|
|
274
|
+
contextTokens?: number;
|
|
275
|
+
contextWindow?: number;
|
|
276
|
+
cost: number;
|
|
277
|
+
durationMs: number;
|
|
278
|
+
resolvedModel?: string;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export interface SubagentProgressPayload {
|
|
282
|
+
index: number;
|
|
283
|
+
agent: string;
|
|
284
|
+
task: string;
|
|
285
|
+
parentToolCallId?: string;
|
|
286
|
+
assignment?: string;
|
|
287
|
+
progress: AgentProgress;
|
|
288
|
+
sessionFile?: string;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export interface SubagentLifecyclePayload {
|
|
292
|
+
id: string;
|
|
293
|
+
agent: string;
|
|
294
|
+
description?: string;
|
|
295
|
+
status: "started" | "completed" | "failed" | "aborted";
|
|
296
|
+
sessionFile?: string;
|
|
297
|
+
parentToolCallId?: string;
|
|
298
|
+
index: number;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
302
|
+
// Frames (JSON inside the AES-GCM seal)
|
|
303
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
304
|
+
|
|
305
|
+
export type CollabUiSelectItem = string | { label: string; description?: string };
|
|
306
|
+
|
|
307
|
+
export type CollabUiResponseValue = string | undefined;
|
|
308
|
+
|
|
309
|
+
export type CollabUiRequestDraft =
|
|
310
|
+
| {
|
|
311
|
+
kind: "select";
|
|
312
|
+
title: string;
|
|
313
|
+
options: CollabUiSelectItem[];
|
|
314
|
+
initialIndex?: number;
|
|
315
|
+
selectionMarker?: "radio" | "checkbox";
|
|
316
|
+
checkedIndices?: number[];
|
|
317
|
+
markableCount?: number;
|
|
318
|
+
helpText?: string;
|
|
319
|
+
}
|
|
320
|
+
| {
|
|
321
|
+
kind: "editor";
|
|
322
|
+
title: string;
|
|
323
|
+
prefill?: string;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
export type CollabUiRequest = CollabUiRequestDraft & { reqId: number };
|
|
327
|
+
|
|
328
|
+
export type GuestFrame =
|
|
329
|
+
| {
|
|
330
|
+
t: "hello";
|
|
331
|
+
proto: number;
|
|
332
|
+
name: string;
|
|
333
|
+
/**
|
|
334
|
+
* base64url write token proving full-link possession; absent for
|
|
335
|
+
* read-only (view) links. The host marks peers without a valid token
|
|
336
|
+
* read-only and rejects their mutating frames.
|
|
337
|
+
*/
|
|
338
|
+
writeToken?: string;
|
|
339
|
+
}
|
|
340
|
+
| { t: "prompt"; text: string; images?: ImageContent[] }
|
|
341
|
+
| { t: "ui-response"; reqId: number; value?: CollabUiResponseValue }
|
|
342
|
+
| { t: "abort" }
|
|
343
|
+
| { t: "agent-cmd"; cmd: "chat" | "kill" | "revive"; agentId: string; text?: string }
|
|
344
|
+
| { t: "fetch-transcript"; reqId: number; agentId: string; fromByte: number };
|
|
345
|
+
|
|
346
|
+
/** EventBus channels mirrored to guests (task subagent traffic only). */
|
|
347
|
+
export type BusChannel = "task:subagent:progress" | "task:subagent:lifecycle";
|
|
348
|
+
|
|
349
|
+
export type HostFrame =
|
|
350
|
+
| {
|
|
351
|
+
t: "welcome";
|
|
352
|
+
proto: number;
|
|
353
|
+
header: SessionHeader;
|
|
354
|
+
state: SessionState;
|
|
355
|
+
agents: AgentSnapshot[];
|
|
356
|
+
/**
|
|
357
|
+
* Total number of `SessionEntry` items the host will deliver in the
|
|
358
|
+
* `snapshot-chunk` frames that follow. Guests stay in the loading
|
|
359
|
+
* phase until they have accumulated all of them (or a chunk arrives
|
|
360
|
+
* with `final: true`).
|
|
361
|
+
*/
|
|
362
|
+
entryCount: number;
|
|
363
|
+
/** True when this peer joined through a read-only (view) link. */
|
|
364
|
+
readOnly?: boolean;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Targeted snapshot fragment delivered after `welcome`. Hosts split the
|
|
368
|
+
* transcript into chunks bounded by byte size so a multi-MB session is not
|
|
369
|
+
* forced through one giant frame the relay may stall on. The last chunk
|
|
370
|
+
* carries `final: true`; guests finalize the replica on that frame.
|
|
371
|
+
*/
|
|
372
|
+
| { t: "snapshot-chunk"; entries: SessionEntry[]; final: boolean }
|
|
373
|
+
| { t: "entry"; entry: SessionEntry }
|
|
374
|
+
| { t: "event"; event: AgentEvent }
|
|
375
|
+
| { t: "state"; state: SessionState }
|
|
376
|
+
/** Mirrored EventBus traffic (task subagent lifecycle/progress channels only). */
|
|
377
|
+
| { t: "bus"; channel: BusChannel; data: unknown }
|
|
378
|
+
| { t: "agents"; agents: AgentSnapshot[] }
|
|
379
|
+
| { t: "ui-request"; request: CollabUiRequest }
|
|
380
|
+
| { t: "ui-request-end"; reqId: number }
|
|
381
|
+
/** Targeted reply to fetch-transcript; `text` is decoded JSONL from `fromByte`, `newSize` the next offset base. */
|
|
382
|
+
| { t: "transcript"; reqId: number; text: string; newSize: number; error?: string }
|
|
383
|
+
| { t: "bye"; reason: string }
|
|
384
|
+
| { t: "error"; message: string };
|
|
385
|
+
|
|
386
|
+
export type WireFrame = GuestFrame | HostFrame;
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Wire protocol version carried in `hello`; the host rejects mismatches.
|
|
390
|
+
*
|
|
391
|
+
* - `1` (legacy): `welcome` carried the full `entries` array inline.
|
|
392
|
+
* - `2`: `welcome` carries only metadata (header/state/agents/entryCount);
|
|
393
|
+
* transcript entries follow in `snapshot-chunk` frames, so multi-MB
|
|
394
|
+
* sessions are not gated on a single welcome frame fitting under the
|
|
395
|
+
* guest's first-welcome timeout.
|
|
396
|
+
* - `3`: host asks guests through `ui-request`/`ui-request-end` host frames
|
|
397
|
+
* answered by the `ui-response` guest frame. Guests that predate the
|
|
398
|
+
* grammar would silently drop `ui-request` (asks hang forever on the
|
|
399
|
+
* host), so they must be rejected at hello.
|
|
400
|
+
*/
|
|
401
|
+
export const COLLAB_PROTO = 3;
|
|
402
|
+
|
|
403
|
+
/** Parameter key used for intent tracing (e.g. prompt explanation/reasoning) */
|
|
404
|
+
export const INTENT_FIELD = "i";
|
|
405
|
+
|
|
406
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
407
|
+
// Envelope & link constants
|
|
408
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
409
|
+
|
|
410
|
+
/** Plaintext envelope prefix: `[4B uint32 BE peerId][sealed payload]`. */
|
|
411
|
+
export const ENVELOPE_HEADER_LENGTH = 4;
|
|
412
|
+
|
|
413
|
+
export const ROOM_ID_BYTES = 16;
|
|
414
|
+
|
|
415
|
+
/** AES-256-GCM room key; the seal key for every collab frame. */
|
|
416
|
+
export const ROOM_KEY_BYTES = 32;
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Random write token appended to the room key in full links
|
|
420
|
+
* (`base64url(key ∥ token)`); view links carry the bare key. Possession
|
|
421
|
+
* proves prompt/abort/agent-cmd capability to the host.
|
|
422
|
+
*/
|
|
423
|
+
export const WRITE_TOKEN_BYTES = 16;
|
|
424
|
+
|
|
425
|
+
/** Default public relay; bare `<roomId>.<key>` links resolve against it. */
|
|
426
|
+
export const DEFAULT_RELAY_URL = "wss://my.omp.sh";
|
|
427
|
+
|
|
428
|
+
/** Default share viewer/upload base; `/share` links resolve against `<base>/<id>#<key>`. */
|
|
429
|
+
export const DEFAULT_SHARE_URL = "https://my.omp.sh/s";
|
|
430
|
+
|
|
431
|
+
export interface ParsedCollabLink {
|
|
432
|
+
/** wss://host[:port]/r/<roomId> — no query, no fragment. */
|
|
433
|
+
wsUrl: string;
|
|
434
|
+
roomId: string;
|
|
435
|
+
key: Uint8Array;
|
|
436
|
+
/** Write token from a full link; absent for read-only (view) links. */
|
|
437
|
+
writeToken?: Uint8Array;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
441
|
+
// Relay control messages (TEXT JSON, unencrypted, no session data)
|
|
442
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
443
|
+
|
|
444
|
+
/** Relay → host control message. */
|
|
445
|
+
export type RelayControlToHost = { t: "peer-joined" | "peer-left"; peer: number };
|
|
446
|
+
/** Relay → guest control message. */
|
|
447
|
+
export type RelayControlToGuest = { t: "room-closed" };
|
|
448
|
+
export type RelayControlMessage = RelayControlToHost | RelayControlToGuest;
|
|
449
|
+
|
|
450
|
+
export * from "./stream";
|
|
451
|
+
export * from "./tsp";
|