@nanhara/hara 0.121.1 → 0.122.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/README.md +57 -10
  3. package/SECURITY.md +48 -9
  4. package/dist/agent/loop.js +169 -31
  5. package/dist/agent/reminders.js +22 -7
  6. package/dist/agent/repeat-guard.js +26 -7
  7. package/dist/agent/structured.js +231 -0
  8. package/dist/agent/touched.js +24 -6
  9. package/dist/checkpoints.js +103 -17
  10. package/dist/cli.js +16 -0
  11. package/dist/config.js +173 -34
  12. package/dist/context/agents-md.js +44 -9
  13. package/dist/context/mentions.js +10 -4
  14. package/dist/context/subdir-hints.js +40 -7
  15. package/dist/cron/deliver.js +37 -3
  16. package/dist/cron/runner.js +372 -37
  17. package/dist/cron/store.js +11 -3
  18. package/dist/exec/jobs.js +88 -20
  19. package/dist/feedback.js +3 -2
  20. package/dist/fs-read.js +421 -12
  21. package/dist/fs-walk.js +8 -2
  22. package/dist/fs-write.js +433 -21
  23. package/dist/gateway/dingtalk.js +4 -1
  24. package/dist/gateway/discord.js +53 -20
  25. package/dist/gateway/feishu.js +157 -58
  26. package/dist/gateway/flows-pending.js +727 -0
  27. package/dist/gateway/flows.js +391 -16
  28. package/dist/gateway/matrix.js +81 -18
  29. package/dist/gateway/mattermost.js +44 -34
  30. package/dist/gateway/media.js +659 -0
  31. package/dist/gateway/outbound-files.js +379 -0
  32. package/dist/gateway/serve.js +712 -169
  33. package/dist/gateway/sessions.js +475 -78
  34. package/dist/gateway/signal.js +31 -28
  35. package/dist/gateway/slack.js +28 -21
  36. package/dist/gateway/telegram.js +33 -21
  37. package/dist/gateway/tmux-routes.js +11 -3
  38. package/dist/gateway/wecom.js +38 -31
  39. package/dist/gateway/weixin.js +147 -59
  40. package/dist/hooks.js +41 -23
  41. package/dist/index.js +763 -273
  42. package/dist/mcp/client.js +164 -12
  43. package/dist/memory/store.js +68 -22
  44. package/dist/org/planner.js +36 -10
  45. package/dist/org/projects.js +347 -0
  46. package/dist/org/review-chain.js +360 -24
  47. package/dist/org/roles.js +42 -13
  48. package/dist/profile/profile.js +152 -27
  49. package/dist/recall.js +4 -2
  50. package/dist/runtime.js +37 -0
  51. package/dist/sandbox.js +142 -33
  52. package/dist/search/semindex.js +182 -53
  53. package/dist/search/zvec-store.js +121 -42
  54. package/dist/security/permissions.js +326 -19
  55. package/dist/security/private-state.js +299 -0
  56. package/dist/security/project-trust.js +6 -0
  57. package/dist/security/secrets.js +84 -9
  58. package/dist/security/sensitive-files.js +723 -0
  59. package/dist/security/subprocess-env.js +210 -0
  60. package/dist/serve/server.js +774 -318
  61. package/dist/serve/sessions.js +113 -33
  62. package/dist/session/store.js +298 -47
  63. package/dist/skills/skills.js +16 -7
  64. package/dist/tools/all.js +1 -0
  65. package/dist/tools/builtin.js +77 -49
  66. package/dist/tools/codebase.js +3 -1
  67. package/dist/tools/computer.js +98 -92
  68. package/dist/tools/cron.js +6 -0
  69. package/dist/tools/edit.js +22 -9
  70. package/dist/tools/external_agent.js +110 -16
  71. package/dist/tools/memory.js +38 -8
  72. package/dist/tools/patch.js +253 -34
  73. package/dist/tools/search.js +543 -73
  74. package/dist/tools/send.js +11 -5
  75. package/dist/tools/task.js +453 -0
  76. package/dist/tools/todo.js +67 -16
  77. package/dist/tools/web.js +168 -54
  78. package/dist/undo.js +83 -7
  79. package/package.json +11 -10
  80. package/runtime-bootstrap.cjs +72 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,126 @@ All notable changes to `@nanhara/hara`.
5
5
  > Versioning (pre-1.0, SemVer-style): the **minor** (middle) number bumps for a **new feature**; the
6
6
  > **patch** (last) number bumps for **optimizations/fixes of existing features**.
7
7
 
