vibe-coding-master 0.2.4 → 0.2.5
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/dist/backend/adapters/claude-adapter.js +9 -2
- package/dist/backend/api/claude-hook-routes.js +7 -0
- package/dist/backend/server.js +2 -1
- package/dist/backend/services/app-settings-service.js +49 -1
- package/dist/backend/services/claude-hook-service.js +61 -11
- package/dist/backend/services/harness-service.js +6 -5
- package/dist/backend/services/round-service.js +40 -7
- package/dist/backend/services/session-service.js +16 -2
- package/dist/shared/types/app-settings.js +17 -0
- package/dist/shared/types/session.js +37 -1
- package/dist-frontend/assets/index-BRrFbDjL.js +90 -0
- package/dist-frontend/assets/index-DQ-pEUma.css +32 -0
- package/dist-frontend/index.html +2 -2
- package/docs/gateway-design.md +585 -0
- package/docs/product-design.md +49 -7
- package/docs/v0.2-implementation-plan.md +78 -68
- package/docs/vcm-cc-best-practices.md +18 -4
- package/package.json +1 -1
- package/scripts/install-vcm-harness.mjs +3 -2
- package/dist-frontend/assets/index-CfZ3VYXY.js +0 -90
- package/dist-frontend/assets/index-CfqduxVB.css +0 -32
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
# VCM Gateway Design
|
|
2
|
+
|
|
3
|
+
Last updated: 2026-06-10
|
|
4
|
+
|
|
5
|
+
This document defines the first VCM gateway product behavior and implementation
|
|
6
|
+
plan. It is based on the local Tencent iLink smoke test at:
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
/Users/sheldon/Documents/New project 3/weixin-ilink-gateway-test
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The first gateway channel is Tencent iLink Bot API / Weixin DM.
|
|
13
|
+
|
|
14
|
+
## Product Definition
|
|
15
|
+
|
|
16
|
+
VCM Gateway is a mobile conversation bridge between one Weixin DM and one
|
|
17
|
+
desktop VCM instance. It is not a remote terminal, not a group-chat bot, and not
|
|
18
|
+
a multi-user collaboration feature.
|
|
19
|
+
|
|
20
|
+
Product rules:
|
|
21
|
+
|
|
22
|
+
- Support DM only. Group chat is not a supported product mode.
|
|
23
|
+
- Bind one mobile Weixin DM identity to one desktop VCM instance.
|
|
24
|
+
- The binding is not project-specific and not task-specific.
|
|
25
|
+
- The bound phone can manage every project and task available to that desktop
|
|
26
|
+
VCM instance.
|
|
27
|
+
- Gateway user messages talk only to the `project-manager` role.
|
|
28
|
+
- Gateway never sends directly to `architect`, `coder`, or `reviewer`.
|
|
29
|
+
- Gateway may push PM replies to Weixin whenever it is enabled, even when the PM
|
|
30
|
+
turn was started from the desktop UI rather than from Weixin.
|
|
31
|
+
- When translation is enabled, Chinese input is translated to English before it
|
|
32
|
+
is sent to PM.
|
|
33
|
+
- The prompt sent to PM does not include the original Chinese text.
|
|
34
|
+
- There is no allowed-user list. The security model is one bound DM identity.
|
|
35
|
+
|
|
36
|
+
The short product sentence is:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
One phone DM binds to one desktop VCM; the phone can select project/task context,
|
|
40
|
+
send ordinary messages to the current task's PM, and receive translated PM
|
|
41
|
+
replies while gateway is enabled.
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Binding Model
|
|
45
|
+
|
|
46
|
+
The binding target is the desktop VCM instance.
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
Weixin DM identity
|
|
50
|
+
<-> desktop VCM instance
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The gateway stores the iLink bot account token and the bound Weixin user identity
|
|
54
|
+
in app-local state. If the QR login result provides a login user id, VCM stores
|
|
55
|
+
that identity. If the channel cannot determine the bound user id at login time,
|
|
56
|
+
VCM stores the first inbound DM user id after the user confirms binding from the
|
|
57
|
+
desktop UI.
|
|
58
|
+
|
|
59
|
+
Messages from the bound identity are accepted. Messages from any other identity
|
|
60
|
+
are ignored or receive a minimal "not bound" reply. They are not treated as
|
|
61
|
+
secondary users.
|
|
62
|
+
|
|
63
|
+
Changing phones or Weixin accounts is a rebind operation:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
desktop settings -> disable gateway or reset binding -> QR login / bind again
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Mobile Context
|
|
70
|
+
|
|
71
|
+
Because the binding is to a desktop VCM instance, gateway needs a current mobile
|
|
72
|
+
context:
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
current project
|
|
76
|
+
current task
|
|
77
|
+
current role = project-manager
|
|
78
|
+
translation enabled/disabled
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Plain text messages are sent to the PM session of the current task. If no
|
|
82
|
+
project or task is selected, gateway replies with a short setup hint.
|
|
83
|
+
|
|
84
|
+
The desktop UI remains the source of truth. Gateway only changes context through
|
|
85
|
+
explicit commands.
|
|
86
|
+
|
|
87
|
+
## Command Surface
|
|
88
|
+
|
|
89
|
+
MVP commands:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
/help
|
|
93
|
+
/status
|
|
94
|
+
/projects
|
|
95
|
+
/use-project <index-or-path>
|
|
96
|
+
/tasks
|
|
97
|
+
/use-task <index-or-task-slug>
|
|
98
|
+
/translate on
|
|
99
|
+
/translate off
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Plain text that does not start with `/` is treated as a PM message for the
|
|
103
|
+
current task.
|
|
104
|
+
|
|
105
|
+
Commands intentionally not in MVP:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
/approve
|
|
109
|
+
/reject
|
|
110
|
+
/pause
|
|
111
|
+
/resume
|
|
112
|
+
/start-session
|
|
113
|
+
/stop-session
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The first version should not expose state-changing workflow controls beyond
|
|
117
|
+
selecting the mobile context and toggling gateway translation. More controls can
|
|
118
|
+
be added later after the PM-only conversation path is reliable.
|
|
119
|
+
|
|
120
|
+
## Inbound Message Flow
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
Tencent iLink getupdates
|
|
124
|
+
-> weixin-ilink channel
|
|
125
|
+
-> verify bound DM identity
|
|
126
|
+
-> dedupe message id
|
|
127
|
+
-> parse text / command
|
|
128
|
+
-> if command: execute gateway command and reply
|
|
129
|
+
-> if plain text:
|
|
130
|
+
-> require current project + current task
|
|
131
|
+
-> require current task PM session running and hook-idle
|
|
132
|
+
-> translate Chinese to English when enabled
|
|
133
|
+
-> submit English prompt to PM
|
|
134
|
+
-> reply with accepted / busy / error status
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
PM prompt shape when translation is enabled:
|
|
138
|
+
|
|
139
|
+
```text
|
|
140
|
+
[VCM Gateway]
|
|
141
|
+
<translated English instruction>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The original Chinese is not included in the PM prompt.
|
|
145
|
+
|
|
146
|
+
If translation is disabled, the plain user text is submitted as-is with the same
|
|
147
|
+
`[VCM Gateway]` marker.
|
|
148
|
+
|
|
149
|
+
Gateway should use the same bracketed-paste terminal submission path as the VCM
|
|
150
|
+
desktop input path. A terminal write only proves the text was written to the PTY;
|
|
151
|
+
Claude Code `UserPromptSubmit` remains the acceptance signal.
|
|
152
|
+
|
|
153
|
+
## PM Reply Push Flow
|
|
154
|
+
|
|
155
|
+
Gateway push is not limited to gateway-originated turns. If gateway is enabled,
|
|
156
|
+
VCM should push PM replies to Weixin after PM completes a turn.
|
|
157
|
+
|
|
158
|
+
```text
|
|
159
|
+
PM Claude Code Stop hook
|
|
160
|
+
-> load PM session transcript
|
|
161
|
+
-> extract new assistant text since last pushed transcript cursor
|
|
162
|
+
-> ignore tool logs, raw terminal output, and non-PM roles
|
|
163
|
+
-> translate English reply to Chinese when enabled
|
|
164
|
+
-> send Weixin DM through iLink sendmessage
|
|
165
|
+
-> persist last pushed transcript cursor
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Rules:
|
|
169
|
+
|
|
170
|
+
- Push only PM assistant replies.
|
|
171
|
+
- Do not push token-by-token terminal output.
|
|
172
|
+
- Do not push raw tool logs.
|
|
173
|
+
- Do not push architect/coder/reviewer replies directly.
|
|
174
|
+
- Deduplicate by PM session id and transcript event id or timestamp.
|
|
175
|
+
- If translation fails, send the PM original text with a short translation
|
|
176
|
+
failure note.
|
|
177
|
+
|
|
178
|
+
This keeps the mobile side readable and avoids exposing the embedded terminal as
|
|
179
|
+
a chat stream.
|
|
180
|
+
|
|
181
|
+
## Busy And Error Behavior
|
|
182
|
+
|
|
183
|
+
The MVP should be conservative.
|
|
184
|
+
|
|
185
|
+
If the PM session is busy, do not queue arbitrary mobile prompts. Reply:
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
PM is still working on the current turn. Please wait and send again later.
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
If PM is not running:
|
|
192
|
+
|
|
193
|
+
```text
|
|
194
|
+
The current task's PM session is not running. Start it from desktop VCM first.
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
If no task is selected:
|
|
198
|
+
|
|
199
|
+
```text
|
|
200
|
+
No task is selected. Use /tasks and /use-task first.
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
If gateway translation fails before sending to PM, do not send the original
|
|
204
|
+
Chinese. Reply with a translation failure message and ask the user to retry.
|
|
205
|
+
|
|
206
|
+
## Tencent iLink Feasibility
|
|
207
|
+
|
|
208
|
+
The local smoke test proves the required channel primitives:
|
|
209
|
+
|
|
210
|
+
- QR login with `ilink/bot/get_bot_qrcode`.
|
|
211
|
+
- QR status polling with `ilink/bot/get_qrcode_status`.
|
|
212
|
+
- Long-poll DM receive with `ilink/bot/getupdates`.
|
|
213
|
+
- Text reply with `ilink/bot/sendmessage`.
|
|
214
|
+
- Token and cursor persistence outside a repository.
|
|
215
|
+
- Handling session expiration through iLink error code `-14`.
|
|
216
|
+
|
|
217
|
+
Observed request details from the smoke test:
|
|
218
|
+
|
|
219
|
+
```text
|
|
220
|
+
base URL: https://ilinkai.weixin.qq.com
|
|
221
|
+
default bot_type: 3
|
|
222
|
+
default channel_version: 2.4.3
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Common headers:
|
|
226
|
+
|
|
227
|
+
```text
|
|
228
|
+
Content-Type: application/json
|
|
229
|
+
AuthorizationType: ilink_bot_token
|
|
230
|
+
Authorization: Bearer <token>
|
|
231
|
+
X-WECHAT-UIN: <random base64 uin>
|
|
232
|
+
iLink-App-Id: bot
|
|
233
|
+
iLink-App-ClientVersion: <encoded client version>
|
|
234
|
+
SKRouteTag: <optional route tag>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
QR login:
|
|
238
|
+
|
|
239
|
+
```text
|
|
240
|
+
POST ilink/bot/get_bot_qrcode?bot_type=<bot_type>
|
|
241
|
+
body:
|
|
242
|
+
{
|
|
243
|
+
"local_token_list": ["<saved token>"]
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
GET ilink/bot/get_qrcode_status?qrcode=<qrcode>
|
|
247
|
+
GET ilink/bot/get_qrcode_status?qrcode=<qrcode>&verify_code=<code>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
QR statuses handled by the smoke test:
|
|
251
|
+
|
|
252
|
+
```text
|
|
253
|
+
wait
|
|
254
|
+
scaned
|
|
255
|
+
need_verifycode
|
|
256
|
+
verify_code_blocked
|
|
257
|
+
expired
|
|
258
|
+
scaned_but_redirect
|
|
259
|
+
binded_redirect
|
|
260
|
+
confirmed
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Long poll receive:
|
|
264
|
+
|
|
265
|
+
```text
|
|
266
|
+
POST ilink/bot/getupdates
|
|
267
|
+
body:
|
|
268
|
+
{
|
|
269
|
+
"get_updates_buf": "<cursor>",
|
|
270
|
+
"base_info": {
|
|
271
|
+
"channel_version": "2.4.3",
|
|
272
|
+
"bot_agent": "vcm-gateway/<version>"
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
The response may include:
|
|
278
|
+
|
|
279
|
+
```text
|
|
280
|
+
ret / errcode
|
|
281
|
+
errmsg
|
|
282
|
+
longpolling_timeout_ms
|
|
283
|
+
get_updates_buf
|
|
284
|
+
msgs[]
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Send text DM:
|
|
288
|
+
|
|
289
|
+
```text
|
|
290
|
+
POST ilink/bot/sendmessage
|
|
291
|
+
body:
|
|
292
|
+
{
|
|
293
|
+
"msg": {
|
|
294
|
+
"from_user_id": "",
|
|
295
|
+
"to_user_id": "<bound user id>",
|
|
296
|
+
"client_id": "<unique client id>",
|
|
297
|
+
"message_type": 2,
|
|
298
|
+
"message_state": 2,
|
|
299
|
+
"item_list": [
|
|
300
|
+
{
|
|
301
|
+
"type": 1,
|
|
302
|
+
"text_item": {
|
|
303
|
+
"text": "<reply text>"
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
],
|
|
307
|
+
"context_token": "<optional latest context token>"
|
|
308
|
+
},
|
|
309
|
+
"base_info": {
|
|
310
|
+
"channel_version": "2.4.3",
|
|
311
|
+
"bot_agent": "vcm-gateway/<version>"
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Inbound message handling:
|
|
317
|
+
|
|
318
|
+
- Ignore bot messages where `message_type` is bot.
|
|
319
|
+
- Accept user messages where `message_type` is user or absent.
|
|
320
|
+
- Extract text from `item_list[].text_item.text`.
|
|
321
|
+
- Voice text can be read from `item_list[].voice_item.text` if iLink supplies
|
|
322
|
+
it, but MVP should treat non-text input as unsupported unless text is present.
|
|
323
|
+
- Use `message_id`, then `client_id`, then sender/time fallback for dedupe.
|
|
324
|
+
- Persist `get_updates_buf` after each successful poll response.
|
|
325
|
+
- Persist latest `context_token` per bound user and reuse it for replies.
|
|
326
|
+
|
|
327
|
+
## Local State
|
|
328
|
+
|
|
329
|
+
Gateway state must live outside connected repositories.
|
|
330
|
+
|
|
331
|
+
Recommended files:
|
|
332
|
+
|
|
333
|
+
```text
|
|
334
|
+
~/.vcm/gateway/settings.json
|
|
335
|
+
~/.vcm/gateway/audit.jsonl
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Settings shape:
|
|
339
|
+
|
|
340
|
+
```json
|
|
341
|
+
{
|
|
342
|
+
"version": 1,
|
|
343
|
+
"enabled": false,
|
|
344
|
+
"channel": "weixin-ilink",
|
|
345
|
+
"translationEnabled": true,
|
|
346
|
+
"currentProjectId": null,
|
|
347
|
+
"currentTaskSlug": null,
|
|
348
|
+
"binding": {
|
|
349
|
+
"accountId": null,
|
|
350
|
+
"baseUrl": "https://ilinkai.weixin.qq.com",
|
|
351
|
+
"boundUserId": null,
|
|
352
|
+
"loginUserId": null,
|
|
353
|
+
"token": null,
|
|
354
|
+
"getUpdatesBuf": "",
|
|
355
|
+
"contextTokens": {}
|
|
356
|
+
},
|
|
357
|
+
"dedupe": {
|
|
358
|
+
"recentInboundMessageIds": []
|
|
359
|
+
},
|
|
360
|
+
"pushCursors": {
|
|
361
|
+
"<taskSlug>:project-manager:<claudeSessionId>": {
|
|
362
|
+
"lastTranscriptEventId": null,
|
|
363
|
+
"lastTranscriptTimestamp": null
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
"updatedAt": "..."
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
The token is sensitive. The settings file should be written with user-only file
|
|
371
|
+
permissions where the platform supports it.
|
|
372
|
+
|
|
373
|
+
Audit log rules:
|
|
374
|
+
|
|
375
|
+
- Record state transitions, command names, message ids, result codes, and error
|
|
376
|
+
classes.
|
|
377
|
+
- Redact token, Authorization header, QR URL, and full message bodies by default.
|
|
378
|
+
- Store short message previews only when needed for debugging.
|
|
379
|
+
- Never write gateway credentials to connected repositories, terminal logs,
|
|
380
|
+
`.ai/vcm/**`, PR descriptions, or generated harness files.
|
|
381
|
+
|
|
382
|
+
## Backend Architecture
|
|
383
|
+
|
|
384
|
+
Suggested files:
|
|
385
|
+
|
|
386
|
+
```text
|
|
387
|
+
src/shared/types/gateway.ts
|
|
388
|
+
|
|
389
|
+
src/backend/gateway/
|
|
390
|
+
gateway-service.ts
|
|
391
|
+
gateway-settings-service.ts
|
|
392
|
+
gateway-command-parser.ts
|
|
393
|
+
gateway-pm-bridge.ts
|
|
394
|
+
gateway-notifier.ts
|
|
395
|
+
gateway-audit-log.ts
|
|
396
|
+
channels/
|
|
397
|
+
weixin-ilink-channel.ts
|
|
398
|
+
|
|
399
|
+
src/backend/api/gateway-routes.ts
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Responsibilities:
|
|
403
|
+
|
|
404
|
+
- `gateway-settings-service`: load/save app-local gateway settings and secrets.
|
|
405
|
+
- `weixin-ilink-channel`: QR login, long polling, send text, token expiration.
|
|
406
|
+
- `gateway-command-parser`: parse `/help`, `/status`, `/projects`,
|
|
407
|
+
`/use-project`, `/tasks`, `/use-task`, and `/translate`.
|
|
408
|
+
- `gateway-pm-bridge`: submit plain messages to the current task PM session.
|
|
409
|
+
- `gateway-notifier`: push PM transcript replies to Weixin after PM Stop.
|
|
410
|
+
- `gateway-audit-log`: append redacted JSONL audit entries.
|
|
411
|
+
- `gateway-service`: lifecycle, poll loop, dispatch, and error backoff.
|
|
412
|
+
- `gateway-routes`: desktop UI settings, QR login start/status, enable/disable,
|
|
413
|
+
rebind, and gateway status.
|
|
414
|
+
|
|
415
|
+
Service dependencies:
|
|
416
|
+
|
|
417
|
+
- `ProjectService`: current project and recent project paths.
|
|
418
|
+
- `TaskService`: task list and selected task validation.
|
|
419
|
+
- `SessionService`: PM session state and Claude session metadata.
|
|
420
|
+
- `TerminalRuntime`: controlled PM terminal submission.
|
|
421
|
+
- `ClaudeTranscriptService`: PM assistant output extraction.
|
|
422
|
+
- `TranslationService` / translation provider: inbound Chinese-to-English and
|
|
423
|
+
outbound English-to-Chinese translation.
|
|
424
|
+
- `ClaudeHookService` or hook event integration: trigger PM reply push after PM
|
|
425
|
+
`Stop`.
|
|
426
|
+
|
|
427
|
+
## Desktop UI
|
|
428
|
+
|
|
429
|
+
Add a Gateway section to the sidebar settings area or a dedicated modal:
|
|
430
|
+
|
|
431
|
+
```text
|
|
432
|
+
Gateway: off / on
|
|
433
|
+
Channel: Weixin iLink
|
|
434
|
+
Binding: not bound / bound
|
|
435
|
+
Translation: off / on
|
|
436
|
+
Current project
|
|
437
|
+
Current task
|
|
438
|
+
QR login / Rebind
|
|
439
|
+
Last poll status
|
|
440
|
+
Last message status
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
The user should be able to:
|
|
444
|
+
|
|
445
|
+
- enable or disable gateway
|
|
446
|
+
- start QR login
|
|
447
|
+
- see whether the phone is bound
|
|
448
|
+
- reset binding
|
|
449
|
+
- select current project/task from desktop
|
|
450
|
+
- toggle gateway translation
|
|
451
|
+
- inspect recent gateway errors
|
|
452
|
+
|
|
453
|
+
## Implementation Plan
|
|
454
|
+
|
|
455
|
+
### Phase 1: Types, Settings, And UI Skeleton
|
|
456
|
+
|
|
457
|
+
- Add `src/shared/types/gateway.ts`.
|
|
458
|
+
- Add app-local gateway settings service under `src/backend/gateway`.
|
|
459
|
+
- Add `src/backend/api/gateway-routes.ts`.
|
|
460
|
+
- Add desktop UI controls for enable/disable, translation, binding status, and
|
|
461
|
+
current project/task.
|
|
462
|
+
- Store settings under `~/.vcm/gateway/settings.json`.
|
|
463
|
+
|
|
464
|
+
Validation:
|
|
465
|
+
|
|
466
|
+
- Unit tests for settings normalization and secret redaction.
|
|
467
|
+
- API route tests for enable/disable and settings update.
|
|
468
|
+
|
|
469
|
+
### Phase 2: iLink Channel Adapter
|
|
470
|
+
|
|
471
|
+
- Port the proven smoke-test behavior into `weixin-ilink-channel.ts`.
|
|
472
|
+
- Support QR login and status polling.
|
|
473
|
+
- Support token reuse from saved settings.
|
|
474
|
+
- Support `getupdates` long polling.
|
|
475
|
+
- Support `sendmessage` text DM.
|
|
476
|
+
- Persist `get_updates_buf` and context tokens.
|
|
477
|
+
- Handle `ret` / `errcode` `-14` as expired login.
|
|
478
|
+
|
|
479
|
+
Validation:
|
|
480
|
+
|
|
481
|
+
- Unit tests with mocked fetch for QR statuses, getupdates, sendmessage,
|
|
482
|
+
expiration, redirect host, and retry backoff.
|
|
483
|
+
- Manual smoke test with a real Weixin DM before wiring PM submission.
|
|
484
|
+
|
|
485
|
+
### Phase 3: Inbound Command Handling
|
|
486
|
+
|
|
487
|
+
- Implement `/help`, `/status`, `/projects`, `/use-project`, `/tasks`,
|
|
488
|
+
`/use-task`, `/translate on`, and `/translate off`.
|
|
489
|
+
- Implement bound identity check.
|
|
490
|
+
- Implement persistent inbound message dedupe.
|
|
491
|
+
- Reply with short command results through iLink.
|
|
492
|
+
|
|
493
|
+
Validation:
|
|
494
|
+
|
|
495
|
+
- Parser tests for known commands and invalid commands.
|
|
496
|
+
- Gateway service tests for ignored unbound users and deduped messages.
|
|
497
|
+
|
|
498
|
+
### Phase 4: PM Message Submission
|
|
499
|
+
|
|
500
|
+
- Treat plain DM text as PM input for the current task.
|
|
501
|
+
- Require current project, current task, running PM session, and idle PM
|
|
502
|
+
activity.
|
|
503
|
+
- If gateway translation is enabled, translate Chinese to English before
|
|
504
|
+
submission.
|
|
505
|
+
- Submit only the translated English text to PM with `[VCM Gateway]`.
|
|
506
|
+
- Do not include the original Chinese text in the PM prompt.
|
|
507
|
+
- Use bracketed paste plus Enter through `submitTerminalInput`.
|
|
508
|
+
|
|
509
|
+
Validation:
|
|
510
|
+
|
|
511
|
+
- Tests for no project/task/session/busy/translation failure paths.
|
|
512
|
+
- Tests that translated prompts do not include original Chinese.
|
|
513
|
+
- Tests that successful submit records a gateway turn audit entry.
|
|
514
|
+
|
|
515
|
+
### Phase 5: PM Reply Push
|
|
516
|
+
|
|
517
|
+
- Hook PM `Stop` handling into `gateway-notifier`.
|
|
518
|
+
- Load PM transcript and extract new assistant text since the last push cursor.
|
|
519
|
+
- Push PM replies whenever gateway is enabled, regardless of whether the user
|
|
520
|
+
turn started from gateway or desktop.
|
|
521
|
+
- Translate PM reply to Chinese when gateway translation is enabled.
|
|
522
|
+
- Persist push cursors so restart does not duplicate old PM replies.
|
|
523
|
+
|
|
524
|
+
Validation:
|
|
525
|
+
|
|
526
|
+
- Transcript extraction tests with multiple assistant events.
|
|
527
|
+
- Deduplication tests across repeated Stop hooks and app restart.
|
|
528
|
+
- Translation failure fallback test.
|
|
529
|
+
|
|
530
|
+
### Phase 6: Audit, Recovery, And Packaging
|
|
531
|
+
|
|
532
|
+
- Add redacted audit JSONL writer.
|
|
533
|
+
- Add gateway lifecycle shutdown on VCM server stop.
|
|
534
|
+
- Add status reporting for poll errors, expired login, disabled gateway, and
|
|
535
|
+
last successful message.
|
|
536
|
+
- Document manual smoke test steps.
|
|
537
|
+
|
|
538
|
+
Validation:
|
|
539
|
+
|
|
540
|
+
- Full unit test pass.
|
|
541
|
+
- Build pass.
|
|
542
|
+
- Manual iLink smoke test:
|
|
543
|
+
- QR bind
|
|
544
|
+
- `/status`
|
|
545
|
+
- `/tasks`
|
|
546
|
+
- `/use-task`
|
|
547
|
+
- Chinese plain text to PM
|
|
548
|
+
- PM reply pushed back to Weixin
|
|
549
|
+
- restart without replaying old messages
|
|
550
|
+
|
|
551
|
+
## Key Risks And Decisions
|
|
552
|
+
|
|
553
|
+
PM reply extraction is the main implementation risk. VCM should use Claude
|
|
554
|
+
transcript events, not raw PTY output, because terminal output contains tool
|
|
555
|
+
logs, redraws, and partial text. The notifier must persist a per-PM-session
|
|
556
|
+
cursor.
|
|
557
|
+
|
|
558
|
+
The second risk is accidental command scope growth. The MVP must stay PM-only
|
|
559
|
+
and avoid workflow actions such as approve/reject/start/stop until the mobile
|
|
560
|
+
conversation path is proven.
|
|
561
|
+
|
|
562
|
+
The third risk is token and message leakage. Gateway credentials and audit logs
|
|
563
|
+
must stay under `~/.vcm/gateway`, with secrets redacted from logs and never
|
|
564
|
+
written into connected repositories.
|
|
565
|
+
|
|
566
|
+
The fourth risk is queueing. MVP should not queue multiple arbitrary user
|
|
567
|
+
prompts while PM is running. It should return a busy message and let the user
|
|
568
|
+
retry after PM stops.
|
|
569
|
+
|
|
570
|
+
## Acceptance Criteria
|
|
571
|
+
|
|
572
|
+
Gateway MVP is complete when:
|
|
573
|
+
|
|
574
|
+
- Desktop VCM can enable/disable Weixin iLink gateway.
|
|
575
|
+
- Desktop VCM can QR-bind one Weixin DM identity.
|
|
576
|
+
- Bound phone can send `/status` and receive current VCM status.
|
|
577
|
+
- Bound phone can list and select current project/task context.
|
|
578
|
+
- Bound phone can send Chinese plain text to current task PM.
|
|
579
|
+
- PM receives only the translated English prompt, without original Chinese.
|
|
580
|
+
- Gateway can push PM assistant replies to Weixin whenever enabled.
|
|
581
|
+
- PM replies are translated to Chinese when gateway translation is enabled.
|
|
582
|
+
- Duplicate iLink messages and duplicate PM Stop hooks do not produce duplicate
|
|
583
|
+
sends.
|
|
584
|
+
- Expired iLink token is reported clearly and requires rebind.
|
|
585
|
+
- Gateway credentials and audit logs stay outside connected repositories.
|