@logicseek/hello-cc 1.1.0-rc.7 → 1.1.0-rc.9

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 (76) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/README.md +2 -0
  3. package/README.zh-CN.md +2 -0
  4. package/docs/README.md +2 -0
  5. package/docs/README.zh-CN.md +2 -0
  6. package/docs/commands.md +16 -0
  7. package/docs/commands.zh-CN.md +14 -0
  8. package/docs/dsh.md +6 -0
  9. package/docs/dsh.zh-CN.md +5 -0
  10. package/docs/native.md +21 -1
  11. package/docs/native.zh-CN.md +19 -1
  12. package/docs/private-state.md +115 -0
  13. package/docs/private-state.zh-CN.md +92 -0
  14. package/lib/cli/app.mjs +46 -5
  15. package/lib/cli/commands/hook.mjs +31 -4
  16. package/lib/cli/commands/mcp.mjs +6 -0
  17. package/lib/cli/commands/native.mjs +17 -4
  18. package/lib/cli/commands/peer.mjs +3 -0
  19. package/lib/cli/commands/run.mjs +196 -62
  20. package/lib/cli/commands/tmux.mjs +28 -19
  21. package/lib/cli/commands/up.mjs +25 -17
  22. package/lib/cli-runtime.mjs +16 -1
  23. package/lib/core/coordination/event-retention.mjs +2 -0
  24. package/lib/core/sessions/codex-thread-root.mjs +144 -0
  25. package/lib/core/sessions/launch.mjs +4 -0
  26. package/lib/discover.mjs +95 -231
  27. package/lib/integrations/dsh-collaboration.mjs +9 -2
  28. package/lib/integrations/native/claude.mjs +6 -0
  29. package/lib/integrations/native/codex.mjs +4 -2
  30. package/lib/integrations/native/dsh-acp.mjs +1 -1
  31. package/lib/integrations/native/jsonrpc.mjs +2 -2
  32. package/lib/mcp/scope.mjs +50 -8
  33. package/lib/mcp/tools.mjs +15 -2
  34. package/lib/process/pinned-cwd.mjs +64 -0
  35. package/lib/process/pinned-guidance.mjs +1 -0
  36. package/lib/process/pinned-project-state.mjs +1 -0
  37. package/lib/process/tmux-cwd-handoff.mjs +277 -0
  38. package/lib/runtime/native/cli-submissions.mjs +220 -0
  39. package/lib/runtime/native/client.mjs +7 -1
  40. package/lib/runtime/native/launcher.mjs +20 -4
  41. package/lib/runtime/native/service.mjs +143 -50
  42. package/lib/runtime/native/store.mjs +303 -14
  43. package/lib/runtime/private-state.mjs +435 -24
  44. package/lib/runtime/project-path.mjs +53 -1
  45. package/lib/runtime/project-trust.mjs +6 -2
  46. package/lib/runtime/state-migration.mjs +66 -13
  47. package/lib/shared/redact.mjs +4 -1
  48. package/lib/terminal/tmux.mjs +79 -11
  49. package/lib/ui/help.mjs +1 -0
  50. package/lib/web/codex-app-server.mjs +3 -1
  51. package/lib/web/codex-sessions.mjs +68 -8
  52. package/lib/web/context-files.mjs +194 -41
  53. package/lib/web/http-routes.mjs +43 -12
  54. package/lib/web/native-sessions.mjs +3 -0
  55. package/lib/web/project-contexts.mjs +27 -5
  56. package/lib/web/project-files.mjs +108 -26
  57. package/lib/web/pty-sessions.mjs +15 -2
  58. package/lib/web/runtime-main.mjs +2 -2
  59. package/lib/web/session-serialize.mjs +6 -1
  60. package/lib/web/startup.mjs +3 -1
  61. package/lib/web/tmux-sessions.mjs +156 -52
  62. package/lib/web/tmux-stream.mjs +71 -6
  63. package/lib/web/ui-interactions.mjs +7 -4
  64. package/native/bin/darwin-arm64/hcc-cwd-handoff +0 -0
  65. package/native/bin/darwin-x64/hcc-cwd-handoff +0 -0
  66. package/native/bin/linux-arm64/hcc-cwd-handoff +0 -0
  67. package/native/bin/linux-x64/hcc-cwd-handoff +0 -0
  68. package/native/cwd-handoff/README.md +45 -0
  69. package/native/cwd-handoff/hcc-cwd-handoff.c +693 -0
  70. package/package.json +8 -2
  71. package/scripts/build-cwd-handoff.mjs +47 -0
  72. package/scripts/pack-cwd-handoff.mjs +27 -0
  73. package/scripts/regression.mjs +116 -9
  74. package/scripts/verify-cwd-handoff-package.mjs +60 -0
  75. package/scripts/web-workbench-acceptance.mjs +79 -1
  76. package/scripts/web-workbench-installed-acceptance.mjs +11 -1
package/CHANGELOG.md CHANGED
@@ -16,6 +16,85 @@ the release description from the current changelog section. Use
16
16
  `npm run release:github` with `GH_TOKEN` or `GITHUB_TOKEN` only for local
17
17
  backfills.
18
18
 
