opencode-plugin-teleprompt 0.2.0 → 0.2.2
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/README.md +86 -65
- package/dist/opencode/events.d.ts +43 -1
- package/dist/opencode/events.js +374 -7
- package/dist/opencode/events.js.map +1 -1
- package/dist/opencode/permissions.d.ts +8 -2
- package/dist/opencode/permissions.js +95 -8
- package/dist/opencode/permissions.js.map +1 -1
- package/dist/runtime/controller.d.ts +38 -3
- package/dist/runtime/controller.js +517 -24
- package/dist/runtime/controller.js.map +1 -1
- package/dist/state/store.js +52 -0
- package/dist/state/store.js.map +1 -1
- package/dist/summary/format.js +1 -1
- package/dist/summary/format.js.map +1 -1
- package/dist/telegram/api.d.ts +32 -3
- package/dist/telegram/api.js +49 -6
- package/dist/telegram/api.js.map +1 -1
- package/dist/telegram/callback.d.ts +47 -0
- package/dist/telegram/callback.js +195 -0
- package/dist/telegram/callback.js.map +1 -0
- package/dist/telegram/parser.d.ts +3 -2
- package/dist/telegram/parser.js +129 -0
- package/dist/telegram/parser.js.map +1 -1
- package/dist/telegram/poller.d.ts +4 -2
- package/dist/telegram/poller.js +23 -6
- package/dist/telegram/poller.js.map +1 -1
- package/dist/types.d.ts +84 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -4,11 +4,14 @@ TUI-scoped OpenCode plugin that binds a Telegram channel to one active OpenCode
|
|
|
4
4
|
|
|
5
5
|
## What It Does
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
- Relays OpenCode permission
|
|
11
|
-
-
|
|
7
|
+
- **Frictionless Chat Interface**: Type direct prompts (like `write a python function`) in your Telegram channel without any prefixes!
|
|
8
|
+
- **Direct Slash Commands**: Run administrative commands like `/status`, `/queue`, `/dc` or `/approve <id>` directly in the Telegram channel.
|
|
9
|
+
- **Backward Compatibility**: Fully supports old `/tp <prompt>` and `/tp:<command>` syntax out of the box.
|
|
10
|
+
- **Relays Permission Prompts**: Directly relays OpenCode permission requests and accepts direct `/approve`, `/approve-always`, and `/deny` replies.
|
|
11
|
+
- **Inline Keyboards For Approvals**: Permission prompts arrive as Telegram messages with `Approve once` / `Approve always` / `Deny` buttons — no typing required.
|
|
12
|
+
- **Relays OpenCode Questions**: When the agent asks the user a question (via the OpenCode `question` tool), teleprompt posts it to Telegram with inline option buttons (single-choice direct, multi-choice with toggle+confirm). Tap to reply, or fall back to `/qreply`/`/qreject` text commands.
|
|
13
|
+
- **Lease-based Owner Semantics**: Keeps single-owner bridge semantics across multiple OpenCode consoles.
|
|
14
|
+
- **Instant Ctrl+C Exit**: Stops event streams and cleans up instantly, ensuring no exit lags when shutting down OpenCode.
|
|
12
15
|
|
|
13
16
|
## V1 Limits
|
|
14
17
|
|
|
@@ -67,33 +70,35 @@ OpenCode installs npm plugins automatically at startup.
|
|
|
67
70
|
5. While teleprompt is active, local prompt input is locked for that session.
|
|
68
71
|
6. Disconnect options:
|
|
69
72
|
- Press `Esc` twice in a row in OpenCode
|
|
70
|
-
- Send `/
|
|
73
|
+
- Send `/dc` in Telegram channel
|
|
71
74
|
- Run `/tp:stop` in OpenCode
|
|
72
75
|
7. Use Telegram channel commands:
|
|
73
|
-
-
|
|
74
|
-
- `/tp:interrupt`
|
|
75
|
-
- `/tp:queue`
|
|
76
|
-
- `/tp:cancel <job_id|last>`
|
|
77
|
-
- `/tp:retry`
|
|
78
|
-
- `/tp:context`
|
|
79
|
-
- `/tp:compact`
|
|
80
|
-
- `/tp:newsession`
|
|
81
|
-
- `/tp:reset-context`
|
|
82
|
-
- `/tp:who`
|
|
83
|
-
- `/tp:health`
|
|
84
|
-
- `/tp:reclaim`
|
|
85
|
-
- `/tp:history`
|
|
86
|
-
- `/tp:last-error`
|
|
87
|
-
- `/tp:model`
|
|
88
|
-
- `/tp:model fast`
|
|
89
|
-
- `/tp:model smart`
|
|
90
|
-
- `/tp:model max`
|
|
91
|
-
- `/
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
76
|
+
- `<prompt>` (any message NOT starting with `/` is treated directly as a prompt!)
|
|
77
|
+
- `/interrupt` (or `/tp:interrupt`)
|
|
78
|
+
- `/queue` (or `/tp:queue`)
|
|
79
|
+
- `/cancel <job_id|last>` (or `/tp:cancel <job_id|last>`)
|
|
80
|
+
- `/retry` (or `/tp:retry`)
|
|
81
|
+
- `/context` (or `/tp:context`)
|
|
82
|
+
- `/compact` (or `/tp:compact`)
|
|
83
|
+
- `/newsession` (or `/tp:newsession`)
|
|
84
|
+
- `/reset-context` (or `/tp:reset-context`)
|
|
85
|
+
- `/who` (or `/tp:who`)
|
|
86
|
+
- `/health` (or `/tp:health`)
|
|
87
|
+
- `/reclaim` (or `/tp:reclaim`)
|
|
88
|
+
- `/history` (or `/tp:history`)
|
|
89
|
+
- `/last-error` (or `/tp:last-error`)
|
|
90
|
+
- `/model` (or `/tp:model`)
|
|
91
|
+
- `/model fast` (or `/tp:model fast`)
|
|
92
|
+
- `/model smart` (or `/tp:model smart`)
|
|
93
|
+
- `/model max` (or `/tp:model max`)
|
|
94
|
+
- `/model <provider>/<model>`
|
|
95
|
+
- `/approve <request_id>`
|
|
96
|
+
- `/approve-always <request_id>`
|
|
97
|
+
- `/deny <request_id>`
|
|
98
|
+
- `/qreply <request_id>` (then `<index>:<label1>|<label2>` lines for each question)
|
|
99
|
+
- `/qreject <request_id>`
|
|
100
|
+
- `/status`
|
|
101
|
+
- `/dc` (or `/tp:dc`)
|
|
97
102
|
|
|
98
103
|
During remote runs, teleprompt posts lifecycle updates (`accepted`, `queued`, `running`, `waiting-permission`, `completed`, `failed`) and result summaries are sent as replies to the originating Telegram message for clear correlation.
|
|
99
104
|
|
|
@@ -119,23 +124,34 @@ During remote runs, teleprompt posts lifecycle updates (`accepted`, `queued`, `r
|
|
|
119
124
|
- `OPENCODE_TELEGRAM_CHANNEL_ID`
|
|
120
125
|
3. Start OpenCode and ensure plugin loads without startup errors.
|
|
121
126
|
4. Open one session, run `/tp:start`, verify bridge binds.
|
|
122
|
-
5. Send `/
|
|
123
|
-
6. Send
|
|
124
|
-
7. Send `/
|
|
127
|
+
5. Send `/status` from Telegram and confirm status response.
|
|
128
|
+
6. Send `test ping` and confirm lifecycle + summary reply.
|
|
129
|
+
7. Send `/dc` and confirm local input is unlocked in OpenCode.
|
|
125
130
|
|
|
126
131
|
## Telegram Live E2E Quick Checklist
|
|
127
132
|
|
|
128
133
|
1. Start OpenCode session and run `/tp:start`.
|
|
129
|
-
2. From Telegram channel, run `/
|
|
130
|
-
3. Run `/
|
|
131
|
-
4. Send
|
|
134
|
+
2. From Telegram channel, run `/status` and verify owner/session info.
|
|
135
|
+
3. Run `/model` and switch once with `/model fast` (or explicit provider/model).
|
|
136
|
+
4. Send `write a 1-line summary of this session` and verify:
|
|
132
137
|
- `accepted` -> `running` -> `completed`
|
|
133
138
|
- summary arrives as reply to the same Telegram message
|
|
134
139
|
5. Trigger a permissioned action prompt and verify:
|
|
135
|
-
- plugin posts permission request with `
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
140
|
+
- plugin posts a permission request message with `Approve once`,
|
|
141
|
+
`Approve always`, and `Deny` inline buttons
|
|
142
|
+
- tapping a button (or typing `/approve <request_id>` / `/deny <request_id>`)
|
|
143
|
+
applies the reply via the OpenCode SDK and removes the buttons
|
|
144
|
+
6. Trigger an OpenCode question (e.g. "what's the goal?") and verify:
|
|
145
|
+
- plugin posts a `waiting-question` notice and the question with
|
|
146
|
+
inline option buttons
|
|
147
|
+
- single-choice: tapping an option button replies via
|
|
148
|
+
`client.question.reply` and removes the buttons
|
|
149
|
+
- multi-choice: toggling options marks them with ✅ and a `Confirm`
|
|
150
|
+
button finalizes the reply with all selected labels
|
|
151
|
+
- text fallback: `/qreply <request_id>` with one
|
|
152
|
+
`<index>:<label1>|<label2>` line per question
|
|
153
|
+
7. Send `/interrupt` during a long run and confirm graceful stop.
|
|
154
|
+
8. Send `/dc` and confirm disconnect/unlock behavior.
|
|
139
155
|
|
|
140
156
|
## Command Reference
|
|
141
157
|
|
|
@@ -149,32 +165,37 @@ During remote runs, teleprompt posts lifecycle updates (`accepted`, `queued`, `r
|
|
|
149
165
|
|
|
150
166
|
### Telegram Commands
|
|
151
167
|
|
|
152
|
-
-
|
|
153
|
-
- `/
|
|
154
|
-
- `/
|
|
155
|
-
- `/
|
|
156
|
-
- `/
|
|
157
|
-
- `/
|
|
158
|
-
- `/
|
|
159
|
-
- `/
|
|
160
|
-
- `/
|
|
161
|
-
- `/
|
|
162
|
-
- `/
|
|
163
|
-
- `/
|
|
164
|
-
- `/
|
|
165
|
-
- `/
|
|
166
|
-
- `/
|
|
167
|
-
- `/
|
|
168
|
-
- `/
|
|
169
|
-
- `/
|
|
170
|
-
- `/
|
|
171
|
-
- `/
|
|
172
|
-
- `/
|
|
173
|
-
- `/
|
|
174
|
-
- `/
|
|
175
|
-
|
|
168
|
+
- `<prompt>`: Any message not starting with `/` is queued directly as a prompt for the session.
|
|
169
|
+
- `/interrupt`: Abort the currently running remote prompt without disconnecting the bridge.
|
|
170
|
+
- `/queue`: Show active prompt and queued prompts.
|
|
171
|
+
- `/cancel <job_id|last>`: Remove a queued prompt by job ID, or remove the newest queued prompt with `last`.
|
|
172
|
+
- `/retry`: Re-queue the most recent prompt from prompt history.
|
|
173
|
+
- `/context`: Show compact session context (recent prompts, summaries, and changed files).
|
|
174
|
+
- `/compact`: Trigger session summarization/compaction for long-running sessions.
|
|
175
|
+
- `/newsession`: Create a new OpenCode session and switch teleprompt binding to it.
|
|
176
|
+
- `/reset-context`: Alias behavior for creating/switching to a fresh session context.
|
|
177
|
+
- `/who`: Show lease ownership details (current instance, lease owner, ownership state).
|
|
178
|
+
- `/health`: Show bridge health (lease age/staleness, poller/event stream status, queue stats).
|
|
179
|
+
- `/reclaim`: Try to reclaim bridge ownership for the current instance.
|
|
180
|
+
- `/history`: Show recent run history with status and short summaries.
|
|
181
|
+
- `/last-error`: Show the latest failed or interrupted run summary.
|
|
182
|
+
- `/model`: List available models by provider and show current model selection.
|
|
183
|
+
- `/model fast`: Select a model using the `fast` preset resolver.
|
|
184
|
+
- `/model smart`: Select a model using the `smart` preset resolver.
|
|
185
|
+
- `/model max`: Select a model using the `max` preset resolver.
|
|
186
|
+
- `/model <provider>/<model>`: Select an explicit provider/model for the bound session.
|
|
187
|
+
- `/approve <request_id>`: Approve a pending permission request once.
|
|
188
|
+
- `/approve-always <request_id>`: Approve a pending permission request and persist approval behavior when supported.
|
|
189
|
+
- `/deny <request_id>`: Reject a pending permission request.
|
|
190
|
+
- `/qreply <request_id>`: Answer an OpenCode question. Follow with one line per
|
|
191
|
+
question in the form `<index>:<label1>|<label2>`. Only known option labels
|
|
192
|
+
are accepted; unknown labels are dropped. As a fallback to the inline
|
|
193
|
+
keyboard buttons, you can also use this for single-choice questions.
|
|
194
|
+
- `/qreject <request_id>`: Reject an OpenCode question without answering it.
|
|
195
|
+
- `/status`: Show bridge status from Telegram.
|
|
196
|
+
- `/dc`: Disconnect teleprompt from Telegram and unbind the current session.
|
|
176
197
|
|
|
177
198
|
## Shutdown Behavior
|
|
178
199
|
|
|
179
|
-
- On normal TUI disposal (`Ctrl+C`, clean exit), poller and heartbeat stop and lease is released.
|
|
200
|
+
- On normal TUI disposal (`Ctrl+C`, clean exit), poller and heartbeat stop and lease is released **instantly** without hangs.
|
|
180
201
|
- On unclean termination, lease expires by TTL and next owner instance can reclaim.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { QuestionInfo } from "../types.js";
|
|
1
2
|
type EventHandlers = {
|
|
2
3
|
onAssistantCompleted: (sessionID: string, assistantMessageID: string, parentUserMessageID: string) => Promise<void>;
|
|
3
4
|
onPermissionAsked: (event: {
|
|
@@ -7,6 +8,24 @@ type EventHandlers = {
|
|
|
7
8
|
patterns: string[];
|
|
8
9
|
metadata: Record<string, unknown>;
|
|
9
10
|
}) => Promise<void>;
|
|
11
|
+
onPermissionReplied?: (requestID: string) => Promise<void>;
|
|
12
|
+
onQuestionAsked?: (event: {
|
|
13
|
+
id: string;
|
|
14
|
+
sessionID: string;
|
|
15
|
+
questions: QuestionInfo[];
|
|
16
|
+
tool?: {
|
|
17
|
+
messageID: string;
|
|
18
|
+
callID: string;
|
|
19
|
+
};
|
|
20
|
+
}) => Promise<void>;
|
|
21
|
+
onQuestionReplied?: (event: {
|
|
22
|
+
id: string;
|
|
23
|
+
sessionID: string;
|
|
24
|
+
}) => Promise<void>;
|
|
25
|
+
onQuestionRejected?: (event: {
|
|
26
|
+
id: string;
|
|
27
|
+
sessionID: string;
|
|
28
|
+
}) => Promise<void>;
|
|
10
29
|
onSessionError: (event: {
|
|
11
30
|
sessionID?: string;
|
|
12
31
|
error?: {
|
|
@@ -14,19 +33,42 @@ type EventHandlers = {
|
|
|
14
33
|
};
|
|
15
34
|
}) => Promise<void>;
|
|
16
35
|
onUserMessage: (sessionID: string, userMessageID: string) => Promise<void>;
|
|
36
|
+
onMessagePartUpdated?: (input: {
|
|
37
|
+
sessionID: string;
|
|
38
|
+
messageID: string;
|
|
39
|
+
part: {
|
|
40
|
+
type: string;
|
|
41
|
+
text?: string;
|
|
42
|
+
[key: string]: unknown;
|
|
43
|
+
};
|
|
44
|
+
delta?: string;
|
|
45
|
+
}) => Promise<void>;
|
|
17
46
|
onStreamError?: (error: unknown) => Promise<void>;
|
|
18
47
|
};
|
|
19
48
|
export declare class SessionEventStream {
|
|
20
49
|
private readonly client;
|
|
21
50
|
private readonly sessionID;
|
|
51
|
+
private readonly directory;
|
|
22
52
|
private readonly handlers;
|
|
53
|
+
private readonly debugLog?;
|
|
23
54
|
private abort;
|
|
24
55
|
private running?;
|
|
25
56
|
private stream?;
|
|
26
|
-
|
|
57
|
+
private pollTimer?;
|
|
58
|
+
constructor(client: any, sessionID: string, directory: string, handlers: EventHandlers, debugLog?: ((message: string) => void) | undefined);
|
|
59
|
+
/** Exposed for testing */
|
|
60
|
+
__test?: {
|
|
61
|
+
pollQuestionListOnce: () => Promise<void>;
|
|
62
|
+
seenForwardedQuestionCalls: Set<string>;
|
|
63
|
+
};
|
|
27
64
|
start(): void;
|
|
28
65
|
stop(): Promise<void>;
|
|
66
|
+
private startQuestionPoller;
|
|
67
|
+
private pollQuestionListOnce;
|
|
29
68
|
private loop;
|
|
30
69
|
private handleEvent;
|
|
70
|
+
private readonly seenForwardedQuestionCalls;
|
|
71
|
+
private handleQuestionToolPart;
|
|
72
|
+
private pollQuestionList;
|
|
31
73
|
}
|
|
32
74
|
export {};
|