@chanlerdev/scorel 0.0.1 → 0.0.3

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 (60) hide show
  1. package/README.md +409 -69
  2. package/dist/index.js +4593 -1751
  3. package/dist/index.js.map +4 -4
  4. package/docs/CHANGELOG.md +115 -0
  5. package/docs/ROADMAP.md +112 -9
  6. package/docs/SHIP.md +9 -3
  7. package/docs/spec/channels.md +107 -100
  8. package/docs/spec/client.md +11 -5
  9. package/docs/spec/extensions.md +115 -43
  10. package/docs/spec/ship/S0062-npm-package-and-release-workflow.md +3 -0
  11. package/docs/spec/ship/S0063-ai-release-notes.md +129 -0
  12. package/docs/spec/ship/S0064-gui-product-intent-and-boundary.md +79 -0
  13. package/docs/spec/ship/S0065-gui-electron-shell-and-embedded-host.md +73 -0
  14. package/docs/spec/ship/S0066-gui-local-project-workspace.md +79 -0
  15. package/docs/spec/ship/S0067-gui-relay-device-and-remote-project-selection.md +97 -0
  16. package/docs/spec/ship/S0068-gui-codex-app-polish-and-e2e.md +102 -0
  17. package/docs/spec/ship/S0068-gui-e2e-verification.md +50 -0
  18. package/docs/spec/ship/S0069-gui-codex-ui-refactor.md +371 -0
  19. package/docs/spec/ship/S0070-gui-streaming-and-tool-blocks.md +202 -0
  20. package/docs/spec/ship/S0071-gui-visual-fidelity-and-settings-shell.md +360 -0
  21. package/docs/spec/ship/S0072-gui-glass-sidebar-and-picker-anchoring.md +116 -0
  22. package/docs/spec/ship/S0073-provider-model-profile-contract.md +241 -0
  23. package/docs/spec/ship/S0074-gui-model-provider-settings-split.md +113 -0
  24. package/docs/spec/ship/S0075-provider-catalog-model-cards.md +93 -0
  25. package/docs/spec/ship/S0076-provider-modal-search-and-direct-key.md +70 -0
  26. package/docs/spec/ship/S0077-auxiliary-session-title-generation.md +95 -0
  27. package/docs/spec/ship/S0078-gui-provider-settings-forward-config-and-simplification.md +150 -0
  28. package/docs/spec/ship/S0079-gui-sidebar-layout-controls.md +49 -0
  29. package/docs/spec/ship/S0080-session-title-hook-and-gui-markdown-dark-code.md +58 -0
  30. package/docs/spec/ship/S0081-automatic-memory.md +117 -0
  31. package/docs/spec/ship/S0082-memory-journal-tool-and-idle-dream.md +107 -0
  32. package/docs/spec/ship/S0083-extension-manifest-and-im-channel-runtime.md +338 -0
  33. package/docs/spec/ship/S0084-built-in-telegram-im-extension.md +188 -0
  34. package/docs/spec/ship/S0085-gui-im-extension-settings.md +47 -0
  35. package/docs/spec/ship/S0086-auto-compact-and-session-memory.md +124 -0
  36. package/docs/spec/ship/S0087-gui-ui-polish-sweep.md +153 -0
  37. package/docs/spec/ship/S0088-gui-streaming-thinking-contract.md +35 -0
  38. package/docs/spec/ship/S0089-memory-reliability-and-dream-trigger.md +84 -0
  39. package/docs/spec/ship/S0090-gui-provider-delete-and-dark-code-theme.md +77 -0
  40. package/docs/spec/ship/S0091-built-in-qq-and-wechat-im-extensions.md +125 -0
  41. package/docs/spec/ship/S0092-im-message-media-and-human-cadence.md +83 -0
  42. package/docs/spec/ship/S0093-gui-im-settings-platform-layout.md +66 -0
  43. package/docs/spec/ship/S0094-im-inbound-runtime.md +67 -0
  44. package/docs/spec/ship/S0095-gui-im-session-list-refresh.md +36 -0
  45. package/extensions/builtin/loopback/adapter.js +13 -0
  46. package/extensions/builtin/loopback/scorel.extension.json +7 -0
  47. package/extensions/builtin/loopback/skills/loopback/SKILL.md +9 -0
  48. package/extensions/builtin/qq/adapter.d.ts +27 -0
  49. package/extensions/builtin/qq/adapter.js +384 -0
  50. package/extensions/builtin/qq/scorel.extension.json +7 -0
  51. package/extensions/builtin/qq/skills/qq/SKILL.md +9 -0
  52. package/extensions/builtin/telegram/adapter.d.ts +43 -0
  53. package/extensions/builtin/telegram/adapter.js +259 -0
  54. package/extensions/builtin/telegram/scorel.extension.json +7 -0
  55. package/extensions/builtin/telegram/skills/telegram/SKILL.md +11 -0
  56. package/extensions/builtin/wechat/adapter.d.ts +24 -0
  57. package/extensions/builtin/wechat/adapter.js +226 -0
  58. package/extensions/builtin/wechat/scorel.extension.json +7 -0
  59. package/extensions/builtin/wechat/skills/wechat/SKILL.md +9 -0
  60. package/package.json +6 -2
