@sympoies/dsh-telegram 0.0.0-bootstrap.0 → 0.6.0
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/LICENSE +1 -2
- package/NOTICE +19 -0
- package/README.md +589 -2
- package/cordis.patch.yml +14 -0
- package/lib/access.d.ts +76 -0
- package/lib/access.d.ts.map +1 -0
- package/lib/access.js +137 -0
- package/lib/access.js.map +1 -0
- package/lib/client.js +835 -0
- package/lib/commands.d.ts +51 -0
- package/lib/commands.d.ts.map +1 -0
- package/lib/commands.js +83 -0
- package/lib/commands.js.map +1 -0
- package/lib/config.d.ts +125 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +97 -0
- package/lib/config.js.map +1 -0
- package/lib/diagnostics.d.ts +58 -0
- package/lib/diagnostics.d.ts.map +1 -0
- package/lib/diagnostics.js +59 -0
- package/lib/diagnostics.js.map +1 -0
- package/lib/failures.d.ts +71 -0
- package/lib/failures.d.ts.map +1 -0
- package/lib/failures.js +139 -0
- package/lib/failures.js.map +1 -0
- package/lib/harness/host.d.ts +106 -0
- package/lib/harness/host.d.ts.map +1 -0
- package/lib/harness/host.js +142 -0
- package/lib/harness/host.js.map +1 -0
- package/lib/harness/message.d.ts +45 -0
- package/lib/harness/message.d.ts.map +1 -0
- package/lib/harness/message.js +28 -0
- package/lib/harness/message.js.map +1 -0
- package/lib/harness/model-selection.d.ts +82 -0
- package/lib/harness/model-selection.d.ts.map +1 -0
- package/lib/harness/model-selection.js +103 -0
- package/lib/harness/model-selection.js.map +1 -0
- package/lib/harness/questions-seam.d.ts +34 -0
- package/lib/harness/questions-seam.d.ts.map +1 -0
- package/lib/harness/questions-seam.js +45 -0
- package/lib/harness/questions-seam.js.map +1 -0
- package/lib/harness/types.d.ts +108 -0
- package/lib/harness/types.d.ts.map +1 -0
- package/lib/harness/types.js +23 -0
- package/lib/harness/types.js.map +1 -0
- package/lib/index.d.ts +65 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +977 -0
- package/lib/index.js.map +1 -0
- package/lib/interact/approvals.d.ts +59 -0
- package/lib/interact/approvals.d.ts.map +1 -0
- package/lib/interact/approvals.js +132 -0
- package/lib/interact/approvals.js.map +1 -0
- package/lib/interact/pending.d.ts +56 -0
- package/lib/interact/pending.d.ts.map +1 -0
- package/lib/interact/pending.js +89 -0
- package/lib/interact/pending.js.map +1 -0
- package/lib/interact/questions.d.ts +100 -0
- package/lib/interact/questions.d.ts.map +1 -0
- package/lib/interact/questions.js +255 -0
- package/lib/interact/questions.js.map +1 -0
- package/lib/interact/surface.d.ts +33 -0
- package/lib/interact/surface.d.ts.map +1 -0
- package/lib/interact/surface.js +39 -0
- package/lib/interact/surface.js.map +1 -0
- package/lib/interact/text-capture.d.ts +38 -0
- package/lib/interact/text-capture.d.ts.map +1 -0
- package/lib/interact/text-capture.js +75 -0
- package/lib/interact/text-capture.js.map +1 -0
- package/lib/media/collect.d.ts +157 -0
- package/lib/media/collect.d.ts.map +1 -0
- package/lib/media/collect.js +235 -0
- package/lib/media/collect.js.map +1 -0
- package/lib/media/extractor.d.ts +117 -0
- package/lib/media/extractor.d.ts.map +1 -0
- package/lib/media/extractor.js +207 -0
- package/lib/media/extractor.js.map +1 -0
- package/lib/media/intake.d.ts +57 -0
- package/lib/media/intake.d.ts.map +1 -0
- package/lib/media/intake.js +115 -0
- package/lib/media/intake.js.map +1 -0
- package/lib/media/limits.d.ts +69 -0
- package/lib/media/limits.d.ts.map +1 -0
- package/lib/media/limits.js +97 -0
- package/lib/media/limits.js.map +1 -0
- package/lib/media/ocr.d.ts +66 -0
- package/lib/media/ocr.d.ts.map +1 -0
- package/lib/media/ocr.js +131 -0
- package/lib/media/ocr.js.map +1 -0
- package/lib/media/screenshot.d.ts +57 -0
- package/lib/media/screenshot.d.ts.map +1 -0
- package/lib/media/screenshot.js +101 -0
- package/lib/media/screenshot.js.map +1 -0
- package/lib/media/vision.d.ts +60 -0
- package/lib/media/vision.d.ts.map +1 -0
- package/lib/media/vision.js +74 -0
- package/lib/media/vision.js.map +1 -0
- package/lib/render/clamp.d.ts +34 -0
- package/lib/render/clamp.d.ts.map +1 -0
- package/lib/render/clamp.js +65 -0
- package/lib/render/clamp.js.map +1 -0
- package/lib/render/escape.d.ts +15 -0
- package/lib/render/escape.d.ts.map +1 -0
- package/lib/render/escape.js +17 -0
- package/lib/render/escape.js.map +1 -0
- package/lib/reply/activity.d.ts +31 -0
- package/lib/reply/activity.d.ts.map +1 -0
- package/lib/reply/activity.js +88 -0
- package/lib/reply/activity.js.map +1 -0
- package/lib/reply/rich-stream.d.ts +173 -0
- package/lib/reply/rich-stream.d.ts.map +1 -0
- package/lib/reply/rich-stream.js +286 -0
- package/lib/reply/rich-stream.js.map +1 -0
- package/lib/reply/split-markdown.d.ts +21 -0
- package/lib/reply/split-markdown.d.ts.map +1 -0
- package/lib/reply/split-markdown.js +87 -0
- package/lib/reply/split-markdown.js.map +1 -0
- package/lib/reply/turn-bridge.d.ts +143 -0
- package/lib/reply/turn-bridge.d.ts.map +1 -0
- package/lib/reply/turn-bridge.js +182 -0
- package/lib/reply/turn-bridge.js.map +1 -0
- package/lib/router.d.ts +415 -0
- package/lib/router.d.ts.map +1 -0
- package/lib/router.js +663 -0
- package/lib/router.js.map +1 -0
- package/lib/secrets.d.ts +30 -0
- package/lib/secrets.d.ts.map +1 -0
- package/lib/secrets.js +51 -0
- package/lib/secrets.js.map +1 -0
- package/lib/session/bindings.d.ts +81 -0
- package/lib/session/bindings.d.ts.map +1 -0
- package/lib/session/bindings.js +170 -0
- package/lib/session/bindings.js.map +1 -0
- package/lib/session/history.d.ts +50 -0
- package/lib/session/history.d.ts.map +1 -0
- package/lib/session/history.js +127 -0
- package/lib/session/history.js.map +1 -0
- package/lib/session/models.d.ts +99 -0
- package/lib/session/models.d.ts.map +1 -0
- package/lib/session/models.js +123 -0
- package/lib/session/models.js.map +1 -0
- package/lib/session/permission.d.ts +83 -0
- package/lib/session/permission.d.ts.map +1 -0
- package/lib/session/permission.js +107 -0
- package/lib/session/permission.js.map +1 -0
- package/lib/session/picker.d.ts +49 -0
- package/lib/session/picker.d.ts.map +1 -0
- package/lib/session/picker.js +107 -0
- package/lib/session/picker.js.map +1 -0
- package/lib/session/preferences.d.ts +55 -0
- package/lib/session/preferences.d.ts.map +1 -0
- package/lib/session/preferences.js +102 -0
- package/lib/session/preferences.js.map +1 -0
- package/lib/session/recovery.d.ts +66 -0
- package/lib/session/recovery.d.ts.map +1 -0
- package/lib/session/recovery.js +112 -0
- package/lib/session/recovery.js.map +1 -0
- package/lib/session/runner.d.ts +223 -0
- package/lib/session/runner.d.ts.map +1 -0
- package/lib/session/runner.js +260 -0
- package/lib/session/runner.js.map +1 -0
- package/lib/session/workspaces.d.ts +28 -0
- package/lib/session/workspaces.d.ts.map +1 -0
- package/lib/session/workspaces.js +53 -0
- package/lib/session/workspaces.js.map +1 -0
- package/lib/telegram/addressing.d.ts +39 -0
- package/lib/telegram/addressing.d.ts.map +1 -0
- package/lib/telegram/addressing.js +81 -0
- package/lib/telegram/addressing.js.map +1 -0
- package/lib/telegram/albums.d.ts +46 -0
- package/lib/telegram/albums.d.ts.map +1 -0
- package/lib/telegram/albums.js +104 -0
- package/lib/telegram/albums.js.map +1 -0
- package/lib/telegram/api.d.ts +238 -0
- package/lib/telegram/api.d.ts.map +1 -0
- package/lib/telegram/api.js +518 -0
- package/lib/telegram/api.js.map +1 -0
- package/lib/telegram/poller.d.ts +59 -0
- package/lib/telegram/poller.d.ts.map +1 -0
- package/lib/telegram/poller.js +94 -0
- package/lib/telegram/poller.js.map +1 -0
- package/lib/telegram/types.d.ts +130 -0
- package/lib/telegram/types.d.ts.map +1 -0
- package/lib/telegram/types.js +9 -0
- package/lib/telegram/types.js.map +1 -0
- package/lib/telegram/typing.d.ts +47 -0
- package/lib/telegram/typing.d.ts.map +1 -0
- package/lib/telegram/typing.js +91 -0
- package/lib/telegram/typing.js.map +1 -0
- package/lib/versions.d.ts +63 -0
- package/lib/versions.d.ts.map +1 -0
- package/lib/versions.js +135 -0
- package/lib/versions.js.map +1 -0
- package/package.json +94 -10
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preferences.d.ts","sourceRoot":"","sources":["../../src/session/preferences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAExD,4BAA4B;AAC5B,MAAM,WAAW,qBAAqB;IACpC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAA;CAC5C;AAED,qBAAa,eAAe;IAIxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJzB,OAAO,CAAC,MAAM,CAA4B;IAE1C,OAAO;IAKP;;;;;;;;OAQG;WACU,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC;IAMzF,0DAA0D;IAC1D,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS;IAI/C;;;;;OAKG;IACG,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU3D,uEAAuE;IACjE,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9C,0EAA0E;YAC5D,OAAO;CAQtB"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One durable string a conversation chose for itself.
|
|
3
|
+
*
|
|
4
|
+
* Two things need this and they are the same shape: the directory a chat works
|
|
5
|
+
* in, and the model it talks to. Both are per conversation, both must outlive
|
|
6
|
+
* `/new` — a preference discarded with the session would silently snap back to
|
|
7
|
+
* the deployment default on the next message — and both must outlive a
|
|
8
|
+
* restart.
|
|
9
|
+
*
|
|
10
|
+
* Kept apart from the session binding for exactly that reason: a binding is
|
|
11
|
+
* one conversation with the agent and dies with `/new`, while a preference
|
|
12
|
+
* belongs to the CHAT.
|
|
13
|
+
*/
|
|
14
|
+
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
15
|
+
import { dirname } from 'node:path';
|
|
16
|
+
export class ChatPreferences {
|
|
17
|
+
file;
|
|
18
|
+
accept;
|
|
19
|
+
values = new Map();
|
|
20
|
+
constructor(file, accept) {
|
|
21
|
+
this.file = file;
|
|
22
|
+
this.accept = accept;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Load the store, or start empty when there is nothing readable yet.
|
|
26
|
+
*
|
|
27
|
+
* A corrupt file costs the conversation its preference, which one command
|
|
28
|
+
* restores; refusing to start would cost the whole bot.
|
|
29
|
+
*
|
|
30
|
+
* @param file - absolute path to the JSON document.
|
|
31
|
+
* @param options - how to tell a usable value from one to drop.
|
|
32
|
+
*/
|
|
33
|
+
static async open(file, options) {
|
|
34
|
+
const store = new ChatPreferences(file, options.accept);
|
|
35
|
+
store.values = await read(file, options.accept);
|
|
36
|
+
return store;
|
|
37
|
+
}
|
|
38
|
+
/** What this conversation chose, if it chose anything. */
|
|
39
|
+
forChat(target) {
|
|
40
|
+
return this.values.get(keyOf(target));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Remember a conversation's choice.
|
|
44
|
+
*
|
|
45
|
+
* @param target - the conversation.
|
|
46
|
+
* @param value - the value, already checked by the caller.
|
|
47
|
+
*/
|
|
48
|
+
async set(target, value) {
|
|
49
|
+
if (!this.accept(value))
|
|
50
|
+
return;
|
|
51
|
+
const next = new Map(this.values);
|
|
52
|
+
next.set(keyOf(target), value);
|
|
53
|
+
this.values = next;
|
|
54
|
+
await this.persist();
|
|
55
|
+
}
|
|
56
|
+
/** Forget it, returning the conversation to the deployment default. */
|
|
57
|
+
async clear(target) {
|
|
58
|
+
const next = new Map(this.values);
|
|
59
|
+
if (!next.delete(keyOf(target)))
|
|
60
|
+
return;
|
|
61
|
+
this.values = next;
|
|
62
|
+
await this.persist();
|
|
63
|
+
}
|
|
64
|
+
/** Write the document atomically, so a crash cannot leave a half file. */
|
|
65
|
+
async persist() {
|
|
66
|
+
const document = Object.fromEntries(this.values);
|
|
67
|
+
const temporary = `${this.file}.${process.pid}.tmp`;
|
|
68
|
+
await mkdir(dirname(this.file), { recursive: true });
|
|
69
|
+
await writeFile(temporary, `${JSON.stringify(document, undefined, 2)}\n`, 'utf8');
|
|
70
|
+
await rename(temporary, this.file);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** The durable key for one conversation; a forum topic is its own conversation. */
|
|
74
|
+
function keyOf(target) {
|
|
75
|
+
return target.threadId === undefined ? target.chatId : `${target.chatId}#${target.threadId}`;
|
|
76
|
+
}
|
|
77
|
+
/** Read the document, keeping only entries the caller still accepts. */
|
|
78
|
+
async function read(file, accept) {
|
|
79
|
+
const values = new Map();
|
|
80
|
+
let raw;
|
|
81
|
+
try {
|
|
82
|
+
raw = await readFile(file, 'utf8');
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return values;
|
|
86
|
+
}
|
|
87
|
+
let parsed;
|
|
88
|
+
try {
|
|
89
|
+
parsed = JSON.parse(raw);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return values;
|
|
93
|
+
}
|
|
94
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
95
|
+
return values;
|
|
96
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
97
|
+
if (typeof value === 'string' && value !== '' && accept(value))
|
|
98
|
+
values.set(key, value);
|
|
99
|
+
}
|
|
100
|
+
return values;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=preferences.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preferences.js","sourceRoot":"","sources":["../../src/session/preferences.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Getting a conversation out of a state it cannot leave on its own.
|
|
3
|
+
*
|
|
4
|
+
* Some failures are transient and worth retrying. One is not: a provider
|
|
5
|
+
* inspects the whole request history for images, so once a session's log holds
|
|
6
|
+
* one, every later turn fails on a model that cannot read it — however plain
|
|
7
|
+
* that turn's own text. Retrying is futile, and the user has no way of knowing
|
|
8
|
+
* that from the error, which talks about image input on a message they may
|
|
9
|
+
* have sent an hour ago.
|
|
10
|
+
*
|
|
11
|
+
* The only exit is a new session, and asking the user to remember a command
|
|
12
|
+
* for it is asking them to diagnose the plugin. So the plugin recognises the
|
|
13
|
+
* failure and offers the exit as a button.
|
|
14
|
+
*/
|
|
15
|
+
import type { ChatTarget } from '../interact/surface.js';
|
|
16
|
+
import type { ChatSurface } from '../interact/surface.js';
|
|
17
|
+
import type { PendingRegistry } from '../interact/pending.js';
|
|
18
|
+
import type { Logger } from '../harness/types.js';
|
|
19
|
+
/** How a turn failed, as the session log reports it. */
|
|
20
|
+
export interface TurnFailure {
|
|
21
|
+
readonly message: string;
|
|
22
|
+
readonly code?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Whether only a new conversation can clear this failure.
|
|
26
|
+
*
|
|
27
|
+
* `UNSUPPORTED_CONTENT` is the provider saying the request carries something
|
|
28
|
+
* it will not take. Since the request carries the whole history, that is not
|
|
29
|
+
* about the message just sent, and nothing the user types next will help.
|
|
30
|
+
*/
|
|
31
|
+
export declare function needsFreshConversation(failure: TurnFailure): boolean;
|
|
32
|
+
/** Everything the offer needs from the rest of the plugin. */
|
|
33
|
+
export interface RecoveryOfferOptions {
|
|
34
|
+
readonly surface: ChatSurface;
|
|
35
|
+
readonly pending: PendingRegistry<unknown>;
|
|
36
|
+
/** Resolve a session id to its chat, or undefined when it is not a Telegram one. */
|
|
37
|
+
readonly targetOf: (sessionId: string) => ChatTarget | undefined;
|
|
38
|
+
/** Forget the conversation's session, exactly as `/new` does. */
|
|
39
|
+
readonly reset: (target: ChatTarget) => Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
export declare class RecoveryOffer {
|
|
42
|
+
private readonly options;
|
|
43
|
+
private readonly logger;
|
|
44
|
+
/** Conversations already offered a way out, so a retry loop cannot spam. */
|
|
45
|
+
private readonly offered;
|
|
46
|
+
constructor(options: RecoveryOfferOptions, logger?: Logger);
|
|
47
|
+
/**
|
|
48
|
+
* Tell the user a turn failed, and offer the exit when there is one.
|
|
49
|
+
*
|
|
50
|
+
* @param sessionId - the session whose turn failed.
|
|
51
|
+
* @param failure - the structured failure from the session log.
|
|
52
|
+
*/
|
|
53
|
+
offer(sessionId: string, failure: TurnFailure): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Route one button press.
|
|
56
|
+
*
|
|
57
|
+
* @param data - raw `callback_data` from the update.
|
|
58
|
+
* @returns whether the press belonged to an open offer.
|
|
59
|
+
*/
|
|
60
|
+
handleCallback(data: string | undefined): boolean;
|
|
61
|
+
/** Forget every outstanding offer — the plugin is unloading. */
|
|
62
|
+
dispose(): void;
|
|
63
|
+
/** Post one notice, swallowing delivery failures. */
|
|
64
|
+
private say;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../src/session/recovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAE7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAMjD,wDAAwD;AACxD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAGpE;AAED,8DAA8D;AAC9D,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,CAAA;IAC1C,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,UAAU,GAAG,SAAS,CAAA;IAChE,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACtD;AAED,qBAAa,aAAa;IAMtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,4EAA4E;IAC5E,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;gBAGzB,OAAO,EAAE,oBAAoB,EAC9C,MAAM,CAAC,EAAE,MAAM;IAKjB;;;;;OAKG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA0CnE;;;;;OAKG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO;IAYjD,gEAAgE;IAChE,OAAO,IAAI,IAAI;IAIf,qDAAqD;YACvC,GAAG;CAWlB"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Getting a conversation out of a state it cannot leave on its own.
|
|
3
|
+
*
|
|
4
|
+
* Some failures are transient and worth retrying. One is not: a provider
|
|
5
|
+
* inspects the whole request history for images, so once a session's log holds
|
|
6
|
+
* one, every later turn fails on a model that cannot read it — however plain
|
|
7
|
+
* that turn's own text. Retrying is futile, and the user has no way of knowing
|
|
8
|
+
* that from the error, which talks about image input on a message they may
|
|
9
|
+
* have sent an hour ago.
|
|
10
|
+
*
|
|
11
|
+
* The only exit is a new session, and asking the user to remember a command
|
|
12
|
+
* for it is asking them to diagnose the plugin. So the plugin recognises the
|
|
13
|
+
* failure and offers the exit as a button.
|
|
14
|
+
*/
|
|
15
|
+
import { escapeHtml } from '../render/escape.js';
|
|
16
|
+
import { SILENT_LOGGER } from '../harness/types.js';
|
|
17
|
+
/** Prefix marking callback data as belonging to a recovery offer. */
|
|
18
|
+
const KIND = 'r';
|
|
19
|
+
/**
|
|
20
|
+
* Whether only a new conversation can clear this failure.
|
|
21
|
+
*
|
|
22
|
+
* `UNSUPPORTED_CONTENT` is the provider saying the request carries something
|
|
23
|
+
* it will not take. Since the request carries the whole history, that is not
|
|
24
|
+
* about the message just sent, and nothing the user types next will help.
|
|
25
|
+
*/
|
|
26
|
+
export function needsFreshConversation(failure) {
|
|
27
|
+
if (failure.code === 'UNSUPPORTED_CONTENT')
|
|
28
|
+
return true;
|
|
29
|
+
return /does not (?:accept|support) image input/i.test(failure.message);
|
|
30
|
+
}
|
|
31
|
+
export class RecoveryOffer {
|
|
32
|
+
options;
|
|
33
|
+
logger;
|
|
34
|
+
/** Conversations already offered a way out, so a retry loop cannot spam. */
|
|
35
|
+
offered = new Set();
|
|
36
|
+
constructor(options, logger) {
|
|
37
|
+
this.options = options;
|
|
38
|
+
this.logger = logger ?? SILENT_LOGGER;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Tell the user a turn failed, and offer the exit when there is one.
|
|
42
|
+
*
|
|
43
|
+
* @param sessionId - the session whose turn failed.
|
|
44
|
+
* @param failure - the structured failure from the session log.
|
|
45
|
+
*/
|
|
46
|
+
async offer(sessionId, failure) {
|
|
47
|
+
const target = this.options.targetOf(sessionId);
|
|
48
|
+
if (!target)
|
|
49
|
+
return;
|
|
50
|
+
const stuck = needsFreshConversation(failure);
|
|
51
|
+
if (!stuck) {
|
|
52
|
+
// A failure that may pass on its own needs no button, only saying so.
|
|
53
|
+
await this.say(target, `⚠️ ${escapeHtml(failure.message)}`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (this.offered.has(sessionId))
|
|
57
|
+
return;
|
|
58
|
+
this.offered.add(sessionId);
|
|
59
|
+
const waiter = this.options.pending.open({
|
|
60
|
+
onCancel: () => this.offered.delete(sessionId),
|
|
61
|
+
});
|
|
62
|
+
await this.say(target, [
|
|
63
|
+
`⚠️ ${escapeHtml(failure.message)}`,
|
|
64
|
+
'',
|
|
65
|
+
'This conversation cannot continue on the current model — an earlier ' +
|
|
66
|
+
'message carries content it will not accept. A new conversation clears it.',
|
|
67
|
+
].join('\n'), [[{ text: '🆕 Start a fresh conversation', callbackData: `${KIND}:${waiter.token}:0` }]]);
|
|
68
|
+
const pressed = await waiter.promise;
|
|
69
|
+
this.offered.delete(sessionId);
|
|
70
|
+
if (pressed === undefined)
|
|
71
|
+
return;
|
|
72
|
+
try {
|
|
73
|
+
await this.options.reset(target);
|
|
74
|
+
await this.say(target, '🆕 Started a fresh conversation. Send your message again.');
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
this.logger.error('[dsh-telegram] could not start a fresh conversation', error);
|
|
78
|
+
await this.say(target, '⚠️ Could not start a fresh conversation. Try /new.');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Route one button press.
|
|
83
|
+
*
|
|
84
|
+
* @param data - raw `callback_data` from the update.
|
|
85
|
+
* @returns whether the press belonged to an open offer.
|
|
86
|
+
*/
|
|
87
|
+
handleCallback(data) {
|
|
88
|
+
if (data === undefined)
|
|
89
|
+
return false;
|
|
90
|
+
const parts = data.split(':');
|
|
91
|
+
if (parts.length !== 3 || parts[0] !== KIND)
|
|
92
|
+
return false;
|
|
93
|
+
const token = parts[1];
|
|
94
|
+
if (token === '')
|
|
95
|
+
return false;
|
|
96
|
+
return this.options.pending.settle(token, { token });
|
|
97
|
+
}
|
|
98
|
+
/** Forget every outstanding offer — the plugin is unloading. */
|
|
99
|
+
dispose() {
|
|
100
|
+
this.offered.clear();
|
|
101
|
+
}
|
|
102
|
+
/** Post one notice, swallowing delivery failures. */
|
|
103
|
+
async say(target, html, keyboard) {
|
|
104
|
+
try {
|
|
105
|
+
await this.options.surface.send(target, html, keyboard);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
this.logger.warn('[dsh-telegram] could not deliver a recovery offer', error);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=recovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../src/session/recovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One harness session per Telegram conversation.
|
|
3
|
+
*
|
|
4
|
+
* The lifecycle has three states and the transitions between them are the
|
|
5
|
+
* whole job: a conversation with no session yet, one whose session is loaded
|
|
6
|
+
* in this process, and one whose session exists only in the durable log
|
|
7
|
+
* because the harness has restarted since.
|
|
8
|
+
*
|
|
9
|
+
* The third case is why `resume` exists and why a failed resume is not fatal.
|
|
10
|
+
* A session log can be pruned, moved, or written by an incompatible version;
|
|
11
|
+
* when it can no longer be loaded, the user gets a fresh conversation and a
|
|
12
|
+
* note, which is far better than a bot that answers every message with the
|
|
13
|
+
* same load error.
|
|
14
|
+
*
|
|
15
|
+
* The agent host is injected rather than reached for directly, so this logic
|
|
16
|
+
* is exercised without a running harness.
|
|
17
|
+
*/
|
|
18
|
+
import type { ChatTarget } from '../interact/surface.js';
|
|
19
|
+
import type { PromptPart } from '../media/collect.js';
|
|
20
|
+
import type { ModelRoute } from '../harness/model-selection.js';
|
|
21
|
+
import type { AgentRunner } from '../router.js';
|
|
22
|
+
import type { Logger } from '../harness/types.js';
|
|
23
|
+
import type { BindingStore } from './bindings.js';
|
|
24
|
+
/** One line of a conversation's description. */
|
|
25
|
+
export interface StatusRow {
|
|
26
|
+
readonly label: string;
|
|
27
|
+
readonly value: string;
|
|
28
|
+
}
|
|
29
|
+
/** A live agent this plugin drives. */
|
|
30
|
+
export interface RunningAgent {
|
|
31
|
+
readonly sessionId: string;
|
|
32
|
+
/** Queue a user message and wake the driver. */
|
|
33
|
+
followup(content: readonly PromptPart[]): void;
|
|
34
|
+
/**
|
|
35
|
+
* Route the next step onto another model, or back to the agent's own.
|
|
36
|
+
*
|
|
37
|
+
* @param route - the override, or undefined to drop it.
|
|
38
|
+
* @returns whether the agent accepts an override at all; a borrowed agent,
|
|
39
|
+
* or a harness that offers no selection seam, does not.
|
|
40
|
+
*/
|
|
41
|
+
useModel(route: ModelRoute | undefined): boolean;
|
|
42
|
+
/** Cancel the in-flight turn and any queued work. */
|
|
43
|
+
cancel(reason: string): void;
|
|
44
|
+
/** Stop the agent and release its session. */
|
|
45
|
+
dispose(): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
/** Creating and loading agents — the harness surface, narrowed. */
|
|
48
|
+
export interface AgentHost {
|
|
49
|
+
/** The agent for a session if it is already loaded in this process. */
|
|
50
|
+
live(sessionId: string): RunningAgent | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Start a new session and agent under `sessionId`.
|
|
53
|
+
*
|
|
54
|
+
* @param route - forces the model rather than taking the deployment's
|
|
55
|
+
* default. Used by the throwaway session an image is read in, which has to
|
|
56
|
+
* run somewhere that can see.
|
|
57
|
+
*/
|
|
58
|
+
create(sessionId: string, cwd: string, route?: ModelRoute): Promise<RunningAgent>;
|
|
59
|
+
/** Load a persisted session; undefined when it can no longer be loaded. */
|
|
60
|
+
resume(sessionId: string): Promise<RunningAgent | undefined>;
|
|
61
|
+
}
|
|
62
|
+
/** Something that can replace an image in a prompt with what it says. */
|
|
63
|
+
export interface PromptResolver {
|
|
64
|
+
/** Whether reading is possible at all right now. */
|
|
65
|
+
readonly available: boolean;
|
|
66
|
+
resolve(content: readonly PromptPart[], route: ModelRoute | undefined): Promise<PromptPart[]>;
|
|
67
|
+
}
|
|
68
|
+
/** Construction options. */
|
|
69
|
+
export interface SessionRunnerOptions {
|
|
70
|
+
readonly host: AgentHost;
|
|
71
|
+
readonly bindings: BindingStore;
|
|
72
|
+
/**
|
|
73
|
+
* The directory a conversation's next session opens in.
|
|
74
|
+
*
|
|
75
|
+
* Per conversation rather than one value, because `/cd` is per chat — and
|
|
76
|
+
* read late, so a change reaches the next session rather than the next
|
|
77
|
+
* restart. A session's own cwd never moves: the sandbox derives its writable
|
|
78
|
+
* root from it and the harness calls that root immutable, which is why
|
|
79
|
+
* changing directory starts a fresh conversation.
|
|
80
|
+
*/
|
|
81
|
+
readonly cwdFor: (target: ChatTarget) => string;
|
|
82
|
+
/** Session id factory; injected so tests can assert on stable ids. */
|
|
83
|
+
readonly newSessionId?: () => string;
|
|
84
|
+
/**
|
|
85
|
+
* Puts a session on the conversation's permission preset.
|
|
86
|
+
*
|
|
87
|
+
* Applied to a resumed session as well as a new one: the preset is a
|
|
88
|
+
* property of the surface the conversation arrives over, and a session that
|
|
89
|
+
* outlived a restart is still that conversation.
|
|
90
|
+
*/
|
|
91
|
+
readonly permission?: {
|
|
92
|
+
apply(target: ChatTarget, sessionId: string): void;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Remembers which conversations this chat has had, so `/sessions` can offer
|
|
96
|
+
* one back. Absent makes `/new` the one-way door it used to be.
|
|
97
|
+
*/
|
|
98
|
+
readonly history?: {
|
|
99
|
+
remember(target: ChatTarget, session: {
|
|
100
|
+
sessionId: string;
|
|
101
|
+
startedAt: number;
|
|
102
|
+
cwd: string;
|
|
103
|
+
label?: string;
|
|
104
|
+
}): Promise<void>;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Reads an image with a vision model so the conversation receives text.
|
|
108
|
+
*
|
|
109
|
+
* This is what keeps a conversation on its own model: a provider inspects
|
|
110
|
+
* the whole request history, so an image that reaches the conversation binds
|
|
111
|
+
* it to a model that can see for as long as it lives.
|
|
112
|
+
*/
|
|
113
|
+
readonly extractor?: PromptResolver;
|
|
114
|
+
/**
|
|
115
|
+
* Whether the model this conversation runs on reads images itself.
|
|
116
|
+
*
|
|
117
|
+
* When it does, extraction is not merely unnecessary — it is worse. The
|
|
118
|
+
* conversation would receive a transcription instead of the picture, so a
|
|
119
|
+
* diagram, a chart or a misaligned layout becomes scattered words, and the
|
|
120
|
+
* model never gets to look at the thing it was asked about.
|
|
121
|
+
*
|
|
122
|
+
* The indirection exists because a provider inspects the whole request
|
|
123
|
+
* history: an image left in a conversation binds it to a model that can see.
|
|
124
|
+
* When that model IS the one the conversation chose, there is nothing to be
|
|
125
|
+
* stuck on and nothing to work around.
|
|
126
|
+
*/
|
|
127
|
+
readonly modelSees?: (target: ChatTarget) => Promise<boolean>;
|
|
128
|
+
/**
|
|
129
|
+
* The model this conversation reads images with.
|
|
130
|
+
*
|
|
131
|
+
* Per conversation because `/vision` is, and read late so a change lands on
|
|
132
|
+
* the next such turn. It is also what a conversation moves onto when the
|
|
133
|
+
* picture could not be read and had to go through as it was.
|
|
134
|
+
*/
|
|
135
|
+
readonly visionRoute?: (target: ChatTarget) => ModelRoute | undefined;
|
|
136
|
+
/**
|
|
137
|
+
* The model this conversation chose, if it chose one.
|
|
138
|
+
*
|
|
139
|
+
* Read per prompt rather than at session creation, because the harness reads
|
|
140
|
+
* a mutable selection while assembling each step — which is what lets
|
|
141
|
+
* `/model` take effect on the very next message rather than the next `/new`.
|
|
142
|
+
*/
|
|
143
|
+
readonly chosenRoute?: (target: ChatTarget) => ModelRoute | undefined;
|
|
144
|
+
readonly logger?: Logger;
|
|
145
|
+
}
|
|
146
|
+
export declare class SessionRunner implements AgentRunner {
|
|
147
|
+
private readonly options;
|
|
148
|
+
private readonly logger;
|
|
149
|
+
private readonly newSessionId;
|
|
150
|
+
/** Serialises work per conversation, so two fast messages cannot both create. */
|
|
151
|
+
private readonly chains;
|
|
152
|
+
constructor(options: SessionRunnerOptions);
|
|
153
|
+
/**
|
|
154
|
+
* Deliver a prompt, opening or resuming the conversation's session first.
|
|
155
|
+
*
|
|
156
|
+
* @param target - the conversation the prompt came from.
|
|
157
|
+
* @param text - what the user typed.
|
|
158
|
+
*/
|
|
159
|
+
prompt(target: ChatTarget, content: readonly PromptPart[]): Promise<void>;
|
|
160
|
+
/**
|
|
161
|
+
* Turn images into text, where there is anything able to do it.
|
|
162
|
+
*
|
|
163
|
+
* A failure here is not the turn's failure: the picture simply goes through
|
|
164
|
+
* as it is, and the conversation moves to a model that can see it.
|
|
165
|
+
*/
|
|
166
|
+
private resolve;
|
|
167
|
+
/**
|
|
168
|
+
* Which model this conversation's next step must run on.
|
|
169
|
+
*
|
|
170
|
+
* Not a per-turn choice, though it looks like one. A provider checks the
|
|
171
|
+
* WHOLE request history for images, so once a session's log carries one,
|
|
172
|
+
* every later turn — however plain its own text — fails on a model that
|
|
173
|
+
* cannot see. The override therefore sticks to the conversation from the
|
|
174
|
+
* first image until it is reset with `/new`.
|
|
175
|
+
*/
|
|
176
|
+
private routeFor;
|
|
177
|
+
/**
|
|
178
|
+
* Point a conversation at a session it had before.
|
|
179
|
+
*
|
|
180
|
+
* The session is not loaded here: the next message resumes it through the
|
|
181
|
+
* ordinary path, which is also the path that handles a log that can no
|
|
182
|
+
* longer be read.
|
|
183
|
+
*
|
|
184
|
+
* @param target - the conversation.
|
|
185
|
+
* @param sessionId - a session this chat had earlier.
|
|
186
|
+
*/
|
|
187
|
+
adopt(target: ChatTarget, sessionId: string): Promise<void>;
|
|
188
|
+
/**
|
|
189
|
+
* Forget the conversation's session so the next prompt opens a new one.
|
|
190
|
+
*
|
|
191
|
+
* @param target - the conversation to reset.
|
|
192
|
+
*/
|
|
193
|
+
reset(target: ChatTarget): Promise<void>;
|
|
194
|
+
/**
|
|
195
|
+
* Cancel whatever the conversation's agent is doing.
|
|
196
|
+
*
|
|
197
|
+
* @returns whether there was a loaded agent to cancel.
|
|
198
|
+
*/
|
|
199
|
+
stop(target: ChatTarget): Promise<boolean>;
|
|
200
|
+
/**
|
|
201
|
+
* Describe the conversation as rows, for the caller to render.
|
|
202
|
+
*
|
|
203
|
+
* Rows rather than finished markup, because the caller adds its own — the
|
|
204
|
+
* model, the effort, what the agent may do — and stitching those onto a
|
|
205
|
+
* rendered string would mean parsing it back apart.
|
|
206
|
+
*/
|
|
207
|
+
status(target: ChatTarget): Promise<StatusRow[]>;
|
|
208
|
+
/**
|
|
209
|
+
* The agent for a conversation: loaded, resumed, or freshly created.
|
|
210
|
+
*
|
|
211
|
+
* A binding whose session cannot be resumed is replaced rather than
|
|
212
|
+
* reported: the user's next message should work.
|
|
213
|
+
*/
|
|
214
|
+
private agentFor;
|
|
215
|
+
/**
|
|
216
|
+
* Run one conversation's work after its previous work.
|
|
217
|
+
*
|
|
218
|
+
* Two messages arriving in the same poll batch would otherwise both see no
|
|
219
|
+
* binding and both create a session, orphaning the first.
|
|
220
|
+
*/
|
|
221
|
+
private serialize;
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/session/runner.ts"],"names":[],"mappings":"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"}
|