@tt-a1i/hive 1.5.0 → 1.6.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.en.md +21 -0
  3. package/README.md +8 -0
  4. package/dist/src/cli/team.js +17 -0
  5. package/dist/src/server/agent-run-starter.d.ts +6 -7
  6. package/dist/src/server/agent-run-starter.js +3 -3
  7. package/dist/src/server/agent-runtime-contract.d.ts +10 -0
  8. package/dist/src/server/agent-runtime-stop-run.d.ts +1 -1
  9. package/dist/src/server/agent-runtime-stop-run.js +4 -1
  10. package/dist/src/server/agent-runtime.d.ts +2 -1
  11. package/dist/src/server/agent-runtime.js +10 -5
  12. package/dist/src/server/agent-startup-instructions.d.ts +7 -8
  13. package/dist/src/server/agent-startup-instructions.js +5 -3
  14. package/dist/src/server/agent-stdin-dispatcher.d.ts +20 -7
  15. package/dist/src/server/agent-stdin-dispatcher.js +22 -10
  16. package/dist/src/server/feature-flags.d.ts +42 -0
  17. package/dist/src/server/feature-flags.js +24 -0
  18. package/dist/src/server/hive-team-guidance.d.ts +4 -3
  19. package/dist/src/server/hive-team-guidance.js +14 -13
  20. package/dist/src/server/recovery-summary.d.ts +5 -6
  21. package/dist/src/server/recovery-summary.js +3 -2
  22. package/dist/src/server/report-outbox-store.d.ts +36 -0
  23. package/dist/src/server/report-outbox-store.js +33 -0
  24. package/dist/src/server/restart-policy-support.d.ts +4 -5
  25. package/dist/src/server/restart-policy.d.ts +5 -1
  26. package/dist/src/server/restart-policy.js +51 -33
  27. package/dist/src/server/routes-settings.js +3 -3
  28. package/dist/src/server/routes-tasks.js +23 -0
  29. package/dist/src/server/routes-workspaces.js +5 -0
  30. package/dist/src/server/runtime-restart-policy.d.ts +3 -3
  31. package/dist/src/server/runtime-restart-policy.js +2 -3
  32. package/dist/src/server/runtime-store-contract.d.ts +3 -0
  33. package/dist/src/server/runtime-store-helpers.d.ts +2 -0
  34. package/dist/src/server/runtime-store-helpers.js +14 -9
  35. package/dist/src/server/runtime-store-workflows.js +8 -0
  36. package/dist/src/server/runtime-store.js +1 -0
  37. package/dist/src/server/sqlite-schema.js +13 -0
  38. package/dist/src/server/task-deps.d.ts +32 -0
  39. package/dist/src/server/task-deps.js +40 -0
  40. package/dist/src/server/tasks-file-watcher.d.ts +6 -7
  41. package/dist/src/server/tasks-file-watcher.js +3 -2
  42. package/dist/src/server/tasks-file.d.ts +2 -1
  43. package/dist/src/server/tasks-file.js +3 -2
  44. package/dist/src/server/team-authz.d.ts +1 -1
  45. package/dist/src/server/team-authz.js +1 -0
  46. package/dist/src/server/team-operations.d.ts +7 -1
  47. package/dist/src/server/team-operations.js +81 -19
  48. package/dist/src/server/webhook-notifier.d.ts +34 -0
  49. package/dist/src/server/webhook-notifier.js +47 -0
  50. package/dist/src/server/workflow-output-schema.d.ts +18 -0
  51. package/dist/src/server/workflow-output-schema.js +41 -0
  52. package/dist/src/server/workflow-runner.js +11 -1
  53. package/package.json +1 -1
  54. package/web/dist/assets/{AddWorkerDialog-CcC-7kgG.js → AddWorkerDialog-CGbaxu0T.js} +2 -2
  55. package/web/dist/assets/{AddWorkspaceDialog-BDpOTfmt.js → AddWorkspaceDialog-CNgExu6b.js} +1 -1
  56. package/web/dist/assets/{FirstRunWizard-BYX_ocQn.js → FirstRunWizard-DxGApUNc.js} +1 -1
  57. package/web/dist/assets/{MarketplaceDrawer-DUxSk7db.js → MarketplaceDrawer-Bk6cpukn.js} +1 -1
  58. package/web/dist/assets/{WhatsNewDialog-B_RlCXcV.js → WhatsNewDialog-CSGzk-2U.js} +1 -1
  59. package/web/dist/assets/{WorkerModal-D9-7YfZZ.js → WorkerModal-i2F3n3nZ.js} +1 -1
  60. package/web/dist/assets/{WorkspaceTaskDrawer-BCKoF7qc.js → WorkspaceTaskDrawer-C_Ta_K13.js} +1 -1
  61. package/web/dist/assets/WorkspaceTerminalPanels-VdDxtrQF.js +1 -0
  62. package/web/dist/assets/index-5zh61jMg.css +1 -0
  63. package/web/dist/assets/index-CAgGM6nb.js +75 -0
  64. package/web/dist/index.html +2 -2
  65. package/web/dist/sw.js +1 -1
  66. package/web/dist/assets/WorkspaceTerminalPanels-Dq8y91t2.js +0 -1
  67. package/web/dist/assets/index-BiOvKIVw.css +0 -1
  68. package/web/dist/assets/index-DMRUklT3.js +0 -73
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  All notable user-facing changes will be documented in this file.
4
4
 