8
+ ## 0.122.1 — 2026-07-14 — protected files and explicit extension trust
9
+
10
+ - **The npm CLI now requires Node.js 22.12+.** Node 20 is end-of-life, so Hara no longer makes a security or
11
+ compatibility promise for it. npm metadata, `hara doctor`, and startup diagnostics now agree; an older
12
+ runtime exits with a direct `nvm install 22 && nvm use 22` upgrade hint. Standalone binaries and Desktop
13
+ sidecars remain self-contained and do not require a host Node installation.
14
+ - **Built-in file access fails closed before ordinary approval/dispatch.** Canonical and real-path checks
15
+ reject `.env`/`.env.*`, credential stores, private keys, and private Hara runtime state in file reads,
16
+ edit/patch pre-reads, grep/glob/ls, `@file`/completion, codebase and stale semantic indexes, checkpoints,
17
+ gateway file delivery, and cron command admission. Safe templates such as `.env.example`, `.env.sample`,
18
+ and `.env.template` remain readable. `HARA_ALLOW_SENSITIVE_FILES=1` is an explicit launch-time exposure
19
+ switch for one process; it removes both these built-in denies and that process's shell protected-read mask.
20
+ - **Shell protection now reflects the host's guarantees.** Tool subprocesses receive a scrubbed environment;
21
+ inherited variables can be explicitly retained with the launch-time `HARA_SUBPROCESS_ENV_ALLOW` list, and
22
+ output is redacted by secret pattern and exact inherited value. Under the default protected-file policy,
23
+ shell admission blocks literal protected paths and environment dump commands on every platform. macOS
24
+ additionally applies a Seatbelt read mask to existing protected
25
+ files/directories, even when the general write sandbox is off. Linux and Windows do not have that kernel
26
+ read mask: their arbitrary shell code is not a security sandbox and static preflight can be bypassed.
27
+ - **MCP servers and external coding agents are explicit trusted extensions.** They execute outside Hara's
28
+ protected-file boundary, require confirmation on every interactive tool call (including `full-auto`), and
29
+ are disabled in non-interactive runs by default. Reviewed automation may opt in before launch with
30
+ `HARA_ALLOW_TRUSTED_EXTENSIONS=1`; inherited credentials are still scrubbed.
31
+ - **Repositories no longer silently control Hara's privileged configuration.** Project config is limited by
32
+ default to validated presentation/model preferences; provider routes, credentials, hooks, MCP, sandbox,
33
+ guardian, approval, and automation settings require the launch-time `HARA_TRUST_PROJECT_CONFIG=1` opt-in.
34
+ Project permissions may tighten global policy but cannot grant new access, and Git-tracked `.hara-profile`
35
+ pins are ignored unless that same trust decision was made before startup. Symlink, hard-link, size, identity,
36
+ and concurrent-change checks protect each of these inputs, while diagnostics reveal key names but no values.
37
+ - **Coding and project-state writes are transactional.** Edit, patch, undo, memory, skills, plans, profiles,
38
+ indexes, and gateway state use no-follow descriptors, inode checks, bounded reads, atomic replace or
39
+ compare-and-swap semantics, and private modes where appropriate. Protected search uses the same verified
40
+ descriptor path instead of handing sensitive-path decisions to `rg`, closing replacement and hard-link
41
+ races. `AGENTS.md`, subdirectory hints, mentions, and touched-file recall are bounded and reject protected
42
+ aliases before adding repository content to model context.
43
+ - **Semantic search and review automation distrust stale or historical content.** Semantic indexes carry a
44
+ versioned manifest with content hashes and are rebuilt when source identity or bytes change. Review-chain
45
+ prompts receive bounded status/path metadata rather than Git patches; an auto-commit proceeds only when the
46
+ staged blob still matches the verified worktree descriptor, including deletion and SHA-256 repository edge
47
+ cases. Read-only auto-run Git policy excludes commands that can expose historical blobs, credentials, remote
48
+ URLs, or patch bodies.
49
+ - **Old private state is tightened and stale checkpoint history is rotated.** Startup repairs `~/.hara` and
50
+ sensitive runtime trees to owner-only modes on POSIX systems. The protected checkpoint format rebuilds old
51
+ derived shadow repositories, filters protected paths independently of the launch-time exposure switch, and
52
+ purges a checkpoint repository if a protected blob ever reaches its index.
53
+ - **Long-running work has bounded ownership and shutdown.** Shell jobs, external agents, gateway children,
54
+ approved organization flows, and cron commands terminate the whole process tree with TERM-to-KILL escalation
55
+ and stop accepting output after cancellation. Background job counts, cron run time/log size, lock takeover,
56
+ and context fan-out are capped; Windows shell aliases and environment-dump variants are denied consistently.
57
+ - **Gateway outbound files are immutable after admission.** Telegram, Feishu, Slack, Discord, Mattermost,
58
+ Matrix, WeCom, and Weixin upload verified in-memory bytes plus a safe filename, so a queued path cannot be
59
+ swapped for a secret before delivery. Counts and bytes are bounded at queue and consume time, cleanup checks
60
+ inode identity, and Signal outbound files fail closed because its RPC accepts only reopenable filesystem paths.
61
+
62
+ ## 0.122.0 — 2026-07-13 — structured runs, durable work, and a fail-closed gateway
63
+
64
+ - **Machine-safe headless runs.** `hara -p … --schema <json|file>` installs a run-scoped
65
+ `structured_output` contract, validates the value against JSON Schema, and writes exactly one JSON value to
66
+ stdout. Prose, auth notices, retries, and provider/schema failures cannot masquerade as machine output:
67
+ diagnostics go to stderr and failures exit non-zero. `hara -p … --role <id>` now applies the role's persona,
68
+ model, and allow/deny tool policy instead of changing the prompt alone.
69
+ - **Agents have stable homes.** `hara projects add/list/remove` maintains a private, atomic registry of project
70
+ homes; `hara agents` builds one address book from global and registered-project roles.
71
+ `hara org --role project:agent …` resolves unambiguously and executes at that home with its project context/config. Concurrent
72
+ CLIs safely share the registry, and dead lock owners can be reclaimed without stealing a live lock.
73
+ - **Two levels of work state.** `todo_write` is now isolated per interactive/headless/sub-agent/serve session
74
+ and persists with the session, so concurrent agents cannot overwrite each other's checklist. The new `task`
75
+ tool is a durable cross-session project pool with owners, statuses, `blockedBy` dependencies, cycle checks,
76
+ and private atomic cross-process persistence. `hara serve` exposes the pool through `tasks.list`.
77
+ - **10-platform gateway, with project-agent roaming.** Telegram, WeChat, Discord, Feishu/Lark, Slack,
78
+ Mattermost, Matrix, DingTalk, WeCom, and Signal adapters now classify direct vs multi-party chats
79
+ conservatively. The full coding agent accepts only verified private messages; unknown channel shapes fail
80
+ closed. `/agent <name|project:name>` pins a thread to an indexed agent and its home, while `/agent main`
81
+ restores the prior project thread. Chat/session preferences survive project round-trips and group-member
82
+ state is isolated.
83
+ - **Untrusted group flows cannot reach coding tools.** Opt-in `~/.hara/flows.json` rules are hot-reloaded and
84
+ evaluated through a bounded, stateless provider call with `tools: []` — no shell, files, MCP, session, or
85
+ project context. Rules support trigger filters, JSON Schema, disposition-based notification/auto-reply,
86
+ redacted rotating logs, and rate/concurrency caps. Proposed sends and agent dispatches are parked unless an
87
+ explicitly configured safe `replyOn` disposition applies.
88
+ - **Single-owner, idempotent approvals.** Consequential flow actions require a unique allowlisted owner and a
89
+ verified private channel. Deterministic `/approve <id>`, `/edit <id> <content>`, and `/reject <id>` commands
90
+ avoid “latest draft” ambiguity; blank edits fail closed. Pending actions use private atomic storage and a
91
+ compare-and-set execution claim, shared by chat and the new serve `approvals.list/resolve` inbox, so two
92
+ approval surfaces cannot double-send. Deferred actions are parked only for one-shot delivery targets
93
+ (Telegram, Feishu/Lark, or WeChat); other adapters fail closed instead of presenting an unusable approval.
94
+ - **Bounded gateway execution and media.** Turns are FIFO per session (depth 8), with four active children,
95
+ bounded global/session-key backlogs, a 15-minute default/30-minute hard timeout, and TERM-to-KILL shutdown;
96
+ non-zero or signalled children fail visibly. Inbound attachments are authorized before download, limited to
97
+ four files and 20 MiB each, streamed into private random paths with time/concurrency/retention quotas, and
98
+ expired after 24 hours. Progress markers are recalled in `finally`, gateway chat/session files are `0600`
99
+ with lock-before-load persistence, and ambiguous/stale locks are never reclaimed merely by age.
100
+ - **Layered flow admission.** Group classifiers are capped globally (20/minute, 120/hour), per rule/platform/chat
101
+ (10/minute, 60/hour), per sender (5/minute), and at four active runs. Rate maps and key sizes are bounded and
102
+ saturation fails closed, so rotating identities cannot grow memory or bypass the host-wide ceiling.
103
+ - **Live config for persistent clients.** `hara serve` rebuilds cwd-specific provider routes, credentials, and
104
+ guardian settings on sessions/turns; `initialize`, `models.list`, and new sessions reflect current defaults.
105
+ Resumed sessions retain their explicit model pin while using the live provider route, so credential rotation
106
+ and project config edits no longer require a server restart.
107
+ - **Coding-path robustness.** File reads/search/edit/patch now reject FIFOs/devices and symlink races, cap reads,
108
+ use bounded subprocess search with a linear glob matcher, preserve exact file modes, and roll back multi-file
109
+ patches without overwriting concurrent replacements; writes move-claim the expected inode and commit with
110
+ create-if-absent semantics, while `/undo` applies the same identity checks. Blank
111
+ project/env routing values no longer mask valid global config; precedence is environment > project > overlay
112
+ > global. Web fetch/search blocks IPv4-mapped and full link-local IPv6 SSRF forms and tries regional providers
113
+ sequentially instead of broadcasting a successful query. Package installs stay attached with bounded
114
+ long-operation timeouts, tunnel commands preflight their binaries, TUI resize repaints reliably, persisted/
115
+ public text redacts secrets, and session writes are private, atomic, and cross-process safe.
116
+ - **Interrupts and non-interactive outcomes are honest.** Cancelling a parallel tool round records matching
117
+ interrupted results while preserving completed side effects; serve approvals settle immediately on interrupt.
118
+ The stream-stall watchdog is a hard Promise boundary even when a provider ignores its abort signal.
119
+ Headless and org runs now exit non-zero on provider errors, empty/halted turns, schema failures, and rejected
120
+ reviews; plan atoms stop on the same outcomes instead of being verified as done, and failed implementers are
121
+ never committed. Read-only roles are resolved before startup and launch neither hooks nor configured/plugin
122
+ MCP subprocesses.
123
+ - **Private serve lifecycle.** `~/.hara`/`serve.json` are tightened to `0700`/`0600` and discovery is fsynced,
124
+ atomically replaced without following symlinks, and removed only by its owning instance. Discovery failures
125
+ close the listening socket. Serve-side compaction is mutually exclusive with turns/config changes, carries an
126
+ abort signal, releases locks on interrupt/shutdown, and has a 60-second hard deadline.
127
+
8
128
  ## 0.121.1 — field-feedback reliability & credential safety