19
+ ## 1.1.0-rc.9
20
+
21
+ ### Summary
22
+
23
+ This preview binds project state, managed launches and Web/native sessions to
24
+ the selected directory's filesystem identity. Replacing a directory at the
25
+ same pathname no longer authorizes reuse of its old state or Codex history.
26
+ It continues the existing npm preview channel and adds packaged native helpers
27
+ for authenticated tmux directory-descriptor handoff.
28
+
29
+ ### Highlights
30
+
31
+ - Introduce private-state v2 bindings with positive directory birth-time
32
+ receipts and separate replacement generations. Preserve the original store;
33
+ historical v1 upgrades require independent root verification, a matching
34
+ inspection receipt and an explicit offline drain. Interrupted upgrades remain
35
+ fenced until a reviewed retry completes.
36
+ - Pass an already-open directory descriptor to the authorized tmux pane through
37
+ a native broker/client, validating kernel peer identity and the bound receipt
38
+ before launch. Ship Darwin/Linux arm64/x64 helpers, static Linux binaries and
39
+ build/package checks; a missing trusted helper rejects the launch without a
40
+ pathname fallback. `HCC_PINNED_LAUNCH_MODE=hold` pauses new guarded sessions.
41
+ - Recheck selected-directory identity before CLI launches, hook registration
42
+ and managed discovery. Persist Codex thread-to-root receipts and require an
43
+ explicit verified thread for terminal resume/fork; pathname-only histories
44
+ and managed `--last` launches cannot establish that binding.
45
+ - Add a durable Native owner record alongside the existing file-lock guard.
46
+ Native, hook and scoped MCP mutations recheck runtime/state generations and
47
+ process identity under the writer fence. Journal CLI submissions before
48
+ admission and recover with the original ID; uncertain provider execution is
49
+ not automatically replayed. Retain sandbox and expected-generation checks.
50
+ - Bind Web project/session keys, Codex history and file/context operations to
51
+ the selected directory and database. Preserve active sessions' original
52
+ bindings when a replacement is explicitly selected. Keep tmux ownership and
53
+ cleanup conditional, reject invalid routes before state resolution, and make
54
+ user-question answers require current control and an explicit submit action.
55
+ - Document the migration, ownership and historical-session boundaries in
56
+ Chinese and English. Add four-platform helper build/installed-package CI and
57
+ verify executable modes and source stamps in the frozen npm package.
58
+ - Add bounded, structured shim-registration diagnostics that distinguish
59
+ provider entry, persisted binding/pane evidence and peer-list database
60
+ fallback. Public diagnostics retain fixture-owned booleans and finite enums.
61
+ - Keep separate ACL cache entries for Number and BigInt filesystem stats.
62
+ Alternating validation paths reuse unchanged results while preserving exact
63
+ inode and change-time checks; permission and ACL changes still force a fresh
64
+ inspection in both representations.
65
+
66
+ ### Validation
67
+
68
+ The rc.9 source passed 1,992 local unit tests with three platform/privilege
69
+ skips and all 13 regression stages. The integration browser baseline passed
70
+ 45 checks each for source and temporary npm installations. These checks use real
71
+ HTTP, WebSocket and SQLite with simulated providers, cover desktop and 390px
72
+ layouts, and retain separate cleanup and source-hash receipts.
73
+
74
+ Native helper formats, executable permissions, C-source stamps and packed-file
75
+ hashes are checked separately from platform execution. Final source-commit CI,
76
+ the immutable rc.9 archive and tag acceptance retain their own receipts.
77
+ These checks do not establish real-model calls, device upgrades or business
78
+ acceptance. Existing released archives and their evidence remain unchanged.
79
+
80
+ ## 1.1.0-rc.8
81
+
82
+ ### Summary
83
+
84
+ Background tmux display refreshes no longer block the shared HTTP runtime while
85
+ waiting for terminal snapshots. This preserves responsive Agent coordination
86
+ when a tmux display command is slow, and continues the existing preview channel.
87
+
88
+ ### Highlights
89
+
90
+ - Bound asynchronous background snapshots, keep one request in flight per
91
+ session, and preserve the previous display when capture fails or times out.
92
+ - Discard stale results after a session stops, changes panes or is rebound;
93
+ terminal display refresh does not replace process ownership checks.
94
+ - Retain the rc.7 original-App cooperation path and add delayed-tmux regression
95
+ coverage. A reproduced synchronous display stall motivated this repair; the
96
+ exact server-side cause of the earlier macOS CI timeout remains unconfirmed.
97
+
19
98
  ## 1.1.0-rc.7
20
99
 
21
100
  ### Summary
package/README.md CHANGED
@@ -207,6 +207,8 @@ generic session-isolation assumptions.
207
207
  - [User Guide](docs/guide.md): setup, Web console, workflows, coordination
208
208
  semantics, and environment behavior.
209
209
  - [Command Reference](docs/commands.md): compact public command list.
210
+ - [Project directory identity and private state](docs/private-state.md): v2
211
+ bindings, replacement generations, offline upgrades and verified Codex history.
210
212
  - [DeepSeek Harness](docs/dsh.md): hooks/Cordis collaboration, ACP workers,
211
213
  project setup and Web launch, with real-model and local-package acceptance evidence.
212
214
  - [Native Workers](docs/native.md): HCC-owned Codex, Claude SDK, and dsh ACP
package/README.zh-CN.md CHANGED
@@ -186,6 +186,8 @@ hcc task done --id 1 --summary "Done"
186
186
  - [文档目录](docs/README.zh-CN.md):全部用户文档和实现文档入口。
187
187
  - [用户指南](docs/guide.zh-CN.md):安装、Web 控制台、协作流程、协作语义和环境变量行为。
188
188
  - [命令参考](docs/commands.zh-CN.md):紧凑公共命令清单。
189
+ - [项目目录身份与私有状态](docs/private-state.zh-CN.md):v2 绑定、替换目录分代、
190
+ 离线升级及已核实的 Codex 历史边界。
189
191
  - [DeepSeek Harness 接入](docs/dsh.zh-CN.md):hooks/Cordis 协作、ACP worker、
190
192
  项目配置与 Web 启动;附真实模型和本地包验收记录。
191
193
  - [Native 后台 worker](docs/native.zh-CN.md):HCC 持有的 Codex、Claude SDK、dsh ACP