5
+ ## 1.6.0 - 2026-06-02
6
+
7
+ Orchestrator controls, worker visibility, and protocol-trust fixes.
8
+
9
+ - Adds a Stop control to the running Orchestrator pane so a runaway Orchestrator
10
+ can be halted from the UI (the default is auto-approve, so this is the only
11
+ in-UI kill switch).
12
+ - Surfaces per-worker queue depth and a latest-activity line on worker cards,
13
+ with a legend on the working badge clarifying that Hive does not auto-detect
14
+ stalls — the terminal is the source of truth.
15
+ - Redelivers worker reports the Orchestrator missed: a report is no longer
16
+ silently lost when the Orchestrator is down or restarting; it is queued and
17
+ redelivered on the next report or `team list`.
18
+ - Stops injecting the crash-recovery handover after a deliberate Stop and
19
+ Restart, so the Orchestrator is not handed stale open tasks it was meant to
20
+ drop.
21
+ - Adds an outbound completion webhook setting: Hive POSTs a small JSON payload
22
+ to a URL you choose when a worker reports or a workflow finishes — wire it to
23
+ Slack, ntfy, Feishu, and the like.
24
+ - Adds opt-in structured output to workflow `agent()` via `outputSchema`, so
25
+ fan-out/verify scripts receive a parsed object instead of parsing free text.
26
+ - Adds `team next`: tasks in `.hive/tasks.md` can carry an optional
27
+ `[needs: #2]` dependency, and `team next` returns the tasks that are unblocked
28
+ now.
29
+
5
30
  ## 1.5.0 - 2026-05-31
6
31
 
7
32
  Workflow runtime, experimental team automation, and Codex reliability.
package/README.en.md CHANGED
@@ -123,6 +123,18 @@ First-run flow:
123
123
  5. Ask the Orchestrator to delegate work. It sends tasks with
124
124
  `team send <worker-name> "<task>"`; workers report back with `team report`.
125
125
 
126
+ If you want the Orchestrator to size the team itself, leave **Auto-staff**
127
+ enabled (it is on by default). It can `team spawn` the right temporary mix of
128
+ coders, testers, and reviewers for the task, then Hive dismisses those
129
+ temporary workers when their work is done.
130
+
131
+ For stronger automation, enable the experimental **Workflows** toggle in
132
+ settings. The Orchestrator can then author and run multi-agent workflows that
133
+ fan out across implementation, review, testing, or other stages. The topbar
134
+ **Workflows** panel shows runs, phase results, logs, schedules, and stop
135
+ controls. The same panel also lets you choose which CLI workflow-created
136
+ agents use by default and which CLIs they are allowed to use.
137
+
126
138
  ## How It Works
127
139
 
128
140
  ```text
@@ -175,8 +187,17 @@ same shell environment you use to start Hive.
175
187
  - Orchestrator and worker terminals backed by real PTYs.
176
188
  - Add Worker flow with role presets for coder, reviewer, tester, and fully
177
189
  custom prompts and commands — wire any CLI agent into the role you need.
190
+ - Auto-staff (experimental, on by default): the Orchestrator can create
191
+ temporary coders, testers, and reviewers based on the task, and Hive cleans
192
+ them up after their dispatch reports back.
193
+ - Workflows (experimental, off by default): the Orchestrator can run
194
+ multi-stage, multi-agent workflows while Hive shows runs, logs, results,
195
+ schedules, and stop controls in the Workflows panel.
196
+ - Workflow CLI policy: choose the default CLI for workflow-created agents and
197
+ restrict which CLIs workflow scripts may launch.
178
198
  - `.hive/tasks.md` editor with external-file conflict handling.
179
199
  - Background PTY preservation and best-effort native session resume.
200
+ - A What's New dialog after upgrades with curated release highlights.
180
201
  - Local SQLite metadata under `~/.config/hive` by default, or `$HIVE_DATA_DIR`
181
202
  when set.
182
203
 
package/README.md CHANGED
@@ -84,6 +84,10 @@ PWA 只是 UI 壳,Hive 后端仍需要在终端里跑着。如果启动 PWA
84
84
  4. 在 Team Members 面板里添加 Worker。
85
85
  5. 跟 Orchestrator 说一声让它派活,它会用 `team send <worker-name> "<task>"` 发任务,Worker 完事后用 `team report` 回报。
86
86
 
87
+ 想让 Orchestrator 自己决定团队规模,可以保留 **自动组队** 开关开启(默认开启):它会按任务需要临时 `team spawn` 合适数量的 coder / tester / reviewer,任务结束后自动收回临时成员。
88
+
89
+ 想试更强的自动化,可以在右上角设置里开启实验性的 **Workflow** 开关。开启后,Orchestrator 可以编写并运行多 agent workflow,把一个目标拆成 fan-out / review / test 等阶段;顶部的 **Workflows** 面板会显示运行记录、阶段结果、定时任务和停止按钮。Workflow 创建的新 agent 默认使用哪种 CLI、允许使用哪些 CLI,也可以在 Workflows 面板里配置。
90
+
87
91
  ## 工作方式
88
92
 
89
93
  ```text
@@ -131,8 +135,12 @@ Hive 不替你安装这些 CLI。请在启动 Hive 的同一个 shell 环境里
131
135
  - Workspace 侧边栏,方便在多个本机项目之间切换。
132
136
  - Orchestrator 和 Worker 终端都是真实 PTY 支撑的。
133
137
  - Add Worker 预置 coder / reviewer / tester 等角色模板,也支持完全自定义 prompt 与命令——把任何 CLI agent 编排成你需要的角色。
138
+ - 自动组队(实验性,默认开启):Orchestrator 可以根据任务动态创建临时 coder / tester / reviewer,完成后自动回收。
139
+ - Workflows(实验性,默认关闭):Orchestrator 可以运行多阶段、多 agent 的 workflow,Hive 在 Workflows 面板里展示运行、日志、结果、定时任务和停止控制。
140
+ - Workflow CLI 策略:为 workflow 创建的 agent 选择默认 CLI,并限制允许使用的 CLI,避免脚本误启未配置的 agent。
134
141
  - `.hive/tasks.md` 编辑器,带外部文件冲突处理。
135
142
  - PTY 后台保留 + 尽力使用各 CLI 原生 session 恢复。
143
+ - 升级后的 What's New 弹窗,用简短 release highlights 告诉你新版改了什么。
136
144
  - 元数据存在本机 SQLite,默认在 `~/.config/hive`,或者通过 `$HIVE_DATA_DIR` 指定。
137
145
 
138
146
  Hive **不**提供 sandbox 隔离、多用户认证、云端托管,也不自带任何 agent 模型。它只负责调度你已经在用的本机 CLI。