9
129
 
10
130
  - **Terminal resize no longer erases the composer.** Hara clears stale Ink output only before a real
package/README.md CHANGED
@@ -12,11 +12,11 @@
12
12
 
13
13
  **Highlights**
14
14
  - **An org, not just an agent** — `hara org "<task>"` routes work to the role that *owns* it; `hara plan "<task>"` decomposes a task into a verified DAG of atoms (frame → atomize → sequence → execute → **verify gate**), and `hara plan --parallel` runs independent atoms concurrently.
15
- - **Drive it from chat** — `hara gateway` runs your local hara from **Telegram · WeChat · Discord · Feishu/Lark · Slack · Mattermost · Matrix · DingTalk · WeCom · Signal** (10 platforms), with **two-way images** (send a photo it sees it; ask for a file it sends one), per-chat resumable sessions, and project roaming. Connects out — no public webhook. See **[docs/gateway.md](docs/gateway.md)**.
15
+ - **Drive it from chat** — `hara gateway` runs your local hara from **Telegram · WeChat · Discord · Feishu/Lark · Slack · Mattermost · Matrix · DingTalk · WeCom · Signal** (10 platforms), with **two-way images where the platform has a byte-upload API**, resumable per-chat sessions, project/agent roaming, bounded per-thread queues, and approval-gated group automations. Connects out — no public webhook. See **[docs/gateway.md](docs/gateway.md)**.
16
16
  - **Real terminal UX** — an **ink TUI**: bottom-pinned input box, **plan mode** (read-only investigation → the model submits its plan via `exit_plan` → approve → execute), selectable approvals with "don't ask again", windowed reasoning, **paste images** (Ctrl+V) for vision models, light/dark theme.
