@revealui/harnesses 0.1.7 → 0.1.8

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/LICENSE CHANGED
@@ -1,22 +1,109 @@
1
- MIT License
1
+ Functional Source License, Version 1.1, MIT Future License
2
2
 
3
- Copyright (c) 2025-2026 RevealUI Studio
3
+ Abbreviation
4
4
 
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
5
+ FSL-1.1-MIT
11
6
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
7
+ Notice
14
8
 
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
9
+ Copyright 2025-2026 RevealUI Studio (founder@revealui.com)
22
10
 
11
+ Terms and Conditions
12
+
13
+ Licensor: RevealUI Studio
14
+
15
+ Licensed Work: @revealui/harnesses
16
+ The Licensed Work is copyright 2025-2026 RevealUI Studio.
17
+
18
+ Change Date: 2028-04-08
19
+
20
+ Change License: MIT
21
+
22
+ For information about alternative licensing arrangements for the Licensed Work,
23
+ please contact: founder@revealui.com
24
+
25
+ License text below is the Functional Source License, Version 1.1, MIT Future
26
+ License, as published at https://fsl.software/FSL-1.1-MIT.template.md
27
+
28
+ ---
29
+
30
+ ## Terms and Conditions
31
+
32
+ ### Acceptance
33
+
34
+ In order to get any license under these terms, you must agree to them as
35
+ both strict obligations and conditions to all your licenses.
36
+
37
+ ### Copyright License
38
+
39
+ The licensor grants you a non-exclusive, royalty-free, worldwide,
40
+ non-sublicensable, non-transferable license to use, copy, distribute, make
41
+ available, and prepare derivative works of the licensed work, in each case
42
+ subject to the limitations and conditions below.
43
+
44
+ ### Limitations
45
+
46
+ You may not make the functionality of the licensed work or a modified
47
+ version available to third parties as a service, or distribute the
48
+ licensed work or a modified version in a way that makes the functionality
49
+ of the software available to third parties. Making the functionality of
50
+ the licensed work available to third parties includes, without limitation,
51
+ enabling third parties to interact with the functionality of the licensed
52
+ work remotely through a computer network, offering a service the value of
53
+ which entirely or primarily derives from the value of the licensed work,
54
+ or offering a service that accomplishes for users the primary purpose of
55
+ the licensed work or a modified version.
56
+
57
+ ### Patents
58
+
59
+ The licensor grants you a license, under any patent claims the licensor
60
+ can license, or becomes able to license, to make, have made, use, sell,
61
+ offer for sale, import and have imported the licensed work, in each case
62
+ subject to the limitations and conditions in this license. This license
63
+ does not cover any patent claims that you cause to be infringed by
64
+ modifications or additions to the licensed work. If you or your company
65
+ make any written claim that the licensed work infringes or contributes to
66
+ infringement of any patent, your patent license for the licensed work
67
+ granted under these terms ends immediately. If your company makes such a
68
+ claim, your patent license ends immediately for work on behalf of your
69
+ company.
70
+
71
+ ### Fair Use
72
+
73
+ This license is not intended to limit any right of fair use, fair
74
+ dealing, or other applicable copyright exception or limitation.
75
+
76
+ ### No Other Rights
77
+
78
+ These terms do not allow you to sublicense or transfer any of your
79
+ licenses to anyone else, or prevent the licensor from granting licenses
80
+ to anyone else. These terms do not imply any other licenses.
81
+
82
+ ### Termination
83
+
84
+ If you use the licensed work in violation of these terms, such use is
85
+ not licensed, and your licenses may be revoked if you do not cure the
86
+ violation.
87
+
88
+ The licensor may also revoke your licenses if you fail to comply with
89
+ these terms.
90
+
91
+ ### No Liability
92
+
93
+ ***As far as the law allows, the licensed work comes as is, without any
94
+ warranty or condition, and the licensor will not be liable to you for any
95
+ damages arising out of these terms or the use or nature of the licensed
96
+ work, under any kind of legal claim.***
97
+
98
+ ### Change Date
99
+
100
+ On the Change Date, or the fourth anniversary of the first publicly
101
+ available distribution of a specific version of the Licensed Work under
102
+ this License, whichever comes first, the Licensor hereby grants you
103
+ rights under the terms of the Change License, and the rights granted in
104
+ the paragraphs above terminate.
105
+
106
+ ### Change License
107
+
108
+ On the Change Date, the Licensed Work will be made available under the
109
+ Change License specified above (MIT).
@@ -77,7 +77,8 @@ async function withLockAsync(lockPath, fn, timeoutMs) {
77
77
  }
78
78
  }
79
79
  function atomicWriteSync(filePath, content) {
80
- const tmpPath = `${filePath}.tmp.${process.pid}`;
80
+ const suffix = `${process.pid}.${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
81
+ const tmpPath = `${filePath}.tmp.${suffix}`;
81
82
  writeFileSync(tmpPath, content, "utf8");
82
83
  renameSync(tmpPath, filePath);
83
84
  }
@@ -537,4 +538,4 @@ export {
537
538
  detectSessionType,
538
539
  deriveSessionId
539
540
  };
540
- //# sourceMappingURL=chunk-JG6CAG4A.js.map
541
+ //# sourceMappingURL=chunk-4F4ANKIZ.js.map
@@ -0,0 +1 @@
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"]}