@chanlerdev/scorel 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +110 -0
  2. package/dist/index.js +6675 -0
  3. package/dist/index.js.map +7 -0
  4. package/docs/CHANGELOG.md +12 -0
  5. package/docs/README.md +116 -0
  6. package/docs/ROADMAP.md +669 -0
  7. package/docs/SHIP.md +242 -0
  8. package/docs/spec/channels.md +156 -0
  9. package/docs/spec/client.md +326 -0
  10. package/docs/spec/daemon.md +408 -0
  11. package/docs/spec/events.md +423 -0
  12. package/docs/spec/extensions.md +255 -0
  13. package/docs/spec/relay.md +391 -0
  14. package/docs/spec/runtime.md +251 -0
  15. package/docs/spec/session.md +380 -0
  16. package/docs/spec/ship/S0001-docs-baseline.md +41 -0
  17. package/docs/spec/ship/S0002-package-skeleton.md +56 -0
  18. package/docs/spec/ship/S0003-protocol-contracts.md +49 -0
  19. package/docs/spec/ship/S0004-session-core.md +50 -0
  20. package/docs/spec/ship/S0005-runtime-loop.md +48 -0
  21. package/docs/spec/ship/S0006-embedded-daemon-client.md +51 -0
  22. package/docs/spec/ship/S0007-cli-alpha.md +49 -0
  23. package/docs/spec/ship/S0008-coding-tools.md +107 -0
  24. package/docs/spec/ship/S0009-code-discovery-tools.md +82 -0
  25. package/docs/spec/ship/S0010-todo-tool-and-cli.md +81 -0
  26. package/docs/spec/ship/S0011-coding-agent-alpha-smoke.md +110 -0
  27. package/docs/spec/ship/S0012-coding-tools-maturity.md +143 -0
  28. package/docs/spec/ship/S0013-local-daemon-protocol.md +57 -0
  29. package/docs/spec/ship/S0014-local-daemon-lifecycle.md +64 -0
  30. package/docs/spec/ship/S0015-local-attach-and-broadcast.md +58 -0
  31. package/docs/spec/ship/S0016-local-daemon-resync-smoke.md +60 -0
  32. package/docs/spec/ship/S0017-grep-files-output-mode.md +49 -0
  33. package/docs/spec/ship/S0018-daemon-entrypoint-smoke.md +48 -0
  34. package/docs/spec/ship/S0019-remote-transport-contract.md +59 -0
  35. package/docs/spec/ship/S0020-remote-websocket-server.md +56 -0
  36. package/docs/spec/ship/S0021-remote-websocket-client-transport.md +55 -0
  37. package/docs/spec/ship/S0022-remote-daemon-cli-lifecycle.md +60 -0
  38. package/docs/spec/ship/S0023-remote-control-e2e-validation.md +66 -0
  39. package/docs/spec/ship/S0024-remote-attach-interactive-stream.md +49 -0
  40. package/docs/spec/ship/S0025-remote-attach-session-event-view.md +57 -0
  41. package/docs/spec/ship/S0026-attach-project-cache-and-dual-seq-reconnect.md +87 -0
  42. package/docs/spec/ship/S0027-session-diagnostics-log.md +77 -0
  43. package/docs/spec/ship/S0028-client-attach-diagnostics-log.md +70 -0
  44. package/docs/spec/ship/S0029-project-index-for-session-lookup.md +119 -0
  45. package/docs/spec/ship/S0030-webui-product-intent.md +73 -0
  46. package/docs/spec/ship/S0031-daemon-projectslug-rule.md +72 -0
  47. package/docs/spec/ship/S0032-daemon-protocol-completion.md +123 -0
  48. package/docs/spec/ship/S0033-webui-skeleton-routing.md +92 -0
  49. package/docs/spec/ship/S0034-webui-device-settings.md +121 -0
  50. package/docs/spec/ship/S0035-webui-device-handshake.md +83 -0
  51. package/docs/spec/ship/S0036-webui-project-session-sync.md +70 -0
  52. package/docs/spec/ship/S0037-webui-chatbox-v1.md +97 -0
  53. package/docs/spec/ship/S0038-webui-cancel-multiclient.md +65 -0
  54. package/docs/spec/ship/S0039-webui-e2e-newchat.md +74 -0
  55. package/docs/spec/ship/S0040-webui-codex-visual-tokens.md +227 -0
  56. package/docs/spec/ship/S0041-webui-markdown-and-tool-block.md +248 -0
  57. package/docs/spec/ship/S0042-webui-streaming-ux-autoscroll.md +130 -0
  58. package/docs/spec/ship/S0043-startup-ergonomics.md +278 -0
  59. package/docs/spec/ship/S0044-webui-chatbox-rebuild.md +556 -0
  60. package/docs/spec/ship/S0045-webui-card-sidebar-and-session-fixes.md +469 -0
  61. package/docs/spec/ship/S0046-webui-empty-composer-and-lazy-session.md +428 -0
  62. package/docs/spec/ship/S0047-webui-project-hover-newchat-and-dynamic-greeting.md +176 -0
  63. package/docs/spec/ship/S0048-device-level-host-project-registry.md +253 -0
  64. package/docs/spec/ship/S0049-webui-add-project-directory-browser.md +217 -0
  65. package/docs/spec/ship/S0050-instruction-snapshot-and-agents-assembly.md +338 -0
  66. package/docs/spec/ship/S0051-harness-item-and-system-reminder.md +190 -0
  67. package/docs/spec/ship/S0052-follow-up-queue-and-dual-loop.md +195 -0
  68. package/docs/spec/ship/S0053-skill-index-and-skill-tool.md +252 -0
  69. package/docs/spec/ship/S0054-webui-running-message-behavior.md +72 -0
  70. package/docs/spec/ship/S0055-webui-composer-acceptance-and-queue-strip.md +68 -0
  71. package/docs/spec/ship/S0056-relay-and-hosted-webui-contract.md +106 -0
  72. package/docs/spec/ship/S0057-relay-service-protocol-skeleton.md +161 -0
  73. package/docs/spec/ship/S0058-host-outbound-relay-and-pair-command.md +138 -0
  74. package/docs/spec/ship/S0059-relay-transport-and-hosted-webui-connector.md +140 -0
  75. package/docs/spec/ship/S0060-relay-hosted-webui-e2e-validation.md +132 -0
  76. package/docs/spec/ship/S0060-relay-hosted-webui-e2e-validation.verification.md +90 -0
  77. package/docs/spec/ship/S0061-hosted-defaults-and-cli-command-surface.md +208 -0
  78. package/docs/spec/ship/S0062-npm-package-and-release-workflow.md +166 -0
  79. package/docs/spec/tools.md +173 -0
  80. package/package.json +51 -0