17
17
  - **Persistent memory + self-evolution** — `memory_*` tools over global/project `MEMORY.md`; the agent recalls before acting, **proactively saves** durable facts, and grows its own playbooks (a lexical guard screens what it writes). Inspect/consolidate it with **`hara memory show`** and **`hara memory distill`** (promote recent daily logs → durable memory). Lexical-first by design — semantic search is opt-in, never required.
18
18
  - **Multi-provider, all streamed** — Anthropic (Claude) or any OpenAI-compatible endpoint (Qwen/DashScope, GLM, Kimi, OpenAI) with live Markdown + visible reasoning.
19
- - **Delegate to other agents** — the **`external_agent`** tool hands a self-contained task to **Claude Code** or **Codex** running headless, and returns the result — so you pick the best engine per task. Gated by approval, trust-tiered, and never exposed to read-only sub-agents.
19
+ - **Delegate to other agents** — the **`external_agent`** tool hands a self-contained task to **Claude Code** or **Codex** running headless, and returns the result — so you pick the best engine per task. It is a trusted extension outside Hara's protected-file boundary: every interactive call requires confirmation, and non-interactive use is disabled by default.
20
20
  - **Honest under a slow network** — a live "waiting for the model… Ns" status, a stall watchdog that
21
21
  auto-fails-over instead of hanging, big pastes folding to a token, and a startup update notice — the
22
22
  terminal never feels dead.
@@ -26,6 +26,9 @@ Track it: https://github.com/hara-cli/hara · https://hara.run
26
26
 
27
27
  ## Install
28
28
 
29
+ The npm package requires **Node.js 22.12 or newer**. If needed, upgrade first with
30
+ `nvm install 22 && nvm use 22`. Node.js 20 is end-of-life and is not a supported Hara runtime.
31
+
29
32
  ```bash
30
33
  npm i -g @nanhara/hara
31
34
  ```
@@ -135,8 +138,16 @@ hara expresses it the way each endpoint wants (OpenAI `reasoning_effort`, Anthro
135
138
  DashScope `enable_thinking`, **DeepSeek** V4 `thinking` + `reasoning_effort` where `max` genuinely raises the
136
139
  effort). In the TUI, bare `/model` opens a picker — ↑↓ pick a model, **←→ set the thinking level**.
137
140
 
138
- Config lives in `~/.hara/config.json`. Env vars override it: `HARA_PROVIDER`, `HARA_MODEL`,
139
- `HARA_BASE_URL`, `HARA_API_KEY`, or the provider key (`ANTHROPIC_API_KEY` / `DASHSCOPE_API_KEY`).
141
+ Config lives in `~/.hara/config.json`; the nearest project `.hara/config.json` may set the explicitly safe
142
+ project preferences `model`, `theme`, `vimMode`, `autoCompact`, and `reasoningEffort`. Repository config is
143
+ untrusted by default: routing/credential, hook/MCP, approval/sandbox, computer-control, and other privileged
144
+ keys are ignored with a key-name-only warning. For a repository you have reviewed, launch with
145
+ `HARA_TRUST_PROJECT_CONFIG=1` to enable all of its project keys for that process. The opt-in is captured at
146
+ startup, and project config itself must be a bounded regular file under a real (non-symlink) `.hara` directory.
147
+ Effective precedence for enabled keys is **environment > project > selected overlay > global**. Empty routing
148
+ values are ignored, so an empty project/env value cannot hide a working global credential or endpoint. Env overrides include
149
+ `HARA_PROVIDER`, `HARA_MODEL`, `HARA_BASE_URL`, `HARA_API_KEY`, and the provider key
150
+ (`ANTHROPIC_API_KEY` / `DASHSCOPE_API_KEY`).
140
151
 
