@tt-a1i/hive 1.3.0 → 1.3.4

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 (55) hide show
  1. package/CHANGELOG.md +42 -14
  2. package/README.en.md +30 -8
  3. package/README.md +21 -6
  4. package/assets/hive-team-view.png +0 -0
  5. package/dist/src/cli/team.d.ts +6 -0
  6. package/dist/src/cli/team.js +48 -0
  7. package/dist/src/server/agent-launch-cache.js +25 -6
  8. package/dist/src/server/agent-manager.d.ts +2 -2
  9. package/dist/src/server/agent-runtime-contract.d.ts +3 -0
  10. package/dist/src/server/agent-runtime.js +3 -0
  11. package/dist/src/server/agent-startup-instructions.js +1 -1
  12. package/dist/src/server/agent-stdin-dispatcher.d.ts +4 -0
  13. package/dist/src/server/agent-stdin-dispatcher.js +12 -0
  14. package/dist/src/server/app.js +1 -1
  15. package/dist/src/server/dispatch-ledger-store.d.ts +22 -1
  16. package/dist/src/server/dispatch-ledger-store.js +34 -3
  17. package/dist/src/server/hive-team-guidance.js +3 -1
  18. package/dist/src/server/route-types.d.ts +7 -0
  19. package/dist/src/server/routes-dispatches.js +4 -2
  20. package/dist/src/server/routes-runtime.js +1 -0
  21. package/dist/src/server/routes-team.js +22 -0
  22. package/dist/src/server/runtime-store-helpers.d.ts +2 -1
  23. package/dist/src/server/runtime-store-helpers.js +14 -4
  24. package/dist/src/server/runtime-store.d.ts +5 -8
  25. package/dist/src/server/runtime-store.js +1 -0
  26. package/dist/src/server/tasks-websocket-server.d.ts +2 -1
  27. package/dist/src/server/tasks-websocket-server.js +18 -2
  28. package/dist/src/server/team-authz.d.ts +1 -1
  29. package/dist/src/server/team-authz.js +1 -1
  30. package/dist/src/server/team-operations.d.ts +16 -1
  31. package/dist/src/server/team-operations.js +28 -1
  32. package/dist/src/server/terminal-input-profile.d.ts +10 -0
  33. package/dist/src/server/terminal-input-profile.js +15 -0
  34. package/dist/src/server/terminal-stream-hub.js +10 -2
  35. package/dist/src/server/terminal-ws-server.d.ts +2 -1
  36. package/dist/src/server/terminal-ws-server.js +2 -2
  37. package/dist/src/server/workspace-shell-runtime.d.ts +2 -1
  38. package/dist/src/server/workspace-shell-runtime.js +3 -2
  39. package/dist/src/server/workspace-store-contract.d.ts +1 -0
  40. package/dist/src/server/workspace-store-mutations.d.ts +1 -0
  41. package/dist/src/server/workspace-store-mutations.js +1 -0
  42. package/dist/src/server/workspace-store.js +2 -1
  43. package/package.json +2 -2
  44. package/web/dist/assets/AddWorkerDialog-D6-K1wJm.js +1 -0
  45. package/web/dist/assets/AddWorkspaceDialog-Du0lndJ0.js +1 -0
  46. package/web/dist/assets/FirstRunWizard-B8k7S5De.js +1 -0
  47. package/web/dist/assets/WorkerModal-B3XhIvAX.js +1 -0
  48. package/web/dist/assets/WorkspaceTaskDrawer-CjEoLJvS.js +1 -0
  49. package/web/dist/assets/chevron-right-BvbSCniy.js +1 -0
  50. package/web/dist/assets/index-DB5fHAMI.js +81 -0
  51. package/web/dist/assets/index-Sbdu6Se0.css +1 -0
  52. package/web/dist/index.html +2 -2
  53. package/web/dist/sw.js +1 -1
  54. package/web/dist/assets/index-CSEt-Qiy.js +0 -66
  55. package/web/dist/assets/index-RsXXnrVz.css +0 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,41 @@
2
2
 
3
3
  All notable user-facing changes will be documented in this file.
4
4
 
5
+ ## 1.3.4 - 2026-05-21
6
+
7
+ Terminal performance and Tasks panel polish.
8
+
9
+ - Improves terminal responsiveness by keeping mounted terminal hosts stable,
10
+ lazy-loading heavier xterm addons, and reducing terminal-run polling churn.
11
+ - Fixes OpenCode TUI wheel and mouse handling by preserving binary input for
12
+ normal terminals while translating OpenCode legacy mouse reports into the
13
+ SGR format it handles correctly.
14
+ - Sends a current `.hive/tasks.md` snapshot when a Tasks websocket connects,
15
+ and guards that initial read so a missing or unreadable file does not break
16
+ the websocket session.
17
+ - Renames the visible Todo entry to **Tasks** and widens the Tasks side panel
18
+ to match the default Team Members pane width.
19
+ - Enlarges the Team Members header and count badge for better readability.
20
+
21
+ ## 1.3.3 - 2026-05-21
22
+
23
+ OpenCode terminal scrolling and small UI polish.
24
+
25
+ - Restores the Task Graph topbar entry so the graph view is available again
26
+ from the main shell.
27
+ - Updates generated worker names to use localized, role-scoped historical
28
+ figure name pools, making Chinese and English workspaces feel less generic.
29
+ - Fixes OpenCode TUI mouse-wheel scrolling inside Hive terminal panels. Hive now
30
+ tags each terminal run with an input profile and maps OpenCode wheel events to
31
+ the keys OpenCode's message viewport actually handles (`Ctrl+D` / `Ctrl+U`),
32
+ while leaving other alternate-screen TUIs on the existing arrow-key fallback.
33
+ - Preserves that OpenCode profile when the user selects the OpenCode preset but
34
+ starts it through a custom startup command such as `opencode --continue`.
35
+ - Keeps workspace shell terminals on the default input profile.
36
+ - Enlarges the Team Members header count for better readability.
37
+ - Documents that npm's `prebuild-install@7.1.3` deprecation warning comes from
38
+ the upstream native-binary installer chain and is safe to ignore.
39
+
5
40
  ## 1.3.0 - 2026-05-20