package/docs/README.md CHANGED
@@ -17,6 +17,8 @@ authenticated selection of any existing server directory are accepted risks.
17
17
  workflow, stable peer identity, and environment behavior.
18
18
  - [Command Reference](commands.md): compact list of public commands and the
19
19
  intended use of each command group.
20
+ - [Project directory identity and private state](private-state.md): state locations,
21
+ v2 generations, cold migration receipts, launch holds and Codex history identity.
20
22
  - [DeepSeek Harness](dsh.md): project setup, hooks/Cordis collaboration, ACP workers,
21
23
  installable bundles, and session routing boundaries.
22
24
  - [Native Workers](native.md): owned background workers, provider adapters,
@@ -14,6 +14,8 @@ provider peer ID 已变更且不会映射旧 ID;受保护接口使用 Runtime
14
14
  - [用户指南](guide.zh-CN.md):安装、启动、Web 控制台、协作语义、工作流、稳定
15
15
  peer 身份和环境变量行为。
16
16
  - [命令参考](commands.zh-CN.md):公共命令的紧凑清单,以及每组命令的用途。
17
+ - [项目目录身份与私有状态](private-state.zh-CN.md):状态位置、v2 分代、冷停机
18
+ 迁移回执、暂停新启动和 Codex 历史身份。
17
19
  - [DeepSeek Harness 接入](dsh.zh-CN.md):项目配置、hooks/Cordis 协作、ACP worker、
18
20
  可安装 bundle 与会话路由边界。
19
21
  - [Native 后台 worker](native.zh-CN.md):后台 worker 所有权、provider adapter、
package/docs/commands.md CHANGED
@@ -21,6 +21,22 @@ hcc uninstall [--purge --yes]
21
21
  hooks, shims, and the shell PATH entry; add `--purge --yes` only when you also want to remove the
22
22
  current project's `.hello-cc` data and guidance blocks.
23
23
 
24
+ ## Offline State Migration
25
+
26
+ ```text
27
+ hcc --root DIR migrate-state --offline --yes
28
+ hcc --root DIR --json migrate-state --inspect-private-binding
29
+ hcc --root DIR migrate-state --upgrade-private-binding --offline --yes --assert-historical-root --expect-receipt=SHA256
30
+ ```
31
+
32
+ The first command snapshots retained project-local state into private storage.
33
+ The other two inspect and explicitly upgrade an existing private v1 directory
34
+ binding. Independently verify the historical root, back up its state and stop
35
+ all writers before an upgrade; the inspection receipt is not historical-root
36
+ proof. Interrupted publication stays fenced until a reviewed retry completes.
37
+ See [Project directory identity and private state](private-state.md) for recovery
38
+ boundaries, v2 replacement generations and `HCC_PINNED_LAUNCH_MODE=hold`.
39
+
24
40
  ## Read-only Diagnostics
25
41
 
26
42
  ```text
@@ -19,6 +19,20 @@ hcc uninstall [--purge --yes]
19
19
  只有在确定也要删除当前项目的 `.hello-cc` 数据和指导块时,才加
20
20
  `--purge --yes`。
21
21
 
22
+ ## 离线状态迁移
23
+
24
+ ```text
25
+ hcc --root DIR migrate-state --offline --yes
26
+ hcc --root DIR --json migrate-state --inspect-private-binding
27
+ hcc --root DIR migrate-state --upgrade-private-binding --offline --yes --assert-historical-root --expect-receipt=SHA256
28
+ ```
29
+
30
+ 第一条命令将保留的项目局部状态制作快照,迁入私有目录。后两条用于检查并
31
+ 明确升级已有私有 v1 目录绑定。升级前独立核实历史根目录、备份状态,并停止
32
+ 全部写入者;检查回执不证明当前目录就是历史根。发布中断时保留封锁标记,
33
+ 经核实的重试完成后才恢复访问。恢复边界、v2 替换目录分代和
34
+ `HCC_PINNED_LAUNCH_MODE=hold` 见[项目目录身份与私有状态](private-state.zh-CN.md)。
35
+
22
36
  ## 只读诊断
23
37
 
24
38
  ```text
package/docs/dsh.md CHANGED
@@ -20,6 +20,12 @@ Below, `hcc` means a CLI containing these changes. An older global installation
20
20
 
21
21
  macOS 1.0.5 upgrades use a stable boot-session identity. Follow the [process identity migration notes](macos-process-identity.md) and restart owned runtimes normally.
22
22
 
23
+ Project state may resolve to private v2 storage. The `.hello-cc` paths below are
24
+ logical project-state paths; managed setup and session routing use the resolved
25
+ store. Stop Harness and other state writers before a migration. Existing private
26
+ v1 bindings require the explicit historical-root inspection and upgrade receipt
27
+ flow in [Project directory identity and private state](private-state.md).
28
+
23
29
  ## Choose An Entry Point
24
30
 
25
31
  | Entry | Use | Lifecycle owner |
package/docs/dsh.zh-CN.md CHANGED
@@ -20,6 +20,11 @@ node /absolute/path/to/hello-cc/bin/hcc.mjs --root /path/to/project dsh web -- -
20
20
 
21
21
  macOS 从 1.0.5 起使用稳定的启动周期标记。升级时按[进程身份迁移说明](macos-process-identity.md)正常重启所管理的运行时,保留身份无法比较的旧进程。
22
22
 
23
+ 项目状态可以解析到私有 v2 目录。下文 `.hello-cc` 路径表示逻辑项目状态位置;
24
+ 受管理 setup 和会话路由使用解析后的状态目录。迁移前应停止 Harness 和其他
25
+ 状态写入者。已有私有 v1 绑定必须经过明确核实历史根目录的检查与回执升级流程,
26
+ 详见[项目目录身份与私有状态](private-state.zh-CN.md)。
27
+
23
28
  ## 选择入口
24
29
 
