grokbot-control 0.0.0-stage → 0.1.3

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 (122) hide show
  1. package/README.md +160 -2
  2. package/dist/auth/lock.d.ts +14 -0
  3. package/dist/auth/lock.js +71 -0
  4. package/dist/auth/provider.d.ts +89 -0
  5. package/dist/auth/provider.js +884 -0
  6. package/dist/auth/store.d.ts +25 -0
  7. package/dist/auth/store.js +106 -0
  8. package/dist/auth/types.d.ts +138 -0
  9. package/dist/auth/types.js +1 -0
  10. package/dist/cli/config.d.ts +26 -0
  11. package/dist/cli/config.js +98 -0
  12. package/dist/cli/help.d.ts +1 -0
  13. package/dist/cli/help.js +83 -0
  14. package/dist/cli/io.d.ts +6 -0
  15. package/dist/cli/io.js +61 -0
  16. package/dist/cli/main.d.ts +2 -0
  17. package/dist/cli/main.js +275 -0
  18. package/dist/cli/sync.d.ts +4 -0
  19. package/dist/cli/sync.js +105 -0
  20. package/dist/connect/client.d.ts +32 -0
  21. package/dist/connect/client.js +257 -0
  22. package/dist/dto/result.d.ts +33 -0
  23. package/dist/dto/result.js +29 -0
  24. package/dist/dto/types.d.ts +63 -0
  25. package/dist/dto/types.js +2 -0
  26. package/dist/errors.d.ts +31 -0
  27. package/dist/errors.js +64 -0
  28. package/dist/index.d.ts +13 -0
  29. package/dist/index.js +11 -0
  30. package/dist/mcp/main.d.ts +2 -0
  31. package/dist/mcp/main.js +82 -0
  32. package/dist/mcp/tools.d.ts +24 -0
  33. package/dist/mcp/tools.js +408 -0
  34. package/dist/mcp/zod-json.d.ts +2 -0
  35. package/dist/mcp/zod-json.js +4 -0
  36. package/dist/ops/auth.d.ts +48 -0
  37. package/dist/ops/auth.js +89 -0
  38. package/dist/ops/bot.d.ts +52 -0
  39. package/dist/ops/bot.js +211 -0
  40. package/dist/ops/limits.d.ts +10 -0
  41. package/dist/ops/limits.js +31 -0
  42. package/dist/ops/map.d.ts +7 -0
  43. package/dist/ops/map.js +57 -0
  44. package/dist/ops/message.d.ts +33 -0
  45. package/dist/ops/message.js +118 -0
  46. package/dist/ops/plane.d.ts +26 -0
  47. package/dist/ops/plane.js +33 -0
  48. package/dist/ops/progress.d.ts +34 -0
  49. package/dist/ops/progress.js +92 -0
  50. package/dist/ops/session.d.ts +13 -0
  51. package/dist/ops/session.js +34 -0
  52. package/dist/ops/sidebar.d.ts +70 -0
  53. package/dist/ops/sidebar.js +205 -0
  54. package/dist/sync/attachments.d.ts +76 -0
  55. package/dist/sync/attachments.js +274 -0
  56. package/dist/sync/core.d.ts +92 -0
  57. package/dist/sync/core.js +1027 -0
  58. package/dist/sync/decode.d.ts +83 -0
  59. package/dist/sync/decode.js +229 -0
  60. package/dist/sync/download-policy.d.ts +3 -0
  61. package/dist/sync/download-policy.js +103 -0
  62. package/dist/sync/fs.d.ts +24 -0
  63. package/dist/sync/fs.js +252 -0
  64. package/dist/sync/hydrate.d.ts +43 -0
  65. package/dist/sync/hydrate.js +472 -0
  66. package/dist/sync/render.d.ts +5 -0
  67. package/dist/sync/render.js +100 -0
  68. package/dist/testing/mock-persist.d.ts +3 -0
  69. package/dist/testing/mock-persist.js +45 -0
  70. package/dist/testing/mock-transport.d.ts +18 -0
  71. package/dist/testing/mock-transport.js +202 -0
  72. package/docs/acceptance.md +19 -0
  73. package/docs/architecture.md +128 -0
  74. package/docs/auth-lock.md +87 -0
  75. package/docs/implementation-plan.md +14 -0
  76. package/docs/implementation-status.md +92 -0
  77. package/docs/proto-drift.md +9 -0
  78. package/docs/protocol/README.md +34 -0
  79. package/docs/protocol/attachment-pagination-0.79.3.md +32 -0
  80. package/docs/protocol/auth-flow.md +51 -0
  81. package/docs/protocol/baseline-manifest.md +72 -0
  82. package/docs/protocol/baselines/full-schema-diff-0.78.4-to-0.79.2.txt +239 -0
  83. package/docs/protocol/baselines/full-schema-diff-0.78.4-to-0.79.3.txt +240 -0
  84. package/docs/protocol/baselines/full-schema-diff-0.79.2-to-0.79.3.txt +23 -0
  85. package/docs/protocol/baselines/schema-0.79.3/README.md +7 -0
  86. package/docs/protocol/baselines/schema-0.79.3/agent.v1.proto +5184 -0
  87. package/docs/protocol/baselines/schema-0.79.3/aiserver.v1.proto +18182 -0
  88. package/docs/protocol/baselines/schema-0.79.3/aiserver.v1_2.proto +54 -0
  89. package/docs/protocol/baselines/schema-0.79.3/anyrun.v1.proto +37 -0
  90. package/docs/protocol/baselines/schema-0.79.3/internapi.v1.proto +35 -0
  91. package/docs/protocol/baselines/schema-0.79.3/snapshot.json.gz +0 -0
  92. package/docs/protocol/baselines/schema-0.79.3/svc_aiserver.v1.AgentStoreService.proto +34 -0
  93. package/docs/protocol/baselines/schema-0.79.3/svc_aiserver.v1.AiService.proto +17 -0
  94. package/docs/protocol/baselines/schema-0.79.3/svc_aiserver.v1.AnalyticsService.proto +17 -0
  95. package/docs/protocol/baselines/schema-0.79.3/svc_aiserver.v1.BackgroundComposerService.proto +19 -0
  96. package/docs/protocol/baselines/schema-0.79.3/svc_aiserver.v1.DashboardService.proto +101 -0
  97. package/docs/protocol/baselines/schema-0.79.3/svc_aiserver.v1.GrokBotService.proto +473 -0
  98. package/docs/protocol/baselines/schema-0.79.3/svc_aiserver.v1.SandBoxService.proto +64 -0
  99. package/docs/protocol/baselines/used-surface-0.78.4.json +3694 -0
  100. package/docs/protocol/baselines/used-surface-0.79.2.json +3835 -0
  101. package/docs/protocol/baselines/used-surface-0.79.3.json +3835 -0
  102. package/docs/protocol/baselines/used-surface-diff-0.78.4-to-0.79.2.md +34 -0
  103. package/docs/protocol/baselines/used-surface-diff-0.78.4-to-0.79.3.md +33 -0
  104. package/docs/protocol/compat-matrix.md +20 -0
  105. package/docs/protocol/static-restore.md +64 -0
  106. package/docs/protocol/surface-map.md +39 -0
  107. package/docs/protocol/transcript-body-0.79.3.md +98 -0
  108. package/docs/protocol/update-runbook.md +30 -0
  109. package/docs/protocol-map.md +106 -0
  110. package/docs/references.md +55 -0
  111. package/docs/requirements.md +26 -0
  112. package/docs/review.md +36 -0
  113. package/docs/sync-format.md +98 -0
  114. package/package.json +60 -4
  115. package/vendor/proto-gen/NOTICE.md +23 -0
  116. package/vendor/proto-gen/agent.v1_pb.js +9503 -0
  117. package/vendor/proto-gen/aiserver.v1_2_pb.js +105 -0
  118. package/vendor/proto-gen/aiserver.v1_pb.js +33163 -0
  119. package/vendor/proto-gen/anyrun.v1_pb.js +64 -0
  120. package/vendor/proto-gen/internapi.v1_pb.js +60 -0
  121. package/vendor/proto-gen/package.json +10 -0
  122. package/vendor/proto-gen/svc_aiserver.v1.GrokBotService_connect.js +4193 -0