141
152
  ## Use
142
153
 
@@ -146,12 +157,17 @@ hara init # analyze the project & (re)generate AGENTS.md
146
157
  hara doctor # check your setup (auth / model / node / assets / roles)
147
158
  hara roles init # scaffold role-agents (implementer / reviewer / docs)
148
159
  hara org "review src/ for bugs" # dispatch a task to the role that owns it (or --role <id>)
160
+ hara projects add shop /absolute/path/to/shop # register an agent home
161
+ hara agents # list global + registered project agents
162
+ hara org --role shop:reviewer "audit auth" # run that agent at its own home
149
163
  hara plan "add a /health endpoint with a test" # decompose → sequence (DAG) → run each step + verify
150
164
  hara plan --parallel "..." # run independent atoms concurrently · hara plan resume # continue a stopped plan
151
165
  hara review # review uncommitted changes for bugs/security/missing tests (--staged · --base main)
152
166
  hara commit # AI commit message from staged changes, then commit (-a to stage all · -y to skip confirm)
153
167
  hara index # build the semantic search index (after: hara config set embedProvider ollama|qwen)
154
168
  hara -p "summarize @README.md and fix the lint errors in src/" # one-shot; @path attaches a file
169
+ hara -p "extract package metadata" --schema ./schema.json # stdout is exactly schema-valid JSON
170
+ hara -p "review the current diff" --role reviewer # persona + model + tool policy from the role
155
171
  hara --approval auto-edit # suggest (default) | auto-edit | full-auto (-y = full-auto)
156
172
  hara --sandbox workspace-write # confine shell writes to the project (macOS Seatbelt)
157
173
  hara -c # resume the most recent session in this directory
@@ -159,6 +175,12 @@ hara --profile work # use a named profile from ~/.hara/config.json
159
175
  hara -m glm-5 # pick a model
160
176
  ```
161
177
 
178
+ For automation, `--schema` accepts inline JSON Schema or a schema file. The model must return through the
179
+ validated `structured_output` tool; on success stdout contains only the JSON value, while diagnostics go to
180
+ stderr and missing/invalid output exits non-zero. `--role reviewer` resolves locally, `--role global:reviewer`
181
+ uses the portable global persona in the current project, and `--role shop:reviewer` runs at that registered
182
+ project home. Each form enforces the role's persona, model, `allowTools`/`denyTools`, and `readOnly` policy.
183
+
162
184
  Inside the REPL: `/help` `/init` `/tools` `/model` `/approval` `/org` `/plan` `/roles` `/usage` `/doctor` `/sessions` `/undo` `/compact` `/recall` `/reset` `/exit` (type `/`+Tab to complete). Type `@` + Tab to attach a file (fuzzy, walks subdirectories).
163
185
 
164
186
  The interactive REPL is an **ink TUI**: a bordered **input box pinned at the bottom** — session name in
@@ -206,19 +228,37 @@ vector DB needed, and lexical still works when there's no index. Re-running `har
206
228
  only changed files re-embed (a full repo rebuild that takes ~a minute re-runs in well under a second).
207
229
 
208
230
  **Approval modes**: `suggest` confirms edits & shell · `auto-edit` auto-applies file edits but confirms shell · `full-auto` runs everything.
209
- **Sandbox** (macOS): `--sandbox workspace-write|read-only` runs the `bash` tool under Seatbelt **file-write confinement** (writes confined to the project / blocked). It does not restrict reads, network, or process exec; on non-macOS the shell runs unsandboxed (with a warning).
231
+ **Protected files and shell sandboxing**: built-in file, search, and context paths hard-reject `.env`/credential/private-key/private-Hara-state files before the ordinary approval/dispatch path can authorize them. Safe templates (`.env.example`, `.env.sample`, `.env.template`) remain readable. `HARA_ALLOW_SENSITIVE_FILES=1` is an explicit one-process exposure switch: it removes these built-in denies and that process's shell protected-read mask. Shell subprocesses receive a scrubbed environment; explicitly retain a named inherited variable with `HARA_SUBPROCESS_ENV_ALLOW=NAME[,NAME]` (output is still redacted). With the protected-file policy enabled, shell preflight rejects literal protected paths and environment-dump commands on every OS. On macOS, Seatbelt also masks existing protected files/directories from the shell and `--sandbox workspace-write|read-only` provides **file-write confinement**. Linux/Windows have no equivalent kernel read mask: static shell preflight is a useful guardrail, not a security sandbox, and arbitrary code can bypass it.
210
232
  **Screen control** (opt-in): the `computer` tool drives desktop software (screenshot → click/type), native per OS
211
233
  (mac `screencapture`+`cliclick` · Windows PowerShell · Linux `scrot`+`xdotool`). Off by default — enable a tier with
212
234
  `hara config set computerUse read|click|full` and allowlist apps with `hara config set computerApps "App, …"`. Guarded
213
235
  by the tier, the frontmost-app allowlist, a dangerous-key blocklist, and a once-per-session grant. Screenshots are read via your
