@nanhara/hara 0.125.1 → 0.126.0
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/CHANGELOG.md +76 -0
- package/README.md +22 -8
- package/SECURITY.md +6 -4
- package/dist/agent/limits.js +2 -2
- package/dist/agent/loop.js +413 -77
- package/dist/config.js +172 -4
- package/dist/gateway/flows-pending.js +14 -36
- package/dist/gateway/wecom.js +9 -1
- package/dist/index.js +439 -227
- package/dist/mcp/client.js +73 -6
- package/dist/providers/bounded-turn.js +6 -0
- package/dist/providers/factory.js +54 -0
- package/dist/providers/models.js +39 -6
- package/dist/providers/openai.js +6 -1
- package/dist/providers/reasoning.js +12 -0
- package/dist/providers/registry.js +1 -0
- package/dist/providers/target.js +98 -0
- package/dist/security/guardian.js +6 -1
- package/dist/security/private-state.js +1 -1
- package/dist/security/secrets.js +19 -0
- package/dist/serve/protocol.js +7 -2
- package/dist/serve/server.js +157 -40
- package/dist/serve/sessions.js +11 -2
- package/dist/session/operation-drain.js +45 -0
- package/dist/session/task.js +78 -0
- package/dist/statusbar.js +15 -2
- package/dist/tools/agent.js +1 -0
- package/dist/tools/all.js +1 -0
- package/dist/tools/ask_user.js +8 -3
- package/dist/tools/builtin.js +22 -1
- package/dist/tools/codebase.js +1 -0
- package/dist/tools/computer.js +1 -0
- package/dist/tools/cron.js +10 -0
- package/dist/tools/external_agent.js +1 -0
- package/dist/tools/memory.js +2 -0
- package/dist/tools/registry.js +95 -4
- package/dist/tools/result-limit.js +172 -2
- package/dist/tools/runtime.js +67 -0
- package/dist/tools/search.js +3 -0
- package/dist/tools/skill.js +1 -0
- package/dist/tools/task.js +5 -0
- package/dist/tools/todo.js +3 -2
- package/dist/tools/web.js +4 -0
- package/dist/tui/App.js +49 -17
- package/dist/tui/model-picker.js +11 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,82 @@ 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.126.0 — 2026-07-19 — provider control plane, bounded tools, and honest execution time
|
|
9
|
+
|
|
10
|
+
- **Desktop can configure providers through an authenticated, redacted Serve control plane.** The shared
|
|
11
|
+
catalog distinguishes cloud, local, OAuth, and managed routes; settings can list, test, and save a Personal
|
|
12
|
+
route without returning credentials to the UI. Switching provider or endpoint requires a matching new key
|
|
13
|
+
instead of replaying another vendor's credential, named/managed profiles remain explicit, and launch-time
|
|
14
|
+
environment overrides stay immutable from Desktop.
|
|
15
|
+
- **Ollama and LM Studio are first-class local providers.** They require no fake API key, accept only
|
|
16
|
+
loopback HTTP endpoints, omit the Authorization header on the wire, and can discover installed models
|
|
17
|
+
through the same bounded connection-test path. Cloud custom endpoints continue to require HTTPS except
|
|
18
|
+
for an explicit loopback target.
|
|
19
|
+
- **Tool execution has conservative runtime traits.** Tools without a complete declaration default to
|
|
20
|
+
side-effect-capable and serial; input-level classification can narrow a real read but cannot turn a
|
|
21
|
+
mutating call into an inherited read approval. Parallel calls share an aggregate result budget so one
|
|
22
|
+
round cannot flood the next model request.
|
|
23
|
+
- **Long-tail tool schemas and large results are demand-loaded.** `tool_search` activates only matched,
|
|
24
|
+
role-allowed capabilities for the next model round. Oversized, redacted tool output is kept in a
|
|
25
|
+
quota/TTL-bounded private store and exposed only through opaque `tool_result_read` continuation IDs, never
|
|
26
|
+
arbitrary filesystem paths.
|
|
27
|
+
|
|
28
|
+
- Human clarification and approval waits no longer consume `runTimeoutMs`. The non-renewable budget now
|
|
29
|
+
measures active model/tool execution, resumes with the exact remaining time after an answer, and still
|
|
30
|
+
stops genuinely stuck providers, tools, and loops. Esc, shutdown, and explicit cancellation continue to
|
|
31
|
+
dismiss a waiting prompt immediately.
|
|
32
|
+
- The TUI distinguishes this state as `waiting for your answer · task timer paused`; five-minute and 80%
|
|
33
|
+
lifecycle notices no longer fire while Hara is waiting for a person.
|
|
34
|
+
- `hara serve` now exposes authenticated `server.shutdown` so Desktop can gracefully release sessions and
|
|
35
|
+
locks before relaunching after an update.
|
|
36
|
+
- Timed-out or interrupted provider/tool calls retain their Serve session lease until the underlying
|
|
37
|
+
operation physically settles. A second turn, session deletion, or updater shutdown receives `BUSY`
|
|
38
|
+
instead of overlapping a non-cooperative request or releasing its lock early.
|
|
39
|
+
- Headless resumed sessions use the same physical-lifetime rule for dynamically nested sub-agents and
|
|
40
|
+
automatic compaction, so a late child request cannot escape a cleanup snapshot and release the
|
|
41
|
+
cross-process lock early.
|
|
42
|
+
- The WeCom gateway now uses the bundled `ws` transport because Node 22's native WebSocket handshake is
|
|
43
|
+
rejected by WeCom's production endpoint before authentication. A late authentication frame can no longer
|
|
44
|
+
revive an already-aborted connection or install a heartbeat after cleanup.
|
|
45
|
+
- Upgrade with `npm i -g @nanhara/hara@0.126.0`.
|
|
46
|
+
|
|
47
|
+
## 0.125.3 — 2026-07-18 — conversation/task boundary and deliberate execution
|
|
48
|
+
|
|
49
|
+
- **Chat delivery is now separate from task execution.** Slash controls such as `/model` no longer create a
|
|
50
|
+
fake steer target; input typed while a picker or other local control is busy is visibly queued as the next
|
|
51
|
+
turn. If a real turn ends between enqueue and delivery, Hara promotes the raced steer to a normal turn
|
|
52
|
+
instead of rejecting it with `there is no task to steer` or dropping the input. Queued controls execute
|
|
53
|
+
alone rather than absorbing the following task as command arguments, and paused/completed tasks are never
|
|
54
|
+
implicit steer targets—only explicit `/continue` may reopen one. User-invocable slash Skills remain real
|
|
55
|
+
Agent turns, so refinements typed while `/design`, `/video`, or another Skill is running steer that Skill
|
|
56
|
+
instead of becoming an unrelated task.
|
|
57
|
+
- **Main tasks now establish an explicit understanding checkpoint before side effects.** The engine-owned
|
|
58
|
+
`task_intake` records intent, interpreted goal, constraints, acceptance checks, and short steps in the
|
|
59
|
+
durable task state. Reads and diagnosis may happen first, but edits, non-read-only commands, computer
|
|
60
|
+
actions, external agents, and MCP connections are blocked until the brief is accepted in a completed tool
|
|
61
|
+
round. Starting or stopping a background process is also treated as a state change even when its command or
|
|
62
|
+
job tool is otherwise read-only; malformed string-valued `background` flags are rejected. Read-only actions
|
|
63
|
+
inside mixed tools (`task list`, `cronjob list`) stay available for evidence gathering. Brief revisions
|
|
64
|
+
replace the old prompt copy and cannot share a round with a side effect. A later steer cannot be overwritten
|
|
65
|
+
by a concurrent or revised checkpoint.
|
|
66
|
+
- **Alibaba Coding Plan discovery follows the current exact model contract.** `/model` prefers the endpoint's
|
|
67
|
+
live response and uses the documented ten-model list only for official Coding Plan hosts. Context guards now
|
|
68
|
+
use each model's published window, and `qwen3-coder-next` / `qwen3-coder-plus` no longer expose an unsupported
|
|
69
|
+
thinking control. Desktop/serve derives the reasoning dial from each session's pinned model rather than the
|
|
70
|
+
current global default.
|
|
71
|
+
- **Configured MCP servers no longer execute or ask for permission when Hara starts.** Hara exposes a
|
|
72
|
+
zero-side-effect `mcp_connect` capability; the agent calls it only when the current task materially needs
|
|
73
|
+
one named server, receives the existing external-boundary approval, and discovers that server's tools for
|
|
74
|
+
the next model round. Other configured servers remain stopped.
|
|
75
|
+
- Interactive calls to connected MCP tools still require confirmation every time, protected-file-shaped
|
|
76
|
+
inputs remain blocked, read-only roles cannot receive the launcher, and headless runs remain fail-closed
|
|
77
|
+
unless `HARA_ALLOW_TRUSTED_EXTENSIONS=1` was set before launch.
|
|
78
|
+
- MCP stderr now drops only npm 11's repeated `Unknown user config "always-auth"` and `"home"` deprecation
|
|
79
|
+
lines. Other npm warnings and all actual server errors remain visible and redacted. Hara does not read,
|
|
80
|
+
rewrite, or weaken the user's `.npmrc`. Esc, interruption, and the active-execution deadline now also cancel a
|
|
81
|
+
lazy MCP startup/tool call and close an unresponsive child instead of leaving it behind.
|
|
82
|
+
- Upgrade with `npm i -g @nanhara/hara@0.125.3`.
|
|
83
|
+
|
|
8
84
|
## 0.125.1 — 2026-07-18 — installed plugin commands inside agent tasks
|
|
9
85
|
|
|
10
86
|
- **Commands contributed by an installed Hara plugin are now available to Hara's own tool subprocesses.**
|
package/README.md
CHANGED
|
@@ -109,8 +109,11 @@ hara config set model qwen-plus # or qwen-max, qwen3-coder-plus, …
|
|
|
109
109
|
# coding-plan keys (sk-sp-…) use the coding endpoint instead:
|
|
110
110
|
# hara config set baseURL https://coding.dashscope.aliyuncs.com/v1
|
|
111
111
|
# hara config set model qwen3.7-plus
|
|
112
|
-
#
|
|
113
|
-
#
|
|
112
|
+
# supported exact ids (2026-07-18):
|
|
113
|
+
# qwen3.7-plus, qwen3.6-plus, qwen3.5-plus, qwen3-max-2026-01-23,
|
|
114
|
+
# qwen3-coder-next, qwen3-coder-plus, kimi-k2.5, glm-5, glm-4.7,
|
|
115
|
+
# MiniMax-M2.5
|
|
116
|
+
# /model uses the endpoint's live list first and this official list only as a fallback.
|
|
114
117
|
```
|
|
115
118
|
|
|
116
119
|
> Plan keys (Coding Plan / Token Plan) are licensed **only** for use inside AI coding agents /
|
|
@@ -266,7 +269,7 @@ Type-ahead Enter steers the exact live turn; `/next <message>` queues a separate
|
|
|
266
269
|
clients use `session.steer` with `expectedTurnId` (durable before ACK) and may pass `newTask: true` to force a
|
|
267
270
|
new execution. The `hara resume` launcher preserves terminal input in both npm/Node and standalone-binary
|
|
268
271
|
installs.
|
|
269
|
-
**MCP**: add an `mcpServers` map to global config (a reviewed project config additionally needs `HARA_TRUST_PROJECT_CONFIG=1` at launch)
|
|
272
|
+
**MCP**: add an `mcpServers` map to global config (a reviewed project config additionally needs `HARA_TRUST_PROJECT_CONFIG=1` at launch). Hara starts with every configured server stopped and exposes only `mcp_connect`; when the current task first needs one server, the agent requests permission to connect that server and its tools appear on the next round as `mcp__<server>__<tool>`. Unrelated servers remain stopped. Configured MCP servers, like `external_agent`, are trusted host extensions outside Hara's protected-file boundary. Every interactive external-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).
|
|
270
273
|
**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.
|
|
271
274
|
**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`. Every job has a 30-minute deadline and the whole sequential tick has a non-renewable 60-minute watchdog: a job timeout kills its process tree, records `timed out` + duration/error, then continues with the next due job; a tick timeout stops the remainder and releases the global lock. Add `--deliver feishu:<chatId>` (or Telegram/webhook) for outcomes and `--alert-after N` for the consecutive-failure 🚨 threshold (default 3). Delivery intent is durable before transport, uses a stable idempotency key, and retries with bounded backoff on later ticks until confirmed. A failed channel cannot grow `jobs.json` forever: each job keeps at most 64 pending effects, reserves outcome/alert room before launch, and disables itself with a visible backlog error when full; restore delivery, let the queue drain, then re-enable it. Tune milliseconds with `HARA_CRON_JOB_TIMEOUT_MS` (hard max 24h) and `HARA_CRON_TICK_TIMEOUT_MS` (hard max 5h); scheduled jobs are also capped by the tick. After upgrading from a version whose tick is already stuck, terminate that specific legacy `hara cron tick` process tree once (or reboot); the next scheduler minute marks over-age state interrupted/disabled and recovers the lock without replaying a possibly orphaned task.
|
|
272
275
|
**Work coordination**: `todo_write` is the agent's short, session-scoped checklist; it persists with that
|
|
@@ -274,10 +277,21 @@ session and is isolated between simultaneous sub-agents and serve sessions. `tas
|
|
|
274
277
|
for work that outlives a conversation: add/update/list/remove items with `pending|in_progress|done`, an optional
|
|
275
278
|
owner, and `blockedBy` dependencies. The private, atomic store is shared by concurrent hara processes for the
|
|
276
279
|
same project and rejects missing/self/cyclic dependencies.
|
|
280
|
+
**Conversation and execution**: the composer first routes an input as a local control, a new task, a steer of
|
|
281
|
+
the exact live task, or an explicit next task. User-invocable slash Skills are executable turns and accept
|
|
282
|
+
live steering; local controls such as `/model` are serialized without pretending to own a task. A main task keeps the raw user request and a separate accepted
|
|
283
|
+
brief (intent, interpreted goal, constraints, acceptance checks, and steps). Hara permits read-only
|
|
284
|
+
investigation before that brief, but its engine blocks side effects until the understanding has been
|
|
285
|
+
checkpointed in its own model/tool round. Mixed tools are classified by action (`task list` is a read), and
|
|
286
|
+
a brief revision cannot share its round with a side effect. `/task` shows the current execution identity and brief. See
|
|
287
|
+
[`docs/conversation-task-execution.md`](docs/conversation-task-execution.md).
|
|
277
288
|
**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.
|
|
278
|
-
**Run limits and loop alarms**: every agent turn has a non-renewable 30-minute
|
|
279
|
-
model/tool cap.
|
|
280
|
-
|
|
289
|
+
**Run limits and loop alarms**: every agent turn has a non-renewable 30-minute active-execution budget and a
|
|
290
|
+
64-round model/tool cap. Time spent waiting for an engine-owned question or approval does not consume that
|
|
291
|
+
budget; the status row says the task timer is paused, while Esc, shutdown, and explicit cancellation still
|
|
292
|
+
take effect immediately. Answering resumes the remaining budget rather than resetting it. Hara warns after
|
|
293
|
+
five active minutes or at 75% of the round budget, stops the third identical failing tool call, and surfaces
|
|
294
|
+
the final reason in CLI, Desktop, or gateway output. Tune intentional long work with
|
|
281
295
|
`hara config set runTimeoutMs 45m` (1s..2h) and `hara config set maxAgentRounds 96` (1..256), or
|
|
282
296
|
`HARA_RUN_TIMEOUT_MS` / `HARA_MAX_AGENT_ROUNDS`; neither boundary can be disabled. Sub-agents are capped at
|
|
283
297
|
8 minutes/24 rounds and inherit the parent's cancellation. Auxiliary model work (planning, verification,
|
|
@@ -286,8 +300,8 @@ provider that ignores cancellation cannot strand the CLI outside the main loop.
|
|
|
286
300
|
**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.
|
|
287
301
|
Reviewer/read-only/plan runs and parallel read-only sub-agents suppress both hook phases: PreToolUse and
|
|
288
302
|
PostToolUse commands are arbitrary shell, so either could otherwise bypass their read-only contract indirectly.
|
|
289
|
-
They also
|
|
290
|
-
|
|
303
|
+
They also omit the lazy connector for configured and plugin-provided MCP servers, so a read-only role cannot
|
|
304
|
+
start an external tool server.
|
|
291
305
|
**Profiles and live config**: select an identity with `--profile <name>`; use `overlays` in
|
|
292
306
|
`~/.hara/config.json` for named config overlays. Project `.hara/config.json` files get the safe preference
|
|
293
307
|
allowlist above; project-specific routing requires `HARA_TRUST_PROJECT_CONFIG=1` before launch.
|
package/SECURITY.md
CHANGED
|
@@ -70,11 +70,13 @@ local user (who already has your shell).
|
|
|
70
70
|
- **MCP/external agents are trusted extensions.** They execute outside Hara's protected-file boundary. Every
|
|
71
71
|
interactive tool call requires confirmation, including in `full-auto`; non-interactive runs disable them by
|
|
72
72
|
default. Reviewed automation can explicitly enable them before launch with
|
|
73
|
-
`HARA_ALLOW_TRUSTED_EXTENSIONS=1`.
|
|
74
|
-
|
|
73
|
+
`HARA_ALLOW_TRUSTED_EXTENSIONS=1`. Merely configuring an MCP server does not start it: `mcp_connect` requests
|
|
74
|
+
permission and starts one named server only when a task needs it. Their inherited environment is still
|
|
75
|
+
scrubbed, but the extension may use its own credentials or access anything its host process permits.
|
|
75
76
|
- **Plugins are code you trust.** Installing a plugin (`hara plugin add`) grants its author code execution:
|
|
76
|
-
its MCP servers and hooks run
|
|
77
|
-
plugin
|
|
77
|
+
its MCP servers may run on first relevant use and its hooks may run around matching tool calls. `hara plugin
|
|
78
|
+
add` **prints the exact commands** a plugin can run so you can review them; disable with
|
|
79
|
+
`hara plugin disable <name>`.
|
|
78
80
|
- **Coding-plan keys.** Provider keys you configure are used only to call the model endpoint you set.
|
|
79
81
|
|
|
80
82
|
## What is *not* a security boundary
|
package/dist/agent/limits.js
CHANGED
|
@@ -19,8 +19,8 @@ export function parseAgentRunTimeoutMs(value) {
|
|
|
19
19
|
const parsed = amount * multiplier;
|
|
20
20
|
return Number.isFinite(parsed) ? parsed : undefined;
|
|
21
21
|
}
|
|
22
|
-
/** Parse
|
|
23
|
-
* as `30m`, `90s`, and `1h` are accepted in config; HARA_RUN_TIMEOUT_MS also accepts plain milliseconds. */
|
|
22
|
+
/** Parse the active execution deadline. Invalid/zero values cannot disable the safety boundary. Human values
|
|
23
|
+
* such as `30m`, `90s`, and `1h` are accepted in config; HARA_RUN_TIMEOUT_MS also accepts plain milliseconds. */
|
|
24
24
|
export function agentRunTimeoutMs(value = process.env.HARA_RUN_TIMEOUT_MS) {
|
|
25
25
|
const parsed = parseAgentRunTimeoutMs(value);
|
|
26
26
|
if (parsed === undefined || parsed <= 0)
|