25
30
  | 入口 | 适用场景 | 会话由谁持有 |
package/docs/native.md CHANGED
@@ -16,6 +16,25 @@ multiple pending inbox messages into the same turn. ACP resume and session close
16
16
  depend on the extensions the provider advertises. HCC does not claim that an ACP
17
17
  resume replays the original conversation transcript.
18
18
 
19
+ ## Runtime ownership and directory identity
20
+
21
+ The runtime retains its file-lock ownership guard and adds a durable SQLite
22
+ owner fence. The owner records runtime generation, mesh database, private-state
23
+ generation, root directory identity and process PID/start token/command hash.
24
+ Native writes and derived hook/scoped MCP writes recheck that owner under the
25
+ shared writer fence. A live or unverified owner blocks takeover; a successor can
26
+ claim released state or an owner proven to have exited. Directory replacement
27
+ and stale executor capabilities cannot authorize writes to the successor.
28
+
29
+ Old runtime pointers without a directory identity receipt or owner fence are
30
+ rejected with `PROJECT_PATH_CHANGED` or `NATIVE_OWNER_UNVERIFIED`, respectively.
31
+ Stop the old runtime using its running build and confirm exit before starting a new one. Native
32
+ provider processes use their scoped HCC MCP tools; ordinary `hcc` commands from
33
+ that environment are rejected with `NATIVE_CLI_SCOPE_REQUIRED`. These checks
34
+ retain the existing sandbox-policy and expected-runtime-generation rules.
35
+ See [Project directory identity and private state](private-state.md) for v1
36
+ binding upgrade receipts and replacement generations.
37
+
19
38
  ## Session fork and retries with the original ID
20
39
 
21
40
  `hcc native fork --parent codex-reviewer --peer codex-branch` invokes the provider's
@@ -221,7 +240,8 @@ resolve the reported problem and retry `native down`.
221
240
 
222
241
  Worker events retain semantic output and completion evidence; token deltas are
223
242
  not persisted as delivery receipts. The runtime keeps local state under
224
- `<project>/.hello-cc/native/` and coordination records in the selected HCC
243
+ `<resolved-project-state>/native/` (project-local or private storage described
244
+ above) and coordination records in the selected HCC
225
245
  database. Runtime control is a local authenticated API, separate from the
226
246
  browser terminal transport. Treat its local runtime pointer and log as private
227
247
  project state.
@@ -14,6 +14,23 @@ Native 模式运行由 HCC 创建并持有的后台 worker。共享 peer、任
14
14
  不代表 host 会把多条待处理消息塞进同一个 turn。ACP 的 resume 和 session close
15
15
  取决于 provider 声明的扩展;不能把 ACP resume 等同于重放原始会话历史。
16
16
 
17
+ ## Runtime 所有权与目录身份
18
+
19
+ Runtime 保留原有 file-lock owner 保护,并增加持久化的 SQLite owner 校验。
20
+ owner 保存 runtime generation、mesh 数据库、私有状态 generation、根目录身份
21
+ 以及进程 PID、启动标记和命令哈希。Native 写入及派生 hook/scoped MCP 写入在
22
+ 同一个 writer fence 中复核 owner。存活或无法核实的 owner 阻止接管;后继进程
23
+ 只能接管已释放或已证实退出的 owner。目录替换或旧执行器 capability 不能授权
24
+ 写入后继状态。
25
+
26
+ 旧 runtime pointer 缺少目录身份回执或 owner fence 时分别被
27
+ `PROJECT_PATH_CHANGED` 或 `NATIVE_OWNER_UNVERIFIED` 拒绝。
28
+ 先用正在运行的旧版本停止它,确认退出后再启动新版本。Native provider 子进程
29
+ 通过其 scoped HCC MCP 工具协作;在该环境中直接调用普通 `hcc` 命令会收到
30
+ `NATIVE_CLI_SCOPE_REQUIRED`。原 sandbox policy 和 expected runtime generation
31
+ 规则继续生效。v1 绑定升级回执和替换目录分代见
32
+ [项目目录身份与私有状态](private-state.zh-CN.md)。
33
+
17
34
  ## 会话 fork 与原 ID 重试
18
35
 
19
36
  `hcc native fork --parent codex-reviewer --peer codex-branch` 使用 provider 自身的
@@ -188,7 +205,8 @@ HCC 不为只读 worker 注入自有的任务/锁/结果写操作 MCP,也
188
205
  `status.shutdown_error` 报告错误;处理所报问题后,再次执行 `native down`。
189
206
 
190
207
  事件保留语义输出和完成证据,不把 token delta 持久化为投递回执。runtime 状态位于
191
- `<project>/.hello-cc/native/`,协作记录仍写入所选 HCC 数据库。控制接口是独立于 Web
208
+ `<解析后的项目状态目录>/native/`,可以是项目局部或上方说明的私有状态;协作记录
209
+ 仍写入所选 HCC 数据库。控制接口是独立于 Web
192
210
  终端传输的本地认证 API;其 runtime pointer 和日志属于项目私有状态。
193
211
 
194
212
  ## 如何理解投递回执
