@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.
- package/README.md +110 -0
- package/dist/index.js +6675 -0
- package/dist/index.js.map +7 -0
- package/docs/CHANGELOG.md +12 -0
- package/docs/README.md +116 -0
- package/docs/ROADMAP.md +669 -0
- package/docs/SHIP.md +242 -0
- package/docs/spec/channels.md +156 -0
- package/docs/spec/client.md +326 -0
- package/docs/spec/daemon.md +408 -0
- package/docs/spec/events.md +423 -0
- package/docs/spec/extensions.md +255 -0
- package/docs/spec/relay.md +391 -0
- package/docs/spec/runtime.md +251 -0
- package/docs/spec/session.md +380 -0
- package/docs/spec/ship/S0001-docs-baseline.md +41 -0
- package/docs/spec/ship/S0002-package-skeleton.md +56 -0
- package/docs/spec/ship/S0003-protocol-contracts.md +49 -0
- package/docs/spec/ship/S0004-session-core.md +50 -0
- package/docs/spec/ship/S0005-runtime-loop.md +48 -0
- package/docs/spec/ship/S0006-embedded-daemon-client.md +51 -0
- package/docs/spec/ship/S0007-cli-alpha.md +49 -0
- package/docs/spec/ship/S0008-coding-tools.md +107 -0
- package/docs/spec/ship/S0009-code-discovery-tools.md +82 -0
- package/docs/spec/ship/S0010-todo-tool-and-cli.md +81 -0
- package/docs/spec/ship/S0011-coding-agent-alpha-smoke.md +110 -0
- package/docs/spec/ship/S0012-coding-tools-maturity.md +143 -0
- package/docs/spec/ship/S0013-local-daemon-protocol.md +57 -0
- package/docs/spec/ship/S0014-local-daemon-lifecycle.md +64 -0
- package/docs/spec/ship/S0015-local-attach-and-broadcast.md +58 -0
- package/docs/spec/ship/S0016-local-daemon-resync-smoke.md +60 -0
- package/docs/spec/ship/S0017-grep-files-output-mode.md +49 -0
- package/docs/spec/ship/S0018-daemon-entrypoint-smoke.md +48 -0
- package/docs/spec/ship/S0019-remote-transport-contract.md +59 -0
- package/docs/spec/ship/S0020-remote-websocket-server.md +56 -0
- package/docs/spec/ship/S0021-remote-websocket-client-transport.md +55 -0
- package/docs/spec/ship/S0022-remote-daemon-cli-lifecycle.md +60 -0
- package/docs/spec/ship/S0023-remote-control-e2e-validation.md +66 -0
- package/docs/spec/ship/S0024-remote-attach-interactive-stream.md +49 -0
- package/docs/spec/ship/S0025-remote-attach-session-event-view.md +57 -0
- package/docs/spec/ship/S0026-attach-project-cache-and-dual-seq-reconnect.md +87 -0
- package/docs/spec/ship/S0027-session-diagnostics-log.md +77 -0
- package/docs/spec/ship/S0028-client-attach-diagnostics-log.md +70 -0
- package/docs/spec/ship/S0029-project-index-for-session-lookup.md +119 -0
- package/docs/spec/ship/S0030-webui-product-intent.md +73 -0
- package/docs/spec/ship/S0031-daemon-projectslug-rule.md +72 -0
- package/docs/spec/ship/S0032-daemon-protocol-completion.md +123 -0
- package/docs/spec/ship/S0033-webui-skeleton-routing.md +92 -0
- package/docs/spec/ship/S0034-webui-device-settings.md +121 -0
- package/docs/spec/ship/S0035-webui-device-handshake.md +83 -0
- package/docs/spec/ship/S0036-webui-project-session-sync.md +70 -0
- package/docs/spec/ship/S0037-webui-chatbox-v1.md +97 -0
- package/docs/spec/ship/S0038-webui-cancel-multiclient.md +65 -0
- package/docs/spec/ship/S0039-webui-e2e-newchat.md +74 -0
- package/docs/spec/ship/S0040-webui-codex-visual-tokens.md +227 -0
- package/docs/spec/ship/S0041-webui-markdown-and-tool-block.md +248 -0
- package/docs/spec/ship/S0042-webui-streaming-ux-autoscroll.md +130 -0
- package/docs/spec/ship/S0043-startup-ergonomics.md +278 -0
- package/docs/spec/ship/S0044-webui-chatbox-rebuild.md +556 -0
- package/docs/spec/ship/S0045-webui-card-sidebar-and-session-fixes.md +469 -0
- package/docs/spec/ship/S0046-webui-empty-composer-and-lazy-session.md +428 -0
- package/docs/spec/ship/S0047-webui-project-hover-newchat-and-dynamic-greeting.md +176 -0
- package/docs/spec/ship/S0048-device-level-host-project-registry.md +253 -0
- package/docs/spec/ship/S0049-webui-add-project-directory-browser.md +217 -0
- package/docs/spec/ship/S0050-instruction-snapshot-and-agents-assembly.md +338 -0
- package/docs/spec/ship/S0051-harness-item-and-system-reminder.md +190 -0
- package/docs/spec/ship/S0052-follow-up-queue-and-dual-loop.md +195 -0
- package/docs/spec/ship/S0053-skill-index-and-skill-tool.md +252 -0
- package/docs/spec/ship/S0054-webui-running-message-behavior.md +72 -0
- package/docs/spec/ship/S0055-webui-composer-acceptance-and-queue-strip.md +68 -0
- package/docs/spec/ship/S0056-relay-and-hosted-webui-contract.md +106 -0
- package/docs/spec/ship/S0057-relay-service-protocol-skeleton.md +161 -0
- package/docs/spec/ship/S0058-host-outbound-relay-and-pair-command.md +138 -0
- package/docs/spec/ship/S0059-relay-transport-and-hosted-webui-connector.md +140 -0
- package/docs/spec/ship/S0060-relay-hosted-webui-e2e-validation.md +132 -0
- package/docs/spec/ship/S0060-relay-hosted-webui-e2e-validation.verification.md +90 -0
- package/docs/spec/ship/S0061-hosted-defaults-and-cli-command-surface.md +208 -0
- package/docs/spec/ship/S0062-npm-package-and-release-workflow.md +166 -0
- package/docs/spec/tools.md +173 -0
- 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.
|