@@ -0,0 +1,338 @@
1
+ # S0083: Extension Manifest And IM Channel Runtime
2
+
3
+ ## Goal
4
+
5
+ Add the smallest extension and IM channel foundation that lets Scorel receive external IM messages, run them through the existing Host/Runtime path, and send replies back through the same channel.
6
+
7
+ The business value is a new entry surface without a second agent stack: IM becomes a thin channel extension around `ScorelHost`, not a new runtime, queue, session store, or relay path.
8
+
9
+ The design principles for this spec are:
10
+
11
+ - elegant: channel code should be a narrow bridge, not a parallel product core;
12
+ - minimally invasive: reuse `ScorelHost`, `DaemonClient`, session JSONL, follow-up, steer, skills, tools, and memory;
13
+ - extensible: Telegram is only the first built-in channel, not a special case in core;
14
+ - simple: V1 supports current-conversation replies only.
15
+
16
+ ## Source Of Truth
17
+
18
+ - `docs/architecture.md`: CLI, WebUI, GUI, IM, and HTTP API are thin entries over the same Host.
19
+ - `docs/spec/extensions.md`: extensions live under `~/.scorel/extensions/` and optional project `.scorel/extensions/`.
20
+ - `docs/spec/channels.md`: channel docs must be updated by this spec to match the extension-backed channel bridge.
21
+ - `docs/spec/ship/S0051-harness-item-and-system-reminder.md`: channel context must use `harness_item` and `<system-reminder>`.
22
+ - `docs/spec/ship/S0052-follow-up-queue-and-dual-loop.md`: IM must reuse existing follow-up and steer queues.
23
+ - `docs/spec/ship/S0053-skill-index-and-skill-tool.md`: enabled extension skill roots must enter the existing skill scanner.
24
+
25
+ ## Scope
26
+
27
+ ### 1. Extension Manifest
28
+
29
+ Introduce a manifest file named:
30
+
31
+ ```text
32
+ scorel.extension.json
33
+ ```
34
+
35
+ Minimum schema:
36
+
37
+ ```typescript
38
+ type ExtensionManifest = {
39
+ id: string;
40
+ kind: "im";
41
+ displayName: string;
42
+ adapter: string;
43
+ skills?: string[];
44
+ mcp?: unknown[];
45
+ };
46
+ ```
47
+
48
+ Rules:
49
+
50
+ - `id` is the stable extension id and channel id, for example `telegram`.
51
+ - `kind = "im"` means the extension exposes an IM adapter.
52
+ - `adapter` is a path relative to the manifest directory.
53
+ - `skills` is a list of directories relative to the manifest directory.
54
+ - `mcp` is parsed and preserved for future specs, but S0083 does not start MCP servers.
55
+
56
+ ### 2. Extension Roots
57
+
58
+ Load enabled extension manifests from:
59
+
60
+ ```text
61
+ ~/.scorel/extensions/
62
+ .scorel/extensions/
63
+ extensions/builtin/
64
+ ```
65
+
66
+ Rules:
67
+
68
+ - user extensions are under `~/.scorel/extensions/`;
69
+ - project extensions are under `.scorel/extensions/`;
70
+ - built-in extensions are shipped in the repo/package under `extensions/builtin/`;
71
+ - project extension id wins over user extension id;
72
+ - built-in extension id loses to both project and user extension id;
73
+ - V1 only starts extensions explicitly enabled in config.
74
+
75
+ ### 3. Config
76
+
77
+ Add explicit config for enabled IM extensions.
78
+
79
+ Example:
80
+
81
+ ```toml
82
+ [extensions.telegram]
83
+ enabled = true
84
+ kind = "im"
85
+
86
+ [extensions.telegram.config]
87
+ botTokenEnv = "SCOREL_TELEGRAM_BOT_TOKEN"
88
+ ```
89
+
90
+ Rules:
91
+
92
+ - secrets are referenced by env var name only;
93
+ - raw tokens must not be written to config, JSONL, diagnostics, or memory;
94
+ - disabled extensions may be discovered but must not start.
95
+
96
+ ### 4. IM Adapter Contract
97
+
98
+ Define the adapter boundary around platform IO only:
99
+
100
+ ```typescript
101
+ interface ImAdapter {
102
+ start(ctx: ImAdapterContext): Promise<void>;
103
+ stop(): Promise<void>;
104
+ sendMessage(target: ImTarget, message: ImOutgoingMessage): Promise<void>;
105
+ setTyping?(target: ImTarget, typing: boolean): Promise<void>;
106
+ }
107
+
108
+ interface ImAdapterContext {
109
+ onMessage(message: ImIncomingMessage): Promise<void>;
110
+ logger: Logger;
111
+ }
112
+ ```
113
+
114
+ Adapter rules:
115
+
116
+ - adapter receives platform events and calls `onMessage`;
117
+ - adapter sends messages to platform targets through its `sendMessage` method;
118
+ - adapter must not create sessions;
119
+ - adapter must not write JSONL;
120
+ - adapter must not implement follow-up, steer, memory, tools, or skill loading;
121
+ - adapter errors are isolated and logged without crashing the Host.
122
+
123
+ ### 5. Channel Bridge
124
+
125
+ Add a Host-side channel bridge that connects enabled IM adapters to existing Host use cases.
126
+
127
+ Responsibilities:
128
+
129
+ - map `(extensionId, externalConversationId)` to a fixed Scorel session;
130
+ - ensure the default workspace project exists at `~/.scorel/workspace`;
131
+ - call existing `send_message` / Host application service for incoming text;
132
+ - inject channel source context as a harness item for the current turn;
133
+ - expose the current channel context to `SendChannelMessage`;
134
+ - keep platform raw ids out of model-authored arguments.
135
+
136
+ The bridge is allowed to keep a small durable binding index under `~/.scorel/`, for example:
137
+
138
+ ```text
139
+ ~/.scorel/channels/im-bindings.json
140
+ ```
141
+
142
+ The binding value must be replayable:
143
+
144
+ ```typescript
145
+ type ImSessionBinding = {
146
+ extensionId: string;
147
+ externalConversationId: string;
148
+ projectId: ProjectId;
149
+ sessionId: SessionId;
150
+ createdAt: number;
151
+ updatedAt: number;
152
+ };
153
+ ```
154
+
155
+ ### 6. Default Workspace
156
+
157
+ IM sessions use the default workspace project:
158
+
159
+ ```text
160
+ ~/.scorel/workspace
161
+ ```
162
+
163
+ Rules:
164
+
165
+ - the directory is created when the first enabled IM message needs it;
166
+ - it is registered as a normal Project in the existing Project Registry;
167
+ - it is not a special Runtime mode;
168
+ - future GUI settings may allow users to choose another workspace, but S0083 does not add that UI.
169
+
170
+ ### 7. Incoming Message Semantics
171
+
172
+ Incoming IM messages must enter the existing runtime path.
173
+
174
+ Idle session:
175
+
176
+ ```text
177
+ IM message -> normal send_message -> ordinary user_message
178
+ ```
179
+
180
+ Running session:
181
+
182
+ ```text
183
+ default -> runningBehavior: "follow_up"
184
+ explicit /steer or /interrupt -> runningBehavior: "steer"
185
+ ```
186
+
187
+ Rules:
188
+
189
+ - no new follow-up queue;
190
+ - no new steer queue;
191
+ - no new IM runtime loop;
192
+ - no new Host daemon;
193
+ - no Relay dependency.
194
+
195
+ ### 8. Channel Source Reminder
196
+
197
+ Before the Host runs the turn, append a channel source `harness_item`.
198
+
199
+ Example content:
200
+
201
+ ```xml
202
+ <system-reminder>
203
+ This message came from an IM channel.
204
+
205
+ channel: telegram
206
+ conversation_type: group
207
+ sender_display_name: Chanler
208
+ mentioned_bot: true
209
+
210
+ Use SendChannelMessage to reply to the current conversation when needed.
211
+ </system-reminder>
212
+ ```
213
+
214
+ Rules:
215
+
216
+ - use `harness_item`, not provider-level system prompt;
217
+ - the reminder is session evidence, not a user message;
218
+ - the reminder must not include raw platform secrets;
219
+ - raw user ids and raw group ids are allowed only in structured event data when needed for routing, not in model-facing text.
220
+
221
+ ### 9. SendChannelMessage Tool
222
+
223
+ Add a built-in runtime tool available only when the current turn has channel context:
224
+
225
+ ```typescript
226
+ SendChannelMessage({
227
+ text: string
228
+ })
229
+ ```
230
+
231
+ Optional V1 shape is allowed if implementation needs explicit channel echo:
232
+
233
+ ```typescript
234
+ SendChannelMessage({
235
+ channel: string,
236
+ target: "current",
237
+ text: string
238
+ })
239
+ ```
240
+
241
+ Rules:
242
+
243
+ - default target is the current external conversation;
244
+ - the model must not provide Telegram chat ids, Feishu open ids, Slack channel ids, or other raw platform ids;
245
+ - the Host resolves the current channel context to an adapter target;
246
+ - if no channel context exists, the tool returns `no_channel_context`;
247
+ - if the adapter send fails, the tool result is an error result and diagnostics record the adapter failure;
248
+ - the tool result must not echo secrets or raw tokens.
249
+
250
+ ### 10. Extension Skills
251
+
252
+ When an extension is enabled, each manifest `skills` directory enters the existing skill scanner.
253
+
254
+ Rules:
255
+
256
+ - extension skills are discovered by the same skill index machinery as project/user skills;
257
+ - skill listing continues to enter the model through existing skill harness events;
258
+ - conflict order is project skill, user skill, extension skill, built-in skill;
259
+ - S0083 does not add a new Skill tool path.
260
+
261
+ ### 11. Loopback IM Extension
262
+
263
+ Add one deterministic built-in loopback IM extension for tests and local verification.
264
+
265
+ It should behave like a channel extension, not a testing bypass:
266
+
267
+ - loaded through `scorel.extension.json`;
268
+ - started through the same extension lifecycle;
269
+ - receives incoming messages through a public local test hook or CLI/dev helper;
270
+ - sends outgoing `SendChannelMessage` payloads into an inspectable in-memory or temp-file outbox.
271
+
272
+ This validates the channel bridge without claiming Telegram support.
273
+
274
+ ## Not In Scope
275
+
276
+ - Real Telegram API. That is S0084.
277
+ - Real Feishu, Slack, Discord, WeCom, or WeChat.
278
+ - Marketplace, remote installation, extension signing, or sandboxing.
279
+ - MCP server startup from extension manifests.
280
+ - Cross-conversation proactive send.
281
+ - User-facing GUI extension management.
282
+ - Webhook mode.
283
+ - Relay-based IM routing.
284
+ - A new Runtime, queue, session store, or daemon process for IM.
285
+
286
+ ## Acceptance Criteria
287
+
288
+ - `scorel.extension.json` is parsed and validated.
289
+ - enabled `kind = "im"` extensions start with Host lifecycle and stop cleanly.
290
+ - disabled extensions do not start.
291
+ - enabled extension skill directories are included in the existing skill index.
292
+ - the default IM workspace is created at `~/.scorel/workspace` and registered as a normal Project.
293
+ - repeated messages from the same `(extensionId, externalConversationId)` reuse the same session.
294
+ - a running session receives normal IM messages as existing `follow_up` items.
295
+ - explicit `/steer` or `/interrupt` IM messages enter the existing steer path.
296
+ - channel source context is appended as a `harness_item` and reaches `buildContext()`.
297
+ - `SendChannelMessage({ text })` sends to the current adapter target without model-authored raw ids.
298
+ - loopback IM extension proves an incoming message can trigger a Host turn and a tool reply can reach the adapter outbox.
299
+ - `docs/spec/extensions.md` and `docs/spec/channels.md` are updated to match the implemented extension-backed channel bridge.
300
+
301
+ ## Testing Requirements
302
+
303
+ - Manifest parser tests for required fields, relative paths, disabled extensions, and invalid manifests.
304
+ - Extension lifecycle tests proving enabled IM adapters start/stop and failures are isolated.
305
+ - Skill index tests proving enabled extension skill roots are scanned.
306
+ - Daemon/Host tests using the loopback IM extension with real temporary `~/.scorel` state and real JSONL sessions.
307
+ - Daemon/Host tests proving running IM messages reuse existing follow-up and steer queues.
308
+ - Tool tests proving `SendChannelMessage` resolves current channel context and rejects missing context.
309
+ - Full `pnpm typecheck && pnpm test`.
310
+
311
+ ## Impacted Files
312
+
313
+ - `docs/spec/extensions.md`
314
+ - `docs/spec/channels.md`
315
+ - `packages/protocol/src/*`
316
+ - `packages/core/src/tools/*`
317
+ - `packages/core/src/session/*`
318
+ - `packages/daemon/src/*`
319
+ - `packages/client/src/*` if the Host application service needs a client-facing option type
320
+ - `extensions/builtin/loopback/*`
321
+ - tests near the changed modules
322
+
323
+ ## Risks And Boundaries
324
+
325
+ - Extension loading can become a platform too quickly. S0083 must only implement what IM channel runtime needs.
326
+ - Raw platform ids are useful for routing but should not become model-authored parameters.
327
+ - Follow-up and steer already exist. Reimplementing them in channel code is a regression.
328
+ - The default workspace is convenient but broad. It must be a normal Project so future UI can inspect or move it.
329
+ - Loopback channel is a product-path verification tool for the channel bridge, not proof that a real IM provider works.
330
+
331
+ ## Follow-Up Specs
332
+
333
+ - S0084: Built-in Telegram IM Extension.
334
+ - Feishu IM extension.
335
+ - Slack or Discord IM extension.
336
+ - GUI Settings for enabling and configuring IM extensions.
337
+ - Extension MCP startup.
338
+ - Cross-conversation channel target handles.
@@ -0,0 +1,188 @@
1
+ # S0084: Built-In Telegram IM Extension
2
+
3
+ ## Goal
4
+
5
+ Ship Telegram as the first real built-in IM extension on top of the S0083 extension/channel foundation.
6
+
7
+ The business value is a real, locally runnable IM entry path: a user can message a Telegram bot, have the message enter a fixed Scorel session, and receive replies through `SendChannelMessage`.
8
+
9
+ Telegram is the first provider because Bot API long polling works from a local Host without webhook, public ingress, or Relay.
10
+
11
+ ## Depends On
12
+
13
+ - S0083: Extension Manifest And IM Channel Runtime.
14
+
15
+ ## Scope
16
+
17
+ ### 1. Built-In Extension Layout
18
+
19
+ Add:
20
+
21
+ ```text
22
+ extensions/builtin/telegram/
23
+ scorel.extension.json
24
+ adapter.js or adapter.ts
25
+ skills/
26
+ telegram/
27
+ SKILL.md
28
+ ```
29
+
30
+ The manifest must use:
31
+
32
+ ```json
33
+ {
34
+ "id": "telegram",
35
+ "kind": "im",
36
+ "displayName": "Telegram",
37
+ "adapter": "./adapter.js",
38
+ "skills": ["./skills"]
39
+ }
40
+ ```
41
+
42
+ The extension skill should explain Telegram-specific behavior to the model at a high level: group vs DM, mention expectations, and reply etiquette. It must not contain secrets.
43
+
44
+ ### 2. Config
45
+
46
+ Telegram is enabled through normal extension config:
47
+
48
+ ```toml
49
+ [extensions.telegram]
50
+ enabled = true
51
+ kind = "im"
52
+
53
+ [extensions.telegram.config]
54
+ botTokenEnv = "SCOREL_TELEGRAM_BOT_TOKEN"
55
+ # Or store a direct bot API key when local plaintext config is acceptable:
56
+ # apiKey = "123456:telegram-bot-token"
57
+ pollIntervalMs = 1000
58
+ allowedChatIds = "-1001234567890,123456789"
59
+ ```
60
+
61
+ Rules:
62
+
63
+ - either `apiKey` or `botTokenEnv` is required when enabled;
64
+ - `apiKey` is stored directly in config; `botTokenEnv` reads the raw bot token from env at runtime;
65
+ - `allowedChatIds` is an optional comma-separated string in V1 and defaults to no allow-list in local dev;
66
+ - diagnostics must never print the raw bot token.
67
+
68
+ ### 3. Telegram Adapter
69
+
70
+ Implement Telegram Bot API long polling.
71
+
72
+ Required behavior:
73
+
74
+ - call `getUpdates` with offset tracking;
75
+ - convert text messages into S0083 `ImIncomingMessage`;
76
+ - support private chats;
77
+ - support group/supergroup messages where the bot is mentioned or replied to;
78
+ - ignore non-text messages in V1 with a diagnostic entry;
79
+ - call `sendMessage` for `SendChannelMessage`;
80
+ - use `sendChatAction` for typing when available;
81
+ - stop polling cleanly on Host shutdown.
82
+
83
+ ### 4. Conversation Identity
84
+
85
+ Telegram conversation id should be stable and deterministic.
86
+
87
+ Recommended mapping:
88
+
89
+ ```text
90
+ private chat -> telegram:private:<chat.id>
91
+ group chat -> telegram:group:<chat.id>
92
+ ```
93
+
94
+ The raw `chat.id` is used for routing in adapter context, but model-facing reminders should only expose safe descriptive fields:
95
+
96
+ - `channel: telegram`
97
+ - `conversation_type: private | group | supergroup`
98
+ - `sender_display_name`
99
+ - `mentioned_bot`
100
+
101
+ ### 5. Incoming Semantics
102
+
103
+ Reuse S0083:
104
+
105
+ - idle session -> normal prompt;
106
+ - running session -> default `follow_up`;
107
+ - `/steer ...` or `/interrupt ...` -> existing steer path;
108
+ - all turns use channel source reminder;
109
+ - replies use `SendChannelMessage`.
110
+
111
+ ### 6. Reply Semantics
112
+
113
+ Telegram `SendChannelMessage` maps to Bot API `sendMessage`.
114
+
115
+ Rules:
116
+
117
+ - V1 sends plain text only;
118
+ - no Markdown/HTML parse mode by default;
119
+ - adapter may split messages that exceed Telegram length limits;
120
+ - send failures return tool error results and diagnostics.
121
+
122
+ ### 7. Manual Smoke
123
+
124
+ Add a documented manual smoke path:
125
+
126
+ ```bash
127
+ export SCOREL_TELEGRAM_BOT_TOKEN=...
128
+ pnpm scorel host serve
129
+ ```
130
+
131
+ Then:
132
+
133
+ 1. send the bot a private message;
134
+ 2. verify `~/.scorel/workspace` project exists;
135
+ 3. verify the same Telegram chat reuses one Scorel session;
136
+ 4. verify JSONL contains the user message and channel reminder;
137
+ 5. verify the model can call `SendChannelMessage` and the Telegram chat receives the reply.
138
+
139
+ ## Not In Scope
140
+
141
+ - Telegram webhook mode.
142
+ - Inline keyboards.
143
+ - Media, files, voice, stickers, images, or albums.
144
+ - Markdown/HTML formatting.
145
+ - Multiple bot accounts.
146
+ - Proactive cross-chat sends.
147
+ - Admin commands.
148
+ - GUI settings for Telegram.
149
+ - Relay deployment.
150
+
151
+ ## Acceptance Criteria
152
+
153
+ - Telegram built-in extension is discoverable through the S0083 manifest loader.
154
+ - Telegram starts only when enabled and either direct `apiKey` exists or `botTokenEnv` resolves.
155
+ - Telegram long polling receives private text messages.
156
+ - Telegram group messages are accepted only when bot mention/reply rules pass.
157
+ - incoming Telegram messages reuse the fixed session for the Telegram conversation.
158
+ - incoming messages enter existing Host runtime with S0083 channel reminder.
159
+ - running Telegram messages reuse existing follow-up/steer behavior.
160
+ - `SendChannelMessage` sends a Telegram `sendMessage` to the current chat.
161
+ - stop shuts down long polling without leaving active timers.
162
+ - manual smoke with a real Telegram bot token is documented.
163
+
164
+ ## Testing Requirements
165
+
166
+ - Unit tests for Telegram update normalization.
167
+ - Unit tests for mention/reply acceptance rules.
168
+ - Unit tests for token redaction in diagnostics.
169
+ - Adapter tests using a local HTTP Telegram API stub for `getUpdates`, `sendMessage`, and `sendChatAction`.
170
+ - Host integration tests may use S0083 loopback for runtime semantics; Telegram tests cover provider-specific HTTP behavior.
171
+ - Manual smoke with a real Telegram bot token before marking S0084 done.
172
+ - Full `pnpm typecheck && pnpm test`.
173
+
174
+ ## Impacted Files
175
+
176
+ - `extensions/builtin/telegram/*`
177
+ - `packages/daemon/src/*`
178
+ - `packages/core/src/config/*`
179
+ - `docs/spec/extensions.md`
180
+ - `docs/spec/channels.md`
181
+ - tests near the changed modules
182
+
183
+ ## Risks And Boundaries
184
+
185
+ - Telegram Bot API is reliable enough for first IM support, but still an external network dependency. Automated tests should use a local HTTP stub, while completion requires a real manual smoke.
186
+ - Group chats can be noisy. V1 should require bot mention or reply in groups.
187
+ - Telegram raw chat ids are routing data, not model-authored parameters.
188
+ - S0084 must not add Telegram-specific branches to core channel logic. Provider-specific code stays inside the built-in extension.
@@ -0,0 +1,47 @@
1
+ # S0085: GUI IM Extension Settings
2
+
3
+ ## Goal
4
+
5
+ Add a real GUI control for enabling the built-in Telegram IM extension.
6
+
7
+ The business value is that local GUI users no longer need to hand-edit `~/.scorel/config.toml` to try Telegram. The GUI writes the same user-level extension config that `scorel host serve` reads, and the embedded local Host applies the change immediately.
8
+
9
+ ## Scope
10
+
11
+ - Add daemon/client protocol requests for reading and updating IM extension settings.
12
+ - Persist extension settings through the shared config renderer, preserving existing provider, memory, and model config.
13
+ - Refresh local Host IM adapters after settings are updated.
14
+ - Add a GUI Settings page for IM with a Telegram toggle and basic config fields:
15
+ - credential mode: env or direct API key
16
+ - `credentialMode`
17
+ - `apiKey`
18
+ - `botTokenEnv`
19
+ - `pollIntervalMs`
20
+ - `allowedChatIds`
21
+ - `botUsername`
22
+ - Use `~/.scorel/config.toml` as the user-level config path for GUI extension settings.
23
+ - Include `extensions/` in package files and make built-in extension discovery work from the package root.
24
+
25
+ ## Not In Scope
26
+
27
+ - Remote Relay IM control.
28
+ - Telegram manual smoke with a real bot token.
29
+ - GUI diagnostics timeline for extension startup errors.
30
+
31
+ ## Acceptance Criteria
32
+
33
+ - GUI Settings exposes an IM page with a Telegram enable switch.
34
+ - Toggling Telegram writes `[extensions.telegram]` to `~/.scorel/config.toml`.
35
+ - Telegram config fields write `[extensions.telegram.config]`.
36
+ - Telegram can use either an env-backed token or a directly stored `apiKey`.
37
+ - Direct mode stays selected before an API key is entered.
38
+ - Updating settings triggers local Host IM extension refresh without restarting the GUI.
39
+ - Missing Telegram token does not crash the GUI; the extension remains configured but inactive.
40
+ - Built-in extension discovery works when running from an installed package.
41
+
42
+ ## Testing Requirements
43
+
44
+ - Core config renderer test for extension settings merge.
45
+ - GUI local Host test for extension settings IPC path and persisted config.
46
+ - GUI shell render test includes the IM settings page in navigation.
47
+ - Full `pnpm typecheck && pnpm test`.
@@ -0,0 +1,124 @@
1
+ # S0086: Auto Compact And Session Memory
2
+
3
+ ## Goal
4
+
5
+ Ship the first simple automatic context compaction path and optional session memory maintenance.
6
+
7
+ The business value is continuity in long-running GUI/CLI sessions: Scorel should avoid waiting until context overflow, keep recent turns intact, and preserve enough current-task state for the model to continue after older history is summarized.
8
+
9
+ In S0086, **session memory is context management, not long-term memory**. It is an asynchronous pre-compact summary of the current session. It exists so auto compact can replace old context immediately at the threshold instead of blocking the user turn while generating a summary.
10
+
11
+ ## Scope
12
+
13
+ ### Auto Compact
14
+
15
+ Before a new user turn runs, Host estimates the current LLM context size for the active session. If the estimate reaches the configured threshold, Host appends one persistent `compact` event. Host prefers the already-maintained session memory summary, but still falls back to the original foreground agent compact when session memory is missing.
16
+
17
+ Default threshold:
18
+
19
+ ```text
20
+ memory.autoCompactThreshold = 0.8
21
+ ```
22
+
23
+ The threshold is a ratio of the selected model context window. If the selected model has no context window, Scorel falls back to the existing internal 200,000 token window assumption.
24
+
25
+ Compact behavior:
26
+
27
+ - compact only old history before the recent retention window;
28
+ - keep up to the most recent 8 conversation events after the compact event, starting only from a replay-safe boundary;
29
+ - write session memory content into JSONL as a `compact` event when available;
30
+ - if session memory is unavailable, run a foreground auxiliary compact summary and write that summary instead;
31
+ - do not delete or rewrite old JSONL events;
32
+ - `buildContext()` injects the compact summary and stops walking earlier history;
33
+ - compact is cheap at threshold time when session memory is fresh, but must still compact through the original foreground path when no session memory exists.
34
+
35
+ The first implementation keeps a small fixed retention budget of recent conversation events. The retained suffix must start at `user_message`, `compact`, or an `assistant_message` that contains a `tool_call`. This keeps recent long-running tool evidence replayable while avoiding an orphan `tool_result` as the first retained message. It does not expose manual compact UX.
36
+
37
+ ### Session Memory
38
+
39
+ Add a project setting:
40
+
41
+ ```text
42
+ memory.sessionMemory = true
43
+ ```
44
+
45
+ When enabled, Host maintains a per-session memory file after completed turns:
46
+
47
+ ```text
48
+ ~/.scorel/context/session-memory/<projectId>/<sessionId>.md
49
+ ```
50
+
51
+ Session memory is current-session continuity, not root/project memory. It captures:
52
+
53
+ - current task/status;
54
+ - important decisions;
55
+ - files or commands that matter for continuation;
56
+ - blockers/follow-ups;
57
+ - a short recent-work log.
58
+
59
+ Session memory is updated asynchronously after completed turns. At compact time, Host directly uses this file as the compact summary when session memory is enabled and available.
60
+
61
+ If a session memory update is already in flight when compact is needed, Host waits up to 5 seconds for that update. If the update does not finish in time, Host continues with foreground compact instead of waiting indefinitely. This gives the async pre-compact path a realistic chance to finish without letting a stale or slow background task block the main user turn.
62
+
63
+ If `memory.sessionMemory = false`, auto compact still works through the original foreground compact path. The toggle only controls asynchronous pre-compact maintenance.
64
+
65
+ ### GUI Settings
66
+
67
+ GUI Settings Memory section exposes:
68
+
69
+ - session memory toggle;
70
+ - auto compact threshold select.
71
+
72
+ These controls use the existing project-scoped memory config path.
73
+
74
+ ## Not In Scope
75
+
76
+ - Manual `/compact` command.
77
+ - User-facing compact history browser.
78
+ - Token counting through provider APIs.
79
+ - Semantic recall or vector memory.
80
+ - Topic memory files.
81
+ - Cross-process scheduled session memory repair.
82
+ - Deleting, truncating, or rewriting historical JSONL.
83
+
84
+ ## Acceptance Criteria
85
+
86
+ - `[memory]` parses/renders `sessionMemory` and `autoCompactThreshold`.
87
+ - GUI Settings renders real controls for session memory and compact threshold.
88
+ - `PersistentEvent` includes `compact`.
89
+ - Session replay treats `compact` as a conversation barrier: context starts with the summary and does not include older events.
90
+ - Host checks compact before each user turn and appends `compact` once the estimated context reaches 80% of the selected model window.
91
+ - Compact keeps recent context after the barrier by replaying the compact summary plus the retained recent safe event suffix.
92
+ - After completed turns, Host updates the session memory file when enabled.
93
+ - Session memory is project/session-scoped and does not update root or project `MEMORY.md`.
94
+ - If session memory maintenance is in flight when compact is needed, Host briefly waits for it and then proceeds.
95
+ - If session memory is unavailable, Host falls back to foreground auxiliary compact and still appends `compact`.
96
+ - Failures in session-memory maintenance write diagnostics but do not fail the user turn.
97
+
98
+ ## Testing Requirements
99
+
100
+ - Core session tests for `compact` parsing and barrier context.
101
+ - Config tests for new memory fields.
102
+ - Daemon tests proving auto compact appends `compact` and session memory is maintained.
103
+ - GUI render tests proving the controls are visible.
104
+ - Full `pnpm typecheck && pnpm test`.
105
+
106
+ ## Impacted Files
107
+
108
+ - `packages/protocol/src/events.ts`
109
+ - `packages/core/src/config/index.ts`
110
+ - `packages/core/src/session/index.ts`
111
+ - `packages/core/src/session/session.test.ts`
112
+ - `packages/core/src/memory/index.ts`
113
+ - `packages/core/src/memory/memory.test.ts`
114
+ - `packages/daemon/src/index.ts`
115
+ - `packages/daemon/src/embedded/embedded.test.ts`
116
+ - `apps/gui/src/renderer/settings/sections/MemorySection.tsx`
117
+ - `apps/gui/src/renderer/gui-shell.test.tsx`
118
+ - `docs/ROADMAP.md`
119
+
120
+ ## Risks And Boundaries
121
+
122
+ - Token estimation is approximate. This is acceptable for V1 because the trigger is intentionally conservative and uses the same rough estimate style as existing context-budget tooling.
123
+ - A stale session memory can lose nuance. V1 keeps recent turns intact and preserves full JSONL evidence for audit/replay.
124
+ - Session memory should be treated as continuity notes, not current code truth.