@chanlerdev/scorel 0.0.2 → 0.0.4

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 (42) hide show
  1. package/README.md +55 -2
  2. package/dist/index.js +1162 -192
  3. package/dist/index.js.map +3 -3
  4. package/docs/CHANGELOG.md +106 -0
  5. package/docs/ROADMAP.md +34 -2
  6. package/docs/SHIP.md +1 -1
  7. package/docs/spec/channels.md +15 -5
  8. package/docs/spec/extensions.md +6 -5
  9. package/docs/spec/ship/S0060-relay-hosted-webui-e2e-validation.verification.md +1 -1
  10. package/docs/spec/ship/S0073-provider-model-profile-contract.md +6 -6
  11. package/docs/spec/ship/S0074-gui-model-provider-settings-split.md +1 -1
  12. package/docs/spec/ship/S0076-provider-modal-search-and-direct-key.md +1 -1
  13. package/docs/spec/ship/S0086-auto-compact-and-session-memory.md +1 -1
  14. package/docs/spec/ship/S0087-gui-ui-polish-sweep.md +153 -0
  15. package/docs/spec/ship/S0088-gui-streaming-thinking-contract.md +35 -0
  16. package/docs/spec/ship/S0089-memory-reliability-and-dream-trigger.md +84 -0
  17. package/docs/spec/ship/S0090-gui-provider-delete-and-dark-code-theme.md +77 -0
  18. package/docs/spec/ship/S0091-built-in-qq-and-wechat-im-extensions.md +125 -0
  19. package/docs/spec/ship/S0092-im-message-media-and-human-cadence.md +83 -0
  20. package/docs/spec/ship/S0093-gui-im-settings-platform-layout.md +66 -0
  21. package/docs/spec/ship/S0094-im-inbound-runtime.md +67 -0
  22. package/docs/spec/ship/S0095-gui-im-session-list-refresh.md +36 -0
  23. package/docs/spec/ship/S0096-glob-stable-order.md +32 -0
  24. package/docs/spec/ship/S0097-rtk-token-saving-settings.md +61 -0
  25. package/docs/spec/ship/S0098-local-daemon-singleton-unified-state.md +96 -0
  26. package/docs/spec/ship/S0099-gui-connection-device-settings.md +85 -0
  27. package/docs/spec/ship/S0100-gui-provider-danger-zone.md +57 -0
  28. package/docs/spec/ship/S0101-gui-device-settings-polish.md +66 -0
  29. package/docs/spec/ship/S0102-device-only-config.md +58 -0
  30. package/extensions/builtin/loopback/skills/loopback/SKILL.md +2 -0
  31. package/extensions/builtin/qq/adapter.d.ts +27 -0
  32. package/extensions/builtin/qq/adapter.js +384 -0
  33. package/extensions/builtin/qq/scorel.extension.json +7 -0
  34. package/extensions/builtin/qq/skills/qq/SKILL.md +9 -0
  35. package/extensions/builtin/telegram/adapter.d.ts +1 -1
  36. package/extensions/builtin/telegram/adapter.js +7 -0
  37. package/extensions/builtin/telegram/skills/telegram/SKILL.md +2 -0
  38. package/extensions/builtin/wechat/adapter.d.ts +24 -0
  39. package/extensions/builtin/wechat/adapter.js +226 -0
  40. package/extensions/builtin/wechat/scorel.extension.json +7 -0
  41. package/extensions/builtin/wechat/skills/wechat/SKILL.md +9 -0
  42. package/package.json +4 -2
