@chanlerdev/scorel 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/dist/index.js +306 -137
- package/dist/index.js.map +3 -3
- package/docs/CHANGELOG.md +32 -0
- package/docs/ROADMAP.md +6 -0
- package/docs/SHIP.md +1 -0
- package/docs/spec/events.md +17 -1
- package/docs/spec/session.md +12 -0
- package/docs/spec/ship/S0106-snip-context-control.md +113 -0
- package/docs/spec/ship/S0107-system-reminder-unification.md +112 -0
- package/docs/spec/ship/S0108-gui-bundled-cli-runtime.md +108 -0
- package/package.json +1 -1
package/docs/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.0.6 - 2026-06-23
|
|
6
|
+
|
|
7
|
+
### Highlights
|
|
8
|
+
|
|
9
|
+
- Packed GUI now bundles its own CLI runtime, enabling fully self-contained local Host startup without Node.js or global CLI.
|
|
10
|
+
- Agents can now use the `snip` tool to hide completed user turns from future context — reducing token waste and keeping conversations focused.
|
|
11
|
+
|
|
12
|
+
### Changes
|
|
13
|
+
|
|
14
|
+
- GUI now bundles its own CLI runtime for fully self-contained Host startup (no Node.js or global CLI required).
|
|
15
|
+
- Added `snip` tool that agents can call to mark a completed user turn as hidden from future LLM context.
|
|
16
|
+
- Protocol version incremented to 4 with new `context_control` event and `hide_user_turn` operation.
|
|
17
|
+
- UI (GUI and WebUI) now hides model-only text blocks (like `snip` reminders) from the visible transcript.
|
|
18
|
+
- Provider adapters now pass each tool's own parameter schema instead of hardcoding tool-specific parameters.
|
|
19
|
+
|
|
20
|
+
### Fixes
|
|
21
|
+
|
|
22
|
+
- Increased Host startup timeout from 10s to 30s to prevent timeouts in slower development environments.
|
|
23
|
+
|
|
24
|
+
### Breaking Changes
|
|
25
|
+
|
|
26
|
+
- Protocol version incremented from 3 to 4; requires protocol-version-aware clients.
|
|
27
|
+
- Packaged GUI no longer accepts `SCOREL_CLI_ENTRYPOINT` or `SCOREL_NODE_PATH` environment variables.
|
|
28
|
+
|
|
29
|
+
### Verification
|
|
30
|
+
|
|
31
|
+
- Unit and release tests verify bundled CLI usage in packaged GUI, snip tool end-to-end behavior, hidden spans in context builds, protocol version bump, and UI projector rendering.
|
|
32
|
+
|
|
33
|
+
### Internal
|
|
34
|
+
|
|
35
|
+
- Added planned spec S0107 for system reminder unification (documentation only).
|
|
36
|
+
|
|
5
37
|
## 0.0.5 - 2026-06-19
|
|
6
38
|
|
|
7
39
|
### Highlights
|
package/docs/ROADMAP.md
CHANGED
|
@@ -600,6 +600,9 @@ M5 WebUI 的正式产品方向记录在 [`S0030`](spec/ship/S0030-webui-product-
|
|
|
600
600
|
| M9.F1.24 | [`S0102`](spec/ship/S0102-device-only-config.md) | Config 彻底收敛为设备级唯一配置,移除 Project config 运行时语义 | Done |
|
|
601
601
|
| M9.F1.25 | [`S0103`](spec/ship/S0103-daemon-lifecycle-and-settings-resilience.md) | Daemon 生命周期按入口区分,并修复 GUI Settings remote 切换黑屏风险 | Done |
|
|
602
602
|
| M9.F1.26 | [`S0105`](spec/ship/S0105-cli-update-and-gui-release.md) | CLI 命令面统一补齐、NPM 手动/自动更新、GUI release 打包和增量更新框架 | Done |
|
|
603
|
+
| M9.F1.27 | [`S0106`](spec/ship/S0106-snip-context-control.md) | `context_control` 持久事件和 `snip` tool,让 agent 隐藏已完成 user turn 的未来 LLM context 投影 | Done |
|
|
604
|
+
| M9.F1.28 | [`S0107`](spec/ship/S0107-system-reminder-unification.md) | 统一 system reminder 的持久化、构造、LLM 投影和 UI visibility 语义 | Planned |
|
|
605
|
+
| M9.F1.29 | [`S0108`](spec/ship/S0108-gui-bundled-cli-runtime.md) | GUI release 内置同版本 CLI runtime,packaged GUI 用 bundle 内可执行文件启动本地 Host | Active |
|
|
603
606
|
|
|
604
607
|
**Not in M9 Follow-up**:
|
|
605
608
|
|
|
@@ -779,6 +782,9 @@ HTTP adapter 必须映射已有 Host use cases,不复制领域逻辑。
|
|
|
779
782
|
| [`S0103`](spec/ship/S0103-daemon-lifecycle-and-settings-resilience.md) | Daemon lifecycle and Settings resilience | Done |
|
|
780
783
|
| [`S0104`](spec/ship/S0104-tool-result-artifacts.md) | Tool result artifacts for oversized Bash output | Done |
|
|
781
784
|
| [`S0105`](spec/ship/S0105-cli-update-and-gui-release.md) | CLI update and GUI release | Done |
|
|
785
|
+
| [`S0106`](spec/ship/S0106-snip-context-control.md) | Snip context control | Done |
|
|
786
|
+
| [`S0107`](spec/ship/S0107-system-reminder-unification.md) | System reminder unification | Planned |
|
|
787
|
+
| [`S0108`](spec/ship/S0108-gui-bundled-cli-runtime.md) | GUI bundled CLI runtime | Active |
|
|
782
788
|
|
|
783
789
|
---
|
|
784
790
|
|
package/docs/SHIP.md
CHANGED
|
@@ -209,6 +209,7 @@ pnpm release patch --no-generate-notes
|
|
|
209
209
|
- 执行 WebUI production build
|
|
210
210
|
- 执行 GUI production build
|
|
211
211
|
- 构建 public `scorel` npm package
|
|
212
|
+
- 将同版本 public `scorel` CLI runtime vendored 进 GUI app bundle,供 packaged GUI 用 bundle 内绝对路径启动本机 Host
|
|
212
213
|
- 执行 `npm pack` 安装烟雾测试
|
|
213
214
|
- bump 所有 package version
|
|
214
215
|
- 构建 GUI macOS dmg / zip release assets,并生成 `latest-mac.yml` 与 blockmap 增量更新 metadata
|
package/docs/spec/events.md
CHANGED
|
@@ -143,7 +143,22 @@ interface CompactEvent extends PersistentEventBase {
|
|
|
143
143
|
|
|
144
144
|
构建 context 时:从 leaf 往 root 走,遇到 CompactEvent → 注入 summary,停止继续向上。旧事件仍在 JSONL 中(可查阅),但不进入 LLM context。
|
|
145
145
|
|
|
146
|
-
### 4.5 `
|
|
146
|
+
### 4.5 `context_control` — 控制上下文投影
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
interface ContextControlEvent extends PersistentEventBase {
|
|
150
|
+
type: "context_control";
|
|
151
|
+
operation: "hide_user_turn";
|
|
152
|
+
anchorUserEventId: EventId;
|
|
153
|
+
throughEventId: EventId;
|
|
154
|
+
actor: "agent" | "user" | "system";
|
|
155
|
+
reason?: string;
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`hide_user_turn` 隐藏一个已完成 user turn span:从 `anchorUserEventId` 开始,到同一路径下一条 `user_message` 前一条事件结束。它只影响未来 `buildContext()` 的 LLM 输入投影;原始 JSONL 事件仍保留并可用于 UI、审计和 resync。
|
|
160
|
+
|
|
161
|
+
### 4.6 `channel_inject` — 外部来源元数据
|
|
147
162
|
|
|
148
163
|
```typescript
|
|
149
164
|
interface ChannelInjectEvent extends PersistentEventBase {
|
|
@@ -340,6 +355,7 @@ type LlmAction =
|
|
|
340
355
|
| `rewind` | `skip` | "回退到此处" 标记 |
|
|
341
356
|
| `branch` | `skip` | "切换分支" 标记 |
|
|
342
357
|
| `compact` | `barrier` — 注入 summary,停止向上遍历 | "已压缩" 折叠块 |
|
|
358
|
+
| `context_control` | `filter` — 从未来 LLM context 中排除指定 user turn span | "已 snip" 标记 |
|
|
343
359
|
| `channel_inject` | `skip` | 来源 badge "from Telegram" |
|
|
344
360
|
| `session_info` | `skip` | "模型切换为 X" 通知 |
|
|
345
361
|
| `custom` | `skip` | Extension 自定义 |
|
package/docs/spec/session.md
CHANGED
|
@@ -345,6 +345,17 @@ if (estimateTokens(compactCandidates) > threshold) {
|
|
|
345
345
|
- 在 compact 点之前分叉的其他分支不受影响
|
|
346
346
|
- 旧事件仍在 JSONL 中,可供历史浏览
|
|
347
347
|
|
|
348
|
+
### 6.3 Snip Context Control
|
|
349
|
+
|
|
350
|
+
`snip` 不删除 JSONL。它 append `context_control operation="hide_user_turn"`,让后续 `buildContext()` 在 active path 上过滤一个已完成 user turn span。
|
|
351
|
+
|
|
352
|
+
安全边界:
|
|
353
|
+
|
|
354
|
+
- 目标必须是当前 active path 上的 `user_message`。
|
|
355
|
+
- 目标之后必须存在下一条 `user_message`,因此不能 snip 当前正在执行的 turn。
|
|
356
|
+
- 隐藏范围是目标 `user_message` 到下一条 `user_message` 前一条事件,保证 tool_call / tool_result 成对隐藏,不留下孤儿工具结果。
|
|
357
|
+
- 原始事件仍保留在 JSONL 中,UI 和审计可以继续查看。
|
|
358
|
+
|
|
348
359
|
---
|
|
349
360
|
|
|
350
361
|
## 7. 两层消息在本模块的落点
|
|
@@ -355,6 +366,7 @@ if (estimateTokens(compactCandidates) > threshold) {
|
|
|
355
366
|
- buildContext 通用遍历时调用每个 event 的 handler,不 hardcode 任何类型
|
|
356
367
|
- `rewind` / `branch` / `channel_inject` / `session_info` / `custom` → `skip`(不进入 LLM)
|
|
357
368
|
- `compact` → `barrier`(注入 summary,停止向上)
|
|
369
|
+
- `context_control` → `filter`(从未来 LLM context 排除指定 user turn span)
|
|
358
370
|
- `message`(meta.source = "steer")→ `merge_prev`(合入前一条 tool_result 的 `<system-reminder>`)
|
|
359
371
|
|
|
360
372
|
换言之,应用层能玩的花样很多,LLM 始终只看到 handler 声明要暴露的内容。
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# S0106: Snip Context Control
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Let long-running sessions remove a completed user turn from future model context without deleting the underlying session record.
|
|
6
|
+
|
|
7
|
+
The business value is context hygiene: when an earlier user turn led the agent down a noisy or obsolete path, the user or agent can mark that whole turn as no longer relevant so future model calls stop paying attention to it. The original JSONL remains the evidence chain for UI, audit, resync, and debugging.
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
### Stable User Turn References
|
|
12
|
+
|
|
13
|
+
Every persisted `user_message` already has a durable `EventId`. S0106 treats that event id as the source of truth for snip targeting. The model-facing id is a stable short alias derived from the real `EventId`; the alias resolves back to the real event id but is not a storage key.
|
|
14
|
+
|
|
15
|
+
When the Host creates a `user_message`, it appends a model-only text block to that same persisted message:
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
<system-reminder>
|
|
19
|
+
snip.userMessageId: u_...
|
|
20
|
+
</system-reminder>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The block is persisted with the message so future `buildContext()` replays do not rewrite earlier user messages and do not invalidate prompt-cache prefixes. UI projectors hide model-only text blocks from the visible transcript.
|
|
24
|
+
|
|
25
|
+
### Context Control Event
|
|
26
|
+
|
|
27
|
+
Add a persistent control event:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
type ContextControlEvent = {
|
|
31
|
+
type: "context_control";
|
|
32
|
+
operation: "hide_user_turn";
|
|
33
|
+
anchorUserEventId: EventId;
|
|
34
|
+
throughEventId: EventId;
|
|
35
|
+
actor: "agent" | "user" | "system";
|
|
36
|
+
reason?: string;
|
|
37
|
+
};
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The event is append-only and does not participate in the conversation tree. `buildContext()` folds these events as control state and filters the active path before producing LLM messages.
|
|
41
|
+
|
|
42
|
+
`hide_user_turn` semantics:
|
|
43
|
+
|
|
44
|
+
- `anchorUserEventId` must identify a `user_message` on the active session path.
|
|
45
|
+
- The hidden span starts at that user event.
|
|
46
|
+
- The hidden span ends at the event before the next `user_message` on the same path, or the active leaf when there is no later user message.
|
|
47
|
+
- The resolved end is stored as `throughEventId`.
|
|
48
|
+
- The span is hidden from future `buildContext()` calls after the control event is appended.
|
|
49
|
+
- Original events stay in JSONL and continue to be visible through session replay and UI projection.
|
|
50
|
+
|
|
51
|
+
### `snip` Tool
|
|
52
|
+
|
|
53
|
+
Expose a lazily available `Snip` runtime tool that lets the agent request hiding a completed user turn from future context. The tool accepts:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{ "userMessageId": "u_...", "reason": "optional short reason" }
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The Host validates the request, resolves the model-visible short alias to a target span, appends a `context_control` event, and returns a tool result describing what changed. The tool result is still part of the current turn; the hidden span disappears on the next context build.
|
|
60
|
+
|
|
61
|
+
The tool is session-context control, not a generic coding tool. It must be registered by the Host with access to the current lane, not by `createCodingTools()`.
|
|
62
|
+
|
|
63
|
+
Tool parameter schemas are owned by `AgentTool` definitions. Provider adapters must pass through the tool's declared schema instead of hard-coding behavior for specific tool names such as `snip`.
|
|
64
|
+
|
|
65
|
+
## Not In Scope
|
|
66
|
+
|
|
67
|
+
- Physically deleting, truncating, or rewriting historical JSONL.
|
|
68
|
+
- Arbitrary single-message deletion.
|
|
69
|
+
- Snipping across branches.
|
|
70
|
+
- User-facing GUI controls for browsing snipped spans.
|
|
71
|
+
- A CLI slash command.
|
|
72
|
+
- Automatic snip heuristics.
|
|
73
|
+
- Reusing `compact` for snip. Compact summarizes old context; snip excludes a specific user turn.
|
|
74
|
+
|
|
75
|
+
## Acceptance Criteria
|
|
76
|
+
|
|
77
|
+
- `PersistentEvent` includes `context_control`.
|
|
78
|
+
- Session replay folds `hide_user_turn` events into control state.
|
|
79
|
+
- `buildContext()` excludes the hidden user-turn span from future LLM context.
|
|
80
|
+
- The excluded span can include assistant tool calls and tool results without leaving orphan tool results in context.
|
|
81
|
+
- JSONL remains append-only; original user/assistant/tool events remain loadable after snip.
|
|
82
|
+
- Repeated snip of the same target is idempotent enough to keep context stable.
|
|
83
|
+
- Invalid targets fail as tool errors and do not append control events.
|
|
84
|
+
- `snip` is registered by the Host and can append `context_control` from a real runtime tool call path.
|
|
85
|
+
- The model can discover snippable user turn ids from its normal context projection; tests must not rely on out-of-band `send_message` response data.
|
|
86
|
+
- User turn ids are persisted at user-message creation time, so replaying the same turn in later provider calls does not change that message and preserves prompt-cache stability.
|
|
87
|
+
- Model-only short id blocks are hidden from WebUI and GUI transcript projection.
|
|
88
|
+
- The `snip` `AgentTool` schema exposes `userMessageId` and optional `reason`, and provider adapters preserve tool-owned schemas without name-specific branches.
|
|
89
|
+
- Full validation passes with `pnpm typecheck && pnpm test`.
|
|
90
|
+
|
|
91
|
+
## Testing Requirements
|
|
92
|
+
|
|
93
|
+
- Core session tests for context-control parsing, replay, and context filtering.
|
|
94
|
+
- Protocol tests for exhaustive event handling.
|
|
95
|
+
- Daemon embedded test proving `snip` appends `context_control` and the next provider call no longer receives the hidden turn.
|
|
96
|
+
|
|
97
|
+
## Impacted Files
|
|
98
|
+
|
|
99
|
+
- `packages/protocol/src/events.ts`
|
|
100
|
+
- `packages/protocol/src/index.test.ts`
|
|
101
|
+
- `packages/core/src/session/index.ts`
|
|
102
|
+
- `packages/core/src/session/session.test.ts`
|
|
103
|
+
- `packages/core/src/tools/index.ts`
|
|
104
|
+
- `packages/daemon/src/index.ts`
|
|
105
|
+
- `packages/daemon/src/embedded/embedded.test.ts`
|
|
106
|
+
- `docs/ROADMAP.md`
|
|
107
|
+
- `README.md`
|
|
108
|
+
|
|
109
|
+
## Risks And Boundaries
|
|
110
|
+
|
|
111
|
+
- Hiding the wrong turn is worse than compacting too much. Target resolution must use real `EventId`s and reject non-user targets.
|
|
112
|
+
- Tool-call/tool-result pairing can be broken by arbitrary deletion. This spec only hides full user-turn spans.
|
|
113
|
+
- Snip is context projection, not evidence deletion. UI and session replay must still show the original events unless a later explicit product decision adds a separate display filter.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# S0107: System Reminder Unification
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Unify how Scorel represents, persists, projects, and displays system reminders.
|
|
6
|
+
|
|
7
|
+
The business value is prompt and transcript hygiene: runtime guidance should reach the model through one stable contract, without ad-hoc `<system-reminder>` string construction scattered across daemon, session replay, tool-result merge paths, or future context-control features. UI should consistently hide or display reminder evidence based on explicit visibility, not by parsing model-facing text.
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
### Reminder Source Model
|
|
12
|
+
|
|
13
|
+
Define one internal reminder representation for model-facing non-user guidance.
|
|
14
|
+
|
|
15
|
+
Current sources include:
|
|
16
|
+
|
|
17
|
+
- `harness_item` events such as memory, channel context, skill listing, skill delta, and steer.
|
|
18
|
+
- Compact summary messages.
|
|
19
|
+
- Model-only metadata attached to a specific `user_message`, such as `snip.userMessageId`.
|
|
20
|
+
- Future runtime guidance that should be visible to the model but not necessarily displayed as transcript text.
|
|
21
|
+
|
|
22
|
+
The new contract must preserve the existing product distinction:
|
|
23
|
+
|
|
24
|
+
- Some reminders are standalone session events (`harness_item`).
|
|
25
|
+
- Some reminders are attached to a specific message to preserve prompt-cache prefix stability.
|
|
26
|
+
- Some reminders can merge into a previous `tool_result`.
|
|
27
|
+
|
|
28
|
+
### Single Renderer
|
|
29
|
+
|
|
30
|
+
Move `<system-reminder>` wrapping behind a single public core helper or equivalent abstraction. Callers should provide reminder content and placement intent, not hand-write:
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
<system-reminder>
|
|
34
|
+
...
|
|
35
|
+
</system-reminder>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The renderer must keep the existing prompt contract:
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
<system-reminder>
|
|
42
|
+
content
|
|
43
|
+
</system-reminder>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Any future format change must happen in one place.
|
|
47
|
+
|
|
48
|
+
### Visibility And Projection
|
|
49
|
+
|
|
50
|
+
Clarify and consolidate visibility semantics:
|
|
51
|
+
|
|
52
|
+
- `harness_item.visibility` controls whether the harness event is displayed as transcript evidence.
|
|
53
|
+
- Message-level model-only blocks are included in LLM context but hidden from WebUI and GUI transcript projection.
|
|
54
|
+
- Display projectors must not parse `<system-reminder>` text to decide visibility.
|
|
55
|
+
- Provider adapters should receive already-rendered model-facing text or a normalized reminder block from core, not duplicate reminder formatting.
|
|
56
|
+
|
|
57
|
+
### Prompt Cache Stability
|
|
58
|
+
|
|
59
|
+
Reminder placement must not rewrite older model context on later turns.
|
|
60
|
+
|
|
61
|
+
For reminders attached to a specific persisted message, the model-facing block must be created when that message is persisted. Later `buildContext()` calls may clone or filter it, but must not mutate historical messages based on later session state.
|
|
62
|
+
|
|
63
|
+
## Not In Scope
|
|
64
|
+
|
|
65
|
+
- Changing snip semantics from S0106.
|
|
66
|
+
- Replacing `harness_item` with a new event type unless the implementation proves the existing event cannot express the contract.
|
|
67
|
+
- Changing provider-level system prompt assembly.
|
|
68
|
+
- UI controls for browsing hidden reminders.
|
|
69
|
+
- Backfilling or migrating old session JSONL files.
|
|
70
|
+
- Renaming `<system-reminder>` in the model-facing prompt.
|
|
71
|
+
|
|
72
|
+
## Acceptance Criteria
|
|
73
|
+
|
|
74
|
+
- No daemon or feature code hand-writes `<system-reminder>` strings.
|
|
75
|
+
- `buildContext()` uses the shared reminder renderer for `harness_item` and compact summaries.
|
|
76
|
+
- Snip's model-only user-message id block uses the shared reminder renderer or normalized reminder block.
|
|
77
|
+
- WebUI and GUI hide model-only message blocks without parsing reminder text.
|
|
78
|
+
- Existing harness visibility behavior stays intact:
|
|
79
|
+
- hidden harness items do not render as visible turns;
|
|
80
|
+
- display harness items still render as lightweight transcript evidence.
|
|
81
|
+
- Prompt-cache stability is preserved for message-attached reminders: replaying the same persisted user message in later provider calls produces the same content.
|
|
82
|
+
- Provider adapters do not own system-reminder formatting rules.
|
|
83
|
+
- `pnpm typecheck && pnpm test` passes.
|
|
84
|
+
|
|
85
|
+
## Testing Requirements
|
|
86
|
+
|
|
87
|
+
- Core session tests for the shared reminder renderer and `buildContext()` conversion.
|
|
88
|
+
- Daemon embedded test proving snip's message-attached reminder remains stable across later turns.
|
|
89
|
+
- WebUI and GUI projector tests proving model-only blocks are hidden while display harness items remain visible.
|
|
90
|
+
- Regression test or static check that common runtime paths no longer hand-write `<system-reminder>` literals outside the shared renderer and tests/docs.
|
|
91
|
+
|
|
92
|
+
## Impacted Files
|
|
93
|
+
|
|
94
|
+
- `packages/core/src/session/index.ts`
|
|
95
|
+
- `packages/core/src/session/session.test.ts`
|
|
96
|
+
- `packages/core/src/tools/index.ts` or a new core reminder module
|
|
97
|
+
- `packages/daemon/src/index.ts`
|
|
98
|
+
- `packages/daemon/src/embedded/embedded.test.ts`
|
|
99
|
+
- `apps/webui/lib/events/projector.ts`
|
|
100
|
+
- `apps/webui/lib/events/projector.test.ts`
|
|
101
|
+
- `apps/gui/src/renderer/chatbox/projector.ts`
|
|
102
|
+
- `apps/gui/src/renderer/chatbox/projector.test.ts`
|
|
103
|
+
- `docs/spec/events.md`
|
|
104
|
+
- `docs/spec/session.md`
|
|
105
|
+
- `README.md`
|
|
106
|
+
|
|
107
|
+
## Risks And Boundaries
|
|
108
|
+
|
|
109
|
+
- Reminder placement affects prompt-cache behavior. A cleanup that moves snip ids from persisted user messages into later dynamic `buildContext()` injection would regress S0106.
|
|
110
|
+
- Tool-result merge behavior is easy to break. The implementation must preserve valid assistant tool-call / tool-result replay.
|
|
111
|
+
- UI should use explicit visibility metadata, not text parsing. Parsing `<system-reminder>` in UI would make display behavior depend on prompt formatting.
|
|
112
|
+
- A broader content-block redesign may be attractive, but S0107 should stay focused on unifying reminder construction and projection.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# S0108: GUI Bundled CLI Runtime
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Make the packaged desktop GUI self-contained for local Host startup.
|
|
6
|
+
|
|
7
|
+
The business value is that installing `Scorel.app` is enough to use the local GUI path. Users must not need a terminal-launched environment, a global `node`, a global `scorel`, or the source repository layout for GUI Settings and local Project workflows to work.
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
- Vendor the same-version public `scorel` CLI release artifact into the macOS GUI bundle.
|
|
12
|
+
- Make packaged GUI startup resolve the local Host launcher from the app bundle, not from `PATH`.
|
|
13
|
+
- Keep development startup convenient through the existing source CLI path.
|
|
14
|
+
- Remove the packaged-build requirement for `SCOREL_CLI_ENTRYPOINT`.
|
|
15
|
+
- Add release/package tests proving the GUI bundle contract references `Contents/Resources/scorel`.
|
|
16
|
+
- Add a regression test for Finder/Dock-like startup where `PATH` does not contain Node.
|
|
17
|
+
|
|
18
|
+
## Not In Scope
|
|
19
|
+
|
|
20
|
+
- Publishing the GUI through npm.
|
|
21
|
+
- Replacing the CLI with a separate `scorel-host` binary.
|
|
22
|
+
- Changing `scorel host start` daemon lifecycle semantics.
|
|
23
|
+
- Changing the public CLI command surface.
|
|
24
|
+
- Adding LaunchAgent/login-item restart supervision.
|
|
25
|
+
- Supporting Windows/Linux GUI packaging in this spec.
|
|
26
|
+
|
|
27
|
+
## Contract
|
|
28
|
+
|
|
29
|
+
The packaged macOS app contains:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
Scorel.app
|
|
33
|
+
Contents/Resources/
|
|
34
|
+
scorel # launcher script
|
|
35
|
+
scorel.js # bundled CLI artifact
|
|
36
|
+
app.asar
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Packaged GUI starts the local singleton Host with the bundle-owned executable:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
Contents/Resources/scorel host start --port 0 --cwd <bootstrap-project> --idle-timeout-ms <ms> --no-relay
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Development GUI may continue to use the source entrypoint:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
node --import tsx apps/cli/src/index.ts host start ...
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The packaged path must not depend on:
|
|
52
|
+
|
|
53
|
+
- `node` being discoverable in `PATH`;
|
|
54
|
+
- `scorel` being globally installed;
|
|
55
|
+
- `SCOREL_NODE_PATH`;
|
|
56
|
+
- `SCOREL_CLI_ENTRYPOINT`;
|
|
57
|
+
- `apps/cli/src/index.ts` existing inside the app bundle.
|
|
58
|
+
|
|
59
|
+
## Acceptance Criteria
|
|
60
|
+
|
|
61
|
+
- `apps/gui` Electron Builder config includes a generated `.runtime` directory containing `scorel` and `scorel.js` in `Contents/Resources`.
|
|
62
|
+
- The bundled `scorel` launcher uses the app's own Electron executable with `ELECTRON_RUN_AS_NODE=1` to run the built CLI artifact as `scorel.js`.
|
|
63
|
+
- Packaged GUI resolves the Host launcher to `process.resourcesPath/scorel`.
|
|
64
|
+
- Packaged GUI spawns `scorel` directly and does not prepend `node`, `tsx`, or source entrypoint args.
|
|
65
|
+
- Development GUI still supports `SCOREL_CLI_ENTRYPOINT` and `SCOREL_NODE_PATH` for local debugging.
|
|
66
|
+
- A unit test proves packaged Host startup uses the bundle CLI even with an empty/minimal `PATH`.
|
|
67
|
+
- Release tests prove the GUI package depends on the built CLI artifact and bundle destination.
|
|
68
|
+
- `docs/SHIP.md`, `docs/ROADMAP.md`, and `README.md` describe that GUI release assets include a bundled same-version CLI runtime.
|
|
69
|
+
|
|
70
|
+
## Test Requirements
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pnpm --filter @scorel/app-gui test -- src/main/host-launcher.test.ts
|
|
74
|
+
node --test scripts/release-gui.test.mjs
|
|
75
|
+
pnpm --filter @scorel/app-gui build
|
|
76
|
+
pnpm build:package
|
|
77
|
+
git diff --check
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Full release readiness still uses:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pnpm typecheck && pnpm test
|
|
84
|
+
pnpm release patch --dry-run
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Impacted Files
|
|
88
|
+
|
|
89
|
+
- `apps/gui/package.json`
|
|
90
|
+
- `apps/gui/src/main.ts`
|
|
91
|
+
- `apps/gui/src/main/host-launcher.ts`
|
|
92
|
+
- `apps/gui/src/main/host-launcher.test.ts`
|
|
93
|
+
- `apps/gui/scripts/build-runtime.mjs`
|
|
94
|
+
- `apps/gui/scripts/build-runtime.node-test.mjs`
|
|
95
|
+
- `scripts/release-gui.test.mjs`
|
|
96
|
+
- `docs/SHIP.md`
|
|
97
|
+
- `docs/ROADMAP.md`
|
|
98
|
+
- `README.md`
|
|
99
|
+
|
|
100
|
+
## Risks And Boundaries
|
|
101
|
+
|
|
102
|
+
- The vendored CLI must be built before `electron-builder` runs. Release flow already runs `pnpm build:package`; local `dist:mac` must do the same or fail clearly.
|
|
103
|
+
- The bundled CLI runs under Electron's Node mode. A fully native/self-contained CLI binary remains a possible future hardening step, but the packaged GUI no longer depends on user `PATH` for Node.
|
|
104
|
+
- The GUI must never call the user's global `scorel`, because that can drift from the GUI version and mutate the local Host contract.
|
|
105
|
+
|
|
106
|
+
## Status
|
|
107
|
+
|
|
108
|
+
Active.
|