@@ -0,0 +1,18 @@
1
+ import type { SidebarSection } from "../dto/types.js";
2
+ export type MockState = {
3
+ bots: Array<Record<string, unknown>>;
4
+ sections: SidebarSection[];
5
+ /** Force next sidebar update to conflict. */
6
+ forceSidebarConflict?: boolean;
7
+ /** Drop next write response (simulate unknown). */
8
+ dropNextWrite?: boolean;
9
+ messages: Array<Record<string, unknown>>;
10
+ statuses: Array<Record<string, unknown>>;
11
+ sessions: Array<Record<string, unknown>>;
12
+ todos: unknown[];
13
+ tasks: unknown[];
14
+ subagents: unknown[];
15
+ transcript: unknown[];
16
+ };
17
+ export declare function createEmptyMockState(): MockState;
18
+ export declare function createMockTransport(state: MockState): import("@connectrpc/connect").Transport;
@@ -0,0 +1,202 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * Explicit simulated backend using Connect createRouterTransport.
4
+ * Never claims live success — ControlPlane must set simulated: true.
5
+ */
6
+ import { createRouterTransport } from "@connectrpc/connect";
7
+ import { Code, ConnectError } from "@connectrpc/connect";
8
+ import { GrokBotService } from "#proto-gen/GrokBotService";
9
+ export function createEmptyMockState() {
10
+ return {
11
+ bots: [],
12
+ sections: [],
13
+ messages: [],
14
+ statuses: [],
15
+ sessions: [],
16
+ todos: [],
17
+ tasks: [],
18
+ subagents: [],
19
+ transcript: [],
20
+ };
21
+ }
22
+ export function createMockTransport(state) {
23
+ return createRouterTransport(({ service }) => {
24
+ service(GrokBotService, {
25
+ async listGrokBotAgents() {
26
+ return { agents: state.bots, truncated: false, slackConnections: [], teamsConnections: [] };
27
+ },
28
+ async createGrokBotTemporalAgent(req) {
29
+ if (state.dropNextWrite) {
30
+ state.dropNextWrite = false;
31
+ throw new ConnectError("simulated drop", Code.DeadlineExceeded);
32
+ }
33
+ const agent = {
34
+ id: req.agentId,
35
+ agentId: req.agentId,
36
+ name: req.name,
37
+ description: req.description,
38
+ title: req.title,
39
+ harness: "TEMPORAL",
40
+ role: "agent",
41
+ kind: 1,
42
+ viewerSessionId: `sess-${req.agentId}`,
43
+ createdAtMs: BigInt(Date.now()),
44
+ updatedAtMs: BigInt(Date.now()),
45
+ avatarShape: req.avatarShape,
46
+ avatarColor: req.avatarColor,
47
+ legacyAgentId: "",
48
+ memberAgentIds: [],
49
+ teamDefaultForViewer: false,
50
+ suggestions: [],
51
+ viewerAllowedActions: [],
52
+ people: [],
53
+ };
54
+ state.bots.push(agent);
55
+ state.sessions.push({
56
+ agentId: req.agentId,
57
+ sessionId: `sess-${req.agentId}`,
58
+ kind: 1,
59
+ createdAtMs: BigInt(Date.now()),
60
+ updatedAtMs: BigInt(Date.now()),
61
+ });
62
+ state.statuses.push({
63
+ agentId: req.agentId,
64
+ isRunning: false,
65
+ live: { agentId: req.agentId, isRunning: false, sessionId: `sess-${req.agentId}` },
66
+ });
67
+ return { agent, harness: 2 };
68
+ },
69
+ async createGrokBotAgent(req) {
70
+ return this.createGrokBotTemporalAgent(req);
71
+ },
72
+ async updateGrokBotAgent(req) {
73
+ const b = state.bots.find((x) => x.agentId === req.id || x.id === req.id);
74
+ if (!b)
75
+ throw new ConnectError("not found", Code.NotFound);
76
+ if (req.name != null)
77
+ b.name = req.name;
78
+ if (req.description != null)
79
+ b.description = req.description;
80
+ if (req.title != null)
81
+ b.title = req.title;
82
+ return { agent: b };
83
+ },
84
+ async deleteGrokBotAgent(req) {
85
+ if (state.dropNextWrite) {
86
+ state.dropNextWrite = false;
87
+ throw new ConnectError("simulated drop", Code.DeadlineExceeded);
88
+ }
89
+ const before = state.bots.length;
90
+ state.bots = state.bots.filter((b) => b.agentId !== req.id && b.id !== req.id);
91
+ if (state.bots.length === before)
92
+ throw new ConnectError("not found", Code.NotFound);
93
+ return {};
94
+ },
95
+ async sendGrokBotUserMessage(req) {
96
+ if (state.dropNextWrite) {
97
+ state.dropNextWrite = false;
98
+ throw new ConnectError("simulated drop", Code.DeadlineExceeded);
99
+ }
100
+ state.messages.push({ ...req });
101
+ state.transcript.push({
102
+ id: `tr-${req.messageId}`,
103
+ kind: "user",
104
+ plaintext: req.text,
105
+ });
106
+ return { dispatched: true, delivery: 1 };
107
+ },
108
+ async getGrokBotSendStatus(req) {
109
+ const found = state.messages.find((m) => m.messageId === req.messageId);
110
+ return {
111
+ status: found ? 2 : 0,
112
+ acceptedAtMs: found ? BigInt(Date.now()) : undefined,
113
+ };
114
+ },
115
+ async listGrokBotTranscriptEntries() {
116
+ return { entries: state.transcript, generation: 1 };
117
+ },
118
+ async listGrokBotAgentSessions(req) {
119
+ return {
120
+ sessions: state.sessions.filter((s) => s.agentId === req.agentId),
121
+ };
122
+ },
123
+ async listGrokBotAgentStatuses(req) {
124
+ const ids = new Set(req.agentIds);
125
+ return {
126
+ statuses: state.statuses.filter((s) => ids.has(String(s.agentId))),
127
+ };
128
+ },
129
+ async listGrokBotAgentTodos() {
130
+ return { todos: state.todos };
131
+ },
132
+ async listGrokBotAgentTasks() {
133
+ return { tasks: state.tasks };
134
+ },
135
+ async listGrokBotSubagents() {
136
+ return { subagents: state.subagents };
137
+ },
138
+ async interruptGrokBotAgentRun() {
139
+ return { hadActiveRun: true };
140
+ },
141
+ async getGrokBotUserRuntimeSettings() {
142
+ return {
143
+ settings: {
144
+ sidebarSections: {
145
+ sections: state.sections.map((s) => ({
146
+ id: s.id,
147
+ name: s.name,
148
+ agentIds: s.agent_ids,
149
+ })),
150
+ },
151
+ updatedAtMs: BigInt(Date.now()),
152
+ emailProvider: 0,
153
+ xToolAccess: 0,
154
+ },
155
+ };
156
+ },
157
+ async updateGrokBotUserRuntimeSettings(req) {
158
+ if (state.forceSidebarConflict) {
159
+ state.forceSidebarConflict = false;
160
+ throw new ConnectError("SidebarSectionsSaveConflictError", Code.FailedPrecondition);
161
+ }
162
+ if (state.dropNextWrite) {
163
+ state.dropNextWrite = false;
164
+ throw new ConnectError("simulated drop", Code.DeadlineExceeded);
165
+ }
166
+ // Best-effort base check
167
+ if (req.sidebarSectionsBase) {
168
+ const base = JSON.stringify((req.sidebarSectionsBase.sections ?? []).map((s) => ({
169
+ id: s.id,
170
+ name: s.name,
171
+ agent_ids: s.agentIds,
172
+ })));
173
+ const cur = JSON.stringify(state.sections);
174
+ if (base !== cur) {
175
+ throw new ConnectError("SidebarSectionsSaveConflictError", Code.FailedPrecondition);
176
+ }
177
+ }
178
+ if (req.sidebarSections) {
179
+ state.sections = (req.sidebarSections.sections ?? []).map((s) => ({
180
+ id: s.id,
181
+ name: s.name,
182
+ agent_ids: [...(s.agentIds ?? [])],
183
+ }));
184
+ }
185
+ return {
186
+ settings: {
187
+ sidebarSections: {
188
+ sections: state.sections.map((s) => ({
189
+ id: s.id,
190
+ name: s.name,
191
+ agentIds: s.agent_ids,
192
+ })),
193
+ },
194
+ updatedAtMs: BigInt(Date.now()),
195
+ emailProvider: 0,
196
+ xToolAccess: 0,
197
+ },
198
+ };
199
+ },
200
+ });
201
+ });
202
+ }
@@ -0,0 +1,19 @@
1
+ # 验收(细化)
2
+
3
+ ## 理念
4
+
5
+ App 能做且纳入范围 → MCP 与 CLI **等价完成**(保留服务端权限/人工确认)。不模仿 GUI。
6
+
7
+ ## 必含场景(真机另授;本轮不执行)
8
+
9
+ 1. 独立 test bot:create→list/get 可见
10
+ 2. send→send_status/history 有进展(delivery≠业务成功分列)
11
+ 3. status/todos 或 tasks 可读;interrupt 需确认
12
+ 4. sidebar:改 sections→读回 verify;故意冲突→conflict;**删 section 后 bot 仍在**
13
+ 5. delete confirm;token 过期/组织策略拒绝路径
14
+ 6. 丢 send 响应→unknown,禁止自动重发
15
+ 7. 同剧本 CLI 与 MCP 各一遍;无 TTY + `--json`
16
+
17
+ ## 证据分层
18
+
19
+ mock/离线 ≠ 交付;协议映射 ≠ 交付;真机 = 该能力完成。
@@ -0,0 +1,128 @@
1
+ # 架构(细化)
2
+
3
+ ## 1. 定位
4
+
5
+ 人原先在 **Grok Bot App** 操作;本项目提供 **MCP + CLI** 可编程控制面,经 **Connect 原生 RPC** 驱动同一后端。
6
+ 上层 agent CLI 负责需求沟通与编排;本层**不**强制自建 task 账本。
7
+
8
+ ```
9
+ 上层 agent CLI / 人
10
+ ├─ MCP adapter (stdio…) ─┐
11
+ └─ CLI adapter (示意 gbc) ─┴─► shared SDK
12
+ ├─ auth/credentials(独立 store)
13
+ ├─ connect client + interceptors
14
+ ├─ ops: bot | message | session | sidebar | progress
15
+ └─ outcome/error 归一
16
+ │
17
+ ▼
18
+ GrokBotService / DashboardService
19
+ ```
20
+
21
+ ## 2. 建议模块与目录(设计,未实现)
22
+
23
+ ```
24
+ src/
25
+ auth/ # bootstrap, store, refresh single-flight, logout(本地)
26
+ connect/ # transport, headers, deadline, error map
27
+ ops/
28
+ bot.ts # list/create/get(via list)/update/delete
29
+ message.ts # send/status/history
30
+ session.ts
31
+ sidebar.ts # sections read/patch/verify
32
+ progress.ts # status/todos/tasks/subagents/interrupt
33
+ room.ts # 显式分列,非默认分组
34
+ dto/ # 稳定 JSON schema_version
35
+ cli/ # 薄适配
36
+ mcp/ # 薄适配 + toolsets
37
+ ```
38
+
39
+ **禁止默认提供:** 任意 `raw_rpc`、Admin/Internal 代理、Vault 明文工具。
40
+
41
+ ## 3. 原生 ID 与命名
42
+
43
+ | 概念 | 协议字段 | SDK/DTO 建议 |
44
+ |---|---|---|
45
+ | Bot | `GrokBotAgent.agent_id` 与 `id` 均存在 | 对外主字段 `agent_id`;写入时与 App 一样以客户端 nonce 预分配(create) |
46
+ | Session | `viewer_session_id` / 请求 `session_id` | `session_id`;默认:若未传则用 list 中该 bot 的 `viewer_session_id`(**待真机确认始终有效**) |
47
+ | Message | 客户端生成 `message_id` | UUID;**丢响应后不自动重发同 id 除非有服务端幂等证据**(未证实前不承诺) |
48
+ | Transcript cursor | `generation` / seq | DTO 保留;Watch 短生命周期 |
49
+ | Sidebar section | `id`,`name`,`agent_ids` | 删除 section ≠ 删除 bot |
50
+
51
+ **无独立 GetBot RPC:** `bot.get` = `ListGrokBotAgents` 后客户端按 `agent_id` 筛选;找不到→`not_found`。
52
+
53
+ ## 4. 统一结果契约(CLI `--json` 与 MCP structuredContent)
54
+
55
+ ```json
56
+ {
57
+ "schema_version": "1.0.0",
58
+ "ok": true,
59
+ "outcome": "known_success | known_failure | unknown",
60
+ "code": "STRING_CODE",
61
+ "retryable": false,
62
+ "request_id": "uuid",
63
+ "data": {},
64
+ "error": { "message": "", "hint": "" }
65
+ }
66
+ ```
67
+
68
+ - MCP:业务失败优先 `isError: true` + 同上 JSON 文本/structuredContent(对齐 MCP tools 规范)。
69
+ - CLI:stdout 仅结果 JSON/文本;错误 JSON→stderr;退出码表见下。
70
+ - **MCP stdio 进程禁止打印 CLI 风格日志到 stdout。**
71
+
72
+ ### CLI 退出码(设计决定;写入 --help 后不改含义)
73
+
74
+ | 码 | 含义 |
75
+ |---:|---|
76
+ | 0 | known_success |
77
+ | 2 | usage |
78
+ | 4 | auth |
79
+ | 5 | not_found |
80
+ | 6 | conflict(含 sidebar base 冲突) |
81
+ | 7 | denied / dry-run 外的写拒绝 |
82
+ | 8 | retryable(瞬时) |
83
+ | 9 | known_failure |
84
+ | 10 | unknown outcome(丢响应、证据不足) |
85
+ | 1 | 其它错误 |
86
+
87
+ ## 5. 写安全
88
+
89
+ - 删除/破坏性:`--confirm=<agent_id>`(或资源名)+ 可选 `--dry-run`(**纯**无 RPC)
90
+ - create/send/delete:**超时/取消后不自动重试写**;引导 `list`/`get_send_status`/transcript 对账;无法判定→`unknown`
91
+ - idempotency:**仅当真机证明**服务端对 `message_id`/`agent_id` 去重后,才在文档承诺;此前标「客户端尽最大努力」
92
+ - sidebar:read → 本地改最小 sections 集合 → write(sections, base=读到的快照) → read verify;进程内锁;**不声称跨 App 事务**
93
+
94
+ ## 6. Auth
95
+
96
+ - 独立 bootstrap(browser URL + 内存 verifier + poll);secure store;refresh 跨进程单飞锁
97
+ - account scope 与 `GBT_BASE_URL`(或配置 backend)绑定;自定义 backend 需显式信任确认,防 token 打到错误域(RFC9700 / 鉴权报告)
98
+ - Cursor vs xAI kind 分离;logout 只清本工具凭据
99
+ - 无 auto-approve;不读桌面 sand-secrets
100
+
101
+ ## 7. 进展模型
102
+
103
+ | 层 | 含义 | 不可当作 |
104
+ |---|---|---|
105
+ | delivery / send_status | 发送路径接受 | 任务业务成功 |
106
+ | live running / awaiting | App 状态条 | 最终用户可见完成 |
107
+ | transcript 助手可见消息 | 用户可见进展 | 自动「验收通过」 |
108
+ | Interrupt | 中断 run | MCP cancelled 通知 |
109
+
110
+ Watch:按需、有界、可取消订阅;断连/CLI 退出 **默认不** Interrupt。
111
+
112
+ ## 8. 工具面治理(借鉴 GitHub MCP)
113
+
114
+ 建议 toolsets:`auth` / `bot` / `message` / `sidebar` / `progress` / `room(opt)`;默认启用前四+progress;`--read-only` 跳过写工具。
115
+
116
+ ## 9. CLI 设计:规范 / 本项目决定 / 待验证
117
+
118
+ | 项 | 归类 | 说明 |
119
+ |---|---|---|
120
+ | stdout=数据、stderr=诊断;无 TTY 不阻塞 | **规范要求**(agent-cli-design / clig 共识) | MCP stdio 进程另遵守 MCP transports,禁止夹带 CLI JSON |
121
+ | 退出码分类表写入 --help 后不改 | **规范要求** | 见 §4 |
122
+ | `--json` 字段稳定 + schema_version | **规范要求** | 人类文本可迭代 |
123
+ | 列表默认 limit + truncated hint | **规范要求** | |
124
+ | dry-run 纯 + `--confirm=<id>` | **规范要求**(破坏性确认共识) | 避免万能 `--yes` |
125
+ | 默认输出文本还是 JSON | **有争议**→**本项目决定**:人机默认文本,脚本/agent 显式 `--json` | |
126
+ | 幂等键对 create/send | **待验证** | 未证实服务端去重前不承诺 |
127
+ | `--agent` / `--no-input` 命名 | **有争议** | 先用 isatty + 无提示;命名后定 |
128
+ | 默认只读全局开关 | **有争议**→倾向 MCP `--read-only` toolset;CLI 写操作自带 confirm | |
@@ -0,0 +1,87 @@
1
+ # Native auth locks (t50u)
2
+
3
+ ## Selection and evidence
4
+
5
+ `fs-native-extensions@1.5.3` (exact runtime dependency, Apache-2.0), upstream
6
+ [holepunchto/fs-native-extensions](https://github.com/holepunchto/fs-native-extensions/tree/8abcff878845fc0137360d76b1a821728ed8e0bf).
7
+ Inspected npm tarball sources, loader, package license and packaged prebuilds; not merely npm platform claims.
8
+
9
+ | Platform | Source | Nonblocking exclusive primitive | Packaged Node prebuilds |
10
+ |---|---|---|---|
11
+ | Linux | `src/linux.c:21–29` | `fcntl(F_OFD_SETLK)` / `F_WRLCK` | x64, arm64 |
12
+ | macOS | `src/apple.c:18` | `flock(LOCK_EX\|LOCK_NB)` | x64, arm64 |
13
+ | Windows | `src/win32.c:14–36` | `LockFileEx(LOCKFILE_EXCLUSIVE_LOCK\|LOCKFILE_FAIL_IMMEDIATELY)` | x64, arm64 |
14
+
15
+ `binding.c` converts Node fd with `uv_get_osfhandle`; N-API version 9 (`CMakeLists.txt:91–108`),
16
+ Node 20+ required. `index.js` returns false on EAGAIN; adapter also handles EBUSY contention.
17
+ `binding.js` uses `require-addon`; the npm tarball contains prebuilds (no postinstall script or runtime downloader).
18
+ Source build uses CMake >=3.25, C compiler/MSVC and upstream cmake-bare/cmake-fetch/cmake-napi/cmake-npm,
19
+ libuv 1.51.0 and bare-compat-napi. No Rust toolchain is required.
20
+
21
+ Compared with GrokBuild `xai-org/grok-build@2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8`,
22
+ `crates/codegen/xai-grok-login/src/manager/lock.rs` and Cargo `fs2=0.4`:
23
+ permanent independent lock, OS-exclusive ownership across exchange, reread/adopt sibling rotation,
24
+ in-process singleflight. Linux OFD record locks have the same needed ownership/crash-release semantics,
25
+ **but are not the same lock namespace as flock**. This is not an fs2 binding and does not interoperate
26
+ with old util-linux-flock clients. Stop **all old MCP/CLI processes before upgrading**; do not mix versions
27
+ against one credential directory. No attempt to coordinate with GrokBuild's separate credentials.
28
+
29
+ ## Runtime contract
30
+
31
+ `src/auth/lock.ts` isolates `AdvisoryLockBinding` from auth/provider/store. Default loads native binding
32
+ lazily. Missing/unsupported binding fails closed (`auth_lock_unavailable`) with installation guidance;
33
+ no mkdir, mtime lease, external executable, daemon, helper process, or silent fallback.
34
+
35
+ Permanent lock files are opened O_CREAT|O_RDWR, never truncated/replaced/unlinked. Unix uses O_NOFOLLOW,
36
+ regular-file/nlink checks and inode recheck after acquisition. File descriptors belong directly to the
37
+ requesting Node process; no child/helper can outlive the owner holding its lock. Node's file opening uses
38
+ non-inheritable/CLOEXEC handles; no fd is exported to child stdio. Unlock/close in finally; process crash
39
+ releases OS locks. A backdated mtime never authorizes takeover. Do not manually delete lock files.
40
+
41
+ Lock order: exchange `singleflight|kind|backend` → mutation `kind|backend`. Refresh holds exchange across
42
+ snapshot, HTTP and conditional save; mutation is held only for short reread/CAS/revoke operations.
43
+ Login/logout acquire only mutation, so they do not wait on network or reverse the order. Force captures
44
+ entry revision/session: a sibling's usable rotation satisfies that force. Changed account/session fails
45
+ rather than returning credentials for a different login. All cooperating writers must use this protocol.
46
+
47
+ Try-lock + async 25ms wait keeps Node's event loop responsive. Wait budget defaults to 30s; AbortSignal
48
+ cancels only waiting, not an acquired exchange. The caller-visible HTTP budget is <=20s, but the IdP
49
+ exchange is never aborted mid-flight (Grok Build BoundedRefresh semantics): on deadline the caller gets
50
+ `outcome=unknown` / `rpc_dispatched=unknown` while the exchange continues under singleflight and still
51
+ persists a successful rotation. A follow-up `invalid_grant` inside that ambiguous window does not wipe
52
+ credentials. Transient cooldown is per `session_id` (memory + disk); a re-login must not inherit the
53
+ previous session's cooldown. Wait/deadline failures are structured and do not retry forever.
54
+
55
+ ## Install/build/distribution
56
+
57
+ Normal users: `npm ci` (or `npm ci --ignore-scripts`) installs exact package + integrity-pinned tarball
58
+ from package-lock.json, including publisher-supplied prebuilds; no compiler/Rust normally needed.
59
+ No binary is committed here. The dependency is required (not optional); missing platform binary is a
60
+ runtime fail-closed error, not a lease downgrade. Non-listed targets (including unverified musl variants)
61
+ are unsupported until a source build and tests pass.
62
+
63
+ Source-build recipe (maintainer/CI, **not automatic install fallback**): in the installed
64
+ `node_modules/fs-native-extensions`, install upstream devDependencies (`npm install --include=dev
65
+ --ignore-scripts`), install upstream bare-make toolchain, remove shipped prebuilds, then run
66
+ `bare-make generate --platform <linux|darwin|win32> --arch <x64|arm64>`, `bare-make build`,
67
+ `bare-make install --prefix prebuilds`. This is upstream's
68
+ [compile-prebuilds action](https://github.com/holepunchto/actions/blob/v1/compile-prebuilds/action.yml).
69
+ Our `.github/workflows/auth-native-lock.yml` configures prebuilt Node20/22 and source-build jobs for
70
+ Linux/macOS/Windows, with no upload/publish; every job runs real-process lifecycle/exchange tests.
71
+ Source-building requires network/toolchain dependencies; npm normal install has no source fallback script.
72
+
73
+ ## Security/platform limits and verification
74
+
75
+ - Local private filesystem only. NFS/SMB/cloud-synced credentials and concurrent uncooperative writers are
76
+ not supported. Lock files must not be renamed/deleted by operators while clients are running.
77
+ - POSIX directory0700/file0600 are enforced best effort as before. Windows mode bits do **not** establish
78
+ an equivalent ACL: use a user-private credential directory and verify NTFS ACLs independently.
79
+ - Linux x64 packaged native prebuild tested locally: 4 actual Node processes → one refresh HTTP; force
80
+ coalescing; revision adoption; actual provider logout/login child processes; owner SIGKILL recovery;
81
+ timeout release; terminal invalid_grant. Native tests additionally cover backdated live-owner mtime,
82
+ responsive event loop, canceled/bounded waits, permanent inode, callback failure and absent binding.
83
+ - macOS/Windows sources and packaged binaries inspected, CI matrix configured; **not run here**. Source
84
+ rebuild jobs are configured but **not locally executed**. No cross-platform execution success claimed.
85
+ - npm audit currently reports four existing dependency advisories (connect-node/busboy/undici/zod);
86
+ none is introduced by this native lock dependency. No broad dependency upgrade performed.
87
+ - OAuth live refresh remains untested/unexecuted; mock HTTP is not live protocol evidence.
@@ -0,0 +1,14 @@
1
+ # 实施分期(每期 CLI+MCP 同步)
2
+
3
+ 不实现本轮。原始功能不得静默缩水。
4
+
5
+ | 阶段 | 内容 | 出口(另授真机后) |
6
+ |---|---|---|
7
+ | **S0** | 鉴权 bootstrap;0.78.4↔0.79.2;Create/Sidebar 冲突码;session 默认规则 | 只读+创建字段矩阵实测记录 |
8
+ | **S1** | bot CRUD + auth whoami;dry-run/confirm;toolsets | App 级 bot 管理可编程 |
9
+ | **S2** | message send/status/history;session;progress+interrupt;outcome=unknown 路径 | 主对话闭环 |
10
+ | **S3** | sidebar sections patch+verify+conflict;Room 独立命令(可选启用) | 分组读回验证通过 |
11
+
12
+ **P1 可选:** Watch 封装、toolset 细拆、HTTP MCP、观测辅助——不挡 S1–S2。
13
+
14
+ **每期测试:** MCP/CLI 参数语义等价;无 TTY/JSON;mock 仅离线;真机用独立 test bot。
@@ -0,0 +1,92 @@
1
+ # 实现状态(2026-11-07,SGT)
2
+
3
+ ## Token 自动刷新加固(t47u,未提交)
4
+ 基于 main@04c1893 本地实现,对照 App 0.79.3 分析最小可靠状态机:
5
+ - 凭据 provenance/revision/session 绑定;client_id 仅 GBT_OAUTH_CLIENT_ID 或登录持久化,不硬编码、不从 JWT 猜。
6
+ - getAccessToken:缺/非法 exp、临期<5min、force → 刷新;进程内 singleflight + 跨进程锁;锁内重读复用。
7
+ - 刷新:固定 HTTPS `/oauth/token`、禁 redirect、20s、schema 校验;CAS 保存旋转 pair。
8
+ - 失败分类:网络/5xx/429 保留+cooldown;shouldLogout 锁内救援后再清,绝不回退已撤销 token。
9
+ - logout/login 与 refresh 共享锁;Linux kernel flock 分离 refresh/mutation 两把锁;不按 mtime 强拆、不 unlink 锁。
10
+ - CLI `auth refresh [--force]` 区分 already_fresh / 实际刷新;whoami 离线;MCP `auth_refresh` 非 READONLY。
11
+ - RPC401:更新凭据供下次,原错误返回;写 0 重放;只读白名单最多 1 次 refresh+retry;预 RPC 失败 `rpc_dispatched=false`。
12
+ - staticTokenProvider 永不自动 refresh / 无磁盘 refresh。
13
+ 默认不做 live refresh 验收(mock 覆盖)。
14
+
15
+ # 实现状态(2026-11-07,SGT)
16
+
17
+ 前轮修订基于939e4f7;正文补读本轮基于 main@8a6cf21,以下分别记录,不扩大 live 声称。
18
+
19
+ ## 本轮修复
20
+ - 保留前次5个未提交修复:update/delete通过List将UUID映射server-id;默认会话使用wire空session和sessions/default。
21
+ - store alias严格规范化两App root/file URL,refs使用session/path SHA-256,中文文件名仅元数据;不读本机alias路径。
22
+ - streaming staging/hash/大小/总预算/截止时间、strict STORE/HTTP、403最多重Resolve一次、公开HTTPS DNS pinning、无Bearer给下载URL。
23
+ - attachments绑定backend/声明account/team/ack;旧无作用域refs不直接信任;scope仍operator_asserted_unverified。
24
+ - user-attachment与send-message attachment两种已证实来源;外部URL/未知路径unsupported。
25
+ - 每次Resolve+条件GET,304先本地完整性检验,远端etag/size匹配才复用;旧对象保留历史。
26
+ - head刷新与generation内backfill游标分离,单页调用交替,两种session跨调用轮转;budget耗尽不推进未处理session。
27
+ - JSONL/data-before-state恢复、64MiB边界、partial/failure结构化;视图标注retained observations,非当前镜像。
28
+
29
+ ## 回归
30
+ 前轮 npm test **128/128**;正文补读后 **174/174**(原128项保留,新增约46项(含starve/merge/partial/gen0/体积回归))。build/typecheck通过;test:protocol-tools **5/5**;git diff --check通过。
31
+ 测试覆盖scope拒绝、严格路径/URL/metadata、超限stream取消/失败字节计数、截止时间、403/304/同路径版本、坏对象重下载、symlink、backfill进展/公平/崩溃恢复。
32
+
33
+ ## 隔离真实后端验收
34
+ 证据在仓库外/workspace/gbc-live-fix-20261107132918(0700),不得提交真实archive。
35
+ 只创建本轮唯一临时bot,发送1条测试指令,没有修改/向现有bot发消息,没有修改共享sidebar或联系第三方。
36
+ - 真实默认会话generation0:page-size=1/max-pages=1,8次pull,4个唯一seq。
37
+ - 实际before边界排他、0 generation省略;backfill cursor从4→3→2→1→空页观察,重抓head不阻止backfill进展。
38
+ - bot发出send-message attachment,file:// alias,显示中文名“验收测试.txt”。下载36 bytes,固定测试标记/中文/本地hash均匹配。
39
+ - 第二次真实Resolve+条件GET得到not_modified,body 0 bytes;同hash对象复用。render包含本地ref;status checksum一致。
40
+ - 实际CLI status/render成功;MCP readonly sync_status成功且不暴露写工具。
41
+ - finally删除临时bot,最终27个bot与初始27个ID相同,测试ID不在列表,初始缺失0。
42
+
43
+ ## 仍未覆盖(不得扩大声称)
44
+ 真实服务没有返回命名session,本轮命名+default公平只有回归测试证据;没有凭空创建不确定会话。
45
+ 真实user-attachment上传、同路径可变覆盖、URL403/过期、超限下载等没有live执行;有mock/静态覆盖不等于live通过。
46
+ 未实现Watch/deletes/clear/cursorTooOld实时补偿、chunk fallback、artifact/任意workspace/外链下载;omitted/blob 已可选补读,live 来源限制见下。
47
+ 全历史行/正文/附件完整性仍unknown;不证明跨页事务快照一致,不保证头页之外旧行变更被刷新。
48
+ 账户ID无verified whoami,本次仅隔离验收声明namespace,不冒充server_verified稳定身份;操作者必须自行核对生产账户。
49
+
50
+ ## 正文补读本轮(main@8a6cf21 上未提交修改)
51
+ - shared SyncOps + CLI/MCP `hydrate=off|missing`,默认off,pure dry-run/status/render 无RPC;额外API/page统计与共享总字节/截止时间,single body硬4MiB。
52
+ - `src/sync/hydrate.ts`:inline优先、同session/generation/seq精确omitted List与updated_seq/entry_id/kind验证;uint64 max无successor拒绝;blob只使用观察ref和store presign严格逐路径映射,无猜hash算法/任意URL。
53
+ - 原始wire/protojson不覆盖;hydration记录独立保存manifest scope摘要、entry版本binding、来源、时间、reread证据、bytes/本地内容SHA256。跨命令只用同版本绑定的历史观察,不按opaque hash建立全局缓存;同批同ref共享一次网络观察。
54
+ - 私有流式staging、DNS pinning、URL过期拒绝、HTTP403最多1次新presign、无Bearer/URL落盘;正文对象与用户附件分离。额外正文证据超session存储预算降级partial并保留原行,不因optional body挤掉原始行;崩溃可能保留孤儿对象,无GC。
55
+ - observed inline/omitted/blob、hydrated bytes、decoded正文覆盖独立统计;失败/未知schema显式partial(exit9),不宣称全文/当前镜像。旧归档离线兼容;未知/坏body缓存不解码,不猜路径。
56
+ - 新回归覆盖中文JSONL/MD rg、精确scope/version/uint64、坏JSON/未知schema、大body/硬budget、过期/重复/错配presign、403上限、同blob去重、跨manifest scope拒用缓存、超时取消、symlink/hardlink、对象已提交后的中断恢复、MCP gates/CLI dryrun。
57
+
58
+ ### 本轮隔离 live(2026-11-07 14:03–14:04 SGT)
59
+ 证据仅仓库外 `/workspace/gbc-body-live-20261107140312`,目录0700/文件0600。
60
+ 新临时bot、仅1条固定无隐私测试指令;本地输出128行中文/测试文件(5888 bytes),不读现有bot transcript、不联系第三方、不改sidebar。
61
+ - PASS:正常create/send/default session、2页List、4条唯一inline记录,全部确认schema可解码;原始覆盖4、omitted0、blob0、hydrated0,completeness仍unknown。
62
+ - PASS:offline CLI render/status/checksum(凭据目录指向不存在路径)、rg中文正文、assistant固定测试marker;MCP stdio readonly sync_status成功且不暴露写工具;scope不一致在transcript RPC前拒绝。
63
+ - SKIP:真实omitted恢复、真实blob presign/下载。正常RPC request没有inline字节上限参数,本次合理小的正文/工具输出未自然触发;不造未公开参数、不把mock算live。
64
+ - PASS(顺带现有实现):send-message attachment中文文件名,5888 bytes固定中文/marker/本地SHA256一致;第二次Resolve+条件GET为not_modified,0 body bytes。
65
+ - SKIP:命名session(服务器列表0,当前无create-session操作,不编造ID);user-attachment上传、真实403/过期/下载超限/代际切换仍未验证。
66
+ - PASS cleanup:finally删除精确测试ID并确认不存在;初始27、最终27,完整ID集合相等,无初始ID缺失;未盲重试未知写。
67
+ 身份仍是隔离验收用operator_asserted_unverified namespace,不能冒充服务端验证账户;生产使用需操作者核对真实稳定account/team。
68
+
69
+ ## 审查修复(t43u)
70
+ - P2 starve:同seq更大updated_seq先merge再补读;确定性失败同版本`retry:never`跳过;本轮新观测优先+seq新到旧。
71
+ - P2 partial:仅本轮hydrate失败/generation_changed→partial/exit9;decoder unsupported只计数。
72
+ - P3体积:hydration/reread不内联重复body_base64,对象+sha256装载。
73
+ - P3 generation0:精确重读始终传`generation:e.generation`(含0)。
74
+
75
+ ## t47u 继续修复:跨进程刷新合并(2026-11-07,Asia/Shanghai)
76
+ - 独立 Linux util-linux `flock` refresh 锁覆盖有界 HTTP;mutation 短锁保护 login/logout/CAS。等待者以进入时 session/revision 为基线,force 也复用已旋转 pair。
77
+ - kernel 锁不 unlink、不按 mtime 猜 stale;父进程退出/崩溃关闭 helper stdin,OS 释放;锁等待超时结构化错误。依赖 Linux `flock`/sh/cat,缺失 fail closed。
78
+ - 429/网络/timeout/5xx cooldown 条件持久化,跨进程等待后不风暴重试。HTTP400 invalid_grant 明确 terminal;新登录不被旧 terminal 清除。
79
+ - 响应运行时字段校验、非空白 access_token;exp 必须 safe integer;准确300s不刷新(规则为<300s)。配置/malformed不fallback。
80
+ - READONLY 排除所有 authTool 凭据写工具,保留离线 auth_whoami。
81
+ - 新增真正 Node 子进程协同 mock HTTP:4进程 force 仅1次请求、旋转复用、logout/login保存竞争、owner SIGKILL恢复、timeout、invalid_grant。
82
+ - 当前完整 npm test **196/196**(基线174,新增22个顶层测试;子进程场景在1项测试内多断言);typecheck/build、protocol-tools **5/5**、diff --check通过。无live refresh、无commit/push。
83
+ - 测试边界:login/logout竞争的跨进程用例以受控 fixture store clear/save模拟原子落盘时机;provider共享mutation锁的行为另有单进程实例测试。没有真实OAuth服务器验收。
84
+
85
+ ## t50u Native advisory-lock替换(2026-11-07,Asia/Shanghai)
86
+ - 保留此前auth修改;fs-native-extensions **1.5.3**(Apache-2.0)替换外部flock/sh/cat。接口独立在auth/lock.ts,永久lock file,try_lock+异步有界等待,无mtime抢锁/daemon/helper。
87
+ - Linux `F_OFD_SETLK`、macOS `flock`、Windows `LockFileEx`源码已核实;GrokBuild fs2语义对照见[auth-lock.md](auth-lock.md)。Linux原生锁命名空间不同于旧flock,升级必须停止全部旧进程。
88
+ - 实际provider logout/login也用Node子进程测试(替代前轮fixture直接写);native owner crash、mtime回拨不抢锁、cancel/timeout不释放他人锁、event loop响应与缺binding fail-closed已测。
89
+ - Node20/22预编译+三平台源码构建CI配置,无发布/上传;Mac/Windows及源码重构建未在本机执行。Linux x64运行验证不代表其它平台通过。
90
+ - 不新增用户电脑依赖;正常npm ci直接使用integrity锁定npm tarball预编译,无Rust/C++要求;非覆盖平台需维护者CMake/C工具链源码构建。
91
+
92
+ 最终验证:npm test **199/199**;npm run typecheck / build通过;protocol-tools **5/5**;git diff --check通过。当前工作树未提交,未commit/push、未真实refresh。源码构建矩阵为配置证据,不冒充已执行结果。
@@ -0,0 +1,9 @@
1
+ # Proto drift 清单(0.78.4 vs App 0.79.2 静态)
2
+
3
+ | 项 | 状态 |
4
+ |---|---|
5
+ | CreateGrokBotTemporalAgent I/O = CreateGrokBotAgentRequest | 已用 |
6
+ | App 另传 visibility/teamId/botBuilderRequested | 0.78.4 无;0.79.3 vendor 已有字段 18/19/20 → 语义未验证,本实现仍**不发送** |
7
+ | ListGrokBotSubagents(非 ListGrokBotAgentSubagents) | 已对齐生成客户端 |
8
+ | sidebar_sections_base 冲突线上码 | App 映射 SaveConflict;精确码待真机 |
9
+ | delete 后 sidebar 残留 | 未证 |
@@ -0,0 +1,34 @@
1
+ # Protocol archive & restore method
2
+
3
+ Everything needed to (1) know which Grok Bot protocol this repo is built on, (2) re-derive it
4
+ from a new official App build, and (3) update SDK + CLI + MCP safely.
5
+
6
+ | Doc | Purpose |
7
+ |---|---|
8
+ | [baseline-manifest.md](baseline-manifest.md) | Exact upstream proto commit, App versions, file hashes, vendored files |
9
+ | [surface-map.md](surface-map.md) | RPCs / messages this repo uses → SDK / CLI / MCP, with evidence level |
10
+ | [auth-flow.md](auth-flow.md) | Independent Cursor login / poll / refresh, stable code anchors |
11
+ | [static-restore.md](static-restore.md) | How the protocol and auth were recovered statically (download → extract → schema → anchors) |
12
+ | [update-runbook.md](update-runbook.md) | Step-by-step when the official App ships a new version |
13
+ | [compat-matrix.md](compat-matrix.md) | Version compatibility + remaining risks |
14
+ | [../../proto/](../../proto/) | **正式协议源** — restored `.proto` for current App 0.79.3 (feeds `vendor/proto-gen`) |
15
+ | [baselines/](baselines/) | Versioned used-surface snapshots + archived schema copies (`schema-0.79.3/` keeps a freeze of the same `.proto`) |
16
+ | [../../tools/protocol/](../../tools/protocol/README.md) | Offline scripts: ASAR reader, anchor fingerprint, used-surface diff, generated-code verifier |
17
+
18
+ ## Evidence levels (used throughout)
19
+
20
+ | Tag | Meaning |
21
+ |---|---|
22
+ | **S** | Static reverse engineering of an official App build (not official API docs) |
23
+ | **O** | Verified offline in this repo (fake transport / local fake backend tests) |
24
+ | **L** | Confirmed against the live service (state the date and what exactly) |
25
+
26
+ Mock/offline passing (O) is never evidence of live behaviour (L).
27
+
28
+ ## What is NOT here (by design)
29
+
30
+ - No App installer, ASAR, full bundle, or prettified bundle copies (download them from the official
31
+ source; see static-restore.md). Only hashes, anchors and small schema-derived data are archived.
32
+ - No credentials, tokens, client_id values, account data, bot IDs or login URLs.
33
+ - No `.proto` comments: the App ships compact descriptors without source comments; restored `.proto`
34
+ files (upstream repo) cannot recover original comments or proto file layout.
@@ -0,0 +1,32 @@
1
+ # App 0.79.3:附件与分页实现依据
2
+
3
+ 只静态读取,不执行App。原成员资源来自官方Linux0.79.3包;派生文件在/workspace/grokbot-sync-evidence-0793(不提交包体)。
4
+
5
+ ## 分页
6
+ coordinator.formatted.cjs:
7
+ - _s 55503:默认session省略;qe 55963:generation0省略、beforeSeq可选、limit+1;gW/SW 55466:hasMore/nextBeforeSeq。
8
+ - ge 55789:精确seq补正文使用beforeSeq=seq+1/limit1,支持排他边界解释;Ee 55822补omitted/blob。
9
+ - readBlobs 53230:PresignSandBoxStoreReads(blobs/hash),不是附件。
10
+ - uu/Oe 56306/55944:entries/deletes合并;Ds 56376/56390:cleared/cursorTooOld authoritative重建。
11
+ - wk 56441:afterUpdatedSeq游标;56557 WatchGrokBotTranscripts。
12
+ - updatedSeqLocked在55975/56126保存,56899用作加入跟踪条件,不能解释成跨页snapshot锁。
13
+ App是UI按需历史+实时尾,不是一次完整导出。本工具不引入Watch,保留历史并显式unknown。
14
+
15
+ ## 路径与来源
16
+ main-core.cjs常量别名:V_=/home/box,hK=sand-data,AK=${V_}/${hK},G_=${V_}/agent-data;模块41导出两root。
17
+ electron-main/main-app.cjs(minified单行,符号稳定定位):
18
+ - J6e/gvn/hvn,字符offset约2283192:两root、agentDir、attachments/assets、64hex.ext、file URL严格校验;canonical为model-visible root。
19
+ - H6e/Q6e约2276288:fallback absolute path、normalize、root/bucket限制。
20
+ - Bvn约2284606:STORE要求size/expires为safe integer和非空etag,https默认;NOT_IN_STORE/NO_STORAGE区分。
21
+ - jie.ge约2287777:按路径agentDir分组Resolve,生产Store请求未携带session,canonical alias携带agentDir;本工具更保守按session隔离ref/请求。
22
+ - jie.ue约2288748:redirect:error,403刷新Resolve一次;jie.J约2289379:Range+段长度检验;q使用If-None-Match/304。
23
+ - W6e约2277065:ReadGrokBotAgentAttachmentChunk,4MiB段长、totalSize变化失败,feature gate;不应对NO_STORAGE盲目扩大读取范围。
24
+ - 附件服务fe/J:先length0获totalSize,循环readAttachmentChunk写文件;失败删除半成品。
25
+ renderer index.eager-app-BXVg2FvL.js:rQ/jbt:user-attachment(file_path/file_name),send-message附件(message.type=attachment,url,file_name)。
26
+ 外部URL/artifact还有独立适配,不表示所有message.url都能交Resolve。
27
+
28
+ ## 安全差异
29
+ 本工具不复制App缓存/凭据,不读本机alias路径;远端alias只经鉴权RPC解析。
30
+ 不假设64hex就是服务端内容hash,落盘自己计算SHA-256;immutable语义未live证实,因此每次重新验证。
31
+ 文件名长/中文只影响显示元数据,refs用canonical-path hash无120byte ID组件限制。
32
+ 附加public-IP DNS pinning是本工具下载policy,不声称App同样实施。