opencode-hive 1.3.4 → 1.3.6

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/README.md CHANGED
@@ -66,8 +66,8 @@ During planning, "don't execute" means "don't implement" (no code edits, no work
66
66
  ### Tasks
67
67
  | Tool | Description |
68
68
  |------|-------------|
69
- | `hive_tasks_sync` | Generate tasks from plan |
70
- | `hive_task_create` | Create manual task |
69
+ | `hive_tasks_sync` | Generate tasks from plan, or rewrite pending plan tasks with `refreshPending: true` after a plan amendment |
70
+ | `hive_task_create` | Create a manual task with explicit `dependsOn` and optional structured metadata |
71
71
  | `hive_task_update` | Update task status/summary |
72
72
 
73
73
  ### Worktree
@@ -108,6 +108,43 @@ When using Dynamic Context Pruning (DCP), use a Hive-safe config in `~/.config/o
108
108
 
109
109
  For local plugin testing, keep OpenCode plugin entry as `"opencode-hive"` (not `"opencode-hive@latest"`).
110
110
 
111
+ #### Compaction recovery and session re-anchoring
112
+
113
+ OpenCode can compact long sessions. When that happens mid-orchestration or mid-task, Hive needs the session to recover its role and task boundaries without re-reading the whole repository.
114
+
115
+ The plugin now persists durable session metadata and uses it during `experimental.session.compacting` to rebuild a compact re-anchor prompt.
116
+
117
+ Where:
118
+
119
+ - Global session state is written to `.hive/sessions.json`.
120
+ - Feature-local mirrors are written to `.hive/features/<feature>/sessions.json`.
121
+ - Session classification distinguishes `primary`, `subagent`, `task-worker`, and `unknown`.
122
+ - Primary and subagent recovery can replay the stored user directive once after compaction.
123
+ - Task-worker recovery uses the strict re-anchor plus one bounded worker-specific synthetic replay after compaction.
124
+ - The task-worker replay can reference `.hive/features/<feature>/tasks/<task>/worker-prompt.md`.
125
+
126
+ Task-worker recovery is intentionally strict:
127
+
128
+ - keep the same role
129
+ - do not delegate
130
+ - do not re-read the full codebase
131
+ - re-read `worker-prompt.md`
132
+ - finish only the current task assignment
133
+ - do not merge
134
+ - do not start the next task
135
+ - do not use orchestration tools unless the worker prompt explicitly says so
136
+ - continue from the last known point
137
+
138
+ This split is deliberate: primary and subagent sessions replay the stored user directive once after compaction, while task-workers also receive one worker-specific synthetic replay after compaction that restates the active task identity and worker boundaries.
139
+
140
+ Manual tasks follow the same DAG model as plan-backed tasks:
141
+
142
+ - `hive_task_create()` stores manual tasks with explicit `dependsOn` metadata instead of inferring sequential order.
143
+ - Structured manual-task fields such as `goal`, `description`, `acceptanceCriteria`, `files`, and `references` are turned into worker-facing `spec.md` content.
144
+ - If review feedback changes downstream sequencing or scope, update `plan.md` and run `hive_tasks_sync({ refreshPending: true })` so pending plan tasks pick up the new `dependsOn` graph and regenerated specs.
145
+
146
+ This recovery path applies to the built-in `forager-worker` and custom agents derived from it, because they are the sessions most likely to be compacted mid-implementation.
147
+
111
148
  ## Prompt Budgeting & Observability
112
149
 
113
150
  Hive automatically bounds worker prompt sizes to prevent context overflow and tool output truncation.
@@ -136,6 +173,8 @@ When limits are exceeded, content is truncated with `...[truncated]` markers and
136
173
 
137
174
  Large prompts are written to `.hive/features/<feature>/tasks/<task>/worker-prompt.md` and passed by file reference (`workerPromptPath`) rather than inlined in tool output. This prevents truncation of large prompts.
138
175
 
176
+ That same `worker-prompt.md` path is also reused during compaction recovery so task workers can re-anchor to the exact task assignment after a compacted session resumes.
177
+
139
178
  ## Plan Format
140
179
 
141
180
  ```markdown
@@ -359,6 +398,13 @@ ID guardrails:
359
398
  - plugin-reserved aliases are blocked (`hive`, `architect`, `swarm`, `scout`, `forager`, `hygienic`, `receiver`)
360
399
  - operational IDs are blocked (`build`, `plan`, `code`)
361
400
 
401
+ Compaction classification follows the base agent:
402
+
403
+ - `forager-worker` derivatives are treated as `task-worker`
404
+ - `hygienic-reviewer` derivatives are treated as `subagent`
405
+
406
+ This ensures custom workers recover with the same execution constraints as their base role.
407
+
362
408
  ### Custom Models
363
409
 
364
410
  Override models for specific agents:
@@ -1,6 +1,7 @@
1
1
  export { FeatureService } from './featureService.js';
2
2
  export { PlanService } from './planService.js';
3
3
  export { TaskService } from './taskService.js';
4
+ export type { SyncOptions } from './taskService.js';
4
5
  export { SubtaskService } from './subtaskService.js';
5
6
  export { WorktreeService, createWorktreeService } from './worktreeService.js';
6
7
  export type { WorktreeInfo, DiffResult, ApplyResult, CommitResult, MergeResult, WorktreeConfig } from './worktreeService.js';
@@ -1,10 +1,17 @@
1
- import { SessionInfo } from '../types.js';
1
+ import type { SessionInfo } from '../types.js';
2
2
  export declare class SessionService {
3
3
  private projectRoot;
4
4
  constructor(projectRoot: string);
5
+ private applySessionPatch;
5
6
  private getSessionsPath;
6
7
  private getSessions;
7
8
  private saveSessions;
9
+ private getGlobalSessions;
10
+ private saveGlobalSessions;
11
+ private updateGlobalSessions;
12
+ trackGlobal(sessionId: string, patch?: Partial<SessionInfo>): SessionInfo;
13
+ bindFeature(sessionId: string, featureName: string, patch?: Partial<SessionInfo>): SessionInfo;
14
+ getGlobal(sessionId: string): SessionInfo | undefined;
8
15
  track(featureName: string, sessionId: string, taskFolder?: string): SessionInfo;
9
16
  setMaster(featureName: string, sessionId: string): void;
10
17
  getMaster(featureName: string): string | undefined;
@@ -1,5 +1,5 @@
1
1
  import { LockOptions } from '../utils/paths.js';
2
- import { TaskStatus, TaskStatusType, TasksSyncResult, TaskInfo, Subtask, SubtaskType, WorkerSession } from '../types.js';
2
+ import { TaskStatus, TaskStatusType, TasksSyncResult, TaskInfo, Subtask, SubtaskType, WorkerSession, ManualTaskMetadata } from '../types.js';
3
3
  /** Current schema version for TaskStatus */
4
4
  export declare const TASK_STATUS_SCHEMA_VERSION = 1;
5
5
  /** Fields that can be updated by background workers without clobbering completion-owned fields */
@@ -13,17 +13,21 @@ export interface CompletionFields {
13
13
  summary?: string;
14
14
  completedAt?: string;
15
15
  }
16
+ export interface SyncOptions {
17
+ refreshPending?: boolean;
18
+ }
16
19
  export declare class TaskService {
17
20
  private projectRoot;
18
21
  constructor(projectRoot: string);
19
- sync(featureName: string): TasksSyncResult;
22
+ sync(featureName: string, options?: SyncOptions): TasksSyncResult;
20
23
  /**
21
24
  * Create a manual task with auto-incrementing index.
22
25
  * Folder format: "01-task-name", "02-task-name", etc.
23
26
  * Index ensures alphabetical sort = chronological order.
24
27
  */
25
- create(featureName: string, name: string, order?: number): string;
28
+ create(featureName: string, name: string, order?: number, metadata?: ManualTaskMetadata): string;
26
29
  private createFromPlan;
30
+ private refreshPendingTask;
27
31
  buildSpecContent(params: {
28
32
  featureName: string;
29
33
  task: {
@@ -72,6 +76,7 @@ export declare class TaskService {
72
76
  */
73
77
  private detectCycles;
74
78
  writeSpec(featureName: string, taskFolder: string, content: string): string;
79
+ readSpec(featureName: string, taskFolder: string): string | null;
75
80
  /**
76
81
  * Update task status with locked atomic write.
77
82
  * Uses file locking to prevent race conditions between concurrent updates.
@@ -120,5 +125,6 @@ export declare class TaskService {
120
125
  readSubtaskReport(featureName: string, taskFolder: string, subtaskId: string): string | null;
121
126
  private listSubtaskFolders;
122
127
  private findSubtaskFolder;
128
+ private buildManualTaskSpec;
123
129
  private slugify;
124
130
  }
@@ -50,6 +50,16 @@ export interface WorkerSession {
50
50
  /** Number of messages exchanged in session */
51
51
  messageCount?: number;
52
52
  }
53
+ export interface ManualTaskMetadata {
54
+ goal?: string;
55
+ description?: string;
56
+ acceptanceCriteria?: string[];
57
+ references?: string[];
58
+ files?: string[];
59
+ reason?: string;
60
+ source?: 'review' | 'operator' | 'ad_hoc';
61
+ dependsOn?: string[];
62
+ }
53
63
  export interface TaskStatus {
54
64
  /** Schema version for forward compatibility (default: 1) */
55
65
  schemaVersion?: number;
@@ -71,6 +81,8 @@ export interface TaskStatus {
71
81
  * Resolved from plan.md dependency annotations during hive_tasks_sync.
72
82
  */
73
83
  dependsOn?: string[];
84
+ /** Structured metadata for manual tasks */
85
+ metadata?: ManualTaskMetadata;
74
86
  }
75
87
  export type ReviewDocument = 'plan' | 'overview';
76
88
  export interface ReviewThread {
@@ -120,9 +132,17 @@ export interface ContextFile {
120
132
  content: string;
121
133
  updatedAt: string;
122
134
  }
135
+ export type SessionKind = 'primary' | 'subagent' | 'task-worker' | 'unknown';
123
136
  export interface SessionInfo {
124
137
  sessionId: string;
138
+ featureName?: string;
125
139
  taskFolder?: string;
140
+ agent?: string;
141
+ baseAgent?: string;
142
+ sessionKind?: SessionKind;
143
+ workerPromptPath?: string;
144
+ directivePrompt?: string;
145
+ replayDirectivePending?: boolean;
126
146
  startedAt: string;
127
147
  lastActiveAt: string;
128
148
  messageCount?: number;
@@ -3,6 +3,7 @@ export declare function normalizePath(filePath: string): string;
3
3
  export declare function getHivePath(projectRoot: string): string;
4
4
  export declare function getFeaturesPath(projectRoot: string): string;
5
5
  export declare function getActiveFeaturePath(projectRoot: string): string;
6
+ export declare function getGlobalSessionsPath(projectRoot: string): string;
6
7
  export declare function listFeatureDirectories(projectRoot: string): FeatureDirectoryInfo[];
7
8
  export declare function resolveFeatureDirectoryName(projectRoot: string, featureName: string): string;
8
9
  export declare function getNextIndexedFeatureDirectoryName(projectRoot: string, featureName: string): string;