6
41
 
7
42
  Installable Hive: turns the web shell into a real PWA so Chrome / Edge can
@@ -135,37 +170,30 @@ Brand polish.
135
170
 
136
171
  ## 1.1.2 - 2026-05-17
137
172
 
138
- Private-release workflow fix.
173
+ Release workflow fix.
139
174
 
140
175
  - Runs npm publish on Ubuntu instead of macOS. Publishing does not require
141
- macOS, and the Ubuntu runner avoids unnecessary private-repo Actions cost.
176
+ macOS, and the Ubuntu runner is a better fit for the publish step.
142
177
 
143
178
  ## 1.1.1 - 2026-05-17
144
179
 
145
- Private-release workflow fix.
180
+ Release workflow fix.
146
181
 
147
- - Publishes from the private product repository without npm provenance because
148
- npm provenance currently requires a public GitHub Actions source repository.
149
- The package contents are otherwise the same 1.1.x private-release line:
150
- Workspace terminal tabs, hidden dormant Blueprint entry, and no production
151
- source maps in the npm tarball.
182
+ - Publishes without production source maps in the npm tarball while keeping the
183
+ user-facing package contents unchanged.
152
184
 
153
185
  ## 1.1.0 - 2026-05-17
154
186
 
155
- Private-release line.
187
+ Workspace terminal release.
156
188
 
157
189
  - Added a Workspace terminal that opens from the active workspace and runs in
158
190
  the workspace directory. It supports multiple shell tabs, full-height terminal
159
191
  space, tab switching, and closing individual tabs without closing the whole
160
192
  dialog.
161
193
  - Kept the external install path unchanged. Users still install with
162
- `npm install -g @tt-a1i/hive` or run with `npx @tt-a1i/hive`; this release is
163
- built from the private product repository and published to the same npm
164
- package.
194
+ `npm install -g @tt-a1i/hive` or run with `npx @tt-a1i/hive`.
165
195
  - Hid the dormant task-graph / Blueprint entry from the main UI while keeping
166
196
  the underlying code in place for possible future use.
167
- - Documented the public/private repository split and release policy in
168
- `docs/private-release-strategy.md`.
169
197
 
170
198
  ## 1.0.0 - 2026-05-17
171
199
 
package/README.en.md CHANGED
@@ -1,3 +1,7 @@
1
+ <p align="center">
2
+ <img src="./assets/logo.png" width="120" alt="Hive logo" />
3
+ </p>
4
+
1
5
  # Hive
2
6
 
3
7
  <p align="center">
@@ -15,12 +19,12 @@ Code, research, drafts, translations — if a team can split the work, a hive ca
15
19
 
