@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,408 @@
1
+ # Daemon Host - Device-level Runtime And Project Coordination
2
+
3
+ > 上游:`architecture.md`、`decisions/006-device-host-project-registry.md`、`spec/events.md`、`spec/runtime.md`
4
+ > 归属包:`@scorel/daemon`
5
+
6
+ ---
7
+
8
+ ## 0. 定位
9
+
10
+ Scorel daemon 是 **Device-level Host**:
11
+
12
+ - 一台 Device 默认运行一个 Host。
13
+ - 一个 Host 管理多个 Project。
14
+ - Host 是 Runtime 和 Session JSONL 的唯一持有者与写入者。
15
+ - CLI、WebUI、GUI、IM 和未来 HTTP API 都不能绕过 Host。
16
+
17
+ 对外命令和包名继续使用 `daemon`。代码实现可将当前 `EmbeddedDaemon` 重命名为 `ScorelHost`,避免继续暗示“一 daemon 一 cwd”。
18
+
19
+ ---
20
+
21
+ ## 1. 不变量
22
+
23
+ 1. Host 身份是 `deviceId`,不是 cwd。
24
+ 2. Project 是正式实体,持久化到 `~/.scorel/projects.json`。
25
+ 3. 每个 Project 有 Host 生成的稳定 `projectId`。
26
+ 4. 每个 Session header 必须持久化 `projectId`。
27
+ 5. Runtime 创建时必须通过 `session.meta.projectId` 查询 Registry,再取得 canonical `workDir`。
28
+ 6. Project 可以没有 Session;因此 `list_projects` 不得由 Session JSONL 聚合替代。
29
+ 7. Session JSONL 只有 Host 可以写。
30
+ 8. PersistentEvent 与 TransientEvent 共用 per-session seq。
31
+ 9. 当前 pre-1.0 不保留 `projectSlug`、`workDirHint` 或旧本地状态兼容层。
32
+
33
+ 关键验收:
34
+
35
+ ```text
36
+ Project A -> Session A -> Runtime cwd A
37
+ Project B -> Session B -> Runtime cwd B
38
+ ```
39
+
40
+ 同一 Host 下两个 Session 必须能在两个不同真实仓库中执行工具,不能串 cwd。
41
+
42
+ ---
43
+
44
+ ## 2. 领域模型
45
+
46
+ ### 2.1 IDs
47
+
48
+ ```typescript
49
+ type ProjectId = Brand<string, "ProjectId">;
50
+ ```
51
+
52
+ ### 2.2 Project
53
+
54
+ ```typescript
55
+ type HostProject = {
56
+ projectId: ProjectId;
57
+ displayName: string;
58
+ workDir: string; // canonical absolute path
59
+ createdAt: number;
60
+ updatedAt: number;
61
+ };
62
+ ```
63
+
64
+ 规则:
65
+
66
+ - `projectId` 由 Host 生成,例如 `prj_<uuid>`。
67
+ - `workDir` 在注册时 canonicalize,并由 owning Host 持久化。
68
+ - 相同 canonical `workDir` 重复注册必须返回已有 Project,不能创建重复项。
69
+ - `displayName` 默认取 `basename(workDir)`,以后可允许重命名。
70
+ - 删除 Project 不自动删除 Session JSONL;S0048 只允许删除没有 Session 的 Project,避免隐式数据删除。
71
+
72
+ ### 2.3 Session Meta
73
+
74
+ ```typescript
75
+ type SessionMeta = {
76
+ projectId: ProjectId;
77
+ title?: string;
78
+ model?: string;
79
+ createdAt?: number;
80
+ updatedAt?: number;
81
+ };
82
+ ```
83
+
84
+ `projectId` 是 Session 与 Runtime cwd 的唯一绑定。不得允许 client 提交 cwd 作为 `create_session` 的旁路参数。
85
+
86
+ ---
87
+
88
+ ## 3. Host 结构
89
+
90
+ ```text
91
+ ScorelHost
92
+ ├── DeviceIdentity
93
+ ├── ProjectRegistry
94
+ ├── SessionStore
95
+ ├── RuntimePool
96
+ ├── EventHub
97
+ ├── ConnectionManager
98
+ ├── ChannelManager
99
+ └── TransportAdapters
100
+ ```
101
+
102
+ ### 3.1 ProjectRegistry
103
+
104
+ 持久化:
105
+
106
+ ```text
107
+ ~/.scorel/projects.json
108
+ ```
109
+
110
+ 文件形态:
111
+
112
+ ```typescript
113
+ type ProjectRegistryFile = {
114
+ version: 1;
115
+ projects: HostProject[];
116
+ };
117
+ ```
118
+
119
+ API:
120
+
121
+ ```typescript
122
+ interface ProjectRegistry {
123
+ list(): Promise<HostProject[]>;
124
+ get(projectId: ProjectId): Promise<HostProject | undefined>;
125
+ require(projectId: ProjectId): Promise<HostProject>;
126
+ register(workDir: string): Promise<HostProject>;
127
+ remove(projectId: ProjectId): Promise<void>;
128
+ }
129
+ ```
130
+
131
+ ### 3.2 RuntimePool
132
+
133
+ 每个活跃 Session 最多一个 Runtime:
134
+
135
+ ```typescript
136
+ type SessionLane = {
137
+ session: JsonlSession;
138
+ project: HostProject;
139
+ runtime: ScorelRuntime;
140
+ queue: Promise<unknown>;
141
+ };
142
+ ```
143
+
144
+ Runtime factory 改为 project-aware:
145
+
146
+ ```typescript
147
+ type RuntimeFactory = (input: {
148
+ sessionId: SessionId;
149
+ project: HostProject;
150
+ }) => Promise<ScorelRuntime>;
151
+ ```
152
+
153
+ 真实 runtime 创建流程:
154
+
155
+ ```text
156
+ project.workDir
157
+ -> loadScorelConfig({ cwd: project.workDir })
158
+ -> createCodingTools({ cwd: project.workDir })
159
+ -> createPiAiProvider(...)
160
+ -> ScorelRuntime
161
+ ```
162
+
163
+ ### 3.3 SessionStore
164
+
165
+ 继续使用 flat 目录:
166
+
167
+ ```text
168
+ ~/.scorel/sessions/<sessionId>.jsonl
169
+ ~/.scorel/sessions/<sessionId>.log
170
+ ```
171
+
172
+ 不需要把文件搬入 Project 子目录。Session header 中的 `projectId` 足以索引归属。
173
+
174
+ ### 3.4 EventHub
175
+
176
+ - per-session seq。
177
+ - PersistentEvent 写 JSONL 后广播。
178
+ - TransientEvent 只广播并进入 live buffer。
179
+ - reconnect 使用 dual-seq:
180
+ - `stream_resume`
181
+ - `persistent_fallback`
182
+ - `full_reload`
183
+
184
+ ---
185
+
186
+ ## 4. Host API
187
+
188
+ ```typescript
189
+ interface ScorelHost {
190
+ start(): Promise<void>;
191
+ shutdown(): Promise<void>;
192
+
193
+ listDirectories(path?: string): Promise<DirectoryListing>;
194
+ registerProject(workDir: string): Promise<HostProject>;
195
+ listProjects(): Promise<HostProject[]>;
196
+ removeProject(projectId: ProjectId): Promise<void>;
197
+
198
+ createSession(meta: SessionMeta): Promise<SessionId>;
199
+ loadSession(sessionId: SessionId): Promise<SessionLane>;
200
+ getRuntime(sessionId: SessionId): ScorelRuntime | undefined;
201
+ }
202
+
203
+ type DirectoryEntry = {
204
+ name: string;
205
+ path: string;
206
+ };
207
+
208
+ type DirectoryListing = {
209
+ path: string;
210
+ parentPath?: string;
211
+ entries: DirectoryEntry[];
212
+ };
213
+
214
+ ```
215
+
216
+ 当前可信用户模型允许完整目录浏览。`listDirectories()` 不做 ACL 或根目录限制,但必须:
217
+
218
+ - 只返回目录,不返回文件内容。
219
+ - 对不存在、无权限、非目录路径返回结构化错误。
220
+ - 使用 canonical absolute path。
221
+ - 稳定排序:目录名 locale compare。
222
+
223
+ ---
224
+
225
+ ## 5. Wire Protocol
226
+
227
+ ### 5.1 Project 请求
228
+
229
+ ```typescript
230
+ type ClientRequestMap = {
231
+ list_directories: {
232
+ request: { path?: string };
233
+ response: { path: string; parentPath?: string; entries: DirectoryEntry[] };
234
+ };
235
+ register_project: {
236
+ request: { workDir: string };
237
+ response: { project: HostProject };
238
+ };
239
+ list_projects: {
240
+ request: {};
241
+ response: { projects: HostProject[] };
242
+ };
243
+ remove_project: {
244
+ request: { projectId: ProjectId };
245
+ response: { projectId: ProjectId; removed: boolean };
246
+ };
247
+ };
248
+ ```
249
+
250
+ ### 5.2 Session 请求
251
+
252
+ ```typescript
253
+ type ClientRequestMap = {
254
+ create_session: {
255
+ request: {
256
+ sessionId?: SessionId;
257
+ meta: { projectId: ProjectId; title?: string; model?: string };
258
+ };
259
+ response: { sessionId: SessionId };
260
+ };
261
+ list_sessions: {
262
+ request: { projectId?: ProjectId; limit?: number };
263
+ response: { sessions: SessionSummary[] };
264
+ };
265
+ };
266
+ ```
267
+
268
+ `create_session` 必须拒绝:
269
+
270
+ - 缺少 `projectId`。
271
+ - `projectId` 不存在。
272
+ - client 试图直接提交 cwd。
273
+
274
+ ### 5.3 Connection Identity
275
+
276
+ Handshake 只报告 Device 身份:
277
+
278
+ ```typescript
279
+ type ConnectResult = {
280
+ clientId: ClientId;
281
+ sessionId?: SessionId;
282
+ currentSeq?: Seq;
283
+ deviceId: DeviceId;
284
+ deviceDisplayName?: string;
285
+ };
286
+ ```
287
+
288
+ Project 不属于 connection identity。Client 通过 `list_projects` 获取 Project 列表。
289
+
290
+ ---
291
+
292
+ ## 6. Transport
293
+
294
+ | Transport | 状态 | 场景 |
295
+ |---|---|---|
296
+ | Embedded | 已有 | `scorel chat` 临时 Host |
297
+ | WebSocket | 已有 | `scorel host serve`、WebUI、Direct WS |
298
+ | SSH stdio proxy | 后续 | GUI 远端 Device |
299
+ | HTTP + SSE | 后续 | 纯 API |
300
+
301
+ S0043 已删除 Unix socket transport。不要重新引入旧 socket 路径作为兼容层。
302
+
303
+ ### 6.1 SSH Proxy 边界
304
+
305
+ 后续 `scorel proxy`:
306
+
307
+ - 通过 SSH stdio 转发到远端 Host control endpoint。
308
+ - 不持有 Project。
309
+ - 不持有 Session。
310
+ - 不创建 Runtime。
311
+ - 不复制业务协议。
312
+
313
+ ### 6.2 HTTP 边界
314
+
315
+ 后续 HTTP adapter:
316
+
317
+ - REST 负责 command。
318
+ - SSE 负责 event stream。
319
+ - handler 调用同一 Host application service。
320
+ - 不直接读写 JSONL。
321
+
322
+ ---
323
+
324
+ ## 7. Entry 场景
325
+
326
+ ### 7.1 CLI Embedded
327
+
328
+ ```text
329
+ cd /repo
330
+ scorel
331
+ -> create temporary Host
332
+ -> registry.register(/repo)
333
+ -> createSession({ projectId })
334
+ -> DaemonClient over EmbeddedTransport
335
+ ```
336
+
337
+ ### 7.2 Local WebUI
338
+
339
+ ```text
340
+ scorel up
341
+ -> persistent WS Host
342
+ -> WebUI detects local Device
343
+ -> Add Project
344
+ -> listDirectories + registerProject
345
+ ```
346
+
347
+ ### 7.3 Remote WebUI
348
+
349
+ ```text
350
+ WebUI
351
+ -> connect Device URL + token
352
+ -> Add Project
353
+ -> browse remote Host directories
354
+ -> registerProject
355
+ ```
356
+
357
+ ### 7.4 GUI
358
+
359
+ ```text
360
+ Local Folder Picker
361
+ -> local Host.registerProject
362
+
363
+ SSH Device
364
+ -> scorel proxy
365
+ -> remote Host.listDirectories
366
+ -> remote Host.registerProject
367
+ ```
368
+
369
+ ---
370
+
371
+ ## 8. Diagnostics
372
+
373
+ 每个 Session 保留同目录 `.log`:
374
+
375
+ ```text
376
+ ~/.scorel/sessions/<sessionId>.log
377
+ ```
378
+
379
+ 需要记录:
380
+
381
+ - `project_registered`
382
+ - `project_removed`
383
+ - `session_created` + `projectId`
384
+ - `session_loaded` + `projectId`
385
+ - `runtime_created` + `projectId` + `workDir`
386
+ - connect/disconnect
387
+ - resync mode
388
+ - provider/runtime errors
389
+
390
+ 不得记录 token、API key、SSH password 或私钥内容。
391
+
392
+ ---
393
+
394
+ ## 9. 开发期破坏性重构
395
+
396
+ S0048 直接执行:
397
+
398
+ - 删除 `projectSlug` 和 `workDirHint` wire/schema。
399
+ - 删除 Session header 中旧 Project 字段。
400
+ - 删除从 JSONL 聚合 Project 的 `ProjectAggregator`。
401
+ - `protocolVersion` 增加。
402
+ - 本地开发环境需要清理旧 `~/.scorel/project-index.json`、旧 Session JSONL、旧 attach cache 和浏览器 localStorage。新的 `~/.scorel/projects.json` 由 Registry 创建。
403
+
404
+ 不写迁移器,不保留 fallback。
405
+
406
+ ---
407
+
408
+ *Host 的职责不是守住一个 cwd,而是守住一台 Device 上的 Project、Session 和 Runtime 一致性。*