@sympoies/dsh-telegram 0.0.0-bootstrap.0 → 0.6.1

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.
Files changed (194) hide show
  1. package/LICENSE +1 -2
  2. package/NOTICE +19 -0
  3. package/README.md +588 -2
  4. package/cordis.patch.yml +14 -0
  5. package/lib/access.d.ts +76 -0
  6. package/lib/access.d.ts.map +1 -0
  7. package/lib/access.js +137 -0
  8. package/lib/access.js.map +1 -0
  9. package/lib/client.js +835 -0
  10. package/lib/commands.d.ts +51 -0
  11. package/lib/commands.d.ts.map +1 -0
  12. package/lib/commands.js +83 -0
  13. package/lib/commands.js.map +1 -0
  14. package/lib/config.d.ts +125 -0
  15. package/lib/config.d.ts.map +1 -0
  16. package/lib/config.js +97 -0
  17. package/lib/config.js.map +1 -0
  18. package/lib/diagnostics.d.ts +58 -0
  19. package/lib/diagnostics.d.ts.map +1 -0
  20. package/lib/diagnostics.js +59 -0
  21. package/lib/diagnostics.js.map +1 -0
  22. package/lib/failures.d.ts +71 -0
  23. package/lib/failures.d.ts.map +1 -0
  24. package/lib/failures.js +139 -0
  25. package/lib/failures.js.map +1 -0
  26. package/lib/harness/host.d.ts +106 -0
  27. package/lib/harness/host.d.ts.map +1 -0
  28. package/lib/harness/host.js +142 -0
  29. package/lib/harness/host.js.map +1 -0
  30. package/lib/harness/message.d.ts +45 -0
  31. package/lib/harness/message.d.ts.map +1 -0
  32. package/lib/harness/message.js +28 -0
  33. package/lib/harness/message.js.map +1 -0
  34. package/lib/harness/model-selection.d.ts +82 -0
  35. package/lib/harness/model-selection.d.ts.map +1 -0
  36. package/lib/harness/model-selection.js +103 -0
  37. package/lib/harness/model-selection.js.map +1 -0
  38. package/lib/harness/questions-seam.d.ts +34 -0
  39. package/lib/harness/questions-seam.d.ts.map +1 -0
  40. package/lib/harness/questions-seam.js +45 -0
  41. package/lib/harness/questions-seam.js.map +1 -0
  42. package/lib/harness/types.d.ts +108 -0
  43. package/lib/harness/types.d.ts.map +1 -0
  44. package/lib/harness/types.js +23 -0
  45. package/lib/harness/types.js.map +1 -0
  46. package/lib/index.d.ts +65 -0
  47. package/lib/index.d.ts.map +1 -0
  48. package/lib/index.js +977 -0
  49. package/lib/index.js.map +1 -0
  50. package/lib/interact/approvals.d.ts +59 -0
  51. package/lib/interact/approvals.d.ts.map +1 -0
  52. package/lib/interact/approvals.js +132 -0
  53. package/lib/interact/approvals.js.map +1 -0
  54. package/lib/interact/pending.d.ts +56 -0
  55. package/lib/interact/pending.d.ts.map +1 -0
  56. package/lib/interact/pending.js +89 -0
  57. package/lib/interact/pending.js.map +1 -0
  58. package/lib/interact/questions.d.ts +100 -0
  59. package/lib/interact/questions.d.ts.map +1 -0
  60. package/lib/interact/questions.js +255 -0
  61. package/lib/interact/questions.js.map +1 -0
  62. package/lib/interact/surface.d.ts +33 -0
  63. package/lib/interact/surface.d.ts.map +1 -0
  64. package/lib/interact/surface.js +39 -0
  65. package/lib/interact/surface.js.map +1 -0
  66. package/lib/interact/text-capture.d.ts +38 -0
  67. package/lib/interact/text-capture.d.ts.map +1 -0
  68. package/lib/interact/text-capture.js +75 -0
  69. package/lib/interact/text-capture.js.map +1 -0
  70. package/lib/media/collect.d.ts +157 -0
  71. package/lib/media/collect.d.ts.map +1 -0
  72. package/lib/media/collect.js +235 -0
  73. package/lib/media/collect.js.map +1 -0
  74. package/lib/media/extractor.d.ts +117 -0
  75. package/lib/media/extractor.d.ts.map +1 -0
  76. package/lib/media/extractor.js +207 -0
  77. package/lib/media/extractor.js.map +1 -0
  78. package/lib/media/intake.d.ts +57 -0
  79. package/lib/media/intake.d.ts.map +1 -0
  80. package/lib/media/intake.js +115 -0
  81. package/lib/media/intake.js.map +1 -0
  82. package/lib/media/limits.d.ts +69 -0
  83. package/lib/media/limits.d.ts.map +1 -0
  84. package/lib/media/limits.js +97 -0
  85. package/lib/media/limits.js.map +1 -0
  86. package/lib/media/ocr.d.ts +66 -0
  87. package/lib/media/ocr.d.ts.map +1 -0
  88. package/lib/media/ocr.js +131 -0
  89. package/lib/media/ocr.js.map +1 -0
  90. package/lib/media/screenshot.d.ts +57 -0
  91. package/lib/media/screenshot.d.ts.map +1 -0
  92. package/lib/media/screenshot.js +101 -0
  93. package/lib/media/screenshot.js.map +1 -0
  94. package/lib/media/vision.d.ts +60 -0
  95. package/lib/media/vision.d.ts.map +1 -0
  96. package/lib/media/vision.js +74 -0
  97. package/lib/media/vision.js.map +1 -0
  98. package/lib/render/clamp.d.ts +34 -0
  99. package/lib/render/clamp.d.ts.map +1 -0
  100. package/lib/render/clamp.js +65 -0
  101. package/lib/render/clamp.js.map +1 -0
  102. package/lib/render/escape.d.ts +15 -0
  103. package/lib/render/escape.d.ts.map +1 -0
  104. package/lib/render/escape.js +17 -0
  105. package/lib/render/escape.js.map +1 -0
  106. package/lib/reply/activity.d.ts +31 -0
  107. package/lib/reply/activity.d.ts.map +1 -0
  108. package/lib/reply/activity.js +88 -0
  109. package/lib/reply/activity.js.map +1 -0
  110. package/lib/reply/rich-stream.d.ts +173 -0
  111. package/lib/reply/rich-stream.d.ts.map +1 -0
  112. package/lib/reply/rich-stream.js +286 -0
  113. package/lib/reply/rich-stream.js.map +1 -0
  114. package/lib/reply/split-markdown.d.ts +21 -0
  115. package/lib/reply/split-markdown.d.ts.map +1 -0
  116. package/lib/reply/split-markdown.js +87 -0
  117. package/lib/reply/split-markdown.js.map +1 -0
  118. package/lib/reply/turn-bridge.d.ts +143 -0
  119. package/lib/reply/turn-bridge.d.ts.map +1 -0
  120. package/lib/reply/turn-bridge.js +182 -0
  121. package/lib/reply/turn-bridge.js.map +1 -0
  122. package/lib/router.d.ts +415 -0
  123. package/lib/router.d.ts.map +1 -0
  124. package/lib/router.js +663 -0
  125. package/lib/router.js.map +1 -0
  126. package/lib/secrets.d.ts +30 -0
  127. package/lib/secrets.d.ts.map +1 -0
  128. package/lib/secrets.js +51 -0
  129. package/lib/secrets.js.map +1 -0
  130. package/lib/session/bindings.d.ts +81 -0
  131. package/lib/session/bindings.d.ts.map +1 -0
  132. package/lib/session/bindings.js +170 -0
  133. package/lib/session/bindings.js.map +1 -0
  134. package/lib/session/history.d.ts +50 -0
  135. package/lib/session/history.d.ts.map +1 -0
  136. package/lib/session/history.js +127 -0
  137. package/lib/session/history.js.map +1 -0
  138. package/lib/session/models.d.ts +99 -0
  139. package/lib/session/models.d.ts.map +1 -0
  140. package/lib/session/models.js +123 -0
  141. package/lib/session/models.js.map +1 -0
  142. package/lib/session/permission.d.ts +83 -0
  143. package/lib/session/permission.d.ts.map +1 -0
  144. package/lib/session/permission.js +107 -0
  145. package/lib/session/permission.js.map +1 -0
  146. package/lib/session/picker.d.ts +49 -0
  147. package/lib/session/picker.d.ts.map +1 -0
  148. package/lib/session/picker.js +107 -0
  149. package/lib/session/picker.js.map +1 -0
  150. package/lib/session/preferences.d.ts +55 -0
  151. package/lib/session/preferences.d.ts.map +1 -0
  152. package/lib/session/preferences.js +102 -0
  153. package/lib/session/preferences.js.map +1 -0
  154. package/lib/session/recovery.d.ts +66 -0
  155. package/lib/session/recovery.d.ts.map +1 -0
  156. package/lib/session/recovery.js +112 -0
  157. package/lib/session/recovery.js.map +1 -0
  158. package/lib/session/runner.d.ts +223 -0
  159. package/lib/session/runner.d.ts.map +1 -0
  160. package/lib/session/runner.js +260 -0
  161. package/lib/session/runner.js.map +1 -0
  162. package/lib/session/workspaces.d.ts +28 -0
  163. package/lib/session/workspaces.d.ts.map +1 -0
  164. package/lib/session/workspaces.js +53 -0
  165. package/lib/session/workspaces.js.map +1 -0
  166. package/lib/telegram/addressing.d.ts +39 -0
  167. package/lib/telegram/addressing.d.ts.map +1 -0
  168. package/lib/telegram/addressing.js +81 -0
  169. package/lib/telegram/addressing.js.map +1 -0
  170. package/lib/telegram/albums.d.ts +46 -0
  171. package/lib/telegram/albums.d.ts.map +1 -0
  172. package/lib/telegram/albums.js +104 -0
  173. package/lib/telegram/albums.js.map +1 -0
  174. package/lib/telegram/api.d.ts +238 -0
  175. package/lib/telegram/api.d.ts.map +1 -0
  176. package/lib/telegram/api.js +518 -0
  177. package/lib/telegram/api.js.map +1 -0
  178. package/lib/telegram/poller.d.ts +59 -0
  179. package/lib/telegram/poller.d.ts.map +1 -0
  180. package/lib/telegram/poller.js +94 -0
  181. package/lib/telegram/poller.js.map +1 -0
  182. package/lib/telegram/types.d.ts +130 -0
  183. package/lib/telegram/types.d.ts.map +1 -0
  184. package/lib/telegram/types.js +9 -0
  185. package/lib/telegram/types.js.map +1 -0
  186. package/lib/telegram/typing.d.ts +47 -0
  187. package/lib/telegram/typing.d.ts.map +1 -0
  188. package/lib/telegram/typing.js +91 -0
  189. package/lib/telegram/typing.js.map +1 -0
  190. package/lib/versions.d.ts +63 -0
  191. package/lib/versions.d.ts.map +1 -0
  192. package/lib/versions.js +135 -0
  193. package/lib/versions.js.map +1 -0
  194. package/package.json +94 -10
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Keeping the last things that went wrong where somebody can see them.
3
+ *
4
+ * `ctx.logger` reaches whatever sink the deployment composed, and several
5
+ * profiles compose none. A plugin that only logs its failures is then silent
6
+ * about them: the status file says whether the connection is up, and nothing
7
+ * says that a screenshot failed, an attachment was refused, or a turn threw.
8
+ *
9
+ * That is not a hypothetical. Every fault found in this plugin so far was
10
+ * found by someone noticing odd behaviour in a chat and asking about it, not
11
+ * by reading a log — because there was no log to read.
12
+ *
13
+ * So warnings and errors are also kept here: a small ring in memory for
14
+ * `/diag`, and the same ring on disk for when the bot itself is too broken to
15
+ * answer a command.
16
+ */
17
+ import { mkdir, rename, writeFile } from 'node:fs/promises';
18
+ import { dirname } from 'node:path';
19
+ /** How many to keep. Enough to see a pattern, short enough to read on a phone. */
20
+ const KEEP = 20;
21
+ /** How long to batch writes for; a burst of failures is one write, not twenty. */
22
+ const FLUSH_MS = 2000;
23
+ export class FailureLog {
24
+ options;
25
+ entries = [];
26
+ timer;
27
+ writing = Promise.resolve();
28
+ constructor(options = {}) {
29
+ this.options = options;
30
+ }
31
+ /**
32
+ * Record one failure.
33
+ *
34
+ * @param level - how bad it was.
35
+ * @param message - what happened, already joined into one line.
36
+ */
37
+ record(level, message) {
38
+ const clean = (this.options.redact ?? ((text) => text))(message).trim();
39
+ if (clean === '')
40
+ return;
41
+ const at = (this.options.now?.() ?? new Date()).toISOString();
42
+ this.entries = [{ at, level, message: clean }, ...this.entries].slice(0, this.options.keep ?? KEEP);
43
+ this.schedule();
44
+ }
45
+ /** The failures kept, newest first. */
46
+ recent() {
47
+ return this.entries;
48
+ }
49
+ /** Write now, whatever the batching timer was waiting for. */
50
+ async flush() {
51
+ if (this.timer) {
52
+ clearTimeout(this.timer);
53
+ this.timer = undefined;
54
+ }
55
+ await this.persist();
56
+ }
57
+ /** Stop batching — the plugin is unloading. */
58
+ dispose() {
59
+ if (this.timer)
60
+ clearTimeout(this.timer);
61
+ this.timer = undefined;
62
+ }
63
+ /** Arm the batching timer, unless one is already armed. */
64
+ schedule() {
65
+ if (this.options.file === undefined || this.timer)
66
+ return;
67
+ this.timer = setTimeout(() => {
68
+ this.timer = undefined;
69
+ void this.persist();
70
+ }, this.options.flushMs ?? FLUSH_MS);
71
+ // Never a reason to hold the process open: this is decoration on the way out.
72
+ this.timer.unref?.();
73
+ }
74
+ /** Mirror the ring to disk, one write at a time. */
75
+ async persist() {
76
+ const file = this.options.file;
77
+ if (file === undefined)
78
+ return;
79
+ const snapshot = [...this.entries];
80
+ this.writing = this.writing.then(async () => {
81
+ const temporary = `${file}.${process.pid}.tmp`;
82
+ try {
83
+ await mkdir(dirname(file), { recursive: true });
84
+ await writeFile(temporary, `${JSON.stringify(snapshot, undefined, 2)}\n`, 'utf8');
85
+ await rename(temporary, file);
86
+ }
87
+ catch {
88
+ // The whole point of this file is to be readable when things are
89
+ // broken; failing to write it must not become another failure.
90
+ }
91
+ });
92
+ await this.writing;
93
+ }
94
+ }
95
+ /**
96
+ * A logger that also records what it warns and errors about.
97
+ *
98
+ * Wrapping rather than replacing: whatever sink the deployment composed still
99
+ * gets everything, and this only adds a copy of the parts worth looking back
100
+ * at. Debug and info are deliberately not kept — a ring of twenty filled with
101
+ * routine chatter would push out the one line that mattered.
102
+ *
103
+ * @param base - the harness logger.
104
+ * @param log - the ring to tee into.
105
+ */
106
+ export function recordingLogger(base, log) {
107
+ const join = (message, rest) => [message, ...rest.map(describe)].filter((part) => part !== '').join(' ');
108
+ return {
109
+ debug: (message, ...rest) => base.debug(message, ...rest),
110
+ info: (message, ...rest) => base.info(message, ...rest),
111
+ warn: (message, ...rest) => {
112
+ log.record('warn', join(message, rest));
113
+ base.warn(message, ...rest);
114
+ },
115
+ error: (message, ...rest) => {
116
+ log.record('error', join(message, rest));
117
+ base.error(message, ...rest);
118
+ },
119
+ };
120
+ }
121
+ /** Say what an extra logger argument was, without dumping an object graph. */
122
+ function describe(value) {
123
+ if (value === undefined || value === null)
124
+ return '';
125
+ if (value instanceof Error)
126
+ return value.message;
127
+ if (typeof value === 'string')
128
+ return value;
129
+ if (typeof value === 'object') {
130
+ try {
131
+ return JSON.stringify(value).slice(0, 200);
132
+ }
133
+ catch {
134
+ return '[unserializable]';
135
+ }
136
+ }
137
+ return String(value);
138
+ }
139
+ //# sourceMappingURL=failures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failures.js","sourceRoot":"","sources":["../src/failures.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Driving harness agents.
3
+ *
4
+ * This is the one module that reaches into `ctx.agents`, so the rest of the
5
+ * plugin can be tested without a harness. It translates between the harness's
6
+ * ownership model and the flat {@link RunningAgent} the session runner wants.
7
+ *
8
+ * The distinction that matters is who may tear an agent down. `ctx.agents.get`
9
+ * returns a bare agent that anyone can observe but nobody can dispose;
10
+ * `create`/`resume` return a handle whose disposer is a capability. So handles
11
+ * are kept for the agents this plugin created, and a bare agent found in the
12
+ * registry is borrowed and left alone — disposing something another owner
13
+ * created would pull the session out from under them.
14
+ */
15
+ import type { Logger } from './types.js';
16
+ import type { AgentHost } from '../session/runner.js';
17
+ import type { MessageFactory } from './message.js';
18
+ import type { AgentContextLike, MutableSelection } from './model-selection.js';
19
+ /** A bare agent from the registry. */
20
+ interface HarnessAgentLike {
21
+ readonly id: string;
22
+ followup(message: unknown): void;
23
+ cancel(cause: string, options?: {
24
+ keepInbox?: boolean;
25
+ }): void;
26
+ }
27
+ /** An owned agent plus its disposer. */
28
+ interface HarnessAgentHandle {
29
+ readonly agent: HarnessAgentLike;
30
+ dispose(): Promise<void>;
31
+ }
32
+ /** The model route an agent opens its requests on. */
33
+ export interface ModelRoute {
34
+ readonly provider: string;
35
+ readonly model: string;
36
+ }
37
+ /**
38
+ * The preset roster, narrowed to what one agent's composition needs.
39
+ *
40
+ * A preset is where the tools live. The registries themselves are host-plane,
41
+ * but almost every model-facing row — bash, the editor, grep, skills,
42
+ * subagents, todo, plan mode — is registered into the PRESET's scope layer, so
43
+ * an agent that joins no preset reaches the model with only whatever the host
44
+ * composition registered globally. In this deployment that is the web tools
45
+ * and nothing else.
46
+ */
47
+ export interface AgentPresetsLike {
48
+ resolve(id?: string): Promise<{
49
+ id: string;
50
+ }>;
51
+ mount(agentCtx: unknown, id?: string): Promise<unknown>;
52
+ }
53
+ /** The `ctx.agents` surface this plugin uses. */
54
+ export interface AgentRegistryLike {
55
+ get(sessionId: string): HarnessAgentLike | undefined;
56
+ create(options: {
57
+ sessionId: string;
58
+ meta?: {
59
+ cwd?: string;
60
+ agentPreset?: string;
61
+ };
62
+ agentOptions?: ModelRoute;
63
+ setup?: (agentCtx: unknown) => void | Promise<void>;
64
+ }): Promise<HarnessAgentHandle>;
65
+ resume(options: {
66
+ resumeSessionId: string;
67
+ agentOptions?: ModelRoute;
68
+ setup?: (agentCtx: unknown) => void | Promise<void>;
69
+ }): Promise<HarnessAgentHandle>;
70
+ }
71
+ /** Construction options. */
72
+ export interface HarnessAgentHostOptions {
73
+ readonly agents: AgentRegistryLike;
74
+ readonly message: MessageFactory;
75
+ /**
76
+ * The deployment's model route, read at creation time rather than captured,
77
+ * so a default changed in Settings reaches the next conversation.
78
+ *
79
+ * An agent created without one has no route to name, and prompt assembly
80
+ * fails the turn on an empty `{{model}}` variable — a failure that surfaces
81
+ * as a broken reply rather than as anything about models.
82
+ */
83
+ readonly selectModel?: () => ModelRoute | undefined;
84
+ /**
85
+ * Installs the mutable model selection into a new agent's scope. Absent
86
+ * leaves every turn on the session's own route.
87
+ */
88
+ readonly installSelection?: (agentCtx: AgentContextLike, selection: MutableSelection) => void;
89
+ /**
90
+ * The preset roster, where the deployment composes one. Absent is a real
91
+ * deployment shape — the harness's own agent factory handles it too — and
92
+ * means the agent gets whatever the host composition registered globally.
93
+ */
94
+ readonly presets?: AgentPresetsLike;
95
+ /** Preset to compose Telegram agents from; absent takes the roster default. */
96
+ readonly presetId?: () => string | undefined;
97
+ readonly logger?: Logger;
98
+ }
99
+ /**
100
+ * Build an {@link AgentHost} over the live harness registry.
101
+ *
102
+ * @param options - the registry, the message factory, and a logger.
103
+ */
104
+ export declare function createAgentHost(options: HarnessAgentHostOptions): AgentHost;
105
+ export {};
106
+ //# sourceMappingURL=host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/harness/host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Driving harness agents.
3
+ *
4
+ * This is the one module that reaches into `ctx.agents`, so the rest of the
5
+ * plugin can be tested without a harness. It translates between the harness's
6
+ * ownership model and the flat {@link RunningAgent} the session runner wants.
7
+ *
8
+ * The distinction that matters is who may tear an agent down. `ctx.agents.get`
9
+ * returns a bare agent that anyone can observe but nobody can dispose;
10
+ * `create`/`resume` return a handle whose disposer is a capability. So handles
11
+ * are kept for the agents this plugin created, and a bare agent found in the
12
+ * registry is borrowed and left alone — disposing something another owner
13
+ * created would pull the session out from under them.
14
+ */
15
+ import { sameRoute } from './model-selection.js';
16
+ /**
17
+ * Build an {@link AgentHost} over the live harness registry.
18
+ *
19
+ * @param options - the registry, the message factory, and a logger.
20
+ */
21
+ export function createAgentHost(options) {
22
+ /** Agents this plugin owns, so it can dispose and re-route exactly those. */
23
+ const owned = new Map();
24
+ const wrap = (agent, sessionId) => ({
25
+ sessionId,
26
+ followup(content) {
27
+ agent.followup(options.message(content));
28
+ },
29
+ useModel(route) {
30
+ const entry = owned.get(sessionId);
31
+ // A borrowed agent carries no selection of ours; its route is its own.
32
+ if (!entry)
33
+ return false;
34
+ if (sameRoute(entry.selection.current, route))
35
+ return true;
36
+ entry.selection.current = route;
37
+ return true;
38
+ },
39
+ cancel(reason) {
40
+ agent.cancel(reason);
41
+ },
42
+ async dispose() {
43
+ const entry = owned.get(sessionId);
44
+ owned.delete(sessionId);
45
+ // Only an owner may dispose. A borrowed agent is simply released.
46
+ if (entry)
47
+ await entry.handle.dispose();
48
+ },
49
+ });
50
+ const adopt = (handle, sessionId, selection) => {
51
+ owned.set(sessionId, { handle, selection });
52
+ return wrap(handle.agent, sessionId);
53
+ };
54
+ /**
55
+ * Everything one new agent needs composed into it.
56
+ *
57
+ * The preset is resolved here, before the agent exists, so a bad id fails
58
+ * the creation rather than half-composing a session. The mount itself must
59
+ * happen inside `setup`, which is the one place the agent is still
60
+ * unpublished and a rejected composition can roll the whole thing back.
61
+ */
62
+ const prepareAgent = async () => {
63
+ const selection = { current: undefined };
64
+ const install = options.installSelection;
65
+ let preset;
66
+ if (options.presets) {
67
+ try {
68
+ preset = (await options.presets.resolve(options.presetId?.())).id;
69
+ }
70
+ catch (error) {
71
+ // A named preset that has gone is not worth failing a message over;
72
+ // the roster's own default still composes a usable agent.
73
+ options.logger?.warn('[dsh-telegram] could not resolve the agent preset', error);
74
+ preset = (await options.presets.resolve().catch(() => undefined))?.id;
75
+ }
76
+ }
77
+ // The braces matter. The harness calls `.commit()` on whatever setup
78
+ // returns, so handing back the installer's disposer — as an
79
+ // expression-bodied arrow would — crashes agent creation on a function
80
+ // that has no such method. An async body resolving to undefined is fine.
81
+ //
82
+ // Dropping the disposer is right anyway: the listeners live on the
83
+ // agent's own scope and unwind when the agent does.
84
+ const setup = async (agentCtx) => {
85
+ if (install)
86
+ install(agentCtx, selection);
87
+ if (options.presets)
88
+ await options.presets.mount(agentCtx, preset);
89
+ };
90
+ return {
91
+ selection,
92
+ ...(install || options.presets ? { setup } : {}),
93
+ ...(preset === undefined ? {} : { preset }),
94
+ };
95
+ };
96
+ return {
97
+ live(sessionId) {
98
+ const owner = owned.get(sessionId);
99
+ if (owner)
100
+ return wrap(owner.handle.agent, sessionId);
101
+ const bare = options.agents.get(sessionId);
102
+ return bare ? wrap(bare, sessionId) : undefined;
103
+ },
104
+ async create(sessionId, cwd, forced) {
105
+ // A forced route comes from a caller that knows which model it needs —
106
+ // reading an image — and outranks the deployment's default.
107
+ const route = forced ?? options.selectModel?.();
108
+ const prepared = await prepareAgent();
109
+ const handle = await options.agents.create({
110
+ sessionId,
111
+ // Recorded so a later reader — a cold transcript, the web UI's session
112
+ // list — resolves the same composition this agent runs on.
113
+ meta: { cwd, ...(prepared.preset === undefined ? {} : { agentPreset: prepared.preset }) },
114
+ ...(route ? { agentOptions: route } : {}),
115
+ ...(prepared.setup ? { setup: prepared.setup } : {}),
116
+ });
117
+ return adopt(handle, sessionId, prepared.selection);
118
+ },
119
+ async resume(sessionId) {
120
+ try {
121
+ // Supplied on resume too, matching the harness's own entry points: a
122
+ // log that already names a selection keeps it, and one that does not
123
+ // — such as a session created before this route existed — is repaired.
124
+ const route = options.selectModel?.();
125
+ const prepared = await prepareAgent();
126
+ const handle = await options.agents.resume({
127
+ resumeSessionId: sessionId,
128
+ ...(route ? { agentOptions: route } : {}),
129
+ ...(prepared.setup ? { setup: prepared.setup } : {}),
130
+ });
131
+ return adopt(handle, sessionId, prepared.selection);
132
+ }
133
+ catch (error) {
134
+ // A pruned, moved, or incompatible log is an ordinary outcome here; the
135
+ // runner starts a fresh conversation rather than failing the message.
136
+ options.logger?.debug(`[dsh-telegram] resume of '${sessionId}' failed`, error);
137
+ return undefined;
138
+ }
139
+ },
140
+ };
141
+ }
142
+ //# sourceMappingURL=host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/harness/host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Building the user message the agent's inbox accepts.
3
+ *
4
+ * `agent.followup()` takes an identified, frozen `UserMessage`. The harness
5
+ * has its own factory for that — `createUserMessage` in `@deepseek-ai/dsh-llm`
6
+ * — and this once tried to import it, preferring the real thing.
7
+ *
8
+ * It never once succeeded. Under pnpm's isolated layout a plugin resolves only
9
+ * its own declared dependencies, and the harness packages belong to the host,
10
+ * so the import failed with `ERR_MODULE_NOT_FOUND` on every call and this
11
+ * shape was always what got used. Claiming otherwise in a comment made the
12
+ * code harder to reason about, not easier.
13
+ *
14
+ * So the documented shape is the only path, and it is exact: an id, the user
15
+ * role, the content blocks, and a `user` source, deep-frozen before it is
16
+ * handed over.
17
+ */
18
+ /** One block of model-visible content. */
19
+ export type ContentBlock = {
20
+ readonly type: 'text';
21
+ readonly text: string;
22
+ } | {
23
+ readonly type: 'image';
24
+ readonly attachment: unknown;
25
+ };
26
+ /** The message shape `agent.followup()` accepts. */
27
+ export interface UserMessageLike {
28
+ readonly id: string;
29
+ readonly role: 'user';
30
+ readonly content: readonly ContentBlock[];
31
+ readonly source: {
32
+ readonly kind: 'user';
33
+ };
34
+ }
35
+ /**
36
+ * Builds one user message from content blocks.
37
+ *
38
+ * Blocks rather than a string because a prompt may carry an image: the model
39
+ * sees `{ type: 'image', attachment }` beside the text, which is how a
40
+ * screenshot becomes something it can actually look at.
41
+ */
42
+ export type MessageFactory = (content: readonly ContentBlock[]) => UserMessageLike;
43
+ /** Build one user message in the shape the agent's inbox accepts. */
44
+ export declare function buildUserMessage(content: readonly ContentBlock[]): UserMessageLike;
45
+ //# sourceMappingURL=message.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../src/harness/message.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,0CAA0C;AAC1C,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAAA;AAE5D,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;IACzC,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAC3C;AAED;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,KAAK,eAAe,CAAA;AAElF,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,eAAe,CAOlF"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Building the user message the agent's inbox accepts.
3
+ *
4
+ * `agent.followup()` takes an identified, frozen `UserMessage`. The harness
5
+ * has its own factory for that — `createUserMessage` in `@deepseek-ai/dsh-llm`
6
+ * — and this once tried to import it, preferring the real thing.
7
+ *
8
+ * It never once succeeded. Under pnpm's isolated layout a plugin resolves only
9
+ * its own declared dependencies, and the harness packages belong to the host,
10
+ * so the import failed with `ERR_MODULE_NOT_FOUND` on every call and this
11
+ * shape was always what got used. Claiming otherwise in a comment made the
12
+ * code harder to reason about, not easier.
13
+ *
14
+ * So the documented shape is the only path, and it is exact: an id, the user
15
+ * role, the content blocks, and a `user` source, deep-frozen before it is
16
+ * handed over.
17
+ */
18
+ import { randomUUID } from 'node:crypto';
19
+ /** Build one user message in the shape the agent's inbox accepts. */
20
+ export function buildUserMessage(content) {
21
+ return Object.freeze({
22
+ id: randomUUID(),
23
+ role: 'user',
24
+ content: Object.freeze(content.map((block) => Object.freeze({ ...block }))),
25
+ source: Object.freeze({ kind: 'user' }),
26
+ });
27
+ }
28
+ //# sourceMappingURL=message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message.js","sourceRoot":"","sources":["../../src/harness/message.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAwBxC,qEAAqE;AACrE,MAAM,UAAU,gBAAgB,CAAC,OAAgC;IAC/D,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,EAAE,EAAE,UAAU,EAAE;QAChB,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC;QAC3E,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,CAAC;KACjD,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Changing which model a live agent's next step runs on.
3
+ *
4
+ * The harness holds one mutable selection object per agent, installed while
5
+ * the agent is still unpublished, and reads `current` each time it assembles a
6
+ * prompt. Mutating that object is how the web UI's model picker changes a
7
+ * running session's model, and it is what lets a conversation carrying an
8
+ * image run somewhere its neighbours do not.
9
+ *
10
+ * `current: undefined` means "no override", so the agent falls back to the
11
+ * route it was created with. That is what makes reverting free: there is
12
+ * nothing to restore, only an override to drop.
13
+ *
14
+ * ## Why this is reproduced rather than imported
15
+ *
16
+ * The harness exports `installModelSelection` from `@deepseek-ai/dsh-agent`,
17
+ * and importing it would be the obvious thing to do. It cannot be done: under
18
+ * pnpm's isolated layout a plugin resolves only its own declared dependencies,
19
+ * and the harness packages are the host's, not ours. A dynamic import of them
20
+ * fails with `ERR_MODULE_NOT_FOUND` every time — which is exactly how this
21
+ * feature came to do nothing at all while looking like it worked.
22
+ *
23
+ * What it does is two listener registrations on the agent's own context, so
24
+ * that is what this does. The coupling is to two event names and their
25
+ * payloads; `test/model-selection.test.ts` pins both.
26
+ */
27
+ /** A provider and model an agent's next step should use. */
28
+ export interface ModelRoute {
29
+ readonly provider: string;
30
+ readonly model: string;
31
+ /** Effort to request; absent clears any inherited one. */
32
+ readonly reasoningEffort?: string;
33
+ }
34
+ /** The object read before each prompt assembly. */
35
+ export interface MutableSelection {
36
+ /** The override in force, or undefined to use the agent's own route. */
37
+ current?: ModelRoute | undefined;
38
+ /**
39
+ * The selection the last assembly actually used.
40
+ *
41
+ * Recorded at assembly and read at request time so both halves of one step
42
+ * agree, even if `current` changes in between.
43
+ */
44
+ assembled?: ModelRoute | undefined;
45
+ }
46
+ /** The slice of an agent's scoped context this needs. */
47
+ export interface AgentContextLike {
48
+ on(event: 'system-prompt/assemble', listener: (assembly: unknown, context: unknown, next: () => Promise<PromptAssembly>) => Promise<PromptAssembly>): () => void;
49
+ on(event: 'agent/request', listener: (payload: unknown, next: () => Promise<ModelRequest>) => Promise<ModelRequest>): () => void;
50
+ }
51
+ /** What a prompt assembly carries that this rewrites. */
52
+ interface PromptAssembly {
53
+ variables?: Record<string, unknown>;
54
+ }
55
+ /** What a model request carries that this rewrites. */
56
+ interface ModelRequest {
57
+ provider?: string;
58
+ model?: string;
59
+ reasoningEffort?: string;
60
+ }
61
+ /**
62
+ * Make an agent follow a mutable selection.
63
+ *
64
+ * @param agentCtx - the agent's scoped context, during unpublished setup.
65
+ * @param selection - the object whose `current` decides each step's route.
66
+ * @returns a disposer removing both listeners.
67
+ */
68
+ export declare function installModelSelection(agentCtx: AgentContextLike, selection: MutableSelection): () => void;
69
+ /** Whether two routes name the same model and reasoning effort. */
70
+ export declare function sameRoute(a: ModelRoute | undefined, b: ModelRoute | undefined): boolean;
71
+ /**
72
+ * Parse a `provider/model` setting.
73
+ *
74
+ * A model id may itself contain slashes — `openai/gpt-5` on a router-style
75
+ * provider is ordinary — so only the first separator divides them.
76
+ *
77
+ * @param value - the configured string; empty means no vision model.
78
+ * @returns the route, or undefined when unset or malformed.
79
+ */
80
+ export declare function parseRoute(value: string | undefined): ModelRoute | undefined;
81
+ export {};
82
+ //# sourceMappingURL=model-selection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-selection.d.ts","sourceRoot":"","sources":["../../src/harness/model-selection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,4DAA4D;AAC5D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAClC;AAED,mDAAmD;AACnD,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,CAAA;IAChC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,UAAU,GAAG,SAAS,CAAA;CACnC;AAED,yDAAyD;AACzD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,CACA,KAAK,EAAE,wBAAwB,EAC/B,QAAQ,EAAE,CACR,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,KAChC,OAAO,CAAC,cAAc,CAAC,GAC3B,MAAM,IAAI,CAAA;IACb,EAAE,CACA,KAAK,EAAE,eAAe,EACtB,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,KAAK,OAAO,CAAC,YAAY,CAAC,GACvF,MAAM,IAAI,CAAA;CACd;AAED,yDAAyD;AACzD,UAAU,cAAc;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC;AAED,uDAAuD;AACvD,UAAU,YAAY;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,gBAAgB,EAC1B,SAAS,EAAE,gBAAgB,GAC1B,MAAM,IAAI,CA8CZ;AAED,mEAAmE;AACnE,wBAAgB,SAAS,CAAC,CAAC,EAAE,UAAU,GAAG,SAAS,EAAE,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAOvF;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAW5E"}