@@ -0,0 +1,115 @@
1
+ # Project directory identity and private state
2
+
3
+ English | [中文](private-state.zh-CN.md)
4
+
5
+ Use a build containing the commands below; an older global installation may not
6
+ include them. These instructions describe state handling, not a receipt for an
7
+ installation, model call or production migration.
8
+
9
+ ## Directory selection and state location
10
+
11
+ HCC binds a selected project to its canonical directory and filesystem identity
12
+ (`dev`, `ino`, and `birthtimeNs`). It checks that identity again before opening
13
+ state, launching a session and admitting managed mutations. A replacement at
14
+ the same pathname is a different project directory. `PROJECT_PATH_CHANGED`
15
+ requires selecting the current directory again; retained sessions keep their
16
+ original binding.
17
+
18
+ Stable trusted projects may keep state under `<project>/.hello-cc`. Projects that
19
+ need private state use `~/.hello-cc/projects/<SHA256-of-canonical-root>/`. Once a
20
+ private binding is established, it remains authoritative even if permissions
21
+ change or its directory disappears. HCC refuses to silently fall back to an
22
+ old project-local database.
23
+
24
+ Private binding format v2 records the full directory identity and requires a
25
+ positive filesystem birth time; an unavailable or zero value is rejected. If original A
26
+ has a valid v2 binding and the same path is later occupied by B, explicit Web
27
+ selection can provision B under
28
+ `~/.hello-cc/projects/<hash>.generations/<generation>/`. A's state directory and
29
+ root manifest are retained; the original authority marker is fenced against
30
+ older readers. B receives separate state and a separate managed tmux namespace.
31
+ Missing, conflicting or unbound generation records require recovery rather
32
+ than automatic reuse. This binding version is separate from the mesh database
33
+ schema version.
34
+
35
+ ## Migrating project-local state
36
+
37
+ When HCC reports `STATE_MIGRATION_REQUIRED`, stop every writer before moving
38
+ the retained project-local store into private state. Include Web and Native
39
+ runtimes, PTY/tmux sessions, hooks, Harness processes and external database
40
+ clients. Back up the project-local store and any existing private state,
41
+ authority markers and generation records.
42
+
43
+ ```sh
44
+ hcc --root /absolute/path/to/project migrate-state --offline --yes
45
+ ```
46
+
47
+ The migrator checks known runtime/PTY/mesh process evidence, snapshots SQLite
48
+ including WAL, validates the snapshot and leaves the source directory intact.
49
+ `--offline` is the operator's assertion that all writers have stopped; observed
50
+ process records cannot establish the absence of unregistered writers. Managed
51
+ DSH artifacts are verified and archived, then rebuilt for the private path.
52
+ Edited managed DSH artifacts, unsafe filesystem entries or a source changing
53
+ during the snapshot fail closed.
54
+
55
+ ## Upgrading a historical private v1 binding
56
+
57
+ `STATE_BINDING_UPGRADE_REQUIRED` means an existing private store lacks v2's
58
+ birth-time binding. Matching pathname, device and inode alone cannot identify
59
+ historical A. Independently verify A and cold-drain its writers before upgrade.
60
+ If the path now holds B, restore and verify A at that path first; selecting B
61
+ cannot upgrade A's historical v1 metadata automatically.
62
+
63
+ Inspect without changing the binding:
64
+
65
+ ```sh
66
+ hcc --root /absolute/path/to/project --json migrate-state --inspect-private-binding
67
+ ```
68
+
69
+ The result reports `status` and, when required or pending, a SHA256 `receipt`.
70
+ The receipt describes the currently observed metadata; it does not prove that
71
+ the current directory is historical A. After independent verification, backup
72
+ and cold drain, copy the exact receipt into:
73
+
74
+ ```sh
75
+ hcc --root /absolute/path/to/project migrate-state \
76
+ --upgrade-private-binding --offline --yes --assert-historical-root \
77
+ --expect-receipt=COPY_THE_INSPECTION_SHA256
78
+ ```
79
+
80
+ Upgrade checks known writers both before and after publication, durably fences
81
+ v1 readers first, then writes the v2 root manifest and removes the pending
82
+ fence. It updates binding metadata without copying the store. A publication or
83
+ second offline-check failure leaves access fenced. Inspect again and retry
84
+ with the same verified root and a matching receipt; preserve all evidence if
85
+ manual recovery is reported. Do not fabricate markers or purge a historical
86
+ store to make an upgrade pass. Older readers can reject v2 state, so drain old
87
+ builds before switching execution.
88
+
89
+ ## Pausing new launches
90
+
91
+ `HCC_PINNED_LAUNCH_MODE` accepts `pinned` (also the default when unset) or `hold`.
92
+ `hold` rejects new guarded session launches with `PINNED_LAUNCH_PAUSED`; it does
93
+ not terminate existing processes or revoke a launch already prepared. Invalid
94
+ values are rejected. A provider environment that strips HCC variables cannot
95
+ clear the parent process's hold. Maintenance workers used for pinned file and
96
+ guidance operations remain available.
97
+
98
+ Set this environment on the invoking HCC process. Changing a later shell's
99
+ environment does not update a running Web or Native runtime. A hold is not a
100
+ substitute for stopping every writer before migration.
101
+
102
+ ## Codex terminal history
103
+
104
+ HCC's Codex history controls require a persisted thread-to-root identity receipt.
105
+ Built-in new/fork launches record the resulting thread's original directory;
106
+ resume and fork verify both that identity and the explicit thread ID. The Web
107
+ history list omits unverified or conflicting entries and reports their count.
108
+ An old cwd string, imported peer row or manually assembled command does not
109
+ establish a verified root. `CODEX_HISTORY_UNVERIFIED` and conflicting bindings
110
+ require manual review; HCC provides no automatic historical association command.
111
+
112
+ Terminal `codex resume --last` and `codex fork --last` cannot establish an exact
113
+ thread and are rejected by these managed controls. Select an explicit verified
114
+ thread ID. [Native workers](native.md) retain their separate saved-worker resume
115
+ and ownership rules, including `native start --resume last`.
@@ -0,0 +1,92 @@
1
+ # 项目目录身份与私有状态
2
+
3
+ [English](private-state.md) | 中文
4
+
5
+ 请使用包含下方命令的版本;旧的全局安装可能尚未支持。这份文档说明状态处理
6
+ 规则,不代表已完成设备安装、模型调用或生产状态迁移。
7
+
8
+ ## 目录选择与状态位置
9
+
10
+ HCC 将所选项目绑定到 canonical 目录与文件系统身份(`dev`、`ino`、
11
+ `birthtimeNs`),并在打开状态、启动会话和接收受控修改前复核。同一路径被替换
12
+ 后属于另一个项目目录。遇到 `PROJECT_PATH_CHANGED` 应重新选择当前目录;已有
13
+ 会话仍保留原目录绑定。
14
+
15
+ 稳定且可信的项目可以把状态保存在 `<project>/.hello-cc`。需要私有状态的项目
16
+ 使用 `~/.hello-cc/projects/<canonical-root的SHA256>/`。私有绑定建立后,即使
17
+ 权限变化或目录丢失,它仍是权威状态;HCC 不会静默退回旧的项目局部数据库。
18
+
19
+ 私有绑定 v2 保存完整目录身份,要求文件系统提供正值的目录创建时间;缺失或
20
+ 零值会被拒绝。原目录 A 已有有效 v2 绑定,而同一路径后来被
21
+ B 占用时,Web 的明确目录选择可在
22
+ `~/.hello-cc/projects/<hash>.generations/<generation>/` 为 B 建立独立状态。
23
+ A 的状态目录与根 manifest 保留,原 authority marker 增加阻止旧版读取的标记。
24
+ B 使用独立状态和 managed tmux namespace。分代记录丢失、冲突或没有对应绑定时
25
+ 要求恢复,不会自动复用。私有绑定版本与 mesh 数据库 schema 版本彼此独立。
26
+
27
+ ## 迁移项目局部状态
28
+
29
+ 遇到 `STATE_MIGRATION_REQUIRED` 时,先停止全部写入者,再把保留的项目局部
30
+ 状态迁入私有目录。写入者包括 Web、Native runtime、PTY/tmux 会话、hooks、
31
+ Harness 进程和外部数据库客户端。备份项目局部状态,以及已有的私有目录、
32
+ authority marker 和 generation 记录。
33
+
34
+ ```sh
35
+ hcc --root /absolute/path/to/project migrate-state --offline --yes
36
+ ```
37
+
38
+ 迁移会检查已记录的 runtime、PTY 和 mesh 进程证据,创建包含 WAL 的 SQLite
39
+ 快照并校验,保留源目录。`--offline` 是操作者对全部写入者已停止的断言;已记录
40
+ 进程不能证明没有未登记写入者。受管理的 DSH 文件会先验证、归档,再按私有路径
41
+ 重建。受管理 DSH 文件被改、文件系统条目不安全或快照期间来源改变时会被拒绝。
42
+
43
+ ## 升级历史私有 v1 绑定
44
+
45
+ `STATE_BINDING_UPGRADE_REQUIRED` 表示已有私有状态缺少 v2 的目录创建时间绑定。
46
+ 仅有相同路径、device 和 inode 不能确认当前目录就是历史 A。升级前应独立核实
47
+ A 的身份,并冷停机全部写入者。若当前路径已是 B,应先在该路径恢复并核实 A;
48
+ 选择 B 不会自动升级 A 的历史 v1 元数据。
49
+
50
+ 先检查,保持绑定不变:
51
+
52
+ ```sh
53
+ hcc --root /absolute/path/to/project --json migrate-state --inspect-private-binding
54
+ ```
55
+
56
+ 结果包含 `status`;需要升级或尚未完成时包含 SHA256 `receipt`。回执描述当前
57
+ 观察到的元数据,不能证明当前目录就是历史 A。独立核实、备份并完成冷停机后,
58
+ 把本次准确回执复制到:
59
+
60
+ ```sh
61
+ hcc --root /absolute/path/to/project migrate-state \
62
+ --upgrade-private-binding --offline --yes --assert-historical-root \
63
+ --expect-receipt=COPY_THE_INSPECTION_SHA256
64
+ ```
65
+
66
+ 升级在发布前后检查已知写入者,先持久化阻止 v1 读取的标记,再写入 v2 根
67
+ manifest,最后清除 pending 标记。它只升级绑定元数据,不复制状态目录。发布
68
+ 失败或第二次离线检查失败时,状态仍被封锁。重新检查后,在同一个已核实根目录
69
+ 和匹配回执下重试;报告需要人工恢复时保留全部证据。不要伪造 marker 或清空
70
+ 历史状态来绕过升级。旧版本可能拒绝 v2 状态,应先停止旧版本的执行再切换。
71
+
72
+ ## 暂停新启动
73
+
74
+ `HCC_PINNED_LAUNCH_MODE` 只接受 `pinned`(未设置时也是默认行为)或 `hold`。
75
+ `hold` 以 `PINNED_LAUNCH_PAUSED` 拒绝新的受控会话启动,不终止已有进程,也
76
+ 不撤销已经准备完成的启动。其他取值被拒绝。provider 环境清除 HCC 变量也不能
77
+ 解除父进程的 hold。用于固定目录文件或指导块操作的 maintenance worker 仍可运行。
78
+
79
+ 环境变量应设置在调用 HCC 的进程上;后来修改另一个 shell 的环境不会更新正在
80
+ 运行的 Web 或 Native runtime。hold 不能代替迁移前停止全部写入者。
81
+
82
+ ## Codex 终端历史边界
83
+
84
+ HCC 的 Codex 历史入口要求持久化的 thread 与原根目录身份回执。内置 new/fork
85
+ 启动记录最终 thread 的原目录,resume/fork 同时校验目录身份与明确 thread ID。
86
+ Web 历史列表省略未核实或冲突的记录,并返回这类记录的数量。旧的 cwd 字符串、
87
+ 导入的 peer 记录或手工拼接命令都不能建立可信绑定。
88
+ `CODEX_HISTORY_UNVERIFIED` 和冲突绑定需要人工审查;HCC 未提供自动关联历史的命令。
89
+
90
+ 终端 `codex resume --last` / `codex fork --last` 无法确定准确 thread,因此被这些
91
+ 受控入口拒绝;应选择明确且已核实的 thread ID。[Native worker](native.zh-CN.md)
92
+ 采用独立的保存会话与所有权规则,仍支持 `native start --resume last`。
package/lib/cli/app.mjs CHANGED
@@ -91,7 +91,8 @@ import { assignTeamWorkers, expandTeamWorkers, inferTeamItems } from '../core/co
91
91
  import { autoPeerBasis, autoPeerKind, autoPeerProviderSession, autoPeerResumeId, autoPeerSessionId, readAncestorCliInfo, resolveCurrentPeer, resumeIdFromArgs, shortHash } from '../integrations/peers/identity.mjs';