@@ -0,0 +1,195 @@
1
+ # S0052 — Follow-Up Queue And Dual Loop Runtime
2
+
3
+ ## Goal
4
+
5
+ 实现 Scorel 的双 loop runtime 调度模型:
6
+
7
+ - outer loop 消费 follow-up queue,把排队输入变成下一条真实 `user_message`
8
+ - inner loop 执行当前 user turn,并消费 steer guidance
9
+ - 用 JSONL control events 持久化 queue state,避免 daemon 崩溃或 reconnect 后丢失 follow-up
10
+ - 保证 follow-up 消费时的 `parentId` 指向当前 final leaf,而不是排队时的中间 leaf
11
+
12
+ 本 spec 建立 queue control event 和双 loop,不实现 Skill index。
13
+
14
+ ## Why Now
15
+
16
+ 运行中的用户输入有两种不同语义:
17
+
18
+ - `steer`:引导当前 turn,属于 inner loop guidance。
19
+ - `follow_up`:排队等待当前 turn 结束后执行,属于 outer loop input queue。
20
+
21
+ 如果 follow-up 在收到时直接写成 `user_message`,它的 parent 会挂到当前中间 leaf,破坏 conversation graph。正确做法是先持久化 queue state,等当前 turn 结束后再消费成真正的 user message。
22
+
23
+ ## Scope
24
+
25
+ ### 1. Control Event
26
+
27
+ 新增 queue control event:
28
+
29
+ ```typescript
30
+ type QueueName = "follow_up" | "steer";
31
+
32
+ interface QueueItem {
33
+ id: string;
34
+ content: ScorelMessage["content"];
35
+ createdAt: number;
36
+ updatedAt: number;
37
+ clientId: ClientId;
38
+ data?: Record<string, unknown>;
39
+ }
40
+
41
+ interface QueueUpdateEvent extends PersistentEventBase {
42
+ type: "queue_update";
43
+ queue: QueueName;
44
+ operation: "rewrite";
45
+ items: QueueItem[];
46
+ anchorEventId: EventId | null;
47
+ }
48
+ ```
49
+
50
+ V1 只支持 full rewrite。Add、edit、delete 都通过写入完整 queue snapshot 表达。
51
+
52
+ 理由:
53
+
54
+ - queue 很小,重复写完整状态成本低。
55
+ - replay 简单,最后一条 rewrite 就是当前 queue。
56
+ - UI 同步更直接。
57
+
58
+ ### 2. Control Events Do Not Enter Conversation Tree
59
+
60
+ `queue_update` 是 control event:
61
+
62
+ - 写入 JSONL
63
+ - replay 时更新 queue state
64
+ - 不参与 `SessionTree.getPath()`
65
+ - 不进入 `buildContext()` message list
66
+
67
+ `parentId` 对 control event 不表达 conversation parent。V1 可以保留 `parentId: null` 或迁移为 control-only base,但 public 语义必须明确:conversation order 由 thread parent 表达,control state 由 JSONL seq replay 表达。
68
+
69
+ ### 3. Outer Loop
70
+
71
+ daemon turn loop 必须变成:
72
+
73
+ ```text
74
+ while true:
75
+ if followUpQueue has item:
76
+ consume first item
77
+ append user_message(parent = current final leaf, meta.source = "follow_up")
78
+ run inner loop
79
+ continue
80
+
81
+ wait for normal user send_message
82
+ append user_message(parent = requested parent or active leaf)
83
+ run inner loop
84
+ ```
85
+
86
+ 消费 follow-up 时:
87
+
88
+ - append `user_message`
89
+ - `message.meta.source = "follow_up"`
90
+ - `message.meta.queueItemId = queueItem.id`
91
+ - append `queue_update` rewrite,移除已消费 item
92
+
93
+ ### 4. Inner Loop
94
+
95
+ inner loop 执行当前 user turn:
96
+
97
+ ```text
98
+ while runtime needs continuation:
99
+ build context
100
+ execute model/tool step
101
+ persist assistant/tool_result
102
+ check steer queue
103
+ if steer exists:
104
+ consume steer
105
+ append harness_item kind=steer origin=user
106
+ continue
107
+ ```
108
+
109
+ `steer` 可以也用 `queue_update` 管理,但消费后必须变成 `harness_item`,而不是 `user_message`。
110
+
111
+ ### 5. IDs And Ordering
112
+
113
+ 示例:
114
+
115
+ ```text
116
+ e1 user_message parent=null
117
+ e2 assistant_message parent=e1
118
+ e3 tool_result parent=e2
119
+ q1 queue_update queue=follow_up items=[fu1] anchorEventId=e3
120
+ e4 assistant_message parent=e3
121
+ q2 queue_update queue=follow_up items=[] anchorEventId=e4
122
+ e5 user_message parent=e4 meta.source=follow_up meta.queueItemId=fu1
123
+ ```
124
+
125
+ 关键规则:
126
+
127
+ - JSONL `seq` 是物理 replay 顺序。
128
+ - `parentId` 是 conversation graph。
129
+ - follow-up 排队时的 `anchorEventId` 只用于审计/UI,不决定后续 user message parent。
130
+ - follow-up 消费后的 user message parent 必须是当前 final leaf。
131
+
132
+ ## Explicitly Not In Scope
133
+
134
+ - 多优先级 queue。
135
+ - background task scheduler。
136
+ - WebUI 完整 queue 编辑 UI。
137
+ - branch/rewind 的完整 queue conflict UI。
138
+ - Skill discovery / Skill tool。
139
+
140
+ ## Required Tests
141
+
142
+ ### Protocol / Session
143
+
144
+ - `queue_update` round-trip 通过。
145
+ - replay queue rewrite 能恢复当前 queue state。
146
+ - `queue_update` 不进入 `buildContext()`。
147
+ - `queue_update` 不成为 conversation leaf。
148
+
149
+ ### Follow-Up
150
+
151
+ - 当前 turn 中收到 follow-up 后,JSONL 写入 queue state,但不立即产生 user message。
152
+ - 当前 turn 结束后,follow-up 被消费成新的 `user_message`。
153
+ - 消费后的 `user_message.parentId` 指向当前 final leaf。
154
+ - edit/delete 通过 rewrite 生效。
155
+
156
+ ### Steer
157
+
158
+ - steer queue item 在 inner loop 消费成 `harness_item kind=steer`。
159
+ - steer 不变成 follow-up user message。
160
+
161
+ ### Runtime
162
+
163
+ - follow-up queue 连续多条时,outer loop 逐条消费。
164
+ - daemon restart 后可以从 JSONL 恢复未消费 follow-up queue。
165
+
166
+ ## Likely Files
167
+
168
+ ```text
169
+ packages/protocol/src/events.ts
170
+ packages/core/src/session/index.ts
171
+ packages/core/src/session/session.test.ts
172
+ packages/daemon/src/index.ts
173
+ packages/daemon/src/index.test.ts
174
+ packages/client/src/*
175
+ apps/webui/*
176
+ docs/spec/events.md
177
+ docs/spec/runtime.md
178
+ docs/spec/session.md
179
+ ```
180
+
181
+ ## Risks And Boundaries
182
+
183
+ - 不要把 follow-up 存成普通 user message 再等待消费;那会污染 parent graph。
184
+ - 不要把 queue state 放进 daemon-only memory;必须能从 JSONL replay。
185
+ - 不要用 JSONL 行顺序替代 conversation parent。
186
+ - 如果当前 session branch/leaf 切换,follow-up 消费必须以消费时 active leaf 为 parent。
187
+
188
+ ## Done When
189
+
190
+ - follow-up add/edit/delete/rewrite 都以 `queue_update` control event 持久化。
191
+ - daemon outer loop 会在当前 turn 完成后消费 follow-up。
192
+ - steer 和 follow-up 分别走 inner guidance 与 outer queued input。
193
+ - `buildContext()` 不包含 queue control events。
194
+ - 自动测试与 typecheck 通过。
195
+ - 完成后 commit:`S0052: feat: add follow-up queue dual loop`
@@ -0,0 +1,252 @@
1
+ # S0053 — Skill Index And Skill Tool
2
+
3
+ ## Goal
4
+
5
+ 按 Agent Skills 目录规范为 Scorel 增加 Skill baseline:
6
+
7
+ - 扫描 `~/.scorel/skills` 和 project `.scorel/skills`
8
+ - 建立 session-scoped Skill index,作为 Skill tool 的路由表
9
+ - 用 JSONL control events 持久化 skill index snapshot/delta
10
+ - 通过 `harness_item` 注入 skill listing / skill delta
11
+ - 暴露 `Skill(name)` 工具,按 index 加载完整 `SKILL.md`
12
+
13
+ 本 spec 不做 Skill discovery ranking、不做 embedding/LLM 搜索、不做 marketplace。
14
+
15
+ ## Why Now
16
+
17
+ S0051/S0052 建立了 harness item 与 queue control event。Skill 需要同样的原则:
18
+
19
+ - natural-language listing 只是给模型看的提醒,不是事实来源
20
+ - 工具调用必须查结构化 routing table
21
+ - session resume 后必须从 JSONL 恢复当时的 skill index
22
+
23
+ 否则 `Skill("name")` 会依赖磁盘即时状态和 prompt 文本,无法审计和恢复。
24
+
25
+ ## Scope
26
+
27
+ ### 1. Directory Layout
28
+
29
+ V1 支持:
30
+
31
+ ```text
32
+ ~/.scorel/skills/<skill-name>/SKILL.md
33
+ <project-scope>/.scorel/skills/<skill-name>/SKILL.md
34
+ ```
35
+
36
+ Project-scope discovery:
37
+
38
+ - 从 session `cwd` 往上查找 `.scorel/skills`
39
+ - 如果在 Git repository 内,在最近 Git root 停止
40
+ - 如果不在 Git repository 内,在 home 前停止
41
+ - `~/.scorel/skills` 作为 user global 独立加载
42
+
43
+ V1 只支持目录格式,不支持单个 `.md` 文件作为 skill。
44
+
45
+ ### 2. Name And Routing
46
+
47
+ 调用名以目录名为准:
48
+
49
+ ```text
50
+ ~/.scorel/skills/commit/SKILL.md -> Skill("commit")
51
+ ```
52
+
53
+ frontmatter `name` 只作为 display name,不改变 tool routing key。
54
+
55
+ 理由:
56
+
57
+ - Skill tool 调用必须有稳定、可预测的 key。
58
+ - 不规范 skill 不能通过正文或 display name 改变路由。
59
+ - listing/reminder 给模型看的 name 必须是可调用 name。
60
+
61
+ ### 3. Skill Index
62
+
63
+ ```typescript
64
+ interface SkillIndexEntry {
65
+ name: string;
66
+ path: string;
67
+ scope: "user" | "project";
68
+ description: string;
69
+ displayName?: string;
70
+ mtimeMs: number;
71
+ size: number;
72
+ contentHash: string;
73
+ priority: number;
74
+ shadowed?: boolean;
75
+ diagnostics?: string[];
76
+ }
77
+
78
+ interface SkillIndexSnapshotEvent extends PersistentEventBase {
79
+ type: "skill_index_snapshot";
80
+ anchorEventId: EventId | null;
81
+ entries: SkillIndexEntry[];
82
+ }
83
+
84
+ interface SkillIndexDeltaEvent extends PersistentEventBase {
85
+ type: "skill_index_delta";
86
+ anchorEventId: EventId | null;
87
+ added: SkillIndexEntry[];
88
+ changed: SkillIndexEntry[];
89
+ removed: { name: string; previousPath: string }[];
90
+ }
91
+ ```
92
+
93
+ Replay:
94
+
95
+ ```text
96
+ skill_index_snapshot -> state.skillIndex = entries by name
97
+ skill_index_delta -> patch state.skillIndex
98
+ ```
99
+
100
+ The session state map is the source of truth for `Skill(name)`.
101
+
102
+ ### 4. Scan And Diff
103
+
104
+ Before each real user message:
105
+
106
+ 1. stat known skill paths and candidate skill directories
107
+ 2. only read/parse changed files
108
+ 3. build current index
109
+ 4. compare with replayed session index
110
+ 5. append `skill_index_snapshot` if no index exists
111
+ 6. append `skill_index_delta` if added/changed/removed exists
112
+ 7. append corresponding `harness_item` listing/delta for the model
113
+
114
+ No diff means no event.
115
+
116
+ ### 5. Listing And Delta Injection
117
+
118
+ Initial listing:
119
+
120
+ ```text
121
+ The following skills are available for use with the Skill tool:
122
+
123
+ - commit: Create a concise semantic commit...
124
+ - verify: Run the project's verification flow...
125
+ ```
126
+
127
+ Delta:
128
+
129
+ ```text
130
+ Skill updates detected:
131
+
132
+ Added:
133
+ - deploy: ...
134
+
135
+ Changed:
136
+ - verify: ...
137
+
138
+ Removed:
139
+ - old-skill
140
+ ```
141
+
142
+ Both use `harness_item` and `<system-reminder>` conversion from S0051.
143
+
144
+ ### 6. Skill Tool
145
+
146
+ Expose a builtin tool:
147
+
148
+ ```typescript
149
+ Skill({ name: string, args?: string })
150
+ ```
151
+
152
+ Execution:
153
+
154
+ - lookup `state.skillIndex[name]`
155
+ - if missing, return structured error listing known names
156
+ - read `entry.path`
157
+ - return the full `SKILL.md` content as a tool result or harness item payload for the next LM step
158
+ - record invoked skill metadata for display/diagnostics
159
+
160
+ V1 does not execute arbitrary scripts from skill directories. It only loads instructions and allows the model to inspect referenced files through normal tools.
161
+
162
+ ### 7. Conflict Rules
163
+
164
+ Priority:
165
+
166
+ 1. nearest project `.scorel/skills`
167
+ 2. higher project `.scorel/skills`
168
+ 3. user `~/.scorel/skills`
169
+
170
+ Same `name`:
171
+
172
+ - highest priority wins
173
+ - losing entries are shadowed
174
+ - diagnostics records shadowed paths
175
+ - listing only includes winning entries
176
+
177
+ Invalid skill:
178
+
179
+ - missing `SKILL.md`: ignored
180
+ - missing description: derive from frontmatter or first paragraph
181
+ - still no description: do not list; keep diagnostics
182
+ - unreadable file: do not list; keep diagnostics
183
+
184
+ ## Explicitly Not In Scope
185
+
186
+ - Automatic skill search/ranking.
187
+ - Embedding search or small-model skill search.
188
+ - Plugin/marketplace skills.
189
+ - MCP skills.
190
+ - Running skill scripts automatically.
191
+ - User-facing `/skills` management UI.
192
+ - Conditional `paths` activation beyond parsing and preserving metadata.
193
+
194
+ ## Required Tests
195
+
196
+ ### Discovery
197
+
198
+ - user skills are loaded from `~/.scorel/skills`.
199
+ - project skills are loaded from cwd upward to Git root or home boundary.
200
+ - directory name is the tool routing key.
201
+ - frontmatter display name does not change routing key.
202
+ - same-name project skill shadows user skill.
203
+
204
+ ### Index Events
205
+
206
+ - first scan writes `skill_index_snapshot`.
207
+ - added/changed/removed skill writes `skill_index_delta`.
208
+ - replay from JSONL reconstructs skill index.
209
+ - no diff writes no event.
210
+
211
+ ### Harness Injection
212
+
213
+ - initial snapshot emits `harness_item kind=skill_listing`.
214
+ - delta emits `harness_item kind=skill_delta`.
215
+ - listing/delta do not become routing source; state.skillIndex remains source of truth.
216
+
217
+ ### Skill Tool
218
+
219
+ - `Skill("name")` loads content from indexed path.
220
+ - missing skill returns structured error.
221
+ - changed skill content is loaded after delta replay updates index.
222
+
223
+ ## Likely Files
224
+
225
+ ```text
226
+ packages/protocol/src/events.ts
227
+ packages/core/src/session/index.ts
228
+ packages/core/src/session/session.test.ts
229
+ packages/core/src/tools/*
230
+ packages/daemon/src/index.ts
231
+ packages/daemon/src/index.test.ts
232
+ packages/daemon/src/skills/*
233
+ docs/spec/events.md
234
+ docs/spec/runtime.md
235
+ docs/spec/extensions.md
236
+ ```
237
+
238
+ ## Risks And Boundaries
239
+
240
+ - Do not parse harness reminder text to rebuild skill state.
241
+ - Do not trust frontmatter `name` as routing key.
242
+ - Do not rescan full file contents every turn when stat/hash can avoid it.
243
+ - Do not make Skill discovery ranking part of V1; available listing plus explicit Skill tool is enough.
244
+
245
+ ## Done When
246
+
247
+ - Scorel maintains a session-scoped skill index from JSONL snapshot/delta events.
248
+ - Skill listing/delta are injected through harness items.
249
+ - `Skill(name)` routes through the index and loads `SKILL.md`.
250
+ - Resume reconstructs the same skill index before tool invocation.
251
+ - Automatic tests and typecheck pass.
252
+ - 完成后 commit:`S0053: feat: add skill index and skill tool`
@@ -0,0 +1,72 @@
1
+ # S0054: WebUI Running Message Behavior
2
+
3
+ ## Goal
4
+
5
+ Make WebUI message sending match Codex-style running behavior:
6
+
7
+ - idle sends clear once the daemon's persistent event stream confirms the user message, not when the assistant finishes
8
+ - while a run is active, the composer remains editable
9
+ - running sends can either queue a follow-up or steer the active run
10
+ - users can choose the default running behavior in Settings
11
+ - `Command+Enter` sends with the default behavior, while `Command+Shift+Enter` sends the opposite behavior
12
+
13
+ ## Scope
14
+
15
+ - Extend the protocol `send_message` options with explicit running behavior: `follow_up` or `steer`.
16
+ - Preserve existing CLI/client request semantics: `send_message` responses remain completion/queue responses, not accepted acknowledgements.
17
+ - Let WebUI clear the composer from the canonical persistent event stream instead of a second request-level accepted acknowledgement.
18
+ - Add WebUI settings storage for default running behavior.
19
+ - Update the session composer so Enter inserts a newline, `Command+Enter` sends, and `Command+Shift+Enter` sends the opposite running behavior.
20
+ - Show both send and cancel controls while a run is active.
21
+
22
+ ## Non-Goals
23
+
24
+ - Full queue editing UI.
25
+ - Deleting queued follow-ups.
26
+ - Rich steer/follow-up transcript controls beyond existing event rendering.
27
+ - Changing CLI input behavior.
28
+
29
+ ## Contract
30
+
31
+ `send_message` defaults remain compatible:
32
+
33
+ - idle requests resolve after the user turn completes
34
+ - no `runningBehavior` option while a run is active means `follow_up`
35
+
36
+ WebUI submits:
37
+
38
+ ```ts
39
+ options: {
40
+ runningBehavior: "follow_up" | "steer"
41
+ }
42
+ ```
43
+
44
+ WebUI treats daemon acceptance as confirmed when it observes the matching persistent event:
45
+
46
+ - idle: the daemon has appended and broadcast the source `user_message`
47
+ - running + follow-up: the daemon has appended the `queue_update` for `follow_up`
48
+ - running + steer: the daemon has appended the `queue_update` for `steer`
49
+
50
+ `send_message` responses resolve when:
51
+
52
+ - idle: the user turn finishes
53
+ - running + follow-up: the queued follow-up is consumed and finishes
54
+ - running + steer: the steer queue item is accepted and queued; it does not create a user turn
55
+
56
+ ## Acceptance Criteria
57
+
58
+ - WebUI composer clears and becomes editable after the matching persistent `user_message` or `queue_update`, before assistant completion.
59
+ - While `inFlight`, users can type another message.
60
+ - While `inFlight`, normal send uses the configured default behavior.
61
+ - While `inFlight`, `Command+Shift+Enter` sends the opposite behavior.
62
+ - Settings exposes a persistent default running behavior selector.
63
+ - Follow-up sends append `queue_update(queue="follow_up")`.
64
+ - Steer sends append `queue_update(queue="steer")` and is later consumed into a `harness_item kind="steer"`.
65
+ - `pnpm typecheck` and `pnpm test` pass.
66
+
67
+ ## Test Requirements
68
+
69
+ - Protocol round-trip covers the new send options and response shapes.
70
+ - Daemon tests cover durable persistent acceptance for idle sends, follow-up queue, and steer queue.
71
+ - WebUI composer tests cover editable in-flight input, `Command+Enter`, `Command+Shift+Enter`, send+cancel controls, and early clear.
72
+ - Settings store tests cover default behavior persistence.
@@ -0,0 +1,68 @@
1
+ # S0055: WebUI Composer Acceptance And Queue Strip
2
+
3
+ ## Goal
4
+
5
+ Tighten the S0054 running composer behavior so the UI matches the actual daemon event model:
6
+
7
+ - focused composer controls must not show the heavy global black outline shown in browser screenshots
8
+ - send acceptance remains based on persistent events, but missing live events must recover through resync instead of hanging
9
+ - running follow-up / steer queue items are visible above the input as a small actionable stack
10
+ - project picking uses one shared Codex-like menu from both title and toolbar triggers, with a bounded scroll window and an add-project action
11
+
12
+ ## Scope
13
+
14
+ - Override the global focus-visible outline inside the composer pill and keep the pill's own subtle border as the focus affordance.
15
+ - Track `queue_update` events in the WebUI projector state.
16
+ - Render queued `follow_up` / `steer` items above the textarea inside the composer, with the next item closest to the input.
17
+ - Let visible queue rows switch between follow-up / steer, return to the input for editing, or be deleted via persistent queue rewrite.
18
+ - Keep the project picker menu compact, scrollable, and wired to the existing Add Project dialog.
19
+ - If a `send_message` request completes but the matching persistent event was not observed live, trigger `resync_events` from the current anchors and resolve from the recovered persistent event.
20
+ - If send fails before acceptance, restore the input and surface `send_failed` / `disconnected` as today.
21
+
22
+ ## Non-Goals
23
+
24
+ - Reordering queued items.
25
+ - Rich queue history in the transcript.
26
+ - New daemon protocol messages for queue display.
27
+ - Treating heartbeat as send acceptance. Heartbeat only proves the socket is alive; persistent event or resync proves durable session state.
28
+
29
+ ## Contract
30
+
31
+ For WebUI send acceptance:
32
+
33
+ 1. live matching persistent `user_message` / `queue_update` resolves the send immediately;
34
+ 2. request failure rejects the send;
35
+ 3. request completion without live acceptance triggers `resync_events`;
36
+ 4. resync still missing the matching persistent event rejects as `send_failed`.
37
+
38
+ For queue display:
39
+
40
+ - `queue_update(queue="follow_up")` replaces the displayed follow-up stack.
41
+ - `queue_update(queue="steer")` replaces the displayed steer stack.
42
+ - Display order is stack-like: the oldest/next item is rendered closest to the textarea.
43
+ - Queue rows expose controls to convert follow-up/steer, edit back into the composer input, and delete the row.
44
+ - Convert/edit/delete are persisted by a daemon `rewrite_queue` request that appends a full `queue_update(operation="rewrite")`, matching S0052.
45
+
46
+ For project picking:
47
+
48
+ - The title and toolbar project pickers are two triggers for the same menu component.
49
+ - The menu has a bounded, scrollable project list and does not render native `select` controls.
50
+ - The menu exposes an Add Project action that opens the existing shell Add Project dialog.
51
+
52
+ ## Acceptance Criteria
53
+
54
+ - The composer textarea does not draw the heavy black focus outline.
55
+ - Follow-up and steer queue items render above the textarea while a run is active.
56
+ - Follow-up and steer queue rows expose convert, edit, and delete controls.
57
+ - The project picker menu is compact, scrollable, shared by both triggers, and can open Add Project.
58
+ - A send whose matching persistent event arrives through resync resolves successfully.
59
+ - A send whose request completes but resync cannot find the matching persistent event fails instead of hanging.
60
+
61
+ ## Test Requirements
62
+
63
+ - Composer tests cover no global outline class, queue strip rendering, and queue row actions.
64
+ - Empty composer and sidebar tests cover shared project picker menu scroll bounds and Add Project wiring.
65
+ - Protocol/client/daemon tests cover `rewrite_queue` appending persistent `queue_update` rewrites.
66
+ - Projector tests cover `queue_update` projection and replacement.
67
+ - Session controller tests cover request-complete resync recovery and missing-event failure.
68
+ - `pnpm typecheck` and `pnpm test` pass.
@@ -0,0 +1,106 @@
1
+ # S0056: Relay And Hosted WebUI Contract
2
+
3
+ ## Goal
4
+
5
+ Lock the next product-stage contract for Relay + hosted WebUI before implementation:
6
+
7
+ - Relay is an authenticated proxy and authorization registry, not a hosted daemon.
8
+ - Hosted WebUI can connect to user Devices through Relay without requiring public daemon exposure.
9
+ - Device -> Project -> Session remains the product hierarchy.
10
+ - Existing daemon wire payloads stay Host-owned; Relay only adds routing and authorization around the transport.
11
+
12
+ ## Scope
13
+
14
+ - Add `docs/spec/relay.md` as the abstract Relay contract.
15
+ - Add ADR-007 to lock Relay proxy, `deviceId -> clientId` binding, and no new JSONL actor field.
16
+ - Update architecture docs to show Relay as a transport/control-plane extension.
17
+ - Update client spec to include `RelayTransport` and multi-connector Device semantics.
18
+ - Document the implementation directory boundary: `apps/relay` for the deployable service, shared types in `packages/protocol`, `RelayTransport` in `packages/client`, Host outbound adapter in `packages/daemon`.
19
+ - Update roadmap so Relay + Hosted WebUI becomes the next M stage and GUI/SSH/HTTP move after it.
20
+ - Create the first M8 implementation specs: S0057 through S0060.
21
+
22
+ ## Non-Goals
23
+
24
+ - Implement Relay service code.
25
+ - Implement `RelayTransport`.
26
+ - Implement `scorel pair`.
27
+ - Implement hosted WebUI pairing UI.
28
+ - Add user accounts.
29
+ - Add hosted execution.
30
+ - Change JSONL schema.
31
+ - Add `entryId` or `routeId` to daemon events.
32
+
33
+ ## Contract
34
+
35
+ Relay durable state:
36
+
37
+ - device identity metadata
38
+ - client/Entry identity metadata
39
+ - authorization bindings: `deviceId -> clientId`
40
+
41
+ Relay transient state:
42
+
43
+ - pair sessions
44
+ - online presence
45
+ - socket routing
46
+
47
+ Relay forbidden state:
48
+
49
+ - Project Registry
50
+ - Session JSONL
51
+ - prompts
52
+ - tool results
53
+ - provider responses
54
+ - replay / resync cache
55
+ - Runtime diagnostics that include user content
56
+
57
+ Transport contract:
58
+
59
+ ```text
60
+ Direct:
61
+ DaemonClient -> WsTransport -> Host
62
+
63
+ Relay:
64
+ DaemonClient -> RelayTransport -> Relay -> Host relay adapter -> Host
65
+ ```
66
+
67
+ `clientId` remains the daemon protocol and JSONL field for the Entry that authored user actions. Relay docs may use Entry as a product concept, but V1 does not introduce a separate `entryId` field.
68
+
69
+ ## Acceptance Criteria
70
+
71
+ - `docs/spec/relay.md` defines Relay roles, state boundaries, pairing, routing, WebUI multi-device model, and reconnect semantics.
72
+ - ADR-007 explains why Relay is proxy + authorization registry instead of hosted daemon.
73
+ - Relay directory layout is explicit and does not introduce a top-level `relay/` directory or premature `packages/relay`.
74
+ - S0057-S0060 exist and split M8 into Relay service skeleton, Host outbound pairing, Entry/WebUI connector, and real e2e validation.
75
+ - `docs/ROADMAP.md` makes Relay + Hosted WebUI the next M stage.
76
+ - `docs/architecture.md` points to the Relay spec and does not imply Relay owns Scorel domain state.
77
+ - `docs/spec/client.md` describes `RelayTransport` as a transport adapter over the existing `DaemonTransport` contract.
78
+ - `docs/README.md` links the new Relay spec and ADR.
79
+
80
+ ## Test Requirements
81
+
82
+ Docs-only spec. Verification is document consistency:
83
+
84
+ - grep for Relay references across docs.
85
+ - inspect changed docs for conflicting `entryId` / `clientId` semantics.
86
+ - no code tests required.
87
+
88
+ ## Affected Paths
89
+
90
+ - `docs/spec/relay.md`
91
+ - `docs/decisions/007-relay-proxy-and-entry-routing.md`
92
+ - `docs/spec/ship/S0056-relay-and-hosted-webui-contract.md`
93
+ - `docs/architecture.md`
94
+ - `docs/spec/client.md`
95
+ - `docs/ROADMAP.md`
96
+ - `docs/README.md`
97
+ - `docs/spec/ship/S0057-relay-service-protocol-skeleton.md`
98
+ - `docs/spec/ship/S0058-host-outbound-relay-and-pair-command.md`
99
+ - `docs/spec/ship/S0059-relay-transport-and-hosted-webui-connector.md`
100
+ - `docs/spec/ship/S0060-relay-hosted-webui-e2e-validation.md`
101
+
102
+ ## Risks
103
+
104
+ - Over-expanding Relay into a cloud backend would dilute Scorel's Host authority model.
105
+ - Treating Relay as just a URL alias would miss authorization, multi-device routing, and hosted WebUI requirements.
106
+ - Introducing new identity fields too early would churn JSONL and daemon protocol without product value.