16
20
  [![npm](https://img.shields.io/npm/v/@tt-a1i/hive.svg)](https://www.npmjs.com/package/@tt-a1i/hive)
17
21
  [![ci](https://img.shields.io/github/actions/workflow/status/tt-a1i/hive/release.yml?branch=main&label=ci)](https://github.com/tt-a1i/hive/actions/workflows/release.yml)
18
- [![Website](https://img.shields.io/badge/website-hive--site.pages.dev-5a8a8a.svg)](https://hive-site.pages.dev)
22
+ [![Website](https://img.shields.io/badge/website-hivehq.dev-5a8a8a.svg)](https://hivehq.dev)
19
23
  [![Node](https://img.shields.io/badge/node-%3E%3D22-3c873a.svg)](https://nodejs.org/)
20
24
  [![License](https://img.shields.io/badge/license-BUSL--1.1-orange.svg)](./LICENSE.BSL)
21
25
  [![Platforms](https://img.shields.io/badge/platforms-macOS%20%C2%B7%20Linux%20%C2%B7%20Windows%20(best--effort)-lightgrey.svg)](#platform-support)
22
26
 
23
- 🌐 **Website**: [hive-site.pages.dev/en/](https://hive-site.pages.dev/en/) · [中文](https://hive-site.pages.dev/)
27
+ 🌐 **Website**: [hivehq.dev/en/](https://hivehq.dev/en/) · [中文](https://hivehq.dev/)
24
28
 
25
29
  English · [简体中文](./README.md)
26
30
 
@@ -29,6 +33,10 @@ English · [简体中文](./README.md)
29
33
  > [npm](https://www.npmjs.com/package/@tt-a1i/hive) and the badge above resolves
30
34
  > to it.
31
35
 
36
+ <p align="center">
37
+ <img src="./assets/hive-team-view.png" alt="Hive workbench with a 4-agent team — orchestrator dispatching while workers run" />
38
+ </p>
39
+
32
40
  ## Why Hive
33
41
 
34
42
  CLI agents are powerful, but coordinating several of them manually is
@@ -236,6 +244,11 @@ Hive depends on `node-pty` and `better-sqlite3`, which use native binaries. Use
236
244
  Node.js 22+, keep your package manager cache clean, and verify your platform
237
245
  build tools are available.
238
246
 
247
+ If npm prints a deprecated warning for `prebuild-install@7.1.3`, it is safe to
248
+ ignore. The warning comes from `better-sqlite3`'s native binary download chain;
249
+ it is an upstream installer maintenance notice, not a Hive install failure, and
250
+ does not affect runtime behavior.
251
+
239
252
  **Folder picker does not open on Linux**
240
253
 
241
254
  Install `zenity`, or paste the workspace path manually.
@@ -300,12 +313,21 @@ verifies macOS, Ubuntu, and Windows, then publishes to npm with `NPM_TOKEN`.
300
313
 
301
314
  ## Status
302
315
 
303
- Hive is published as a stable release. Current work focuses on polishing the
304
- multi-agent collaboration workflow, Windows support, and clearer orchestration
305
- observability.
316
+ Hive is in alpha. The core flow is usable today; current work focuses on
317
+ polishing the multi-agent collaboration workflow, Windows support, and clearer
318
+ orchestration observability. Try it out and open issues — feedback shapes what
319
+ gets prioritized next.
306
320
 
307
- ## License
321
+ ## A different form factor: squad
322
+
323
+ If you'd rather have **pure CLI, zero background process, and the ability to
324
+ run on a remote SSH box**, [squad](https://github.com/mco-org/squad) takes the
325
+ same idea down a different path — SQLite as the protocol layer, one terminal
326
+ per agent. The two projects don't replace each other; pick by workflow:
308
327
 
309
- Hive is licensed under the Business Source License 1.1 (see [LICENSE.BSL](LICENSE.BSL)). BSL permits personal use, internal deployment, embedding in non-competitive products, and non-commercial forks; it **only prohibits** offering Hive as a hosted multi-agent orchestration service to third parties.
328
+ - **Hive** visual workbench, one-click restart, workspace sidebar, easier to demo to a team
329
+ - **squad** — lives in tmux, SSH remote dev, no extra background process, Windows servers
330
+
331
+ ## License
310
332
 
311
- See the Additional Use Grant in [LICENSE.BSL](LICENSE.BSL) for the exact boundary.
333
+ Hive is open source under the Business Source License 1.1. Personal use, internal deployment, embedding, and forks are permitted — see [LICENSE.BSL](LICENSE.BSL) for the exact boundary.
package/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ <p align="center">
2
+ <img src="./assets/logo.png" width="120" alt="Hive logo" />
3
+ </p>
4
+
1
5
  # Hive
2
6
 
3
7
  <p align="center">
@@ -10,17 +14,21 @@
10
14
 
11
15
  [![npm](https://img.shields.io/npm/v/@tt-a1i/hive.svg)](https://www.npmjs.com/package/@tt-a1i/hive)
12
16
  [![ci](https://img.shields.io/github/actions/workflow/status/tt-a1i/hive/release.yml?branch=main&label=ci)](https://github.com/tt-a1i/hive/actions/workflows/release.yml)
13
- [![Website](https://img.shields.io/badge/website-hive--site.pages.dev-5a8a8a.svg)](https://hive-site.pages.dev)
17
+ [![Website](https://img.shields.io/badge/website-hivehq.dev-5a8a8a.svg)](https://hivehq.dev)
14
18
  [![Node](https://img.shields.io/badge/node-%3E%3D22-3c873a.svg)](https://nodejs.org/)
15
19
  [![License](https://img.shields.io/badge/license-BUSL--1.1-orange.svg)](./LICENSE.BSL)
16
20
  [![Platforms](https://img.shields.io/badge/platforms-macOS%20%C2%B7%20Linux%20%C2%B7%20Windows%20(best--effort)-lightgrey.svg)](#平台支持)
17
21
 
18
- 🌐 **官网**:[hive-site.pages.dev](https://hive-site.pages.dev/)([English](https://hive-site.pages.dev/en/))
22
+ 🌐 **官网**:[hivehq.dev](https://hivehq.dev/)([English](https://hivehq.dev/en/))
19
23
 
20
24
  [English](./README.en.md) · 简体中文
21
25
 
22
26
  > Hive 是本机优先的工具,只监听 `127.0.0.1`,面向已经在用 CLI Agent 的人。最新稳定版本见 [npm](https://www.npmjs.com/package/@tt-a1i/hive),上面的 badge 会指向它。
23
27
 
28
+ <p align="center">
29
+ <img src="./assets/hive-team-view.png" alt="Hive 工作台:4 个 CLI Agent 团队,Orchestrator 派单、Worker 各自开工" />
30
+ </p>
31
+
24
32
  ## 为什么需要 Hive
25
33
 
26
34
  CLI Agent 各自都很强,但同时管几个就有点别扭:
@@ -173,6 +181,8 @@ hive --port 4020
173
181
 
174
182
  Hive 依赖 `node-pty` 和 `better-sqlite3`,它们用原生二进制。确认 Node.js 22+,清干净 package manager 缓存,并准备好你平台的构建工具(macOS Xcode CLI、Linux build-essential + python3、Windows VS Build Tools)。
175
183
 
184
+ 安装时如果看到 `prebuild-install@7.1.3` 的 deprecated warning,可以忽略。它来自 `better-sqlite3` 的原生二进制下载链路,只是上游安装器维护状态提示,不代表 Hive 安装失败,也不会影响运行。
185
+
176
186
  **Linux 上目录选择器不弹**
177
187
 
178
188
  装 `zenity`,或者直接在对话框里粘路径。
@@ -229,10 +239,15 @@ pnpm release:dry
229
239
 
230
240
  ## 状态
231
241
 
232
- Hive 已发布稳定版。当前重点是继续打磨多 Agent 协作体验、Windows 支持和更清晰的调度可观测性。
242
+ Hive 目前处于 alpha 阶段,核心流程已可用。当前重点是继续打磨多 Agent 协作体验、Windows 支持和更清晰的调度可观测性。欢迎试用、提 issue——反馈会直接影响后续节奏。
233
243
 
234
- ## License
244
+ ## 另一种形态:squad
245
+
246
+ 如果你更喜欢 **纯 CLI、零后台进程、能直接在 SSH 进的远端服务器上跑** 的形态,[squad](https://github.com/mco-org/squad) 是同一个想法的另一条路线——SQLite 当通信层,每个 agent 各自开一个终端。两个项目互不替代,按工作流挑就行:
235
247
 
236
- Hive 使用 Business Source License 1.1([LICENSE.BSL](LICENSE.BSL))。BSL 允许个人使用、内部部署、嵌入其他产品、非商业 fork;**仅禁止**把 Hive 包装成多 agent 协作 SaaS 卖给第三方。
248
+ - **Hive** 想要可视化工作台、一键重启、侧边栏切 workspace、给团队演示
249
+ - **squad** — 活在 tmux 里、SSH 远端开发、不想跑额外后台进程、Windows server
250
+
251
+ ## License
237
252
 
238
- 详细边界见 [LICENSE.BSL](LICENSE.BSL) 的 Additional Use Grant
253
+ Hive 在 Business Source License 1.1 下开源。个人使用、内部部署、嵌入、fork 都可以;详细边界见 [LICENSE.BSL](LICENSE.BSL)。
Binary file
@@ -1,3 +1,7 @@
1
+ interface ParsedCancelArgs {
2
+ dispatchId: string;
3
+ reason: string;
4
+ }
1
5
  export interface ParsedReportArgs {
2
6
  artifacts: string[];
3
7
  dispatchId: string | undefined;
@@ -5,5 +9,7 @@ export interface ParsedReportArgs {
5
9
  useStdin: boolean;
6
10
  }
7
11
  export declare const parseReportArgs: (args: string[], command?: string) => ParsedReportArgs;
12
+ export declare const parseCancelArgs: (args: string[]) => ParsedCancelArgs;
8
13
  export declare const readStdinToString: (command?: string) => Promise<string>;
9
14
  export declare const runTeamCommand: (argv: string[]) => Promise<void>;
15
+ export {};
@@ -10,6 +10,7 @@ const TEAM_USAGE = [
10
10
  'Usage:',
11
11
  ' team list',
12
12
  ' team send <worker-name> "<task>"',
13
+ ' team cancel --dispatch <dispatch-id> "<reason>"',
13
14
  ' team report "<result>" [--dispatch <dispatch-id>] [--artifact <path>]',
14
15
  ' team report --stdin [--dispatch <dispatch-id>] [--artifact <path>]',
15
16
  ' team status "<current status>" [--artifact <path>]',
@@ -80,6 +81,7 @@ const postJson = async (baseUrl, path, body) => {
80
81
  };
81
82
  const REPORT_USAGE = 'Usage: team report (<result> | --stdin) [--dispatch <dispatch-id>] [--artifact <path>]';
82
83
  const STATUS_USAGE = 'Usage: team status (<current status> | --stdin) [--artifact <path>]';
84
+ const CANCEL_USAGE = 'Usage: team cancel --dispatch <dispatch-id> <reason>';
83
85
  const usageFor = (command) => (command === 'status' ? STATUS_USAGE : REPORT_USAGE);
84
86
  const withUsage = (message, command) => `${message}\n\n${usageFor(command)}`;
85
87
  export const parseReportArgs = (args, command = 'report') => {
@@ -139,6 +141,39 @@ export const parseReportArgs = (args, command = 'report') => {
139
141
  }
140
142
  return { result: useStdin ? null : (positionals[0] ?? null), artifacts, dispatchId, useStdin };
141
143
  };
144
+ export const parseCancelArgs = (args) => {
145
+ const positionals = [];
146
+ let dispatchId;
147
+ for (let index = 0; index < args.length; index += 1) {
148
+ const arg = args[index];
149
+ if (arg === undefined)
150
+ continue;
151
+ if (arg === '--dispatch') {
152
+ const next = args[index + 1];
153
+ if (next === undefined || next.startsWith('--')) {
154
+ throw new Error(`--dispatch requires a value\n\n${CANCEL_USAGE}`);
155
+ }
156
+ dispatchId = next;
157
+ index += 1;
158
+ continue;
159
+ }
160
+ if (arg.startsWith('--')) {
161
+ throw new Error(`Unknown argument: ${arg}\n\n${CANCEL_USAGE}`);
162
+ }
163
+ positionals.push(arg);
164
+ }
165
+ if (!dispatchId) {
166
+ throw new Error(`Missing --dispatch <dispatch-id>\n\n${CANCEL_USAGE}`);
167
+ }
168
+ if (positionals.length === 0) {
169
+ throw new Error(`Missing <reason>\n\n${CANCEL_USAGE}`);
170
+ }
171
+ const reason = positionals.join(' ').trim();
172
+ if (!reason) {
173
+ throw new Error(`Missing <reason>\n\n${CANCEL_USAGE}`);
174
+ }
175
+ return { dispatchId, reason };
176
+ };
142
177
  export const readStdinToString = async (command = 'report') => {
143
178
  if (process.stdin.isTTY) {
144
179
  throw new Error(withUsage('--stdin requires piped input, but stdin is a TTY. Did you forget to pipe content in?', command));
@@ -194,6 +229,19 @@ export const runTeamCommand = async (argv) => {
194
229
  console.log(JSON.stringify(await response.json()));
195
230
  return;
196
231
  }
232
+ if (command === 'cancel') {
233
+ const cancel = parseCancelArgs(args);
234
+ const env = getHiveEnv();
235
+ const baseUrl = getBaseUrl(env);
236
+ await postJson(baseUrl, '/api/team/cancel', {
237
+ dispatch_id: cancel.dispatchId,
238
+ project_id: env.HIVE_PROJECT_ID,
239
+ from_agent_id: env.HIVE_AGENT_ID,
240
+ token: env.HIVE_AGENT_TOKEN,
241
+ reason: cancel.reason,
242
+ });
243
+ return;
244
+ }
197
245
  if (command === 'status') {
198
246
  const report = parseReportArgs(args, 'status');
199
247
  const body = report.useStdin ? await readStdinToString('status') : (report.result ?? '');
@@ -1,8 +1,11 @@
1
1
  export const createAgentLaunchCache = (store) => {
2
2
  const launchConfigs = new Map();
3
3
  const workspaceByAgentId = new Map();
4
+ const missingLaunchConfigs = new Set();
4
5
  const cacheKey = (workspaceId, agentId) => `${workspaceId}:${agentId}`;
5
6
  const load = () => {
7
+ launchConfigs.clear();
8
+ workspaceByAgentId.clear();
6
9
  for (const row of store.listLaunchConfigs()) {
7
10
  launchConfigs.set(cacheKey(row.workspaceId, row.agentId), row.config);
8
11
  workspaceByAgentId.set(row.agentId, row.workspaceId);
@@ -11,21 +14,33 @@ export const createAgentLaunchCache = (store) => {
11
14
  load();
12
15
  return {
13
16
  get(workspaceId, agentId) {
14
- const config = launchConfigs.get(cacheKey(workspaceId, agentId));
17
+ const key = cacheKey(workspaceId, agentId);
18
+ const config = launchConfigs.get(key);
15
19
  if (config)
16
20
  return config;
21
+ if (missingLaunchConfigs.has(key)) {
22
+ throw new Error(`Agent launch config not found: ${agentId}`);
23
+ }
17
24
  load();
18
- const reloadedConfig = launchConfigs.get(cacheKey(workspaceId, agentId));
25
+ const reloadedConfig = launchConfigs.get(key);
19
26
  if (reloadedConfig)
20
27
  return reloadedConfig;
28
+ missingLaunchConfigs.add(key);
21
29
  throw new Error(`Agent launch config not found: ${agentId}`);
22
30
  },
23
31
  peek(workspaceId, agentId) {
24
- const config = launchConfigs.get(cacheKey(workspaceId, agentId));
32
+ const key = cacheKey(workspaceId, agentId);
33
+ const config = launchConfigs.get(key);
25
34
  if (config)
26
35
  return config;
36
+ if (missingLaunchConfigs.has(key))
37
+ return undefined;
27
38
  load();
28
- return launchConfigs.get(cacheKey(workspaceId, agentId));
39
+ const reloadedConfig = launchConfigs.get(key);
40
+ if (reloadedConfig)
41
+ return reloadedConfig;
42
+ missingLaunchConfigs.add(key);
43
+ return undefined;
29
44
  },
30
45
  getWorkspaceId(agentId) {
31
46
  return workspaceByAgentId.get(agentId);
@@ -41,12 +56,16 @@ export const createAgentLaunchCache = (store) => {
41
56
  sessionIdCapture: input.sessionIdCapture ?? null,
42
57
  };
43
58
  store.saveLaunchConfig(workspaceId, agentId, normalized);
44
- launchConfigs.set(cacheKey(workspaceId, agentId), normalized);
59
+ const key = cacheKey(workspaceId, agentId);
60
+ launchConfigs.set(key, normalized);
61
+ missingLaunchConfigs.delete(key);
45
62
  workspaceByAgentId.set(agentId, workspaceId);
46
63
  },
47
64
  remove(workspaceId, agentId) {
48
65
  store.deleteLaunchConfig(workspaceId, agentId);
49
- launchConfigs.delete(cacheKey(workspaceId, agentId));
66
+ const key = cacheKey(workspaceId, agentId);
67
+ launchConfigs.delete(key);
68
+ missingLaunchConfigs.add(key);
50
69
  workspaceByAgentId.delete(agentId);
51
70
  },
52
71
  setWorkspaceId(agentId, workspaceId) {
@@ -27,7 +27,7 @@ interface AgentRunRecord extends AgentRunSnapshot {
27
27
  resize: (cols: number, rows: number) => void;
28
28
  resume: () => void;
29
29
  stop: () => void;
30
- write: (text: string) => void;
30
+ write: (input: Buffer | string) => void;
31
31
  };
32
32
  onExit?: (event: {
33
33
  runId: string;
@@ -40,7 +40,7 @@ interface AgentManager {
40
40
  resizeRun: (runId: string, cols: number, rows: number) => void;
41
41
  resumeRun: (runId: string) => void;
42
42
  startAgent: (input: StartAgentInput) => Promise<AgentRunSnapshot>;
43
- writeInput: (runId: string, text: string) => void;
43
+ writeInput: (runId: string, input: Buffer | string) => void;
44
44
  getRun: (runId: string) => AgentRunSnapshot;
45
45
  removeRun: (runId: string) => void;
46
46
  stopRun: (runId: string) => void;
@@ -29,6 +29,9 @@ export interface AgentRuntime {
29
29
  requireActiveRun?: boolean;
30
30
  }) => void;
31
31
  writeSendPrompt: (workspaceId: string, workerId: string, dispatchId: string, fromAgentName: string, workerDescription: string, text: string) => void;
32
+ writeCancelPrompt: (workspaceId: string, workerId: string, dispatchId: string, reason: string, input?: {
33
+ requireActiveRun?: boolean;
34
+ }) => void;
32
35
  writeUserInputPrompt: (workspaceId: string, text: string) => void;
33
36
  }
34
37
  export type { StartAgentOptions };
@@ -115,6 +115,9 @@ export const createAgentRuntime = (agentManager, agentRunStore, sessionStore, ge
115
115
  writeSendPrompt(workspaceId, workerId, dispatchId, fromAgentName, workerDescription, text) {
116
116
  stdinDispatcher.writeSendPrompt(workspaceId, workerId, dispatchId, fromAgentName, workerDescription, text);
117
117
  },
118
+ writeCancelPrompt(workspaceId, workerId, dispatchId, reason, input = {}) {
119
+ stdinDispatcher.writeCancelPrompt(workspaceId, workerId, dispatchId, reason, input);
120
+ },
118
121
  writeUserInputPrompt(workspaceId, text) {
119
122
  stdinDispatcher.writeUserInputPrompt(workspaceId, text);
120
123
  },
@@ -15,7 +15,7 @@ export const buildAgentStartupInstructions = ({ agent, workspace, }) => {
15
15
  '',
16
16
  ];
17
17
  if (agent.role === 'orchestrator') {
18
- lines.push('你的职责:', '- 直接响应 user,澄清需求并拆解任务', `- 维护 ${TASKS_RELATIVE_PATH}`, '- 按 worker 名称派单,并根据汇报推进下一步', '', '可用 team 命令:', '- team list', '- team send <worker-name> "<task>"', '', '派单时必须使用 worker name,不要使用 worker id。', '', 'Hive worker 派单规则:', ...getHiveTeamRules(agent));
18
+ lines.push('你的职责:', '- 直接响应 user,澄清需求并拆解任务', `- 维护 ${TASKS_RELATIVE_PATH}`, '- 按 worker 名称派单,并根据汇报推进下一步', '', '可用 team 命令:', '- team list', '- team send <worker-name> "<task>"', '- team cancel --dispatch <id> "<reason>"', '', '派单时必须使用 worker name,不要使用 worker id。', '取消未完成派单时必须使用 dispatch id。', '', 'Hive worker 派单规则:', ...getHiveTeamRules(agent));
19
19
  }
20
20
  else {
21
21
  lines.push('可用 team 命令:', '- team report "<完整汇报>" [--dispatch <id>] [--artifact <path>] 完成/失败/阻塞汇报', '- team report --stdin [--dispatch <id>] [--artifact <path>] 同上,从 stdin 读正文(适合多行/含引号/特殊字符)', '- team status "<当前状态>" [--artifact <path>] 中段进度/待命/接入状态', '- team status --stdin [--artifact <path>] 同上,从 stdin 读正文', '- team list 查看 workspace 内的 worker(含状态)', '- team --help 仅查命令用法;**不是**汇报手段', '', '语法要点:', '- 正文是第一个 positional argument,flag 顺序任意:`team report "结论" --dispatch X` 和 `team report --dispatch X "结论"` 都成立。', "- 长正文(多行 / 含引号 / shell 特殊字符 / heredoc)一律走 `--stdin`,并用 *quoted* heredoc(`<<'EOF'`)防止 shell 展开 $vars / 反引号 / 命令替换:", " 例:`team report --stdin --dispatch <id> <<'EOF'`", ' `... 长报告(含 $VAR、`backtick`、"引号" 都按字面量保留)...`', ' `EOF`', '- CLI 报错会同时打印 USAGE,可直接对照修正参数。', '', '完成任务后必须执行 `team report "<结论>"`。', '失败、阻塞或部分完成也用 `team report "<当前状态与原因>"` 汇报。', '没有进行中的任务时,用 `team status "<当前状态>"` 汇报接入、待命或阻塞状态。', '不要调用 team send;worker 之间不能直接派单。', '', 'Hive worker 边界:', ...getHiveTeamRules(agent));
@@ -13,6 +13,7 @@ export declare const buildOrchestratorReportPayload: (workerName: string, text:
13
13
  export declare const buildOrchestratorStatusPayload: (workerName: string, text: string, artifacts: string[]) => string;
14
14
  export declare const buildOrchestratorUserInputPayload: (text: string) => string;
15
15
  export declare const buildWorkerDispatchPayload: (fromAgentName: string, workerDescription: string, dispatchId: string, text: string) => string;
16
+ export declare const buildWorkerCancelPayload: (dispatchId: string, reason: string) => string;
16
17
  export declare const createAgentStdinDispatcher: ({ agentManager, getLaunchConfig, getWorkspaceId, registry, syncRun, }: AgentStdinDispatcherInput) => {
17
18
  writeReportPrompt(workspaceId: string, workerName: string, text: string, artifacts: string[], input?: {
18
19
  requireActiveRun?: boolean;
@@ -21,6 +22,9 @@ export declare const createAgentStdinDispatcher: ({ agentManager, getLaunchConfi
21
22
  requireActiveRun?: boolean;
22
23
  }): void;
23
24
  writeSendPrompt(workspaceId: string, workerId: string, dispatchId: string, fromAgentName: string, workerDescription: string, text: string): void;
25
+ writeCancelPrompt(workspaceId: string, workerId: string, dispatchId: string, reason: string, input?: {
26
+ requireActiveRun?: boolean;
27
+ }): void;
24
28
  writeUserInputPrompt(workspaceId: string, text: string): void;
25
29
  };
26
30
  export {};
@@ -33,6 +33,15 @@ export const buildWorkerDispatchPayload = (fromAgentName, workerDescription, dis
33
33
  buildWorkerReminderTail(dispatchId),
34
34
  '',
35
35
  ].join('\n');
36
+ export const buildWorkerCancelPayload = (dispatchId, reason) => [
37
+ `[Hive 系统消息:dispatch ${dispatchId} 已取消]`,
38
+ '',
39
+ '请停止执行这条派单,不要再为它调用 team report。',
40
+ '',
41
+ '取消原因:',
42
+ reason,
43
+ '',
44
+ ].join('\n');
36
45
  export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getWorkspaceId, registry, syncRun, }) => {
37
46
  const writeToActiveAgentRun = (workspaceId, agentId, text, input = {}) => {
38
47
  const run = registry
@@ -72,6 +81,9 @@ export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getW
72
81
  writeSendPrompt(workspaceId, workerId, dispatchId, fromAgentName, workerDescription, text) {
73
82
  writeToActiveAgentRun(workspaceId, workerId, buildWorkerDispatchPayload(fromAgentName, workerDescription, dispatchId, text), { requireActiveRun: true });
74
83
  },
84
+ writeCancelPrompt(workspaceId, workerId, dispatchId, reason, input = {}) {
85
+ writeToActiveAgentRun(workspaceId, workerId, buildWorkerCancelPayload(dispatchId, reason), input);
86
+ },
75
87
  writeUserInputPrompt(workspaceId, text) {
76
88
  writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorUserInputPayload(text));
77
89
  },
@@ -129,6 +129,6 @@ export const createApp = ({ store, pickFolderService = pickFolder, openWorkspace
129
129
  sendJson(response, 500, { error: message });
130
130
  }
131
131
  });
132
- createTerminalWebSocketServer(server, store);
132
+ createTerminalWebSocketServer(server, store, tasksFileService);
133
133
  return { server, store };
134
134
  };
@@ -1,5 +1,5 @@
1
1
  import type { Database } from 'better-sqlite3';
2
- export type DispatchStatus = 'queued' | 'submitted' | 'reported';
2
+ export type DispatchStatus = 'queued' | 'submitted' | 'reported' | 'cancelled';
3
3
  export interface DispatchRecord {
4
4
  artifacts: string[];
5
5
  createdAt: number;
@@ -28,6 +28,11 @@ interface ReportDispatchInput {
28
28
  toAgentId: string;
29
29
  workspaceId: string;
30
30
  }
31
+ interface CancelDispatchInput {
32
+ dispatchId: string;
33
+ reason: string;
34
+ workspaceId: string;
35
+ }
31
36
  export interface ListDispatchesOptions {
32
37
  limit?: number;
33
38
  offset?: number;
@@ -39,12 +44,28 @@ export declare const createDispatchLedgerStore: (db: Database) => {
39
44
  deleteWorkerDispatches: (workspaceId: string, workerId: string) => void;
40
45
  deleteWorkspaceDispatches: (workspaceId: string) => void;
41
46
  findOpenDispatch: (workspaceId: string, toAgentId: string, dispatchId?: string) => DispatchRecord | undefined;
47
+ findOpenDispatchById: (workspaceId: string, dispatchId: string) => DispatchRecord | undefined;
42
48
  listOpenDispatchKinds: () => Array<{
43
49
  type: "send";
44
50
  worker_id: string;
45
51
  workspace_id: string;
46
52
  }>;
47
53
  listWorkspaceDispatches: (workspaceId: string, options?: ListDispatchesOptions) => DispatchRecord[];
54
+ markCancelled: (input: CancelDispatchInput) => {
55
+ reportedAt: number;
56
+ reportText: string;
57
+ status: "cancelled";
58
+ artifacts: string[];
59
+ createdAt: number;
60
+ deliveredAt: number | null;
61
+ fromAgentId: string | null;
62
+ id: string;
63
+ sequence: number | null;
64
+ submittedAt: number | null;
65
+ text: string;
66
+ toAgentId: string;
67
+ workspaceId: string;
68
+ } | undefined;
48
69
  markReportedByWorker: (input: ReportDispatchInput) => {
49
70
  artifacts: string[];
50
71
  reportedAt: number;
@@ -77,7 +77,7 @@ export const createDispatchLedgerStore = (db) => {
77
77
  WHERE id = ?
78
78
  AND workspace_id = ?
79
79
  AND to_agent_id = ?
80
- AND status != 'reported'
80
+ AND status IN ('queued', 'submitted')
81
81
  LIMIT 1`)
82
82
  .get(dispatchId, workspaceId, toAgentId);
83
83
  return row ? toRecord(row) : undefined;
@@ -87,12 +87,23 @@ export const createDispatchLedgerStore = (db) => {
87
87
  FROM dispatches
88
88
  WHERE workspace_id = ?
89
89
  AND to_agent_id = ?
90
- AND status != 'reported'
90
+ AND status IN ('queued', 'submitted')
91
91
  ORDER BY sequence ASC
92
92
  LIMIT 1`)
93
93
  .get(workspaceId, toAgentId);
94
94
  return row ? toRecord(row) : undefined;
95
95
  };
96
+ const findOpenDispatchById = (workspaceId, dispatchId) => {
97
+ const row = db
98
+ .prepare(`SELECT *
99
+ FROM dispatches
100
+ WHERE id = ?
101
+ AND workspace_id = ?
102
+ AND status IN ('queued', 'submitted')
103
+ LIMIT 1`)
104
+ .get(dispatchId, workspaceId);
105
+ return row ? toRecord(row) : undefined;
106
+ };
96
107
  const markReportedByWorker = (input) => {
97
108
  const dispatch = findOpenDispatch(input.workspaceId, input.toAgentId, input.dispatchId);
98
109
  if (!dispatch) {
@@ -113,6 +124,24 @@ export const createDispatchLedgerStore = (db) => {
113
124
  status: 'reported',
114
125
  };
115
126
  };
127
+ const markCancelled = (input) => {
128
+ const dispatch = findOpenDispatchById(input.workspaceId, input.dispatchId);
129
+ if (!dispatch) {
130
+ return undefined;
131
+ }
132
+ const cancelledAt = Date.now();
133
+ db.prepare(`UPDATE dispatches
134
+ SET status = ?,
135
+ reported_at = ?,
136
+ report_text = ?
137
+ WHERE id = ?`).run('cancelled', cancelledAt, input.reason, dispatch.id);
138
+ return {
139
+ ...dispatch,
140
+ reportedAt: cancelledAt,
141
+ reportText: input.reason,
142
+ status: 'cancelled',
143
+ };
144
+ };
116
145
  const listWorkspaceDispatches = (workspaceId, options = {}) => {
117
146
  const offset = options.offset ?? 0;
118
147
  const limit = options.limit ?? 100;
@@ -138,7 +167,7 @@ export const createDispatchLedgerStore = (db) => {
138
167
  return db
139
168
  .prepare(`SELECT workspace_id, to_agent_id AS worker_id, 'send' AS type
140
169
  FROM dispatches
141
- WHERE status != 'reported'
170
+ WHERE status IN ('queued', 'submitted')
142
171
  ORDER BY sequence ASC`)
143
172
  .all();
144
173
  };
@@ -154,8 +183,10 @@ export const createDispatchLedgerStore = (db) => {
154
183
  deleteWorkerDispatches,
155
184
  deleteWorkspaceDispatches,
156
185
  findOpenDispatch,
186
+ findOpenDispatchById,
157
187
  listOpenDispatchKinds,
158
188
  listWorkspaceDispatches,
189
+ markCancelled,
159
190
  markReportedByWorker,
160
191
  markSubmitted,
161
192
  };
@@ -14,7 +14,7 @@
14
14
  * abstract identity restatement.
15
15
  */
16
16
  export const ORCHESTRATOR_REMINDER_TAIL = '<hive-system-reminder>\n' +
17
- 'You are the Hive Orchestrator. Reply by either: (a) `team send "<worker-name>" "<task>"` to dispatch follow-up work to a Hive worker, or (b) plain text to the user. Never call your CLI\'s built-in subagent tools (Task / Explore / etc.) — they bypass Hive and will not appear in the UI.\n' +
17
+ 'You are the Hive Orchestrator. Reply by either: (a) `team send "<worker-name>" "<task>"` to dispatch follow-up work to a Hive worker, (b) `team cancel --dispatch <id> "<reason>"` to cancel an obsolete dispatch, or (c) plain text to the user. Never call your CLI\'s built-in subagent tools (Task / Explore / etc.) — they bypass Hive and will not appear in the UI.\n' +
18
18
  '</hive-system-reminder>';
19
19
  /**
20
20
  * Tail reminder appended to dispatches sent TO a worker. Reinforces the
@@ -31,6 +31,7 @@ const ORCHESTRATOR_RULES = [
31
31
  '普通、低风险、几分钟内能直接完成的小任务可以自己做;不要为了形式感派 worker。需要并行、长时间执行、独立 review/test、专门角色,或 user 明确要求 worker/成员处理时,再用 `team send`。',
32
32
  '如果只有一个可用 worker,直接用 `team send <worker-name> "<task>"` 派给它;不要把选择题丢回给 user。',
33
33
  '当 user 要你“让 worker ...”时,必须用 `team send <worker-name> "<task>"` 派给 Hive worker。',
34
+ '方向变更或 user 明确取消某个未完成派单时,使用 `team cancel --dispatch <id> "<reason>"` 显式关闭旧 dispatch;不要只用自然语言说“取消”。',
34
35
  '不要使用你所在 CLI 的内置 subagent / 子代理工具(如 Task / Explore 等)来代替 Hive worker;它们不会出现在 Hive UI,也不会更新 Hive 调度状态。',
35
36
  '`team list` 返回的 `last_pty_line` 是该 worker PTY 终端的最后一行原始输出(含任意 stdout / help / 控制序列噪声),**不是** worker 的正式汇报。正式汇报只来自 stdin 注入的 `[Hive 系统消息:来自 @<name> 的汇报]` 或 `[Hive 系统消息:来自 @<name> 的状态更新]`——只把这两种来源当作 reply。',
36
37
  ];
@@ -74,6 +75,7 @@ export const buildProtocolDoc = () => [
74
75
  '',
75
76
  '- `team list` — show workspace members and their status',
76
77
  '- `team send "<worker-name>" "<task>"` — dispatch to a worker by name (never id)',
78
+ '- `team cancel --dispatch <id> "<reason>"` — cancel an obsolete open dispatch',
77
79
  '',
78
80
  '## `team` CLI — worker',
79
81
  '',