92
92
  import { inspectProviderProcess } from '../integrations/peers/processes.mjs';
93
93
  import { registerProjectActivity } from '../runtime/projects.mjs';
94
- import { migrateLegacyProjectState } from '../runtime/state-migration.mjs';
94
+ import { assertPrivateStateOffline, migrateLegacyProjectState } from '../runtime/state-migration.mjs';
95
+ import { inspectPrivateProjectBindingUpgrade, upgradePrivateProjectBinding } from '../runtime/private-state.mjs';
95
96
  import { projectDbPath } from '../runtime/paths.mjs';
96
97
  import { ensureDshIntegration } from '../integrations/dsh.mjs';
97
98
 
@@ -590,13 +591,45 @@ function removeGuidanceBlocks(ctx, options = {}) {
590
591
 
591
592
  function cmdMigrateState(ctx, args) {
592
593
  if (wantsHelp(args)) {
593
- console.log(`${CLI_NAME} migrate-state --offline --yes\n\n` +
594
+ console.log(`${CLI_NAME} migrate-state --offline --yes\n` +
595
+ `${CLI_NAME} migrate-state --inspect-private-binding\n` +
596
+ `${CLI_NAME} migrate-state --upgrade-private-binding --offline --yes ` +
597
+ '--assert-historical-root --expect-receipt=SHA256\n\n' +
594
598
  'Stop all old hello-cc Web, Native, PTY, tmux, hook and external database writers first. ' +
595
- 'This command verifies known process evidence, snapshots SQLite including WAL, and leaves the old directory untouched.');
599
+ 'A private binding upgrade requires independent confirmation that the current root is the historical root. ' +
600
+ 'The inspection receipt alone cannot prove this. The ordinary migration snapshots SQLite including WAL ' +
601
+ 'and leaves the old directory untouched.');
596
602
  return;
597
603
  }
598
- const opts = parseOpts(args, { booleans: ['offline', 'yes'] });
599
- validateOpts('migrate-state', opts, ['offline', 'yes']);
604
+ const opts = parseOpts(args, { booleans: ['offline', 'yes', 'inspect-private-binding',
605
+ 'upgrade-private-binding', 'assert-historical-root'] });
606
+ validateOpts('migrate-state', opts, ['offline', 'yes', 'inspect-private-binding',
607
+ 'upgrade-private-binding', 'assert-historical-root', 'expect-receipt']);
608
+ if (opts['inspect-private-binding'] === true) {
609
+ if (opts['upgrade-private-binding'] || opts.offline || opts.yes ||
610
+ opts['assert-historical-root'] || opts['expect-receipt']) {
611
+ throw new CliError('BAD_ARGS', '--inspect-private-binding cannot be combined with upgrade options');
612
+ }
613
+ const result = inspectPrivateProjectBindingUpgrade(ctx.root);
614
+ printResult(ctx, result, () => JSON.stringify(result, null, 2));
615
+ return;
616
+ }
617
+ if (opts['upgrade-private-binding'] === true) {
618
+ const result = upgradePrivateProjectBinding(ctx.root, {
619
+ confirmedOffline: opts.offline === true,
620
+ confirmedHistoricalRoot: opts['assert-historical-root'] === true && opts.yes === true,
621
+ expectedReceipt: opts['expect-receipt'],
622
+ assertOffline: ({ stateDir }) => {
623
+ assertPrivateStateOffline(stateDir);
624
+ return true;
625
+ }
626
+ });
627
+ printResult(ctx, result, () => JSON.stringify(result, null, 2));
628
+ return;
629
+ }
630
+ if (opts['assert-historical-root'] || opts['expect-receipt']) {
631
+ throw new CliError('BAD_ARGS', 'Private binding options require --upgrade-private-binding');
632
+ }
600
633
  if (opts.offline !== true || opts.yes !== true) {
601
634
  throw new CliError('STATE_MIGRATION_OFFLINE_REQUIRED',
602
635
  'Stop every old project-state writer, then acknowledge downtime with migrate-state --offline --yes');
@@ -630,6 +663,14 @@ function cmdMigrateState(ctx, args) {
630
663
  async function dispatch(ctx, rest) {
631
664
  const command = rest[0];
632
665
  const args = rest.slice(1);
666
+ // Native provider children inherit this marker. The ordinary CLI opens the
667
+ // mesh database outside the durable native owner transaction, so only the
668
+ // two owner-fenced entry points may run from that environment.
669
+ if (process.env.HCC_NATIVE_OWNER &&
670
+ !(command === 'hook' || (command === 'mcp' && args[0] === 'serve'))) {
671
+ throw new CliError('NATIVE_CLI_SCOPE_REQUIRED',
672
+ 'Native workers must use their scoped MCP tools; ordinary hcc commands cannot write outside the native owner fence');
673
+ }
633
674
  if (!command || command === '--help' || command === '-h' || command === 'help') return helpMain();
634
675
  if (command === '--version' || command === 'version') return console.log(VERSION);
635
676
  if (command === 'up') return cmdUp(ctx, args);
@@ -6,6 +6,9 @@ import { tx } from '../../db/schema.mjs';
6
6
  import { resolveProjectDatabase } from '../../runtime/project-path.mjs';
7
7
  import { projectDbPath } from '../../runtime/paths.mjs';
8
8
  import { HOOK_ROOT_IDENTITY_ENV, assertHookRootIdentity } from '../../core/sessions/hook-root-identity.mjs';
9
+ import { CODEX_SESSION_ORIGIN_ENV, recordCodexThreadRoot } from '../../core/sessions/codex-thread-root.mjs';
10
+ import { captureSelectedCwdSnapshot, sameSelectedCwdIdentity } from '../../process/selected-cwd-identity.mjs';
11
+ import { acquireNativeOwnerWrite, nativeOwnerGeneration } from '../../runtime/native/store.mjs';
9
12
 
10
13
  export function createHookCommand(deps) {
11
14
  const {
@@ -105,6 +108,17 @@ async function cmdHook(ctx, args) {
105
108
  }
106
109
  }
107
110
 
111
+ const codexOrigin = kind === 'codex' && hookKey === 'sessionstart' && !nativeOwner
112
+ ? process.env[CODEX_SESSION_ORIGIN_ENV] : null;
113
+ if (codexOrigin) {
114
+ if (!['new', 'fork'].includes(codexOrigin) || !launchRootIdentity ||
115
+ !process.env.HCC_PEER || !process.env.HCC_DB ||
116
+ !sameSelectedCwdIdentity(captureSelectedCwdSnapshot(hookCwd), launchRootIdentity)) {
117
+ throw new CliError('CODEX_HISTORY_BINDING_INVALID',
118
+ 'First-party Codex session hook does not match its pinned project launch');
119
+ }
120
+ }
121
+
108
122
  const dshProject = isDsh && !nativeOwner ? resolveProjectDatabase({ root: hccRoot, createStateDir: false }) : null;
109
123
  const nativeProject = nativeOwner ? resolveProjectDatabase({ root: hccRoot,
110
124
  db: process.env.HCC_DB, createStateDir: false }) : null;
@@ -143,9 +157,17 @@ async function cmdHook(ctx, args) {
143
157
  : `${kind}-${shortHash(`${hookCtx.root}:${autoPeerBasis(kind)}`)}`;
144
158
  }
145
159
 
146
- // Hook delivery must not migrate another workspace's registered database.
147
- const db = isDsh || nativeOwner ? connect(hookCtx, { migrateRegistered: false }) : connect(hookCtx);
160
+ // Native hooks lock state.db before connect(), whose schema setup may write
161
+ // mesh.db. Keep that lock through the heartbeat transaction and DB close.
162
+ const nativeGeneration = nativeOwner ? nativeOwnerGeneration(nativeOwner) : null;
163
+ if (nativeOwner && !nativeGeneration) {
164
+ throw new CliError('NATIVE_HOOK_OWNERSHIP_MISMATCH', 'Native hook has no verifiable owner generation');
165
+ }
166
+ const finishNative = nativeOwner ? acquireNativeOwnerWrite(hookCtx, nativeGeneration) : null;
167
+ let db, nativeSuccess = false;
148
168
  try {
169
+ // Hook delivery must not migrate another workspace's registered database.
170
+ db = isDsh || nativeOwner ? connect(hookCtx, { migrateRegistered: false }) : connect(hookCtx);
149
171
  if (!nativeOwner) registerProjectActivity(hookCtx);
150
172
  if (nativeOwner) {
151
173
  // Native delivery owns context injection and completion receipts. Hooks
@@ -166,6 +188,7 @@ async function cmdHook(ctx, args) {
166
188
  ttlCap: 3600, includeExpired: true });
167
189
  if (renewed > 0) addEvent(db, 'lock.renewed_by_hook', peerId, null, { renewed, source: 'native-hook' });
168
190
  });
191
+ nativeSuccess = true;
169
192
  process.exitCode = 0;
170
193
  return;
171
194
  }
@@ -226,6 +249,9 @@ async function cmdHook(ctx, args) {
226
249
  runtime_session_id: peerId
227
250
  };
228
251
  const canonical = upsertCanonicalPeerBinding(db, hookBinding, true);
252
+ if (codexOrigin) {
253
+ recordCodexThreadRoot(db, addEvent, sessionId, launchRootIdentity, codexOrigin, { allowSame: true });
254
+ }
229
255
  if (canonical.peer !== peerId) {
230
256
  const previousPeer = peerId;
231
257
  peerId = canonical.peer;
@@ -254,7 +280,7 @@ async function cmdHook(ctx, args) {
254
280
  };
255
281
  // Keep the dsh conflict check and identity/binding writes atomic, so a
256
282
  // concurrent service/session registration cannot change the inspected row.
257
- const registered = isDsh || kind === 'claude' ? tx(db, registerHookIdentity) : registerHookIdentity();
283
+ const registered = isDsh || kind === 'claude' || codexOrigin ? tx(db, registerHookIdentity) : registerHookIdentity();
258
284
  if (registered === false) { process.exitCode = 0; return; }
259
285
  // hb-06: an active hook is proof the peer is working. During clock grace,
260
286
  // renew retained locks even when the wall-clock jump made them look expired.
@@ -321,7 +347,8 @@ async function cmdHook(ctx, args) {
321
347
  }
322
348
  }
323
349
  } finally {
324
- try { db.close(); } catch {}
350
+ try { db?.close(); } catch {}
351
+ finishNative?.(nativeSuccess);
325
352
  }
326
353
  // Let Node exit after the hook output has flushed instead of truncating a
327
354
  // pipe with process.exit(). A successful write is not provider acceptance.