@@ -0,0 +1,77 @@
1
+ # S0090: GUI Provider Delete And Dark Code Theme
2
+
3
+ ## Goal
4
+
5
+ Fix two GUI regressions before the next IM expansion:
6
+
7
+ - dark theme code blocks must use a dark Shiki theme instead of light-token colors;
8
+ - Provider settings must expose a real delete path for configured providers.
9
+
10
+ The business value is basic settings trust. Users should be able to remove bad provider config and read code blocks in the selected GUI theme.
11
+
12
+ ## Scope
13
+
14
+ ### Dark Code Blocks
15
+
16
+ - Load both light and dark Shiki themes in the GUI code block highlighter.
17
+ - Select the rendered theme from `:root[data-theme]` / system dark preference.
18
+ - Re-render when the GUI theme changes.
19
+ - Keep code block chrome styled by Scorel tokens; only syntax token colors switch.
20
+
21
+ ### Provider Delete
22
+
23
+ - Add a protocol/client/daemon request for removing one provider from a project model profile.
24
+ - Deleting a provider removes:
25
+ - `[providers.<id>]`;
26
+ - provider models owned by that provider;
27
+ - available models pointing at removed provider models;
28
+ - role selections that pointed at removed available models, with stable fallback when possible.
29
+ - GUI Provider Settings exposes a delete button for the selected provider.
30
+ - If no provider remains, the Provider page returns to the empty state without stale selected-provider UI.
31
+
32
+ ## Not In Scope
33
+
34
+ - Deleting individual provider model definitions unless required by provider deletion.
35
+ - Bulk reset of all model profile config.
36
+ - Provider secret migration.
37
+ - Changing provider catalog fetch behavior.
38
+ - IM platform work; covered by S0091-S0093.
39
+
40
+ ## Acceptance Criteria
41
+
42
+ - Dark GUI theme renders Shiki tokens with a dark theme.
43
+ - Light GUI theme keeps the current light code block behavior.
44
+ - Switching GUI theme updates future code block renders without app restart.
45
+ - Provider delete removes the provider and all dependent model profile entries from persisted config.
46
+ - Provider delete does not leave roles pointing at removed models.
47
+ - GUI can delete the selected provider and updates the selected provider to the next available provider or empty state.
48
+ - Remote GUI provider deletion uses the same daemon/client request as local GUI.
49
+
50
+ ## Testing Requirements
51
+
52
+ - GUI Shiki tests prove both light and dark themes are loaded/selected.
53
+ - Config renderer tests cover provider deletion and role fallback.
54
+ - Protocol/client/daemon tests cover the delete request.
55
+ - GUI render test covers delete button and empty-state transition.
56
+ - Full `pnpm typecheck && pnpm test`.
57
+
58
+ ## Impacted Files
59
+
60
+ - `apps/gui/src/renderer/chatbox/ShikiCodeBlock.tsx`
61
+ - `apps/gui/src/renderer/chatbox/ShikiCodeBlock.test.tsx`
62
+ - `apps/gui/src/shiki-theme.test.ts`
63
+ - `apps/gui/src/renderer/settings/sections/ProviderSection.tsx`
64
+ - `apps/gui/src/shared/ipc.ts`
65
+ - `apps/gui/src/main.ts`
66
+ - `apps/gui/src/main/local-host.ts`
67
+ - `apps/gui/src/main/relay-service.ts`
68
+ - `packages/protocol/src/events.ts`
69
+ - `packages/protocol/src/wire.ts`
70
+ - `packages/client/src/index.ts`
71
+ - `packages/core/src/config/index.ts`
72
+ - `packages/core/src/config/*.test.ts`
73
+ - `packages/daemon/src/index.ts`
74
+
75
+ ## Status
76
+
77
+ Done.
@@ -0,0 +1,125 @@
1
+ # S0091: Built-In QQ And WeChat IM Extensions
2
+
3
+ ## Goal
4
+
5
+ Add QQ Bot and WeChat as built-in IM extensions on the existing extension-backed channel bridge.
6
+
7
+ The business value is channel reach. Telegram proved the bridge; QQ and WeChat force the adapter contract to stay generic enough for multiple IM platforms without forking Scorel runtime behavior.
8
+
9
+ ## Product Boundary
10
+
11
+ Scorel must use official or documented bot/server APIs. This spec does not support personal-account reverse engineering, browser automation of consumer clients, unofficial Web WeChat scraping, or any path likely to get user accounts restricted.
12
+
13
+ ## Scope
14
+
15
+ ### Shared IM Adapter Utilities
16
+
17
+ - Extract reusable helpers for HTTP polling/webhook-shaped adapters where the current Telegram code has platform-neutral logic.
18
+ - Keep platform-specific authentication, payload parsing, mention rules, and send APIs inside each built-in extension.
19
+ - Preserve the S0083 adapter boundary: adapters do platform IO only and never create sessions or write JSONL.
20
+
21
+ ### QQ Bot Extension
22
+
23
+ Add:
24
+
25
+ ```text
26
+ extensions/builtin/qq/
27
+ scorel.extension.json
28
+ adapter.js
29
+ adapter.d.ts
30
+ skills/qq/SKILL.md
31
+ ```
32
+
33
+ Expected config shape:
34
+
35
+ ```toml
36
+ [extensions.qq]
37
+ enabled = true
38
+ kind = "im"
39
+
40
+ [extensions.qq.config]
41
+ appId = "..."
42
+ appSecret = "..."
43
+ botId = "..."
44
+ allowedConversationIds = "..."
45
+ ```
46
+
47
+ QQ Bot uses the current official server-side credential flow: Scorel stores the developer-console `App ID` and `App Secret`, calls `https://bots.qq.com/app/getAppAccessToken`, caches the returned access token until shortly before expiry, and sends API requests with `Authorization: QQBot ACCESS_TOKEN`.
48
+
49
+ `apiBaseUrl` and `accessTokenUrl` may remain internal override hooks for tests and sandbox work, but GUI Settings must not expose them as the default setup path.
50
+
51
+ ### WeChat Extension
52
+
53
+ Add:
54
+
55
+ ```text
56
+ extensions/builtin/wechat/
57
+ scorel.extension.json
58
+ adapter.js
59
+ adapter.d.ts
60
+ skills/wechat/SKILL.md
61
+ ```
62
+
63
+ Expected config shape:
64
+
65
+ ```toml
66
+ [extensions.wechat]
67
+ enabled = true
68
+ kind = "im"
69
+
70
+ [extensions.wechat.config]
71
+ webhookUrl = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=..."
72
+ callbackToken = "..."
73
+ callbackHost = "127.0.0.1"
74
+ callbackPort = 0
75
+ ```
76
+
77
+ Use WeCom group robot webhook semantics for outbound send only. The user copies the full webhook URL from the official group robot configuration and pastes it into Scorel. This webhook cannot receive user messages from the group.
78
+
79
+ Inbound WeChat receive is covered by S0094: Scorel can expose an official-account style plaintext callback server when `callbackToken` is configured. Do not make users split `key`, env var names, or base URLs in the default outbound setup path. Do not implement consumer WeChat personal account automation.
80
+
81
+ ### Skills
82
+
83
+ Each built-in extension must include a platform-specific skill that tells the model:
84
+
85
+ - what kind of conversation it is replying to;
86
+ - how mentions/group context should be interpreted;
87
+ - platform etiquette and short-response expectations;
88
+ - when to use `SendChannelMessage`;
89
+ - what not to assume about raw platform ids.
90
+
91
+ ## Not In Scope
92
+
93
+ - Consumer QQ/WeChat personal account login.
94
+ - Public callback deployment, TLS, or hosted ingress.
95
+ - Rich media send support; covered by S0092.
96
+ - GUI Settings layout; covered by S0093.
97
+ - Remote Relay management of IM settings.
98
+
99
+ ## Acceptance Criteria
100
+
101
+ - QQ and WeChat built-in extension manifests are discoverable by the existing loader.
102
+ - Each extension starts only when explicitly enabled and required credentials are present.
103
+ - QQ requires `appId` and `appSecret`; WeChat requires either a full outbound `webhookUrl` or an inbound `callbackToken`.
104
+ - Each adapter normalizes incoming text messages into the existing `ImIncomingMessage` shape.
105
+ - Each adapter sends plain text replies through the existing `SendChannelMessage` path.
106
+ - QQ send obtains and reuses an official access token instead of accepting deprecated bot token config.
107
+ - Adapter diagnostics redact secrets.
108
+ - QQ, WeChat, Telegram, and loopback share the same channel bridge and session binding behavior.
109
+ - No QQ/WeChat-specific branch is added to runtime/session/core channel orchestration.
110
+
111
+ ## Testing Requirements
112
+
113
+ - Manifest loader coverage for QQ and WeChat built-ins.
114
+ - Adapter normalization tests using local HTTP stubs or pure parser fixtures.
115
+ - Send-message tests proving each adapter maps `SendChannelMessage` to its platform send API shape.
116
+ - Secret redaction tests.
117
+ - Full `pnpm typecheck && pnpm test`.
118
+
119
+ ## Local State Boundary
120
+
121
+ Pre-1.0 local config may contain older `tokenEnv`, `token`, `webhookKeyEnv`, `webhookKey`, or `webhookBaseUrl` keys from earlier S0091 drafts. Those keys are no longer the supported setup surface. Users should re-enter QQ `App ID` / `App Secret` or WeChat `Outbound Webhook` / `Callback Token` in GUI Settings.
122
+
123
+ ## Status
124
+
125
+ Done.
@@ -0,0 +1,83 @@
1
+ # S0092: IM Message Media And Human Cadence
2
+
3
+ ## Goal
4
+
5
+ Make IM conversations feel alive and trustworthy by improving outgoing message capability and IM-specific response cadence.
6
+
7
+ The business value is user confidence. In IM, silence for minutes reads as failure; Scorel needs visible progress and short human-style replies without compromising the existing agent loop.
8
+
9
+ ## Scope
10
+
11
+ ### SendChannelMessage Payload
12
+
13
+ Extend `SendChannelMessage` from text-only to a structured outgoing message:
14
+
15
+ ```ts
16
+ type SendChannelMessageInput = {
17
+ text?: string;
18
+ attachments?: Array<{
19
+ type: "image" | "file";
20
+ path?: string;
21
+ url?: string;
22
+ mimeType?: string;
23
+ caption?: string;
24
+ }>;
25
+ channel?: string;
26
+ target?: "current";
27
+ };
28
+ ```
29
+
30
+ Rules:
31
+
32
+ - At least one of `text` or `attachments` is required.
33
+ - Adapters may downgrade unsupported attachments to a clear tool error.
34
+ - Local file paths must be explicit and must not be guessed from raw platform ids.
35
+ - Tool result details report per-attachment status.
36
+
37
+ ### Adapter Capability Contract
38
+
39
+ - Add optional adapter capabilities for outgoing attachment support.
40
+ - Telegram/QQ/WeChat can initially support text and explicitly reject unsupported media.
41
+ - Loopback should support structured capture of text and attachment metadata for tests.
42
+
43
+ ### IM System Prompt / Harness Guidance
44
+
45
+ Add IM-specific guidance to channel context:
46
+
47
+ - acknowledge quickly when work will take time;
48
+ - send brief progress updates through `SendChannelMessage` during long tasks;
49
+ - prefer concise, conversational wording;
50
+ - do not wait until every tool finishes before sending any reply;
51
+ - avoid exposing internal tool names unless useful to the user;
52
+ - keep business-critical facts and file references precise.
53
+
54
+ This guidance must enter through the existing channel harness item / skill path, not a second provider-level system prompt.
55
+
56
+ ## Not In Scope
57
+
58
+ - Full media upload implementation for every platform.
59
+ - Voice, stickers, albums, interactive buttons, or payments.
60
+ - Cross-conversation proactive messages.
61
+ - A new IM runtime loop or queue.
62
+ - Fake progress timers outside the agent loop.
63
+
64
+ ## Acceptance Criteria
65
+
66
+ - `SendChannelMessage` accepts text, image, and file attachment metadata.
67
+ - Text-only calls remain backward compatible.
68
+ - Unsupported attachment sends fail as tool errors, not silent no-ops.
69
+ - IM channel reminders tell the agent to respond early and keep long-running users informed.
70
+ - Built-in IM skills include platform-specific response cadence guidance.
71
+ - Tests prove the model-facing tool schema rejects empty sends and preserves attachment metadata.
72
+
73
+ ## Testing Requirements
74
+
75
+ - Core channel tool tests for structured payload parsing.
76
+ - Loopback adapter tests for captured attachment metadata.
77
+ - Telegram/QQ/WeChat tests for unsupported media errors or implemented send mapping.
78
+ - Instruction/channel context tests for IM cadence guidance.
79
+ - Full `pnpm typecheck && pnpm test`.
80
+
81
+ ## Status
82
+
83
+ Done.
@@ -0,0 +1,66 @@
1
+ # S0093: GUI IM Settings Platform Layout
2
+
3
+ ## Goal
4
+
5
+ Redesign GUI IM Settings so multiple platforms fit without crowding the page.
6
+
7
+ The business value is scale. Telegram was one provider; Telegram + QQ + WeChat needs a repeatable platform row pattern where details expand only when the user asks.
8
+
9
+ ## Scope
10
+
11
+ - Replace the current Telegram-only large form with a platform list.
12
+ - Each platform row shows:
13
+ - platform name;
14
+ - enabled/disabled toggle;
15
+ - active/inactive status;
16
+ - concise credential/config summary;
17
+ - expand/collapse affordance.
18
+ - Clicking a row expands its detailed config below that row.
19
+ - Clicking the expanded row again collapses it.
20
+ - The page defaults to all platforms collapsed unless a previous expanded platform is stored locally.
21
+ - The last expanded platform is remembered locally; collapsing clears that remembered state.
22
+ - Support Telegram, QQ, and WeChat using one reusable component shape.
23
+ - Preserve existing Telegram config fields and persistence behavior.
24
+ - Add QQ and WeChat config fields that match S0091/S0094.
25
+ - For QQ and WeChat, expose the current quick setup fields only: QQ `App ID` / `App Secret`, WeChat `Outbound Webhook` plus inbound callback `Callback Token` / `Callback Host` / `Callback Port`.
26
+ - Keep settings stored through the existing `getExtensionSettings` / `upsertExtensionSettings` IPC path.
27
+ - Absorb immediate GUI review fixes found before push:
28
+ - expanded platform details must have a visible ownership boundary;
29
+ - the composer must not submit while an IME composition is active.
30
+
31
+ ## Not In Scope
32
+
33
+ - Remote Relay IM settings.
34
+ - Diagnostics timeline.
35
+ - Live credential validation beyond existing adapter refresh behavior.
36
+ - Account OAuth or QR login.
37
+
38
+ ## Acceptance Criteria
39
+
40
+ - The IM Settings page renders three compact platform rows.
41
+ - The first render does not expand Telegram by default.
42
+ - No single disabled platform consumes a full settings page height.
43
+ - Expanding Telegram reveals the existing credential/poll/allow-list fields.
44
+ - Expanding QQ or WeChat reveals their S0091 config fields.
45
+ - QQ and WeChat do not expose env-var-first credential fields in the default Settings flow.
46
+ - Re-clicking the expanded platform collapses details.
47
+ - Re-opening IM Settings restores the previously expanded platform when one was stored.
48
+ - Toggling a platform writes the correct extension config and refreshes local Host IM extensions.
49
+ - Direct secret fields are password inputs and are never displayed in summaries.
50
+ - Layout remains readable in narrow GUI widths.
51
+ - Expanded fields are visually grouped under the active platform, not blended into sibling platform rows.
52
+ - Pressing Enter while Chinese/Japanese/Korean IME composition is active does not submit or block candidate selection.
53
+ - Plain Enter still submits when enabled; Shift+Enter remains available for newline input.
54
+
55
+ ## Testing Requirements
56
+
57
+ - GUI render tests for compact rows and expansion.
58
+ - GUI interaction tests for default-collapsed, toggle-to-collapse, and stored expansion restore.
59
+ - GUI interaction tests for toggling and field blur persistence.
60
+ - Existing Telegram settings behavior remains covered.
61
+ - GUI composer tests cover IME composition Enter, plain Enter, and Shift+Enter.
62
+ - Full `pnpm typecheck && pnpm test`.
63
+
64
+ ## Status
65
+
66
+ Done.
@@ -0,0 +1,67 @@
1
+ # S0094: IM Inbound Runtime
2
+
3
+ ## Goal
4
+
5
+ Make built-in QQ and WeChat IM integrations actually receive user messages at runtime and route them through the shared Scorel IM session bridge.
6
+
7
+ ## Scope
8
+
9
+ - Implement QQ Bot inbound receive through the official WebSocket Gateway.
10
+ - Implement WeChat inbound receive through an official HTTP callback surface for official-account style plaintext text messages.
11
+ - Keep WeCom group robot webhook as an outbound-only sender because that official webhook surface does not deliver user messages back to Scorel.
12
+ - Keep adapters platform-IO only. They call `ctx.onMessage(...)`; daemon session creation stays in the existing IM bridge.
13
+ - Update GUI settings and docs so outbound webhook configuration is not presented as if it enables inbound WeChat chat.
14
+
15
+ ## Not In Scope
16
+
17
+ - Consumer personal WeChat reverse engineering, browser automation, or Web WeChat scraping.
18
+ - Hosted public ingress, TLS certificates, relay tunneling, or automatic public URL provisioning.
19
+ - Full WeChat encrypted callback decrypt/encrypt support. Plaintext callback is the S0094 receive baseline.
20
+ - QQ sharding beyond one local gateway connection.
21
+
22
+ ## Acceptance Criteria
23
+
24
+ - QQ adapter `start(ctx)` fetches an access token, fetches `/gateway`, opens a WebSocket, identifies with `QQBot <access_token>`, sends heartbeats, and calls `ctx.onMessage(...)` for text dispatch events normalized by `normalizeQQEvent`.
25
+ - QQ adapter `stop()` closes the WebSocket and heartbeat timer.
26
+ - WeChat adapter starts a local HTTP callback server when callback config is present, responds to GET URL verification, accepts text POST callbacks, normalizes them, and calls `ctx.onMessage(...)`.
27
+ - WeChat adapter can still send outbound text to a configured WeCom group robot webhook; if only webhook URL is configured, inbound is explicitly not started.
28
+ - Tests cover QQ gateway identify/heartbeat/dispatch/stop and WeChat callback verification/message routing.
29
+
30
+ ## Test Requirements
31
+
32
+ - Add focused adapter tests before implementation.
33
+ - Run:
34
+
35
+ ```bash
36
+ pnpm --filter @scorel/daemon test -- qq-adapter.test.ts wechat-adapter.test.ts
37
+ pnpm typecheck
38
+ pnpm test
39
+ ```
40
+
41
+ ## Impacted Files
42
+
43
+ - `extensions/builtin/qq/adapter.js`
44
+ - `extensions/builtin/qq/adapter.d.ts`
45
+ - `extensions/builtin/wechat/adapter.js`
46
+ - `extensions/builtin/wechat/adapter.d.ts`
47
+ - `packages/daemon/src/qq-adapter.test.ts`
48
+ - `packages/daemon/src/wechat-adapter.test.ts`
49
+ - `apps/gui/src/renderer/settings/sections/ImSection.tsx`
50
+ - `docs/spec/ship/S0091-built-in-qq-and-wechat-im-extensions.md`
51
+
52
+ ## Risks And Boundaries
53
+
54
+ - QQ event delivery also depends on the bot's platform-side event subscriptions and permissions. Scorel can connect to the gateway, but QQ may still omit events that are not enabled for the bot.
55
+ - WeChat callback receive requires a URL Tencent can reach. Local-only callback ports are useful for tunnels and tests, but are not publicly reachable by themselves.
56
+ - WeCom group robot webhook remains outbound-only by official product design; Scorel must not imply that it can receive user chat through that webhook.
57
+
58
+ ## References
59
+
60
+ - QQ Bot event delivery and WebSocket gateway: <https://bot.q.qq.com/wiki/develop/api-v2/dev-prepare/interface-framework/event-emit.html>
61
+ - QQ Bot gateway URL API: <https://bot.q.qq.com/wiki/develop/api-v2/openapi/wss/url_get.html>
62
+ - WeCom group robot outbound webhook: <https://developer.work.weixin.qq.com/document/path/91770>
63
+ - WeChat official account callback verification and normal messages: <https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html>, <https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html>
64
+
65
+ ## Status
66
+
67
+ Done.
@@ -0,0 +1,36 @@
1
+ # S0095: GUI IM Session List Refresh
2
+
3
+ ## Goal
4
+
5
+ Make GUI sidebars show IM sessions that are created in the background by Telegram, QQ, or WeChat inbound messages.
6
+
7
+ ## Scope
8
+
9
+ - Notify GUI when the local Host creates a session, including IM sessions created in the background.
10
+ - Refresh only the affected Project session list in response to that notification.
11
+ - Keep IM session storage and Project binding unchanged.
12
+ - Cover the selected/default IM workspace Project so background QQ/Telegram sessions appear without app restart.
13
+
14
+ ## Not In Scope
15
+
16
+ - Changing where session JSONL files are stored.
17
+ - Creating per-platform Projects.
18
+ - Relay or hosted notification push for remote devices.
19
+
20
+ ## Acceptance Criteria
21
+
22
+ - A GUI Project refreshes when the local Host reports a new session for that Project.
23
+ - The refresh does not clear the active transcript.
24
+ - Renderer test covers a background session appearing after initial load.
25
+
26
+ ## Test Requirements
27
+
28
+ ```bash
29
+ pnpm --filter @scorel/app-gui test -- src/renderer/app-session-preload.test.tsx
30
+ pnpm typecheck
31
+ pnpm test
32
+ ```
33
+
34
+ ## Status
35
+
36
+ Done.
@@ -0,0 +1,32 @@
1
+ # S0096: Glob Stable Order
2
+
3
+ ## Goal
4
+
5
+ Make the `Glob` tool return deterministic results across local macOS and Linux CI.
6
+
7
+ ## Scope
8
+
9
+ - Sort `Glob` file results by workspace-relative path before applying `head_limit` / `offset`.
10
+ - Preserve existing `Grep` behavior and pagination shape.
11
+
12
+ ## Not In Scope
13
+
14
+ - Changing ripgrep invocation.
15
+ - Changing Grep content/count ordering.
16
+
17
+ ## Acceptance Criteria
18
+
19
+ - `Glob` result limiting does not depend on filesystem or ripgrep output order.
20
+ - Existing coding tool tests pass on Linux and macOS.
21
+
22
+ ## Test Requirements
23
+
24
+ ```bash
25
+ pnpm --filter @scorel/core test -- src/tools/coding-tools.test.ts
26
+ pnpm typecheck
27
+ pnpm test
28
+ ```
29
+
30
+ ## Status
31
+
32
+ Done.
@@ -0,0 +1,61 @@
1
+ # S0097: RTK Token Saving Settings
2
+
3
+ ## Goal
4
+
5
+ Add an opt-in GUI setting that enables RTK-backed token saving for Scorel Bash tool execution without changing session replay, model prompts, or existing tool input contracts.
6
+
7
+ RTK here means Rust Token Killer: a CLI command rewriter/filter that compresses shell command output before it reaches the LLM context.
8
+
9
+ ## Scope
10
+
11
+ - Add device-scoped `[runtime] tokenSavingRtk = boolean` config.
12
+ - Add GUI Settings page `Token 节省` with an RTK enable toggle and status.
13
+ - When the user enables the setting, Host ensures the `rtk` binary is available:
14
+ - first detect `rtk` on PATH;
15
+ - if missing on macOS/Linux with Homebrew available, attempt `brew install rtk`;
16
+ - if install fails, keep the setting but report RTK as unavailable.
17
+ - Bash tool execution and RTK discovery use the user's default shell path (`options.defaultShell` where provided, then `SHELL`, then OS user shell, then `/bin/sh` fallback), not a hard-coded `/bin/bash`.
18
+ - Shell invocation preserves the command string and uses shell-compatible command flags (`-lc` for sh/bash/zsh-like shells, `-c` for csh/tcsh/fish-like shells).
19
+ - When enabled and available, the Bash tool asks RTK to rewrite the original command and executes the rewritten command, while preserving the original tool input contract and cwd semantics.
20
+ - Bash tool result details expose RTK application state plus estimated output/saved tokens for Scorel-owned UI/diagnostics.
21
+ - Runtime Settings summarizes RTK token savings from a maintained Scorel runtime stats file across projects on the current host.
22
+ - Chat transcript rendering continues to display the original Bash tool-call command, not the RTK rewritten execution command.
23
+ - Session JSONL and persistent events keep the same tool result shape; no prompt or input assembly contract changes.
24
+
25
+ ## Not In Scope
26
+
27
+ - Changing model message assembly.
28
+ - Compressing Read / Grep / Glob built-in tool results.
29
+ - Provider-specific token accounting.
30
+ - A per-session savings breakdown view.
31
+ - Global shell hook installation through `rtk init`.
32
+ - Silent install at app startup.
33
+
34
+ ## Acceptance Criteria
35
+
36
+ - `tokenSavingRtk = false` keeps Bash behavior equivalent to the current path.
37
+ - `tokenSavingRtk = true` uses RTK rewrite for Bash command execution when RTK is available, without changing the tool-call input command string.
38
+ - Tool results sent back into model context include only the user-visible content, not RTK execution metadata or rewritten command details.
39
+ - GUI Bash tool blocks display the original tool-call command even when RTK rewrites the command at execution time.
40
+ - RTK detection and first-enable install checks run in the same default shell environment as command execution, so zsh-configured PATH entries are visible.
41
+ - Runtime creation resolves the RTK executable whenever `tokenSavingRtk` is enabled, so saved settings affect actual Bash tool execution, not only the Settings UI.
42
+ - Runtime Settings token totals come from Scorel-maintained RTK stats updated when Scorel persists tool results, so other agents' RTK usage in the same project is not counted.
43
+ - GUI Settings can enable/disable RTK token saving and shows available/unavailable status.
44
+ - Config parsing rejects unknown `[runtime]` keys.
45
+ - RTK install is only attempted after the user enables the setting.
46
+ - Existing tests, typecheck, and full test suite pass.
47
+
48
+ ## Test Requirements
49
+
50
+ ```bash
51
+ pnpm --filter @scorel/core test -- src/config/config.test.ts src/tools/coding-tools.test.ts
52
+ pnpm --filter @scorel/daemon test -- src/embedded/embedded.test.ts
53
+ pnpm --filter @scorel/app-gui test -- src/renderer/gui-shell.test.tsx
54
+ pnpm verify:m9-gui # service-level local/relay plus Electron CDP settings + prompt smoke
55
+ pnpm typecheck
56
+ pnpm test
57
+ ```
58
+
59
+ ## Status
60
+
61
+ Done.
@@ -0,0 +1,96 @@
1
+ # S0098: Local Daemon Singleton And Unified State
2
+
3
+ ## Goal
4
+
5
+ Make the local Scorel Host a single user-level daemon with one local state root, so CLI, GUI, WebUI, IM, and Relay clients attach to the same Project registry, Session JSONL, runtime stats, and config.
6
+
7
+ ## Scope
8
+
9
+ - Use `~/.scorel` as the only local Host state root:
10
+ - `daemon.json`
11
+ - `projects.json`
12
+ - `sessions/*.jsonl`
13
+ - `runtime-stats.json`
14
+ - `config.toml`
15
+ - `gui-store.json`
16
+ - Remove GUI-local Host state ownership:
17
+ - no new `~/.scorel/gui/projects.json`;
18
+ - no new `~/.scorel/gui/sessions`;
19
+ - GUI still has GUI UI state, but it lives at `~/.scorel/gui-store.json`.
20
+ - Add `scorel host start`, a background daemon start path that starts the singleton local daemon without tying daemon lifetime to the CLI process that launched it.
21
+ - Make local GUI attach to the singleton daemon when available.
22
+ - Make local GUI start the singleton daemon in the background when no live daemon exists, then attach to it.
23
+ - Make `scorel up` ensure the singleton daemon is running, register the current cwd as a Project, and launch/serve UI without owning daemon lifetime.
24
+ - Keep foreground `scorel host serve` for debugging; Ctrl-C on foreground serve still stops that foreground process.
25
+ - Daemon lifecycle:
26
+ - daemon is not killed when one client exits;
27
+ - explicit `scorel host stop` stops it;
28
+ - if no IM extensions are active, daemon idle-shuts down after no connected clients and no active work for the configured timeout;
29
+ - if any IM extension is active, daemon remains alive until explicit stop.
30
+
31
+ ## Not In Scope
32
+
33
+ - Migrating old `~/.scorel/gui/projects.json` or `~/.scorel/gui/sessions` into the unified state. Scorel is pre-1.0; users may remove old GUI-local files manually if needed.
34
+ - System LaunchAgent/login-item installation.
35
+ - Multi-user or system-wide daemon.
36
+ - Remote daemon lifecycle changes.
37
+ - Per-session RTK stats breakdown UI.
38
+ - Restart-on-crash supervisor.
39
+
40
+ ## Acceptance Criteria
41
+
42
+ - GUI local Projects and Sessions are created under `~/.scorel/projects.json` and `~/.scorel/sessions`, not under `~/.scorel/gui`.
43
+ - `gui-store.json` is stored at `~/.scorel/gui-store.json`.
44
+ - `scorel host start` starts or reuses a background singleton daemon and returns without waiting for daemon shutdown.
45
+ - `scorel up` reuses an existing running daemon instead of spawning a child daemon that dies with `scorel up`.
46
+ - Starting GUI when a daemon is alive attaches to it and does not start a second Host writer.
47
+ - Starting GUI when no daemon is alive starts the background singleton daemon, then attaches to it.
48
+ - If no clients remain, no work is active, and no IM extension is active, daemon exits after its idle timeout.
49
+ - If an IM extension is active, daemon does not idle-exit solely because there are no GUI/CLI clients.
50
+ - Model/tool messages remain unchanged: daemon lifecycle and state unification do not alter tool call args or provider tool result context.
51
+
52
+ ## Test Requirements
53
+
54
+ ```bash
55
+ pnpm --filter @scorel/app-cli test -- src/daemon-cli.test.ts src/up-cli.test.ts
56
+ pnpm --filter @scorel/app-gui test -- src/main/local-host.test.ts
57
+ pnpm --filter @scorel/daemon test -- src/embedded/embedded.test.ts
58
+ pnpm verify:m9-gui
59
+ pnpm typecheck
60
+ pnpm test
61
+ ```
62
+
63
+ Manual/E2E:
64
+
65
+ - Start GUI from a clean temp HOME through Electron CDP.
66
+ - Verify local Project registration creates `~/.scorel/projects.json`.
67
+ - Verify local Session creation creates `~/.scorel/sessions/*.jsonl`.
68
+ - Verify no `~/.scorel/gui/projects.json` or `~/.scorel/gui/sessions` is created.
69
+ - Verify GUI start from a clean HOME creates `~/.scorel/daemon.json` and attaches to that daemon.
70
+ - Verify `scorel host start` returns while `scorel host status` still reports a running daemon.
71
+ - Verify `scorel up` exit does not stop the singleton daemon it started.
72
+ - Verify short idle timeout stops a daemon with no clients and no active IM.
73
+ - Verify active IM prevents idle shutdown.
74
+
75
+ ## Impacted Files
76
+
77
+ - `apps/cli/src/daemon-cli.ts`
78
+ - `apps/cli/src/up-cli.ts`
79
+ - `apps/cli/src/index.ts`
80
+ - `apps/gui/src/main.ts`
81
+ - `apps/gui/src/main/local-host.ts`
82
+ - `packages/daemon/src/index.ts`
83
+ - `scripts/verify-m9-gui-cdp-e2e.ts`
84
+ - `docs/SHIP.md`
85
+ - `docs/ROADMAP.md`
86
+
87
+ ## Risks And Boundaries
88
+
89
+ - Background daemon process management must not leave stale `daemon.json` as a false-positive running daemon.
90
+ - The singleton daemon must remain the only local writer for Project and Session files.
91
+ - Electron GUI starts the daemon through the CLI entrypoint; packaged builds must provide `SCOREL_CLI_ENTRYPOINT` when the source tree is unavailable.
92
+ - Old GUI-local state under `~/.scorel/gui` is intentionally not migrated in this spec.
93
+
94
+ ## Status
95
+
96
+ Done.