214
236
  vision model into **actionable** output — interactive elements + positions (pass `focus` to target what you're after) — so even a text-only main model can click.
215
237
  **Sessions**: conversations are saved automatically — `-c` / `--resume <id>` to continue, `hara sessions` to list, `hara export [id] [--out file]` to render one as a Markdown transcript.
216
- **MCP**: add an `mcpServers` map to config (global or project `.hara/config.json`); their tools appear to the agent as `mcp__<server>__<tool>`. hara can also **be** an MCP server — `hara mcp` exposes its read/search tools (esp. **`codebase_search`**) over stdio so other clients (Claude Desktop, Cursor, another hara) can use them; read-only by default (`HARA_MCP_TOOLS` to override).
238
+ **MCP**: add an `mcpServers` map to global config (a reviewed project config additionally needs `HARA_TRUST_PROJECT_CONFIG=1` at launch); their tools appear to the agent as `mcp__<server>__<tool>`. Configured MCP servers, like `external_agent`, are trusted host extensions outside Hara's protected-file boundary. Every interactive tool call requires confirmation (even in `full-auto`), and non-interactive runs disable them by default; reviewed automation can explicitly opt in before launch with `HARA_ALLOW_TRUSTED_EXTENSIONS=1`. hara can also **be** an MCP server — `hara mcp` exposes its read/search tools (esp. **`codebase_search`**) over stdio so other clients (Claude Desktop, Cursor, another hara) can use them; read-only by default (`HARA_MCP_TOOLS` to override).
217
239
  **Vim mode**: `hara config set vimMode true` makes the prompt modal — Esc → normal, `i/a/A/I` insert, `h l 0 $ w b e` motions, `x D C dd cw p` edits. Off by default.
218
240
  **Scheduled tasks**: `hara cron add "0 9 * * 1-5" "<task>"` (or `"every 30m"`, `"in 2h"`) runs a task on a schedule — each run is a fresh hara session. `hara cron install` wires a per-minute tick into launchd/crontab (no daemon); `--org` routes through the role org. Manage with `hara cron list/run/enable/disable/remove/logs`.
241
+ **Work coordination**: `todo_write` is the agent's short, session-scoped checklist; it persists with that
242
+ session and is isolated between simultaneous sub-agents and serve sessions. `task` is the durable project pool
243
+ for work that outlives a conversation: add/update/list/remove items with `pending|in_progress|done`, an optional
244
+ owner, and `blockedBy` dependencies. The private, atomic store is shared by concurrent hara processes for the
245
+ same project and rejects missing/self/cyclic dependencies.
219
246
  **Notifications**: `hara config set notify bell` (terminal bell) or `notify system` (OS notification) pings you when a turn finishes — handy for long runs you've stepped away from. Gated on elapsed time so quick turns stay quiet; off by default.
220
- **Hooks**: run your own shell commands around tool calls via a `"hooks"` map in config. A **`PreToolUse`** hook can **veto** a call (non-zero exit blocks it; its output becomes the reason the model sees) — gate `bash`, forbid edits outside a path, require a clean tree. A **`PostToolUse`** hook observes (format/lint a file the agent just wrote, log, notify). Each has a `matcher` (regex/literal on the tool name, `*` = all) and gets `{tool, payload}` on stdin + `HARA_TOOL_NAME` in env. Plugins can contribute hooks too.
221
- **Profiles**: add a `profiles` map to `~/.hara/config.json` (`--profile <name>`), or drop a project-level `.hara/config.json` that overrides the global config.
247
+ **Hooks**: run your own shell commands around tool calls via a `"hooks"` map in global config; hooks from a reviewed project config require the launch-time `HARA_TRUST_PROJECT_CONFIG=1` opt-in. A **`PreToolUse`** hook can **veto** a call (non-zero exit blocks it; its output becomes the reason the model sees) — gate `bash`, forbid edits outside a path, require a clean tree. A **`PostToolUse`** hook observes (format/lint a file the agent just wrote, log, notify). Each has a `matcher` (regex/literal on the tool name, `*` = all) and gets `{tool, payload}` on stdin + `HARA_TOOL_NAME` in env. Plugins can contribute hooks too.
248
+ Reviewer/read-only/plan runs and parallel read-only sub-agents suppress both hook phases: PreToolUse and
249
+ PostToolUse commands are arbitrary shell, so either could otherwise bypass their read-only contract indirectly.
250
+ They also skip configured and plugin-provided MCP server processes, so starting an external tool server cannot
251
+ bypass the same contract before the first model turn.
252
+ **Profiles and live config**: select an identity with `--profile <name>`; use `overlays` in
253
+ `~/.hara/config.json` for named config overlays. Project `.hara/config.json` files get the safe preference
254
+ allowlist above; project-specific routing requires `HARA_TRUST_PROJECT_CONFIG=1` before launch.
255
+ `.hara-profile` identity pins are read no-follow with size, single-inode, and hard-link checks; pin updates use
256
+ an atomic compare-and-swap, and invalid-pin warnings never echo file contents or paths. A Git-tracked pin is
257
+ repository-controlled and ignored by default; local untracked pins created with `hara profile pin` work
258
+ normally. The same launch-time `HARA_TRUST_PROJECT_CONFIG=1` opt-in enables a reviewed tracked pin.
259
+ Long-lived `hara serve` processes reload provider credentials/routes and guardian settings for the target cwd on
260
+ new sessions and turns. `models.list` and new sessions see current defaults; a resumed session keeps its explicit
261
+ model pin while using the live provider route. No server restart is required after a credential rotation.
222
262
 
223
263
  ### The org — what makes hara different
224
264
 
@@ -234,6 +274,13 @@ to a clean start tree; a review that doesn't pass leaves the work uncommitted).
234
274
  **`agent`** tool spawns **parallel read-only sub-agents** for fan-out — analyze / review / search
235
275
  several things at once (each can take a `role`), bounded to 8 concurrent (`HARA_MAX_CONCURRENCY`).
236
276
 
277
+ Register project homes with `hara projects add <name> <absolute-path>`, then `hara agents` becomes a global
278
+ address book across `~/.hara/roles` and each registered project's roles. A qualified address such as
279
+ `shop:reviewer` is unambiguous; both `hara org --role shop:reviewer "<task>"` and one-shot `hara -p "<task>"
280
+ --role shop:reviewer` execute at that agent's home, with its own `AGENTS.md`, live project config, role model,
281
+ and allow/deny/read-only tool policy. `global:<name>` is portable and runs in the current project. A bare name
282
+ uses the local role first and otherwise must resolve unambiguously.
283
+
237
284
  Beyond routing, **`hara plan "<task>"`** makes the org *plan*: it decomposes the task into atoms,
238
285
  sequences them as a DAG, and executes each step (optionally routed to a role) behind a per-step
239
286
  **verify gate** — frame → atomize → sequence → execute → verify. Each atom may carry a `check` shell
@@ -255,11 +302,11 @@ turn, or **`/undo`** to revert the last edit. In-session **`/diff`**, **`/review
255
302
  - **Project context**: auto-loads `AGENTS.md` (the cross-tool standard) walking up to the repo root; `hara init` writes one by analyzing the repo.
256
303
  - **`@file` mentions**: attach file contents to a message (`@path`); Tab-completes with a **fuzzy** matcher over the project (subdirs, git-tracked + untracked) — `@idx` → `src/index.ts`. `@<dir>` loads a directory listing, `@src/`+Tab drills into a folder, and mistyped tool/file paths get a "did you mean" suggestion.
257
304
  - **Multi-provider**: Anthropic (Claude) or any OpenAI-compatible endpoint (Qwen/DashScope, GLM, Kimi, OpenAI) — **all streamed live**.
258
- - **Chat gateway**: drive your local hara from a chat app — **Telegram · WeChat · Discord · Feishu/Lark · Slack · Mattermost · Matrix · DingTalk**. The daemon connects out (no public webhook), with per-chat sessions, project roaming (`/cd`), and **two-way images** (send a photo it sees it; ask for a file → it sends one). Setup per platform: **[docs/gateway.md](docs/gateway.md)**.
305
+ - **Chat gateway**: drive your local hara from **Telegram · WeChat · Discord · Feishu/Lark · Slack · Mattermost · Matrix · DingTalk · WeCom · Signal**. The daemon connects out (no public webhook), with per-chat sessions, project roaming (`/cd`), agent switching (`/agent`), and **two-way images on byte-upload-capable platforms**. Setup, platform capability details, and the group-flow security model: **[docs/gateway.md](docs/gateway.md)**.
259
306
 
260
307
  ### Roadmap
261
308
 
262
- **Shipped:** ink TUI · plan mode · persistent memory + self-evolution · atomization planner · parallel plan atoms · **multi-role review chains** · parallel sub-agents · MCP client *and* server · **scheduled tasks (`hara cron`)** · **chat gateway (8 platforms, two-way images)** · **single-binary distribution** · **Docker image** · `/compact` context management.
309
+ **Shipped:** ink TUI · plan mode · persistent memory + self-evolution · atomization planner · parallel plan atoms · **multi-role review chains** · global project-agent index · durable project tasks · parallel sub-agents · MCP client *and* server · **scheduled tasks (`hara cron`)** · **chat gateway (10 platforms, capability-aware media)** · **single-binary distribution** · **Docker image** · `/compact` context management.
263
310
  **Next:** SSOT data authority · an enterprise control-plane (fleet + central token management).
264
311
 
265
312
  ## Security
package/SECURITY.md CHANGED
@@ -29,9 +29,45 @@ local user (who already has your shell).
29
29
  - **`web_fetch` SSRF guard.** Refuses to fetch private / loopback / link-local / CGNAT addresses (resolving
30
30
  the hostname first), re-checks on every redirect hop, and reads the body under a byte ceiling — so the
31
31
  model can't reach cloud-metadata endpoints or internal services.
32
- - **Secrets.** `~/.hara/config.json` (API keys) and `~/.hara/qwen-oauth.json` (tokens) are written `0600`.
33
- The optional semantic index respects `.gitignore` and skips secret-named files, so keys aren't embedded or
34
- sent to an embedding provider. The memory guard screens secret-shaped strings out of what the agent saves.
32
+ - **Protected reads in built-in paths.** `.env`, `.env.*` (except explicit templates such as
33
+ `.env.example`), credential stores, private keys, and private Hara state are rejected before the ordinary
34
+ approval/dispatch path can authorize them. The same canonical/real-path check covers built-in file reads,
35
+ edit/patch pre-reads, grep/glob/ls, `@file`, completion, semantic/lexical indexes, checkpoints, gateway file
36
+ delivery, cron command admission, and symlink aliases. Headless, gateway, sub-agent, cron, and `full-auto`
37
+ execution cannot approve those built-in paths past the check. `HARA_ALLOW_SENSITIVE_FILES=1` is an explicit
38
+ launch-time exposure switch for one process: it removes the built-in denies and that process's shell
39
+ protected-read preflight/Seatbelt mask.
40
+ - **Shell guardrails differ by platform.** With the protected-file policy enabled, shell admission statically
41
+ rejects literal protected paths and environment-dump commands on every OS. On macOS, Hara additionally
42
+ applies a Seatbelt read mask to existing
43
+ protected files/directories even when the general write sandbox is off. Linux and Windows have no equivalent
44
+ kernel-enforced read mask: their arbitrary shell code is outside the protected-file boundary, and static
45
+ preflight can be bypassed by indirection or generated code.
46
+ - **Subprocess credentials.** Model-controlled Bash/background jobs, hooks, external agents, and MCP servers
47
+ inherit an environment with secret-shaped and code-injection variables removed. MCP `env` entries are an
48
+ explicit per-server grant; other commands can receive named variables only when the user launches Hara with
49
+ `HARA_SUBPROCESS_ENV_ALLOW=NAME,OTHER`. Tool output is pattern- and exact-value-redacted. Hara's own provider
50
+ process retains its keys.
51
+ - **Secrets at rest.** `~/.hara/config.json` (API keys) and `~/.hara/qwen-oauth.json` (tokens) are written
52
+ `0600`. The optional semantic index skips secret-named files and filters old indexes at query time, so keys
53
+ aren't embedded or sent to an embedding provider. The memory guard screens secret-shaped strings out of
54
+ what the agent saves.
55
+ - **Repository config is untrusted by default.** A project `.hara/config.json` can set only `model`, `theme`,
56
+ `vimMode`, `autoCompact`, and `reasoningEffort`. Endpoint/credential routing, hooks/MCP commands, approval,
57
+ sandbox, guardian, computer control, and all other keys remain global unless the user launches Hara with
58
+ `HARA_TRUST_PROJECT_CONFIG=1` after reviewing that repository. Warnings name ignored/enabled keys but never
59
+ their values. Project config reads reject a symlink `.hara`, final symlinks, hard links, oversized files,
60
+ and files/directories that change identity during the read.
61
+ - **Identity pins are bound, not followed.** `.hara-profile` reads reject symlink/hard-link aliases and
62
+ oversized or changing files. Writes use a canonical-parent, no-follow snapshot plus atomic compare-and-swap;
63
+ invalid pin warnings never include the pin's raw content or untrusted path. Git-tracked pins are ignored by
64
+ default so a cloned repository cannot silently switch to an existing personal/org identity; untracked local
65
+ pins remain usable, and the reviewed-repository `HARA_TRUST_PROJECT_CONFIG=1` launch opt-in enables tracked pins.
66
+ - **MCP/external agents are trusted extensions.** They execute outside Hara's protected-file boundary. Every
67
+ interactive tool call requires confirmation, including in `full-auto`; non-interactive runs disable them by
68
+ default. Reviewed automation can explicitly enable them before launch with
69
+ `HARA_ALLOW_TRUSTED_EXTENSIONS=1`. Their inherited environment is still scrubbed, but the extension may use
70
+ its own credentials or access anything its host process permits.
35
71
  - **Plugins are code you trust.** Installing a plugin (`hara plugin add`) grants its author code execution:
36
72
  its MCP servers and hooks run shell commands on launch. `hara plugin add` **prints the exact commands** a
37
73
  plugin will run so you can review them; disable with `hara plugin disable <name>`.
@@ -39,13 +75,16 @@ local user (who already has your shell).
39
75
 
40
76
  ## What is *not* a security boundary
41
77
 
42
- - **The sandbox confines file writes only** — not reads, not network, not process exec; `/private/tmp`
43
- stays writable. It stops a stray `rm`/overwrite escaping the project, not a determined exfiltration. Treat
44
- a `full-auto` + network-capable shell as able to read and send anything your account can.
45
- - **`@file` mentions** read any file *you* name (including outside the project) that's you attaching
46
- context, not the model exfiltrating; mentions are expanded on your typed input only, never on model output.
78
+ - **The general sandbox confines file writes only** — not arbitrary reads, network, or process exec;
79
+ `/private/tmp` stays writable. The protected-file policy is a narrow boundary for Hara's built-in paths,
80
+ plus an OS-enforced shell read mask on macOS only; it is not a complete hostile-code jail. On Linux and
81
+ Windows, treat any allowed network-capable shell as able to read and send anything the Hara process account
82
+ can access. On macOS, the protected-path mask still does not confine ordinary files, network, or process exec.
83
+ - **`@file` mentions** can read ordinary files *you* name (including outside the project), but protected
84
+ files are refused unless you made the launch-time opt-in. Mentions are expanded on user input only.
47
85
  - **`full-auto` / `-y`** removes the human gate by your explicit choice. Use it on code and in directories
48
- you trust.
86
+ you trust. It does not remove built-in protected-path checks or subprocess environment scrubbing, but it
87
+ does not turn Linux/Windows shell preflight into a security boundary.
49
88
 
50
89
  ## Reporting a vulnerability
51
90