@revealui/harnesses 0.1.8 → 0.1.9
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 +6 -6
- package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +0 -1
- package/dist/{chunk-DGQ5OB6L.js → chunk-ANX4L2PF.js} +273 -2
- package/dist/{chunk-4F4ANKIZ.js → chunk-Y4FFO3TO.js} +27 -7
- package/dist/{chunk-6E2BKO6U.js → chunk-YYAYTCRM.js} +1330 -354
- package/dist/{chunk-PROC6EJC.js → chunk-ZNIQELKZ.js} +185 -341
- package/dist/cli.js +5 -6
- package/dist/content/index.d.ts +1 -1
- package/dist/content/index.js +2 -3
- package/dist/index-w5ashbfb.d.ts +266 -0
- package/dist/index.d.ts +539 -85
- package/dist/index.js +34 -11
- package/dist/storage/index.d.ts +1 -170
- package/dist/storage/index.js +2 -3
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +0 -1
- package/dist/workboard/index.d.ts +26 -14
- package/dist/workboard/index.js +2 -3
- package/package.json +23 -6
- package/dist/chunk-4F4ANKIZ.js.map +0 -1
- package/dist/chunk-6E2BKO6U.js.map +0 -1
- package/dist/chunk-DGQ5OB6L.js.map +0 -1
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-PROC6EJC.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/content/index.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/storage/index.js.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/workboard/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,35 +1,47 @@
|
|
|
1
1
|
import {
|
|
2
|
-
ClaudeCodeAdapter,
|
|
3
|
-
CursorAdapter,
|
|
4
2
|
HarnessCoordinator,
|
|
5
3
|
HarnessRegistry,
|
|
6
4
|
RpcServer,
|
|
5
|
+
TOOL_PROFILES,
|
|
6
|
+
VAUGHN_EVENTS,
|
|
7
|
+
VAUGHN_VERSION,
|
|
8
|
+
VaughnEventNormalizer,
|
|
7
9
|
autoDetectHarnesses,
|
|
8
10
|
checkHarnessesLicense,
|
|
11
|
+
claudeSettingsToVaughnConfig,
|
|
12
|
+
createDefaultCapabilities,
|
|
13
|
+
createEventEnvelope,
|
|
9
14
|
diffAllConfigs,
|
|
10
15
|
diffConfig,
|
|
11
16
|
findAllHarnessProcesses,
|
|
12
17
|
findClaudeCodeSockets,
|
|
13
18
|
findHarnessProcesses,
|
|
14
19
|
findProcesses,
|
|
20
|
+
generateAllConfigs,
|
|
15
21
|
getConfigurableHarnesses,
|
|
22
|
+
getDegradationStrategy,
|
|
16
23
|
getLocalConfigPath,
|
|
17
24
|
getRootConfigPath,
|
|
18
25
|
syncAllConfigs,
|
|
19
26
|
syncConfig,
|
|
20
|
-
validateConfigJson
|
|
21
|
-
|
|
27
|
+
validateConfigJson,
|
|
28
|
+
vaughnConfigToAgentsMd,
|
|
29
|
+
vaughnConfigToClaudeSettings,
|
|
30
|
+
vaughnConfigToCursorrules,
|
|
31
|
+
vaughnEventEnvelopeSchema,
|
|
32
|
+
vaughnEventSchema
|
|
33
|
+
} from "./chunk-YYAYTCRM.js";
|
|
22
34
|
import {
|
|
23
35
|
buildManifest,
|
|
24
36
|
diffContent,
|
|
25
37
|
generateContent,
|
|
26
38
|
listContent,
|
|
27
39
|
validateManifest
|
|
28
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-ZNIQELKZ.js";
|
|
29
41
|
import {
|
|
30
42
|
DaemonStore,
|
|
31
43
|
SCHEMA_SQL
|
|
32
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-ANX4L2PF.js";
|
|
33
45
|
import {
|
|
34
46
|
WorkboardManager,
|
|
35
47
|
acquireLock,
|
|
@@ -40,22 +52,27 @@ import {
|
|
|
40
52
|
releaseLock,
|
|
41
53
|
withLock,
|
|
42
54
|
withLockAsync
|
|
43
|
-
} from "./chunk-
|
|
44
|
-
import "./chunk-
|
|
55
|
+
} from "./chunk-Y4FFO3TO.js";
|
|
56
|
+
import "./chunk-3RG5ZIWI.js";
|
|
45
57
|
export {
|
|
46
|
-
ClaudeCodeAdapter,
|
|
47
|
-
CursorAdapter,
|
|
48
58
|
DaemonStore,
|
|
49
59
|
HarnessCoordinator,
|
|
50
60
|
HarnessRegistry,
|
|
51
61
|
RpcServer,
|
|
52
62
|
SCHEMA_SQL,
|
|
63
|
+
TOOL_PROFILES,
|
|
64
|
+
VAUGHN_EVENTS,
|
|
65
|
+
VAUGHN_VERSION,
|
|
66
|
+
VaughnEventNormalizer,
|
|
53
67
|
WorkboardManager,
|
|
54
68
|
acquireLock,
|
|
55
69
|
atomicWriteSync,
|
|
56
70
|
autoDetectHarnesses,
|
|
57
71
|
buildManifest,
|
|
58
72
|
checkHarnessesLicense,
|
|
73
|
+
claudeSettingsToVaughnConfig,
|
|
74
|
+
createDefaultCapabilities,
|
|
75
|
+
createEventEnvelope,
|
|
59
76
|
deriveSessionId,
|
|
60
77
|
detectSessionType,
|
|
61
78
|
diffAllConfigs,
|
|
@@ -65,8 +82,10 @@ export {
|
|
|
65
82
|
findClaudeCodeSockets,
|
|
66
83
|
findHarnessProcesses,
|
|
67
84
|
findProcesses,
|
|
85
|
+
generateAllConfigs,
|
|
68
86
|
generateContent,
|
|
69
87
|
getConfigurableHarnesses,
|
|
88
|
+
getDegradationStrategy,
|
|
70
89
|
getLocalConfigPath,
|
|
71
90
|
getRootConfigPath,
|
|
72
91
|
listContent,
|
|
@@ -76,7 +95,11 @@ export {
|
|
|
76
95
|
syncConfig,
|
|
77
96
|
validateConfigJson,
|
|
78
97
|
validateManifest,
|
|
98
|
+
vaughnConfigToAgentsMd,
|
|
99
|
+
vaughnConfigToClaudeSettings,
|
|
100
|
+
vaughnConfigToCursorrules,
|
|
101
|
+
vaughnEventEnvelopeSchema,
|
|
102
|
+
vaughnEventSchema,
|
|
79
103
|
withLock,
|
|
80
104
|
withLockAsync
|
|
81
105
|
};
|
|
82
|
-
//# sourceMappingURL=index.js.map
|
package/dist/storage/index.d.ts
CHANGED
|
@@ -1,170 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* PGlite schema for the RevDev Harness daemon.
|
|
3
|
-
*
|
|
4
|
-
* Five tables provide persistent state for multi-agent coordination:
|
|
5
|
-
* - agent_sessions: active and historical agent sessions
|
|
6
|
-
* - agent_messages: inter-agent mailbox (point-to-point + broadcast)
|
|
7
|
-
* - file_reservations: advisory file locks with CAS semantics
|
|
8
|
-
* - tasks: claimable work items with CAS ownership
|
|
9
|
-
* - events: append-only event log for audit trail
|
|
10
|
-
*
|
|
11
|
-
* Uses raw SQL (no Drizzle ORM) to keep the daemon dependency-free.
|
|
12
|
-
* PGlite runs in-process — no external database needed.
|
|
13
|
-
*/
|
|
14
|
-
/** SQL statements to initialize the daemon database. */
|
|
15
|
-
declare const SCHEMA_SQL = "\n CREATE TABLE IF NOT EXISTS agent_sessions (\n id TEXT PRIMARY KEY,\n env TEXT NOT NULL DEFAULT '',\n task TEXT NOT NULL DEFAULT '(starting)',\n files TEXT NOT NULL DEFAULT '',\n pid INTEGER,\n started_at TIMESTAMP NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMP NOT NULL DEFAULT NOW(),\n ended_at TIMESTAMP,\n exit_summary TEXT\n );\n\n CREATE TABLE IF NOT EXISTS agent_messages (\n id SERIAL PRIMARY KEY,\n from_agent TEXT NOT NULL,\n to_agent TEXT NOT NULL,\n subject TEXT NOT NULL DEFAULT '',\n body TEXT NOT NULL DEFAULT '',\n read BOOLEAN NOT NULL DEFAULT FALSE,\n created_at TIMESTAMP NOT NULL DEFAULT NOW()\n );\n\n CREATE INDEX IF NOT EXISTS idx_messages_to_unread\n ON agent_messages (to_agent, read) WHERE read = FALSE;\n\n CREATE TABLE IF NOT EXISTS file_reservations (\n file_path TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL,\n reserved_at TIMESTAMP NOT NULL DEFAULT NOW(),\n expires_at TIMESTAMP NOT NULL,\n reason TEXT NOT NULL DEFAULT ''\n );\n\n CREATE INDEX IF NOT EXISTS idx_reservations_agent\n ON file_reservations (agent_id);\n\n CREATE TABLE IF NOT EXISTS tasks (\n id TEXT PRIMARY KEY,\n description TEXT NOT NULL DEFAULT '',\n status TEXT NOT NULL DEFAULT 'open',\n owner TEXT,\n claimed_at TIMESTAMP,\n completed_at TIMESTAMP,\n created_at TIMESTAMP NOT NULL DEFAULT NOW()\n );\n\n CREATE INDEX IF NOT EXISTS idx_tasks_status\n ON tasks (status);\n\n CREATE INDEX IF NOT EXISTS idx_tasks_owner\n ON tasks (owner) WHERE owner IS NOT NULL;\n\n CREATE TABLE IF NOT EXISTS events (\n id SERIAL PRIMARY KEY,\n agent_id TEXT NOT NULL,\n event_type TEXT NOT NULL,\n payload JSONB NOT NULL DEFAULT '{}',\n created_at TIMESTAMP NOT NULL DEFAULT NOW()\n );\n\n CREATE INDEX IF NOT EXISTS idx_events_agent\n ON events (agent_id, created_at DESC);\n";
|
|
16
|
-
/** Session row shape. */
|
|
17
|
-
interface AgentSession {
|
|
18
|
-
id: string;
|
|
19
|
-
env: string;
|
|
20
|
-
task: string;
|
|
21
|
-
files: string;
|
|
22
|
-
pid: number | null;
|
|
23
|
-
started_at: string;
|
|
24
|
-
updated_at: string;
|
|
25
|
-
ended_at: string | null;
|
|
26
|
-
exit_summary: string | null;
|
|
27
|
-
}
|
|
28
|
-
/** Message row shape. */
|
|
29
|
-
interface AgentMessage {
|
|
30
|
-
id: number;
|
|
31
|
-
from_agent: string;
|
|
32
|
-
to_agent: string;
|
|
33
|
-
subject: string;
|
|
34
|
-
body: string;
|
|
35
|
-
read: boolean;
|
|
36
|
-
created_at: string;
|
|
37
|
-
}
|
|
38
|
-
/** File reservation row shape. */
|
|
39
|
-
interface FileReservation {
|
|
40
|
-
file_path: string;
|
|
41
|
-
agent_id: string;
|
|
42
|
-
reserved_at: string;
|
|
43
|
-
expires_at: string;
|
|
44
|
-
reason: string;
|
|
45
|
-
}
|
|
46
|
-
/** Task row shape. */
|
|
47
|
-
interface AgentTask {
|
|
48
|
-
id: string;
|
|
49
|
-
description: string;
|
|
50
|
-
status: 'open' | 'claimed' | 'completed';
|
|
51
|
-
owner: string | null;
|
|
52
|
-
claimed_at: string | null;
|
|
53
|
-
completed_at: string | null;
|
|
54
|
-
created_at: string;
|
|
55
|
-
}
|
|
56
|
-
/** Event row shape. */
|
|
57
|
-
interface DaemonEvent {
|
|
58
|
-
id: number;
|
|
59
|
-
agent_id: string;
|
|
60
|
-
event_type: string;
|
|
61
|
-
payload: Record<string, unknown>;
|
|
62
|
-
created_at: string;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* DaemonStore — persistent state for the RevDev Harness daemon.
|
|
67
|
-
*
|
|
68
|
-
* Backed by PGlite (in-process PostgreSQL). The database file lives at
|
|
69
|
-
* ~/.local/share/revealui/harness.db and survives daemon restarts.
|
|
70
|
-
*
|
|
71
|
-
* All methods are async (PGlite queries return promises).
|
|
72
|
-
*/
|
|
73
|
-
|
|
74
|
-
/** Configuration for DaemonStore. */
|
|
75
|
-
interface DaemonStoreConfig {
|
|
76
|
-
/** PGlite data directory (default: ~/.local/share/revealui/harness.db) */
|
|
77
|
-
dataDir: string;
|
|
78
|
-
}
|
|
79
|
-
declare class DaemonStore {
|
|
80
|
-
private db;
|
|
81
|
-
private readonly dataDir;
|
|
82
|
-
constructor(config: DaemonStoreConfig);
|
|
83
|
-
/** Initialize PGlite and create tables. */
|
|
84
|
-
init(): Promise<void>;
|
|
85
|
-
/** Shut down the database. */
|
|
86
|
-
close(): Promise<void>;
|
|
87
|
-
private getDb;
|
|
88
|
-
/** Register or update an agent session. */
|
|
89
|
-
registerSession(session: {
|
|
90
|
-
id: string;
|
|
91
|
-
env: string;
|
|
92
|
-
task?: string;
|
|
93
|
-
pid?: number;
|
|
94
|
-
}): Promise<AgentSession>;
|
|
95
|
-
/** Update a session's task and/or files. */
|
|
96
|
-
updateSession(id: string, updates: {
|
|
97
|
-
task?: string;
|
|
98
|
-
files?: string;
|
|
99
|
-
}): Promise<AgentSession | null>;
|
|
100
|
-
/** End a session (mark ended_at, record exit summary). */
|
|
101
|
-
endSession(id: string, exitSummary?: string): Promise<void>;
|
|
102
|
-
/** Get all active sessions (ended_at IS NULL). */
|
|
103
|
-
getActiveSessions(): Promise<AgentSession[]>;
|
|
104
|
-
/** Get session history for an agent (most recent first). */
|
|
105
|
-
getSessionHistory(agentId: string, limit: number): Promise<AgentSession[]>;
|
|
106
|
-
/** Send a message from one agent to another. */
|
|
107
|
-
sendMessage(msg: {
|
|
108
|
-
fromAgent: string;
|
|
109
|
-
toAgent: string;
|
|
110
|
-
subject: string;
|
|
111
|
-
body?: string;
|
|
112
|
-
}): Promise<AgentMessage>;
|
|
113
|
-
/** Broadcast a message to all active agents (except sender). */
|
|
114
|
-
broadcastMessage(msg: {
|
|
115
|
-
fromAgent: string;
|
|
116
|
-
subject: string;
|
|
117
|
-
body?: string;
|
|
118
|
-
}): Promise<number>;
|
|
119
|
-
/** Get unread messages for an agent. */
|
|
120
|
-
getInbox(agentId: string, unreadOnly: boolean): Promise<AgentMessage[]>;
|
|
121
|
-
/** Mark messages as read. */
|
|
122
|
-
markRead(messageIds: number[]): Promise<void>;
|
|
123
|
-
/** Reserve a file for an agent (CAS: fails if already reserved by another). */
|
|
124
|
-
reserveFile(reservation: {
|
|
125
|
-
filePath: string;
|
|
126
|
-
agentId: string;
|
|
127
|
-
ttlSeconds: number;
|
|
128
|
-
reason?: string;
|
|
129
|
-
}): Promise<{
|
|
130
|
-
success: boolean;
|
|
131
|
-
holder?: string;
|
|
132
|
-
}>;
|
|
133
|
-
/** Check who holds a file reservation. */
|
|
134
|
-
checkReservation(filePath: string): Promise<FileReservation | null>;
|
|
135
|
-
/** Release all reservations held by an agent. */
|
|
136
|
-
releaseAllReservations(agentId: string): Promise<number>;
|
|
137
|
-
/** Get all reservations for an agent. */
|
|
138
|
-
getReservations(agentId: string): Promise<FileReservation[]>;
|
|
139
|
-
/** Create a new task. */
|
|
140
|
-
createTask(task: {
|
|
141
|
-
id: string;
|
|
142
|
-
description: string;
|
|
143
|
-
}): Promise<AgentTask>;
|
|
144
|
-
/** Claim a task atomically (CAS: fails if already claimed by another agent). */
|
|
145
|
-
claimTask(taskId: string, agentId: string): Promise<{
|
|
146
|
-
success: boolean;
|
|
147
|
-
owner?: string;
|
|
148
|
-
}>;
|
|
149
|
-
/** Complete a task (only the owner can complete it). */
|
|
150
|
-
completeTask(taskId: string, agentId: string): Promise<boolean>;
|
|
151
|
-
/** Release a claimed task back to open (only the owner can release). */
|
|
152
|
-
releaseTask(taskId: string, agentId: string): Promise<boolean>;
|
|
153
|
-
/** List tasks, optionally filtered by status and/or owner. */
|
|
154
|
-
listTasks(filter?: {
|
|
155
|
-
status?: string;
|
|
156
|
-
owner?: string;
|
|
157
|
-
}): Promise<AgentTask[]>;
|
|
158
|
-
/** Append an event to the audit log. */
|
|
159
|
-
logEvent(event: {
|
|
160
|
-
agentId: string;
|
|
161
|
-
eventType: string;
|
|
162
|
-
payload?: Record<string, unknown>;
|
|
163
|
-
}): Promise<DaemonEvent>;
|
|
164
|
-
/** Get recent events (newest first). */
|
|
165
|
-
getRecentEvents(limit: number): Promise<DaemonEvent[]>;
|
|
166
|
-
/** Prune events older than a given number of days. */
|
|
167
|
-
pruneEvents(olderThanDays: number): Promise<number>;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export { type AgentMessage, type AgentSession, type AgentTask, type DaemonEvent, DaemonStore, type DaemonStoreConfig, type FileReservation, SCHEMA_SQL };
|
|
1
|
+
export { A as AgentMessage, a as AgentSession, b as AgentTask, c as DaemonEvent, D as DaemonStore, d as DaemonStoreConfig, F as FileReservation, S as SCHEMA_SQL } from '../index-w5ashbfb.js';
|
package/dist/storage/index.js
CHANGED
package/dist/types/index.d.ts
CHANGED
|
@@ -122,7 +122,7 @@ interface HealthCheckResult {
|
|
|
122
122
|
* Contract every AI harness adapter must satisfy.
|
|
123
123
|
* Mirrors EditorAdapter from packages/editors.
|
|
124
124
|
*
|
|
125
|
-
* AI tools are external executables
|
|
125
|
+
* AI tools are external executables - never linked libraries.
|
|
126
126
|
* Communication is data-only: commands in, results out.
|
|
127
127
|
*/
|
|
128
128
|
interface HarnessAdapter {
|
|
@@ -132,7 +132,7 @@ interface HarnessAdapter {
|
|
|
132
132
|
readonly name: string;
|
|
133
133
|
/** Returns the static capability set for this harness */
|
|
134
134
|
getCapabilities(): HarnessCapabilities;
|
|
135
|
-
/** Returns live info (version, etc.)
|
|
135
|
+
/** Returns live info (version, etc.) - may shell out */
|
|
136
136
|
getInfo(): Promise<HarnessInfo>;
|
|
137
137
|
/** True if the harness executable is on PATH and accessible */
|
|
138
138
|
isAvailable(): Promise<boolean>;
|
package/dist/types/index.js
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* WorkboardProtocol
|
|
2
|
+
* WorkboardProtocol - types for the multi-agent coordination workboard (v2).
|
|
3
3
|
*
|
|
4
4
|
* The workboard (.claude/workboard.md) is the shared coordination primitive
|
|
5
5
|
* that lets multiple AI coding agents work safely in parallel.
|
|
6
6
|
*
|
|
7
7
|
* v2 adds a task board with claiming protocol:
|
|
8
|
-
* Agents
|
|
9
|
-
* Tasks
|
|
10
|
-
* Blocked
|
|
11
|
-
* Done
|
|
12
|
-
* Log
|
|
8
|
+
* Agents - who's running (hook-managed)
|
|
9
|
+
* Tasks - what needs doing (agent-managed, claimable)
|
|
10
|
+
* Blocked - waiting on external action
|
|
11
|
+
* Done - recently completed
|
|
12
|
+
* Log - audit trail
|
|
13
13
|
*
|
|
14
14
|
* Status values: available | claimed | partial | blocked | done
|
|
15
15
|
* Priority values: P0 | P1 | P2 | P3
|
|
@@ -86,9 +86,9 @@ interface WorkboardState {
|
|
|
86
86
|
preamble: string[];
|
|
87
87
|
/** Active agent sessions */
|
|
88
88
|
agents: WorkboardAgent[];
|
|
89
|
-
/** Task board
|
|
89
|
+
/** Task board - claimable work items */
|
|
90
90
|
tasks: WorkboardTask[];
|
|
91
|
-
/** Blocked tasks
|
|
91
|
+
/** Blocked tasks - waiting on external action */
|
|
92
92
|
blocked: WorkboardBlockedTask[];
|
|
93
93
|
/** Recently completed tasks */
|
|
94
94
|
done: WorkboardDoneTask[];
|
|
@@ -118,7 +118,7 @@ interface WorkboardEntry {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/**
|
|
121
|
-
* WorkboardManager
|
|
121
|
+
* WorkboardManager - reads, parses, and writes .claude/workboard.md (v2).
|
|
122
122
|
*
|
|
123
123
|
* Supports both v1 (Sessions/Recent) and v2 (Agents/Tasks/Blocked/Done/Log)
|
|
124
124
|
* formats for backward compatibility. Always serializes to v2.
|
|
@@ -195,13 +195,25 @@ declare function atomicWriteSync(filePath: string, content: string): void;
|
|
|
195
195
|
*/
|
|
196
196
|
declare function lockPathFor(workboardPath: string): string;
|
|
197
197
|
|
|
198
|
-
/** Type of session detected from the runtime environment. */
|
|
199
|
-
type SessionType = 'zed' | 'cursor' | 'terminal';
|
|
200
198
|
/**
|
|
201
|
-
*
|
|
202
|
-
* or a plain terminal session by walking the parent process chain.
|
|
199
|
+
* Type of session detected from the runtime environment.
|
|
203
200
|
*
|
|
204
|
-
*
|
|
201
|
+
* Extended in VAUGHN Phase 2a to support tool-prefixed identifiers
|
|
202
|
+
* (e.g. 'claude', 'codex') alongside the original IDE-based types.
|
|
203
|
+
*/
|
|
204
|
+
type SessionType = 'claude' | 'codex' | 'zed' | 'cursor' | 'terminal';
|
|
205
|
+
/**
|
|
206
|
+
* Detects the session type using a 7-tier VAUGHN identity cascade.
|
|
207
|
+
*
|
|
208
|
+
* | Priority | Source |
|
|
209
|
+
* |----------|---------------------------------------------------|
|
|
210
|
+
* | 1 | VAUGHN_AGENT_ID env var (explicit override) |
|
|
211
|
+
* | 2 | CLAUDE_AGENT_ROLE env var (tool-specific) |
|
|
212
|
+
* | 3 | Session cache (/tmp/vaughn-session-<ppid>.id) |
|
|
213
|
+
* | 4 | Process tree walk (/proc for tool binaries) |
|
|
214
|
+
* | 5 | IDE detection (Zed, Cursor) |
|
|
215
|
+
* | 6 | TERM_PROGRAM env var |
|
|
216
|
+
* | 7 | Generic 'terminal' fallback |
|
|
205
217
|
*/
|
|
206
218
|
declare function detectSessionType(): SessionType;
|
|
207
219
|
/**
|
package/dist/workboard/index.js
CHANGED
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
unregisterSession,
|
|
11
11
|
withLock,
|
|
12
12
|
withLockAsync
|
|
13
|
-
} from "../chunk-
|
|
14
|
-
import "../chunk-
|
|
13
|
+
} from "../chunk-Y4FFO3TO.js";
|
|
14
|
+
import "../chunk-3RG5ZIWI.js";
|
|
15
15
|
export {
|
|
16
16
|
WorkboardManager,
|
|
17
17
|
acquireLock,
|
|
@@ -25,4 +25,3 @@ export {
|
|
|
25
25
|
withLock,
|
|
26
26
|
withLockAsync
|
|
27
27
|
};
|
|
28
|
-
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revealui/harnesses",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "[Pro] AI harness integration system - adapters, daemon, workboard coordination, and JSON-RPC server",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -12,16 +12,16 @@
|
|
|
12
12
|
"revealui-harnesses": "./dist/cli.js"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@electric-sql/pglite": "^0.
|
|
15
|
+
"@electric-sql/pglite": "^0.4.3",
|
|
16
16
|
"zod": "^4.3.6",
|
|
17
|
-
"@revealui/core": "0.5.
|
|
17
|
+
"@revealui/core": "0.5.6"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@types/node": "^25.
|
|
20
|
+
"@types/node": "^25.5.2",
|
|
21
21
|
"tsup": "^8.5.1",
|
|
22
22
|
"typescript": "^6.0.2",
|
|
23
|
-
"vitest": "^4.
|
|
24
|
-
"dev": "0.0
|
|
23
|
+
"vitest": "^4.1.3",
|
|
24
|
+
"@revealui/dev": "0.1.0"
|
|
25
25
|
},
|
|
26
26
|
"engines": {
|
|
27
27
|
"node": ">=24.13.0"
|
|
@@ -46,6 +46,10 @@
|
|
|
46
46
|
"./storage": {
|
|
47
47
|
"types": "./dist/storage/index.d.ts",
|
|
48
48
|
"import": "./dist/storage/index.js"
|
|
49
|
+
},
|
|
50
|
+
"./vaughn": {
|
|
51
|
+
"types": "./dist/vaughn/index.d.ts",
|
|
52
|
+
"import": "./dist/vaughn/index.js"
|
|
49
53
|
}
|
|
50
54
|
},
|
|
51
55
|
"files": [
|
|
@@ -63,6 +67,19 @@
|
|
|
63
67
|
},
|
|
64
68
|
"type": "module",
|
|
65
69
|
"types": "./dist/index.d.ts",
|
|
70
|
+
"homepage": "https://revealui.com",
|
|
71
|
+
"author": "RevealUI Studio <founder@revealui.com>",
|
|
72
|
+
"bugs": {
|
|
73
|
+
"url": "https://github.com/RevealUIStudio/revealui/issues"
|
|
74
|
+
},
|
|
75
|
+
"keywords": [
|
|
76
|
+
"revealui",
|
|
77
|
+
"harness",
|
|
78
|
+
"ai",
|
|
79
|
+
"workboard",
|
|
80
|
+
"json-rpc",
|
|
81
|
+
"agent"
|
|
82
|
+
],
|
|
66
83
|
"scripts": {
|
|
67
84
|
"build": "tsup",
|
|
68
85
|
"clean": "rm -rf dist",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/workboard/file-lock.ts","../src/workboard/session-identity.ts","../src/workboard/workboard-manager.ts"],"sourcesContent":["import {\n closeSync,\n openSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n writeSync,\n} from 'node:fs';\n\nconst DEFAULT_TIMEOUT_MS = 2000;\nconst RETRY_MS = 50;\n\n/**\n * Check whether a process is still running.\n * Uses signal 0 which doesn't actually send a signal — just checks existence.\n */\nfunction isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Acquire an exclusive file lock using O_EXCL (kernel-level atomic create).\n * Writes the current PID to the lock file for dead-holder detection.\n *\n * Spins with a 50ms interval until timeout. If the current lock holder\n * is dead (process no longer running), the lock is stolen.\n *\n * @returns true if the lock was acquired, false on timeout\n */\nexport function acquireLock(lockPath: string, timeoutMs = DEFAULT_TIMEOUT_MS): boolean {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n try {\n const fd = openSync(lockPath, 'wx');\n writeSync(fd, String(process.pid));\n closeSync(fd);\n return true;\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code !== 'EEXIST') return false;\n // Lock exists — check if holder is alive\n try {\n const holderPid = Number.parseInt(readFileSync(lockPath, 'utf8').trim(), 10);\n if (!(Number.isNaN(holderPid) || isPidAlive(holderPid))) {\n // Holder crashed — steal the lock\n unlinkSync(lockPath);\n continue;\n }\n } catch {\n // Lock file disappeared between checks — retry immediately\n continue;\n }\n // Busy-wait\n const spinUntil = Date.now() + RETRY_MS;\n while (Date.now() < spinUntil) {\n /* spin */\n }\n }\n }\n return false;\n}\n\n/**\n * Release a file lock. Swallows ENOENT (already released).\n */\nexport function releaseLock(lockPath: string): void {\n try {\n unlinkSync(lockPath);\n } catch {\n // Already released or never acquired — non-fatal\n }\n}\n\n/**\n * Execute a synchronous function while holding the file lock.\n * The lock is always released, even if fn throws.\n */\nexport function withLock<T>(lockPath: string, fn: () => T, timeoutMs?: number): T {\n const acquired = acquireLock(lockPath, timeoutMs);\n if (!acquired) {\n throw new Error(\n `Failed to acquire lock: ${lockPath} (timeout ${timeoutMs ?? DEFAULT_TIMEOUT_MS}ms)`,\n );\n }\n try {\n return fn();\n } finally {\n releaseLock(lockPath);\n }\n}\n\n/**\n * Execute an async function while holding the file lock.\n * The lock is always released, even if fn throws.\n */\nexport async function withLockAsync<T>(\n lockPath: string,\n fn: () => Promise<T>,\n timeoutMs?: number,\n): Promise<T> {\n const acquired = acquireLock(lockPath, timeoutMs);\n if (!acquired) {\n throw new Error(\n `Failed to acquire lock: ${lockPath} (timeout ${timeoutMs ?? DEFAULT_TIMEOUT_MS}ms)`,\n );\n }\n try {\n return await fn();\n } finally {\n releaseLock(lockPath);\n }\n}\n\n/**\n * Write a file atomically: write to a temporary file, then rename.\n * rename() on the same filesystem is atomic at the kernel level.\n */\nexport function atomicWriteSync(filePath: string, content: string): void {\n const suffix = `${process.pid}.${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;\n const tmpPath = `${filePath}.tmp.${suffix}`;\n writeFileSync(tmpPath, content, 'utf8');\n renameSync(tmpPath, filePath);\n}\n\n/**\n * Derive a lock path from a workboard path (.md → .lock).\n */\nexport function lockPathFor(workboardPath: string): string {\n return workboardPath.replace(/\\.md$/, '.lock');\n}\n","import { readFileSync } from 'node:fs';\n\n/** Type of session detected from the runtime environment. */\nexport type SessionType = 'zed' | 'cursor' | 'terminal';\n\n/**\n * Detects whether the current process is running inside an AI tool (Zed, Cursor)\n * or a plain terminal session by walking the parent process chain.\n *\n * Uses /proc/<pid>/cmdline on Linux/WSL. Falls back to TERM_PROGRAM env var.\n */\nexport function detectSessionType(): SessionType {\n // Walk parent process chain looking for known AI tool process names.\n try {\n let pid = process.ppid;\n for (let depth = 0; depth < 8; depth++) {\n if (!pid || pid <= 1) break;\n const cmdline = readFileSync(`/proc/${pid}/cmdline`, 'utf8')\n .replace(/\\0/g, ' ')\n .toLowerCase();\n if (cmdline.includes('zed')) return 'zed';\n if (cmdline.includes('cursor')) return 'cursor';\n const status = readFileSync(`/proc/${pid}/status`, 'utf8');\n const m = status.match(/PPid:\\s+(\\d+)/);\n if (!m) break;\n pid = parseInt(m[1] ?? '0', 10);\n }\n } catch {\n // /proc not available (macOS, Windows non-WSL).\n }\n\n // Fallback: TERM_PROGRAM env var (set by some terminal emulators and IDEs).\n const termProgram = (process.env.TERM_PROGRAM ?? '').toLowerCase();\n if (termProgram.includes('zed')) return 'zed';\n if (termProgram.includes('cursor')) return 'cursor';\n\n return 'terminal';\n}\n\n/**\n * Derives a session ID (e.g. \"zed-1\", \"terminal-2\") given a type and a list\n * of existing session IDs already in the workboard.\n *\n * Picks the next available numeric suffix to avoid collisions.\n */\nexport function deriveSessionId(type: SessionType, existingIds: string[]): string {\n const matching = existingIds\n .filter((id) => id.startsWith(`${type}-`))\n .map((id) => parseInt(id.split('-')[1] ?? '0', 10))\n .filter((n) => !Number.isNaN(n));\n\n const maxN = matching.length > 0 ? Math.max(...matching) : 0;\n return `${type}-${maxN + 1}`;\n}\n","import { readFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { atomicWriteSync, lockPathFor, withLock, withLockAsync } from './file-lock.js';\nimport type {\n ConflictResult,\n WorkboardAgent,\n WorkboardState,\n WorkboardTask,\n} from './workboard-protocol.js';\n\nconst STALE_THRESHOLD_MS = 4 * 60 * 60 * 1000; // 4 hours\nconst STARTING_STALE_MS = 60 * 60 * 1000; // 1 hour\n\n// ---------------------------------------------------------------------------\n// Section name normalization (backward compat with v1 workboard)\n// ---------------------------------------------------------------------------\n\nconst SECTION_MAP: Record<\n string,\n keyof Pick<WorkboardState, 'agents' | 'tasks' | 'blocked' | 'done' | 'log'> | null\n> = {\n agents: 'agents',\n active_sessions: 'agents',\n sessions: 'agents',\n tasks: 'tasks',\n blocked: 'blocked',\n done: 'done',\n log: 'log',\n recent: 'log',\n};\n\nfunction normalizeSectionName(title: string): string | null {\n const key = title.toLowerCase().replace(/\\s+/g, '_');\n return SECTION_MAP[key] || null;\n}\n\n// ---------------------------------------------------------------------------\n// Table parsing\n// ---------------------------------------------------------------------------\n\nfunction splitRow(line: string): string[] | null {\n if (!line.startsWith('|')) return null;\n return line\n .split('|')\n .slice(1, -1)\n .map((c) => c.trim());\n}\n\nfunction isSeparatorRow(cells: string[]): boolean {\n return cells.length > 0 && cells.every((c) => /^[-:]+$/.test(c));\n}\n\nfunction parseTable(lines: string[]): { columns: string[]; rows: Record<string, string>[] } {\n if (lines.length < 2) return { columns: [], rows: [] };\n\n const headerCells = splitRow(lines[0]!);\n if (!headerCells) return { columns: [], rows: [] };\n\n const sepCells = splitRow(lines[1]!);\n if (!(sepCells && isSeparatorRow(sepCells))) return { columns: [], rows: [] };\n\n const columns = headerCells.map((h) =>\n h\n .toLowerCase()\n .replace(/[^a-z0-9_]/g, '_')\n .replace(/_+/g, '_')\n .replace(/^_|_$/g, ''),\n );\n\n const rows: Record<string, string>[] = [];\n for (let i = 2; i < lines.length; i++) {\n const cells = splitRow(lines[i]!);\n if (!cells || isSeparatorRow(cells)) continue;\n const row: Record<string, string> = {};\n for (let j = 0; j < columns.length; j++) {\n const col = columns[j]!;\n const raw = (cells[j] || '').trim();\n row[col] = raw === '—' ? '' : raw;\n }\n rows.push(row);\n }\n\n return { columns, rows };\n}\n\n// ---------------------------------------------------------------------------\n// Parse\n// ---------------------------------------------------------------------------\n\nfunction parseWorkboard(content: string): WorkboardState {\n const lines = content.split('\\n');\n const rawSections: Array<{ title: string; line: number }> = [];\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n const m = line.match(/^##\\s+(.+)/);\n if (m?.[1]) rawSections.push({ title: m[1].trim(), line: i });\n }\n\n const state: WorkboardState = {\n preamble: [],\n agents: [],\n tasks: [],\n blocked: [],\n done: [],\n log: [],\n _extra: {},\n };\n\n const firstLine = rawSections.length > 0 ? rawSections[0]?.line : lines.length;\n state.preamble = lines.slice(0, firstLine);\n\n for (let i = 0; i < rawSections.length; i++) {\n const sec = rawSections[i]!;\n const nextLine = i + 1 < rawSections.length ? rawSections[i + 1]?.line : lines.length;\n const body = lines.slice(sec.line + 1, nextLine);\n const normalized = normalizeSectionName(sec.title);\n\n if (\n normalized === 'agents' ||\n normalized === 'tasks' ||\n normalized === 'blocked' ||\n normalized === 'done'\n ) {\n const tableLines = body.filter((l) => l.startsWith('|'));\n if (tableLines.length >= 2) {\n const { rows } = parseTable(tableLines);\n (state[normalized] as unknown as Record<string, string>[]).length = 0;\n (state[normalized] as unknown as Record<string, string>[]).push(...rows);\n }\n } else if (normalized === 'log') {\n state.log = body.filter((l) => l.startsWith('- '));\n } else {\n state._extra[sec.title] = lines.slice(sec.line, nextLine).join('\\n');\n }\n }\n\n return state;\n}\n\n// ---------------------------------------------------------------------------\n// Serialize\n// ---------------------------------------------------------------------------\n\nfunction padCell(value: string | undefined, width: number): string {\n const str = String(value || '—');\n return str.length >= width ? str : str + ' '.repeat(width - str.length);\n}\n\nfunction serializeTable(headers: string[], rows: Record<string, string>[]): string {\n if (headers.length === 0) return '(none)';\n\n const widths = headers.map((h) => {\n const dataMax = rows.reduce((max, row) => Math.max(max, String(row[h] || '—').length), 0);\n return Math.max(h.length, dataMax, 3);\n });\n\n const headerLine = `| ${headers.map((h, i) => padCell(h, widths[i]!)).join(' | ')} |`;\n const sepLine = `| ${widths.map((w) => '-'.repeat(w)).join(' | ')} |`;\n const dataLines = rows.map(\n (row) => `| ${headers.map((h, i) => padCell(row[h] || '', widths[i]!)).join(' | ')} |`,\n );\n\n return [headerLine, sepLine, ...dataLines].join('\\n');\n}\n\nfunction serializeWorkboard(state: WorkboardState): string {\n const out: string[] = [];\n\n if (state.preamble.length > 0) {\n out.push(...state.preamble);\n if (state.preamble[state.preamble.length - 1] !== '') out.push('');\n }\n\n out.push('## Agents', '');\n out.push(\n serializeTable(\n ['id', 'env', 'started', 'task', 'files', 'updated'],\n state.agents as unknown as Record<string, string>[],\n ),\n '',\n );\n\n out.push('## Tasks', '');\n if (state.tasks.length > 0) {\n out.push(\n serializeTable(\n ['id', 'task', 'pri', 'status', 'owner', 'gh', 'updated', 'notes'],\n state.tasks as unknown as Record<string, string>[],\n ),\n '',\n );\n } else {\n out.push('(none)', '');\n }\n\n out.push('## Blocked', '');\n if (state.blocked.length > 0) {\n out.push(\n serializeTable(\n ['id', 'task', 'blocker', 'gh', 'notes'],\n state.blocked as unknown as Record<string, string>[],\n ),\n '',\n );\n } else {\n out.push('(none)', '');\n }\n\n out.push('## Done', '');\n if (state.done.length > 0) {\n out.push(\n serializeTable(\n ['id', 'task', 'owner', 'completed', 'gh', 'notes'],\n state.done as unknown as Record<string, string>[],\n ),\n '',\n );\n } else {\n out.push('(none)', '');\n }\n\n out.push('## Log', '');\n if (state.log.length > 0) out.push(...state.log);\n out.push('');\n\n for (const content of Object.values(state._extra)) {\n out.push(content, '');\n }\n\n return out.join('\\n');\n}\n\n// ---------------------------------------------------------------------------\n// WorkboardManager\n// ---------------------------------------------------------------------------\n\n/**\n * WorkboardManager — reads, parses, and writes .claude/workboard.md (v2).\n *\n * Supports both v1 (Sessions/Recent) and v2 (Agents/Tasks/Blocked/Done/Log)\n * formats for backward compatibility. Always serializes to v2.\n *\n * All mutating methods use file locking (O_EXCL) to prevent race conditions.\n * Writes are atomic (tmp file + rename).\n */\nexport class WorkboardManager {\n private readonly lockPath: string;\n\n constructor(private readonly workboardPath: string) {\n const resolved = resolve(workboardPath);\n if (!resolved.endsWith('.md')) {\n throw new Error('Invalid workboard path: must be a .md file');\n }\n this.lockPath = lockPathFor(resolved);\n }\n\n // ---- Read/Write ----\n\n read(): WorkboardState {\n try {\n return parseWorkboard(readFileSync(this.workboardPath, 'utf8'));\n } catch {\n return emptyState();\n }\n }\n\n write(state: WorkboardState): void {\n withLock(this.lockPath, () => {\n atomicWriteSync(this.workboardPath, serializeWorkboard(state));\n });\n }\n\n async readAsync(): Promise<WorkboardState> {\n try {\n return parseWorkboard(await readFile(this.workboardPath, 'utf8'));\n } catch {\n return emptyState();\n }\n }\n\n async writeAsync(state: WorkboardState): Promise<void> {\n await withLockAsync(this.lockPath, async () => {\n atomicWriteSync(this.workboardPath, serializeWorkboard(state));\n });\n }\n\n // ---- Agent methods ----\n\n registerAgent(agent: WorkboardAgent): void {\n withLock(this.lockPath, () => {\n const state = this.readUnlocked();\n const idx = state.agents.findIndex((a) => a.id === agent.id);\n if (idx >= 0) {\n state.agents[idx] = agent;\n } else {\n state.agents.push(agent);\n }\n this.writeUnlocked(state);\n });\n }\n\n unregisterAgent(id: string): void {\n withLock(this.lockPath, () => {\n const state = this.readUnlocked();\n state.agents = state.agents.filter((a) => a.id !== id);\n this.writeUnlocked(state);\n });\n }\n\n updateAgent(id: string, updates: Partial<WorkboardAgent>): void {\n withLock(this.lockPath, () => {\n const state = this.readUnlocked();\n const idx = state.agents.findIndex((a) => a.id === id);\n if (idx < 0) return;\n state.agents[idx] = { ...state.agents[idx]!, ...updates };\n this.writeUnlocked(state);\n });\n }\n\n // ---- Task claiming ----\n\n /** Claim an available or partial task. Returns true on success. */\n claimTask(taskId: string, agentId: string): boolean {\n let success = false;\n withLock(this.lockPath, () => {\n const state = this.readUnlocked();\n const task = state.tasks.find((t) => t.id === taskId);\n if (!task) return;\n if (task.status !== 'available' && task.status !== 'partial') return;\n task.status = 'claimed';\n task.owner = agentId;\n task.updated = new Date().toISOString().slice(0, 10);\n this.writeUnlocked(state);\n success = true;\n });\n return success;\n }\n\n /** Move a task from Tasks to Done. Returns true on success. */\n completeTask(taskId: string, agentId: string): boolean {\n let success = false;\n withLock(this.lockPath, () => {\n const state = this.readUnlocked();\n const idx = state.tasks.findIndex((t) => t.id === taskId);\n if (idx === -1) return;\n const task = state.tasks.splice(idx, 1)[0]!;\n state.done.unshift({\n id: task.id,\n task: task.task,\n owner: agentId || task.owner,\n completed: new Date().toISOString().slice(0, 10),\n gh: task.gh || '',\n notes: task.notes || '',\n });\n this.writeUnlocked(state);\n success = true;\n });\n return success;\n }\n\n /** Mark a claimed task as partial (agent stopped mid-work). */\n markPartial(taskId: string, notes: string): boolean {\n let success = false;\n withLock(this.lockPath, () => {\n const state = this.readUnlocked();\n const task = state.tasks.find((t) => t.id === taskId);\n if (!task) return;\n task.status = 'partial';\n task.updated = new Date().toISOString().slice(0, 10);\n if (notes) task.notes = notes;\n this.writeUnlocked(state);\n success = true;\n });\n return success;\n }\n\n /** Release a claimed/partial task back to available. */\n releaseTask(taskId: string): boolean {\n let success = false;\n withLock(this.lockPath, () => {\n const state = this.readUnlocked();\n const task = state.tasks.find((t) => t.id === taskId);\n if (!task) return;\n task.status = 'available';\n task.owner = '';\n task.updated = new Date().toISOString().slice(0, 10);\n this.writeUnlocked(state);\n success = true;\n });\n return success;\n }\n\n /** Move a blocked task to Tasks as available. */\n unblockTask(taskId: string, pri: string = 'P2'): boolean {\n let success = false;\n withLock(this.lockPath, () => {\n const state = this.readUnlocked();\n const idx = state.blocked.findIndex((t) => t.id === taskId);\n if (idx === -1) return;\n const blocked = state.blocked.splice(idx, 1)[0]!;\n state.tasks.push({\n id: blocked.id,\n task: blocked.task,\n pri: pri as WorkboardTask['pri'],\n status: 'available',\n owner: '',\n gh: blocked.gh || '',\n updated: new Date().toISOString().slice(0, 10),\n notes: blocked.notes || '',\n });\n this.writeUnlocked(state);\n success = true;\n });\n return success;\n }\n\n // ---- File claims ----\n\n claimFiles(id: string, files: string[]): void {\n withLock(this.lockPath, () => {\n const state = this.readUnlocked();\n const agent = state.agents.find((a) => a.id === id);\n if (!agent) return;\n agent.files = files.join(', ');\n agent.updated = `${new Date().toISOString().slice(0, 16)}Z`;\n this.writeUnlocked(state);\n });\n }\n\n releaseFiles(id: string): void {\n withLock(this.lockPath, () => {\n const state = this.readUnlocked();\n const agent = state.agents.find((a) => a.id === id);\n if (!agent) return;\n agent.files = '';\n agent.updated = `${new Date().toISOString().slice(0, 16)}Z`;\n this.writeUnlocked(state);\n });\n }\n\n // ---- Log ----\n\n addLogEntry(agentId: string, description: string): void {\n withLock(this.lockPath, () => {\n const state = this.readUnlocked();\n const now = new Date();\n const dateStr = now.toISOString().slice(0, 10);\n const timeStr = now.toISOString().slice(11, 16);\n state.log.unshift(`- [${dateStr} ${timeStr}] ${agentId}: ${description}`);\n if (state.log.length > 20) state.log.splice(20);\n this.writeUnlocked(state);\n });\n }\n\n // ---- Queries ----\n\n detectStale(): WorkboardAgent[] {\n const state = this.read();\n const now = Date.now();\n return state.agents.filter((a) => {\n try {\n const age = now - new Date(a.updated).getTime();\n const threshold = a.task === '(starting)' ? STARTING_STALE_MS : STALE_THRESHOLD_MS;\n return age > threshold;\n } catch {\n return false;\n }\n });\n }\n\n checkConflicts(mySessionId: string, files: string[]): ConflictResult {\n const state = this.read();\n const conflicts: ConflictResult['conflicts'] = [];\n\n for (const agent of state.agents) {\n if (agent.id === mySessionId) continue;\n const theirFiles = agent.files\n .split(',')\n .map((f) => f.trim())\n .filter(Boolean);\n const overlapping = files.filter((myFile) =>\n theirFiles.some(\n (theirFile) =>\n myFile === theirFile ||\n myFile.startsWith(theirFile.replace('**', '')) ||\n theirFile.startsWith(myFile.replace('**', '')),\n ),\n );\n if (overlapping.length > 0) {\n conflicts.push({\n thisSession: mySessionId,\n otherSession: agent.id,\n overlappingFiles: overlapping,\n });\n }\n }\n\n return { clean: conflicts.length === 0, conflicts };\n }\n\n getClaimedTasks(agentId: string): WorkboardTask[] {\n const state = this.read();\n return state.tasks.filter(\n (t) => t.status === 'claimed' && t.owner === agentId,\n ) as WorkboardTask[];\n }\n\n // ---- Internal ----\n\n private readUnlocked(): WorkboardState {\n try {\n return parseWorkboard(readFileSync(this.workboardPath, 'utf8'));\n } catch {\n return emptyState();\n }\n }\n\n private writeUnlocked(state: WorkboardState): void {\n atomicWriteSync(this.workboardPath, serializeWorkboard(state));\n }\n}\n\nfunction emptyState(): WorkboardState {\n return { preamble: [], agents: [], tasks: [], blocked: [], done: [], log: [], _extra: {} };\n}\n\n// Re-export for backward compat\n/** @deprecated Use registerAgent instead */\nexport const registerSession = WorkboardManager.prototype.registerAgent;\n/** @deprecated Use unregisterAgent instead */\nexport const unregisterSession = WorkboardManager.prototype.unregisterAgent;\n"],"mappings":"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","names":["readFileSync","readFileSync","readFileSync"]}
|