@@ -9,6 +9,7 @@ const REQUIRED_ENV_KEYS = [
9
9
  const TEAM_USAGE = [
10
10
  'Usage:',
11
11
  ' team list',
12
+ ' team next (tasks in .hive/tasks.md that are unblocked now — those whose [needs: #n] deps are all done)',
12
13
  ' team send <worker-name> "<task>"',
13
14
  ' team spawn <role> [--name <name>] [--cli <claude|codex|opencode|gemini>] [--ephemeral]',
14
15
  ' team dismiss <worker-name>',
@@ -243,6 +244,22 @@ export const runTeamCommand = async (argv) => {
243
244
  console.log(JSON.stringify(await response.json()));
244
245
  return;
245
246
  }
247
+ if (command === 'next') {
248
+ const env = getHiveEnv();
249
+ const baseUrl = getBaseUrl(env);
250
+ const response = await fetchRuntime(baseUrl, `/api/workspaces/${env.HIVE_PROJECT_ID}/tasks/next`, {
251
+ method: 'GET',
252
+ headers: {
253
+ 'x-hive-agent-id': env.HIVE_AGENT_ID,
254
+ 'x-hive-agent-token': env.HIVE_AGENT_TOKEN,
255
+ },
256
+ });
257
+ if (!response.ok) {
258
+ await throwHttpError(response);
259
+ }
260
+ console.log(JSON.stringify(await response.json()));
261
+ return;
262
+ }
246
263
  if (command === 'send') {
247
264
  const [workerName, ...taskParts] = args;
248
265
  const task = taskParts.join(' ').trim();
@@ -6,6 +6,7 @@ import type { AgentSessionStorePort } from './agent-runtime-ports.js';
6
6
  import type { LiveAgentRun } from './agent-runtime-types.js';
7
7
  import type { AgentTokenRegistry } from './agent-tokens.js';
8
8
  import type { CommandPresetRecord } from './command-preset-store.js';
9
+ import { type FeatureFlags } from './feature-flags.js';
9
10
  import type { LiveRunRegistry } from './live-run-registry.js';
10
11
  import type { RestartPolicy } from './restart-policy.js';
11
12
  interface AgentRunStarterInput {
@@ -18,12 +19,10 @@ interface AgentRunStarterInput {
18
19
  getCommandPreset: (id: string) => CommandPresetRecord | undefined;
19
20
  getAgent: ((workspaceId: string, agentId: string) => AgentSummary | undefined) | undefined;
20
21
  restartPolicy: RestartPolicy;
21
- /** Experimental workflow gate gates the `team workflow` line + the
22
- * workflow-authoring rule in the orchestrator's startup prompt. */
23
- getWorkflowsEnabled?: () => boolean;
24
- /** Experimental auto-staff gate (default on) gates the team-sizing rule
25
- * in the orchestrator's startup prompt. */
26
- getAutostaffEnabled?: () => boolean;
22
+ /** Resolves the live experimental flags for the orchestrator's startup
23
+ * prompt (`workflowsEnabled` gates the `team workflow` line + authoring
24
+ * rule; `autostaffEnabled` gates the team-sizing rule). */
25
+ getFlags?: () => FeatureFlags;
27
26
  }
28
- export declare const createAgentRunStarter: ({ agentManager, registry, onAgentExit, store, sessionStore, tokenRegistry, getCommandPreset, getAgent, restartPolicy, getWorkflowsEnabled, getAutostaffEnabled, }: AgentRunStarterInput) => (workspace: WorkspaceSummary, agentId: string, config: AgentLaunchConfigInput, hivePort: string) => Promise<LiveAgentRun>;
27
+ export declare const createAgentRunStarter: ({ agentManager, registry, onAgentExit, store, sessionStore, tokenRegistry, getCommandPreset, getAgent, restartPolicy, getFlags, }: AgentRunStarterInput) => (workspace: WorkspaceSummary, agentId: string, config: AgentLaunchConfigInput, hivePort: string) => Promise<LiveAgentRun>;
29
28
  export {};
@@ -1,8 +1,9 @@
1
1
  import { buildAgentRunBootstrap, startAgentRunCapture } from './agent-run-bootstrap.js';
2
2
  import { handleAgentRunExit } from './agent-run-exit-handler.js';
3
3
  import { buildAgentStartupInstructions } from './agent-startup-instructions.js';
4
+ import { FEATURE_FLAGS_ALL_OFF } from './feature-flags.js';
4
5
  import { createPostStartInputWriter, isInteractiveAgentCommand } from './post-start-input-writer.js';
5
- export const createAgentRunStarter = ({ agentManager, registry, onAgentExit, store, sessionStore, tokenRegistry, getCommandPreset, getAgent, restartPolicy, getWorkflowsEnabled, getAutostaffEnabled, }) => async (workspace, agentId, config, hivePort) => {
6
+ export const createAgentRunStarter = ({ agentManager, registry, onAgentExit, store, sessionStore, tokenRegistry, getCommandPreset, getAgent, restartPolicy, getFlags, }) => async (workspace, agentId, config, hivePort) => {
6
7
  if (!agentManager)
7
8
  throw new Error('Agent manager is required to start agents');
8
9
  const agent = getAgent?.(workspace.id, agentId);
@@ -102,8 +103,7 @@ export const createAgentRunStarter = ({ agentManager, registry, onAgentExit, sto
102
103
  void postStartWriter(run.runId, buildAgentStartupInstructions({
103
104
  agent,
104
105
  workspace,
105
- workflowsEnabled: getWorkflowsEnabled?.() ?? false,
106
- autostaffEnabled: getAutostaffEnabled?.() ?? false,
106
+ flags: getFlags?.() ?? FEATURE_FLAGS_ALL_OFF,
107
107
  })).catch(() => {
108
108
  // The agent may have exited before post-start guidance could be written.
109
109
  });
@@ -34,5 +34,15 @@ export interface AgentRuntime {
34
34
  }) => void;
35
35
  writeUserInputPrompt: (workspaceId: string, text: string) => void;
36
36
  writeSystemMessageToAgent: (workspaceId: string, agentId: string, text: string) => void;
37
+ /** Awaitable report delivery — rejects if the orchestrator PTY write fails,
38
+ * so the caller can persist the report to the redelivery outbox. */
39
+ deliverReportToOrchestrator: (workspaceId: string, workerName: string, text: string, artifacts: string[], input?: {
40
+ requireActiveRun?: boolean;
41
+ }) => Promise<void>;
42
+ /** Awaitable opaque delivery — used to drain the report outbox so an entry
43
+ * is marked delivered only after the PTY write resolves. */
44
+ deliverSystemMessageToAgent: (workspaceId: string, agentId: string, text: string, input?: {
45
+ requireActiveRun?: boolean;
46
+ }) => Promise<void>;
37
47
  }
38
48
  export type { StartAgentOptions };
@@ -1,4 +1,4 @@
1
1
  import type { AgentManager } from './agent-manager.js';
2
2
  import type { LiveAgentRun } from './agent-runtime-types.js';
3
3
  import type { LiveRunRegistry } from './live-run-registry.js';
4
- export declare const stopLiveRun: (agentManager: AgentManager | undefined, registry: LiveRunRegistry, syncRun: (run: LiveAgentRun) => LiveAgentRun, runId: string) => void;
4
+ export declare const stopLiveRun: (agentManager: AgentManager | undefined, registry: LiveRunRegistry, syncRun: (run: LiveAgentRun) => LiveAgentRun, runId: string, onUserStop?: (runId: string) => void) => void;
@@ -1,4 +1,4 @@
1
- export const stopLiveRun = (agentManager, registry, syncRun, runId) => {
1
+ export const stopLiveRun = (agentManager, registry, syncRun, runId, onUserStop) => {
2
2
  if (!agentManager) {
3
3
  throw new Error('Agent manager is required to stop agents');
4
4
  }
@@ -12,5 +12,8 @@ export const stopLiveRun = (agentManager, registry, syncRun, runId) => {
12
12
  else if (['error', 'exited'].includes(agentManager.getRun(runId).status)) {
13
13
  return;
14
14
  }
15
+ // Reaching here means the run was genuinely live and we are about to kill it
16
+ // at the user's request — distinct from a crash that already ended the run.
17
+ onUserStop?.(runId);
15
18
  agentManager.stopRun(runId);
16
19
  };
@@ -3,6 +3,7 @@ import type { AgentManager } from './agent-manager.js';
3
3
  import type { AgentRuntime } from './agent-runtime-contract.js';
4
4
  import type { AgentRunStorePort, AgentSessionStorePort } from './agent-runtime-ports.js';
5
5
  import type { CommandPresetRecord } from './command-preset-store.js';
6
+ import type { FeatureFlags } from './feature-flags.js';
6
7
  import { type RestartPolicy } from './restart-policy.js';
7
- export declare const createAgentRuntime: (agentManager: AgentManager | undefined, agentRunStore: AgentRunStorePort, sessionStore: AgentSessionStorePort, getCommandPreset: (id: string) => CommandPresetRecord | undefined, onAgentExit: (workspaceId: string, agentId: string) => void, restartPolicy?: RestartPolicy, getAgent?: (workspaceId: string, agentId: string) => AgentSummary | undefined, getWorkflowsEnabled?: () => boolean, getAutostaffEnabled?: () => boolean) => AgentRuntime;
8
+ export declare const createAgentRuntime: (agentManager: AgentManager | undefined, agentRunStore: AgentRunStorePort, sessionStore: AgentSessionStorePort, getCommandPreset: (id: string) => CommandPresetRecord | undefined, onAgentExit: (workspaceId: string, agentId: string) => void, restartPolicy?: RestartPolicy, getAgent?: (workspaceId: string, agentId: string) => AgentSummary | undefined, getFlags?: () => FeatureFlags) => AgentRuntime;
8
9
  export type { AgentRuntime };
@@ -10,7 +10,7 @@ import { createAgentStdinDispatcher } from './agent-stdin-dispatcher.js';
10
10
  import { createAgentTokenRegistry } from './agent-tokens.js';
11
11
  import { createLiveRunRegistry } from './live-run-registry.js';
12
12
  import { createNoopRestartPolicy } from './restart-policy.js';
13
- export const createAgentRuntime = (agentManager, agentRunStore, sessionStore, getCommandPreset, onAgentExit, restartPolicy = createNoopRestartPolicy(), getAgent, getWorkflowsEnabled, getAutostaffEnabled) => {
13
+ export const createAgentRuntime = (agentManager, agentRunStore, sessionStore, getCommandPreset, onAgentExit, restartPolicy = createNoopRestartPolicy(), getAgent, getFlags) => {
14
14
  const registry = createLiveRunRegistry();
15
15
  const launchCache = createAgentLaunchCache(agentRunStore);
16
16
  const tokenRegistry = createAgentTokenRegistry();
@@ -29,7 +29,7 @@ export const createAgentRuntime = (agentManager, agentRunStore, sessionStore, ge
29
29
  getWorkspaceId: launchCache.getWorkspaceId,
30
30
  registry,
31
31
  syncRun,
32
- ...(getWorkflowsEnabled ? { getWorkflowsEnabled } : {}),
32
+ ...(getFlags ? { getFlags } : {}),
33
33
  });
34
34
  const startLiveRun = createAgentRunStarter({
35
35
  agentManager,
@@ -41,8 +41,7 @@ export const createAgentRuntime = (agentManager, agentRunStore, sessionStore, ge
41
41
  getCommandPreset,
42
42
  getAgent,
43
43
  restartPolicy,
44
- ...(getWorkflowsEnabled ? { getWorkflowsEnabled } : {}),
45
- ...(getAutostaffEnabled ? { getAutostaffEnabled } : {}),
44
+ ...(getFlags ? { getFlags } : {}),
46
45
  });
47
46
  return {
48
47
  async close() {
@@ -106,7 +105,7 @@ export const createAgentRuntime = (agentManager, agentRunStore, sessionStore, ge
106
105
  return startPromise;
107
106
  },
108
107
  stopAgentRun(runId) {
109
- stopLiveRun(agentManager, registry, syncRun, runId);
108
+ stopLiveRun(agentManager, registry, syncRun, runId, (stoppedRunId) => restartPolicy.markUserStopped(stoppedRunId));
110
109
  },
111
110
  validateAgentToken: tokenRegistry.validate,
112
111
  writeReportPrompt(workspaceId, workerName, _workerId, text, artifacts, input = {}) {
@@ -127,5 +126,11 @@ export const createAgentRuntime = (agentManager, agentRunStore, sessionStore, ge
127
126
  writeSystemMessageToAgent(workspaceId, agentId, text) {
128
127
  stdinDispatcher.writeSystemMessageToAgent(workspaceId, agentId, text);
129
128
  },
129
+ deliverReportToOrchestrator(workspaceId, workerName, text, artifacts, input = {}) {
130
+ return stdinDispatcher.deliverReportToOrchestrator(workspaceId, workerName, text, artifacts, input);
131
+ },
132
+ deliverSystemMessageToAgent(workspaceId, agentId, text, input = {}) {
133
+ return stdinDispatcher.deliverSystemMessageToAgent(workspaceId, agentId, text, input);
134
+ },
130
135
  };
131
136
  };
@@ -1,4 +1,5 @@
1
1
  import type { AgentSummary, WorkspaceSummary } from '../shared/types.js';
2
+ import { type FeatureFlags } from './feature-flags.js';
2
3
  export declare const buildAgentSessionBindingMarker: ({ agent, workspace, }: {
3
4
  agent: AgentSummary;
4
5
  workspace: WorkspaceSummary;
@@ -7,14 +8,12 @@ export declare const buildAgentLegacyIdentityMarker: ({ agent, workspace, }: {
7
8
  agent: AgentSummary;
8
9
  workspace: WorkspaceSummary;
9
10
  }) => string;
10
- export declare const buildAgentStartupInstructions: ({ agent, workspace, workflowsEnabled, autostaffEnabled, }: {
11
+ export declare const buildAgentStartupInstructions: ({ agent, workspace, flags, }: {
11
12
  agent: AgentSummary;
12
13
  workspace: WorkspaceSummary;
13
- /** Experimental workflow gate. When off, the startup prompt omits the
14
- * `team workflow` command + the workflow-authoring rule so a fresh
15
- * orchestrator isn't told to run a command the runtime rejects. */
16
- workflowsEnabled?: boolean;
17
- /** Experimental auto-staff gate (default on). When on, the orchestrator is
18
- * told it may size the team to the task up front. */
19
- autostaffEnabled?: boolean;
14
+ /** Live experimental flags. `workflowsEnabled` gates the `team workflow`
15
+ * command line + the workflow-authoring rule (so a fresh orchestrator isn't
16
+ * told to run a command the runtime rejects); `autostaffEnabled` gates the
17
+ * team-sizing rule. Omitted → all off. */
18
+ flags?: FeatureFlags;
20
19
  }) => string;
@@ -1,8 +1,10 @@
1
+ import { FEATURE_FLAGS_ALL_OFF } from './feature-flags.js';
1
2
  import { getHiveTeamRules } from './hive-team-guidance.js';
2
3
  import { TASKS_RELATIVE_PATH } from './tasks-file.js';
3
4
  export const buildAgentSessionBindingMarker = ({ agent, workspace, }) => `Hive session binding: workspace_id=${workspace.id}; agent_id=${agent.id}`;
4
5
  export const buildAgentLegacyIdentityMarker = ({ agent, workspace, }) => `You are ${agent.name} (${agent.role}) in workspace ${workspace.name}.`;
5
- export const buildAgentStartupInstructions = ({ agent, workspace, workflowsEnabled = false, autostaffEnabled = false, }) => {
6
+ export const buildAgentStartupInstructions = ({ agent, workspace, flags = FEATURE_FLAGS_ALL_OFF, }) => {
7
+ const { workflowsEnabled } = flags;
6
8
  const lines = [
7
9
  '<hive-message kind="startup">',
8
10
  '',
@@ -21,10 +23,10 @@ export const buildAgentStartupInstructions = ({ agent, workspace, workflowsEnabl
21
23
  ? [
22
24
  '- team workflow run --stdin (fan-out / staged work — see .hive/PROTOCOL.md for the DSL)',
23
25
  ]
24
- : []), '', 'Always dispatch by worker name, never by worker id.', 'Always cancel an open dispatch by its dispatch id.', '', 'Hive worker dispatch rules:', ...getHiveTeamRules(agent, workflowsEnabled, autostaffEnabled));
26
+ : []), '', 'Always dispatch by worker name, never by worker id.', 'Always cancel an open dispatch by its dispatch id.', '', 'Hive worker dispatch rules:', ...getHiveTeamRules(agent, flags));
25
27
  }
26
28
  else {
27
- lines.push('Available team commands:', '- team report "<result>" [--dispatch <id>] [--artifact <path>] report done / failed / blocked', '- team report --stdin [--dispatch <id>] [--artifact <path>] same, body read from stdin (multi-line / quotes / special chars)', '- team status "<state>" [--artifact <path>] mid-task progress / standby / connected status', '- team status --stdin [--artifact <path>] same, body read from stdin', '- team list list the workspace workers (with status)', '- team --help command syntax only; NOT a way to report', '', 'Syntax notes:', '- The body is the first positional argument; flag order is free: `team report "result" --dispatch X` and `team report --dispatch X "result"` are both valid.', "- Long bodies (multi-line / quotes / shell metacharacters) always go through `--stdin`, piped in via your shell (POSIX: a quoted heredoc `<<'EOF' … EOF` to stop $var / backtick expansion; Windows cmd: `type body.txt |` or `< body.txt`). `--stdin` only reads from stdin and relies on no shell syntax of its own.", '- On error the CLI also prints USAGE — fix the arguments against it.', '', 'When a task is done you MUST run `team report "<result>"`.', 'Failure, blocked, or partial completion are also reported with `team report "<current state and reason>"`.', 'When no task is in progress, report connection / standby / blocked state with `team status "<state>"`.', 'Do not call team send; workers cannot dispatch to each other.', '', 'Hive worker boundaries:', ...getHiveTeamRules(agent, workflowsEnabled, autostaffEnabled));
29
+ lines.push('Available team commands:', '- team report "<result>" [--dispatch <id>] [--artifact <path>] report done / failed / blocked', '- team report --stdin [--dispatch <id>] [--artifact <path>] same, body read from stdin (multi-line / quotes / special chars)', '- team status "<state>" [--artifact <path>] mid-task progress / standby / connected status', '- team status --stdin [--artifact <path>] same, body read from stdin', '- team list list the workspace workers (with status)', '- team --help command syntax only; NOT a way to report', '', 'Syntax notes:', '- The body is the first positional argument; flag order is free: `team report "result" --dispatch X` and `team report --dispatch X "result"` are both valid.', "- Long bodies (multi-line / quotes / shell metacharacters) always go through `--stdin`, piped in via your shell (POSIX: a quoted heredoc `<<'EOF' … EOF` to stop $var / backtick expansion; Windows cmd: `type body.txt |` or `< body.txt`). `--stdin` only reads from stdin and relies on no shell syntax of its own.", '- On error the CLI also prints USAGE — fix the arguments against it.', '', 'When a task is done you MUST run `team report "<result>"`.', 'Failure, blocked, or partial completion are also reported with `team report "<current state and reason>"`.', 'When no task is in progress, report connection / standby / blocked state with `team status "<state>"`.', 'Do not call team send; workers cannot dispatch to each other.', '', 'Hive worker boundaries:', ...getHiveTeamRules(agent, flags));
28
30
  }
29
31
  lines.push('', '</hive-message>', '');
30
32
  return lines.join('\n');
@@ -1,6 +1,7 @@
1
1
  import type { AgentManager } from './agent-manager.js';
2
2
  import type { AgentLaunchConfigInput } from './agent-run-store.js';
3
3
  import type { LiveAgentRun } from './agent-runtime-types.js';
4
+ import { type FeatureFlags } from './feature-flags.js';
4
5
  import type { LiveRunRegistry } from './live-run-registry.js';
5
6
  interface AgentStdinDispatcherInput {
6
7
  agentManager: AgentManager | undefined;
@@ -8,16 +9,17 @@ interface AgentStdinDispatcherInput {
8
9
  getWorkspaceId: (agentId: string) => string | undefined;
9
10
  registry: LiveRunRegistry;
10
11
  syncRun: (run: LiveAgentRun) => LiveAgentRun;
11
- /** Whether the experimental workflow feature is on. Controls whether the
12
- * orchestrator reminder tail mentions `team workflow`. Defaults to off. */
13
- getWorkflowsEnabled?: () => boolean;
12
+ /** Resolves the live experimental flags. The orchestrator reminder tail
13
+ * appended to every injected message reads `workflowsEnabled` from it.
14
+ * Optional; omitted all flags off. */
15
+ getFlags?: () => FeatureFlags;
14
16
  }
15
- export declare const buildOrchestratorReportPayload: (workerName: string, text: string, artifacts: string[], workflowsEnabled?: boolean) => string;
16
- export declare const buildOrchestratorStatusPayload: (workerName: string, text: string, artifacts: string[], workflowsEnabled?: boolean) => string;
17
- export declare const buildOrchestratorUserInputPayload: (text: string, workflowsEnabled?: boolean) => string;
17
+ export declare const buildOrchestratorReportPayload: (workerName: string, text: string, artifacts: string[], flags?: FeatureFlags) => string;
18
+ export declare const buildOrchestratorStatusPayload: (workerName: string, text: string, artifacts: string[], flags?: FeatureFlags) => string;
19
+ export declare const buildOrchestratorUserInputPayload: (text: string, flags?: FeatureFlags) => string;
18
20
  export declare const buildWorkerDispatchPayload: (fromAgentName: string, workerDescription: string, dispatchId: string, text: string) => string;
19
21
  export declare const buildWorkerCancelPayload: (dispatchId: string, reason: string) => string;
20
- export declare const createAgentStdinDispatcher: ({ agentManager, getLaunchConfig, getWorkspaceId, registry, syncRun, getWorkflowsEnabled, }: AgentStdinDispatcherInput) => {
22
+ export declare const createAgentStdinDispatcher: ({ agentManager, getLaunchConfig, getWorkspaceId, registry, syncRun, getFlags, }: AgentStdinDispatcherInput) => {
21
23
  writeReportPrompt(workspaceId: string, workerName: string, text: string, artifacts: string[], input?: {
22
24
  requireActiveRun?: boolean;
23
25
  }): void;
@@ -33,5 +35,16 @@ export declare const createAgentStdinDispatcher: ({ agentManager, getLaunchConfi
33
35
  * Used by the workflow runner to notify the triggering orchestrator
34
36
  * when a run finishes (mirrors Claude Code's <task-notification>). */
35
37
  writeSystemMessageToAgent(workspaceId: string, agentId: string, text: string): void;
38
+ /** Awaitable report delivery — same payload as writeReportPrompt but the
39
+ * promise is returned so the caller can persist the report to the
40
+ * redelivery outbox if the orchestrator PTY write rejects. */
41
+ deliverReportToOrchestrator(workspaceId: string, workerName: string, text: string, artifacts: string[], input?: {
42
+ requireActiveRun?: boolean;
43
+ }): Promise<void>;
44
+ /** Awaitable opaque delivery — used to drain the report outbox so an entry
45
+ * is marked delivered only after the PTY write actually resolves. */
46
+ deliverSystemMessageToAgent(workspaceId: string, agentId: string, text: string, input?: {
47
+ requireActiveRun?: boolean;
48
+ }): Promise<void>;
36
49
  };
37
50
  export {};
@@ -1,21 +1,22 @@
1
+ import { FEATURE_FLAGS_ALL_OFF } from './feature-flags.js';
1
2
  import { buildOrchestratorReminderTail, buildWorkerReminderTail } from './hive-team-guidance.js';
2
3
  import { PtyInactiveError } from './http-errors.js';
3
4
  import { createPostStartInputWriter } from './post-start-input-writer.js';
4
- export const buildOrchestratorReportPayload = (workerName, text, artifacts, workflowsEnabled = false) => {
5
+ export const buildOrchestratorReportPayload = (workerName, text, artifacts, flags = FEATURE_FLAGS_ALL_OFF) => {
5
6
  const lines = [`<hive-message kind="report" from="@${workerName}">`, text];
6
7
  for (const artifact of artifacts)
7
8
  lines.push(`artifact: ${artifact}`);
8
- lines.push('</hive-message>', '', buildOrchestratorReminderTail(workflowsEnabled), '');
9
+ lines.push('</hive-message>', '', buildOrchestratorReminderTail(flags), '');
9
10
  return lines.join('\n');
10
11
  };
11
- export const buildOrchestratorStatusPayload = (workerName, text, artifacts, workflowsEnabled = false) => {
12
+ export const buildOrchestratorStatusPayload = (workerName, text, artifacts, flags = FEATURE_FLAGS_ALL_OFF) => {
12
13
  const lines = [`<hive-message kind="status" from="@${workerName}">`, text];
13
14
  for (const artifact of artifacts)
14
15
  lines.push(`artifact: ${artifact}`);
15
- lines.push('</hive-message>', '', buildOrchestratorReminderTail(workflowsEnabled), '');
16
+ lines.push('</hive-message>', '', buildOrchestratorReminderTail(flags), '');
16
17
  return lines.join('\n');
17
18
  };
18
- export const buildOrchestratorUserInputPayload = (text, workflowsEnabled = false) => [text, '', buildOrchestratorReminderTail(workflowsEnabled), ''].join('\n');
19
+ export const buildOrchestratorUserInputPayload = (text, flags = FEATURE_FLAGS_ALL_OFF) => [text, '', buildOrchestratorReminderTail(flags), ''].join('\n');
19
20
  export const buildWorkerDispatchPayload = (fromAgentName, workerDescription, dispatchId, text) => [
20
21
  `<hive-message kind="dispatch" from="@${fromAgentName}">`,
21
22
  '',
@@ -44,8 +45,8 @@ export const buildWorkerCancelPayload = (dispatchId, reason) => [
44
45
  '</hive-message>',
45
46
  '',
46
47
  ].join('\n');
47
- export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getWorkspaceId, registry, syncRun, getWorkflowsEnabled, }) => {
48
- const workflowsEnabled = () => getWorkflowsEnabled?.() ?? false;
48
+ export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getWorkspaceId, registry, syncRun, getFlags, }) => {
49
+ const flags = () => getFlags?.() ?? FEATURE_FLAGS_ALL_OFF;
49
50
  const chains = new Map();
50
51
  const getChain = (agentId) => {
51
52
  let chain = chains.get(agentId);
@@ -156,10 +157,10 @@ export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getW
156
157
  };
157
158
  return {
158
159
  writeReportPrompt(workspaceId, workerName, text, artifacts, input = {}) {
159
- swallowQueuedFailure(writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorReportPayload(workerName, text, artifacts, workflowsEnabled()), input));
160
+ swallowQueuedFailure(writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorReportPayload(workerName, text, artifacts, flags()), input));
160
161
  },
161
162
  writeStatusPrompt(workspaceId, workerName, text, artifacts, input = {}) {
162
- swallowQueuedFailure(writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorStatusPayload(workerName, text, artifacts, workflowsEnabled()), input));
163
+ swallowQueuedFailure(writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorStatusPayload(workerName, text, artifacts, flags()), input));
163
164
  },
164
165
  writeSendPrompt(workspaceId, workerId, dispatchId, fromAgentName, workerDescription, text) {
165
166
  return writeToActiveAgentRun(workspaceId, workerId, buildWorkerDispatchPayload(fromAgentName, workerDescription, dispatchId, text), { requireActiveRun: true });
@@ -168,7 +169,7 @@ export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getW
168
169
  swallowQueuedFailure(writeToActiveAgentRun(workspaceId, workerId, buildWorkerCancelPayload(dispatchId, reason), input));
169
170
  },
170
171
  writeUserInputPrompt(workspaceId, text) {
171
- swallowQueuedFailure(writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorUserInputPayload(text, workflowsEnabled())));
172
+ swallowQueuedFailure(writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorUserInputPayload(text, flags())));
172
173
  },
173
174
  /** Generic: deliver an opaque text block to a specific agent's PTY.
174
175
  * Used by the workflow runner to notify the triggering orchestrator
@@ -176,5 +177,16 @@ export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getW
176
177
  writeSystemMessageToAgent(workspaceId, agentId, text) {
177
178
  swallowQueuedFailure(writeToActiveAgentRun(workspaceId, agentId, text));
178
179
  },
180
+ /** Awaitable report delivery — same payload as writeReportPrompt but the
181
+ * promise is returned so the caller can persist the report to the
182
+ * redelivery outbox if the orchestrator PTY write rejects. */
183
+ deliverReportToOrchestrator(workspaceId, workerName, text, artifacts, input = {}) {
184
+ return writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorReportPayload(workerName, text, artifacts, flags()), input);
185
+ },
186
+ /** Awaitable opaque delivery — used to drain the report outbox so an entry
187
+ * is marked delivered only after the PTY write actually resolves. */
188
+ deliverSystemMessageToAgent(workspaceId, agentId, text, input = {}) {
189
+ return writeToActiveAgentRun(workspaceId, agentId, text, input);
190
+ },
179
191
  };
180
192
  };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Every experimental feature flag resolved into one snapshot.
3
+ *
4
+ * Threading this bag through the prompt-build call graph — instead of one
5
+ * `getX` accessor per flag — is the whole point of this module: a new flag adds
6
+ * a field here and is read at the one site that needs it, without touching any
7
+ * of the carriers in between (agent-runtime → run-starter / restart-policy /
8
+ * tasks-watcher → the pure builders in hive-team-guidance).
9
+ *
10
+ * Each flag keeps its own storage semantics and doc in its own file
11
+ * ([[workflow-feature]], [[team-autostaff]]); this module only composes them.
12
+ */
13
+ export interface FeatureFlags {
14
+ /** Workflow runtime — `team workflow`, the scheduler, the Drawer, and the
15
+ * orchestrator guidance that teaches them. Default OFF. */
16
+ workflowsEnabled: boolean;
17
+ /** Auto-staff orchestrator guidance — size the team to the task up front
18
+ * rather than one worker at a time. Default ON. */
19
+ autostaffEnabled: boolean;
20
+ }
21
+ /**
22
+ * Conservative all-off snapshot. Use as the parameter default wherever a
23
+ * builder is called without flags — it preserves the per-flag `= false`
24
+ * defaults the builders carried before this registry existed.
25
+ *
26
+ * This is NOT the storage default: an ABSENT `team.autostaff` key reads back
27
+ * ON via {@link readFeatureFlags}. Only an OMITTED argument falls back to off,
28
+ * which is the safe choice (never inject guidance for a flag nobody resolved).
29
+ */
30
+ export declare const FEATURE_FLAGS_ALL_OFF: FeatureFlags;
31
+ interface AppStateReader {
32
+ getAppState: (key: string) => {
33
+ value: string | null;
34
+ } | undefined;
35
+ }
36
+ /**
37
+ * Read every flag from app_state into one snapshot, each with its own storage
38
+ * default (workflows off-when-absent, auto-staff on-when-absent). Called fresh
39
+ * each time so a Settings toggle takes effect without restarting the runtime.
40
+ */
41
+ export declare const readFeatureFlags: (settings: AppStateReader) => FeatureFlags;
42
+ export {};
@@ -0,0 +1,24 @@
1
+ import { AUTOSTAFF_ENABLED_KEY, readAutostaffEnabled } from './team-autostaff.js';
2
+ import { readWorkflowEnabled, WORKFLOW_ENABLED_KEY } from './workflow-feature.js';
3
+ /**
4
+ * Conservative all-off snapshot. Use as the parameter default wherever a
5
+ * builder is called without flags — it preserves the per-flag `= false`
6
+ * defaults the builders carried before this registry existed.
7
+ *
8
+ * This is NOT the storage default: an ABSENT `team.autostaff` key reads back
9
+ * ON via {@link readFeatureFlags}. Only an OMITTED argument falls back to off,
10
+ * which is the safe choice (never inject guidance for a flag nobody resolved).
11
+ */
12
+ export const FEATURE_FLAGS_ALL_OFF = {
13
+ workflowsEnabled: false,
14
+ autostaffEnabled: false,
15
+ };
16
+ /**
17
+ * Read every flag from app_state into one snapshot, each with its own storage
18
+ * default (workflows off-when-absent, auto-staff on-when-absent). Called fresh
19
+ * each time so a Settings toggle takes effect without restarting the runtime.
20
+ */
21
+ export const readFeatureFlags = (settings) => ({
22
+ workflowsEnabled: readWorkflowEnabled(settings.getAppState(WORKFLOW_ENABLED_KEY)?.value ?? null),
23
+ autostaffEnabled: readAutostaffEnabled(settings.getAppState(AUTOSTAFF_ENABLED_KEY)?.value ?? null),
24
+ });
@@ -1,4 +1,5 @@
1
1
  import type { AgentSummary } from '../shared/types.js';
2
+ import { type FeatureFlags } from './feature-flags.js';
2
3
  import { type WorkflowCliPolicy } from './workflow-cli-policy.js';
3
4
  /**
4
5
  * Tail reminder appended to every message that flows INTO the orchestrator
@@ -17,7 +18,7 @@ import { type WorkflowCliPolicy } from './workflow-cli-policy.js';
17
18
  * the workflow DSL live in `.hive/PROTOCOL.md`, which agents re-read on demand.
18
19
  * A long banner on every turn is itself the noise this envelope exists to beat.
19
20
  */
20
- export declare const buildOrchestratorReminderTail: (workflowsEnabled: boolean) => string;
21
+ export declare const buildOrchestratorReminderTail: ({ workflowsEnabled }: FeatureFlags) => string;
21
22
  /**
22
23
  * Tail reminder appended to dispatches sent TO a worker. Reinforces the
23
24
  * worker identity (so the agent does not regress into its normal CLI
@@ -25,7 +26,7 @@ export declare const buildOrchestratorReminderTail: (workflowsEnabled: boolean)
25
26
  * with dispatch_id pre-bound.
26
27
  */
27
28
  export declare const buildWorkerReminderTail: (dispatchId: string) => string;
28
- export declare const getHiveTeamRules: (agent: Pick<AgentSummary, "role">, workflowsEnabled?: boolean, autostaffEnabled?: boolean) => readonly string[];
29
+ export declare const getHiveTeamRules: (agent: Pick<AgentSummary, "role">, flags?: FeatureFlags) => readonly string[];
29
30
  /**
30
31
  * Workspace-local protocol cheat sheet written to `.hive/PROTOCOL.md`. Agents
31
32
  * are explicitly trained to look at project root markdown when confused, so
@@ -35,4 +36,4 @@ export declare const getHiveTeamRules: (agent: Pick<AgentSummary, "role">, workf
35
36
  * syntax and the workflow DSL reference — the always-on injections only carry
36
37
  * the lean core rules and point here.
37
38
  */
38
- export declare const buildProtocolDoc: (cliPolicy?: WorkflowCliPolicy, workflowsEnabled?: boolean, autostaffEnabled?: boolean) => string;
39
+ export declare const buildProtocolDoc: (cliPolicy?: WorkflowCliPolicy, flags?: FeatureFlags) => string;