@decibelsystems/tools 3.0.0 → 3.1.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 (253) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/dist/agentCommands.d.ts +0 -1
  3. package/dist/agentCommands.d.ts.map +1 -1
  4. package/dist/agentCommands.js +19 -8
  5. package/dist/agentCommands.js.map +1 -1
  6. package/dist/agentPresence.d.ts +5 -3
  7. package/dist/agentPresence.d.ts.map +1 -1
  8. package/dist/agentPresence.js +20 -29
  9. package/dist/agentPresence.js.map +1 -1
  10. package/dist/architectAdrs.d.ts +13 -1
  11. package/dist/architectAdrs.d.ts.map +1 -1
  12. package/dist/architectAdrs.js +83 -1
  13. package/dist/architectAdrs.js.map +1 -1
  14. package/dist/artifacts.d.ts +60 -0
  15. package/dist/artifacts.d.ts.map +1 -0
  16. package/dist/artifacts.js +208 -0
  17. package/dist/artifacts.js.map +1 -0
  18. package/dist/asks.d.ts +93 -0
  19. package/dist/asks.d.ts.map +1 -0
  20. package/dist/asks.js +383 -0
  21. package/dist/asks.js.map +1 -0
  22. package/dist/chat.d.ts +47 -0
  23. package/dist/chat.d.ts.map +1 -0
  24. package/dist/chat.js +288 -0
  25. package/dist/chat.js.map +1 -0
  26. package/dist/daemon.d.ts +14 -3
  27. package/dist/daemon.d.ts.map +1 -1
  28. package/dist/daemon.js +44 -14
  29. package/dist/daemon.js.map +1 -1
  30. package/dist/daemonConfig.d.ts +22 -0
  31. package/dist/daemonConfig.d.ts.map +1 -1
  32. package/dist/daemonConfig.js +11 -0
  33. package/dist/daemonConfig.js.map +1 -1
  34. package/dist/domain/issue.d.ts +19 -15
  35. package/dist/domain/issue.d.ts.map +1 -1
  36. package/dist/domain/issue.js +4 -32
  37. package/dist/domain/issue.js.map +1 -1
  38. package/dist/domain/issueCodec.d.ts.map +1 -1
  39. package/dist/domain/issueCodec.js +10 -0
  40. package/dist/domain/issueCodec.js.map +1 -1
  41. package/dist/domain/issueRepository.d.ts +35 -2
  42. package/dist/domain/issueRepository.d.ts.map +1 -1
  43. package/dist/domain/issueRepository.js +40 -1
  44. package/dist/domain/issueRepository.js.map +1 -1
  45. package/dist/domain/uid.d.ts +16 -0
  46. package/dist/domain/uid.d.ts.map +1 -0
  47. package/dist/domain/uid.js +43 -0
  48. package/dist/domain/uid.js.map +1 -0
  49. package/dist/facades/definitions.d.ts.map +1 -1
  50. package/dist/facades/definitions.js +46 -3
  51. package/dist/facades/definitions.js.map +1 -1
  52. package/dist/hq/defineAgent.d.ts.map +1 -1
  53. package/dist/hq/defineAgent.js +7 -11
  54. package/dist/hq/defineAgent.js.map +1 -1
  55. package/dist/httpServer.d.ts.map +1 -1
  56. package/dist/httpServer.js +201 -48
  57. package/dist/httpServer.js.map +1 -1
  58. package/dist/identity.d.ts +123 -0
  59. package/dist/identity.d.ts.map +1 -0
  60. package/dist/identity.js +312 -0
  61. package/dist/identity.js.map +1 -0
  62. package/dist/kernel.d.ts.map +1 -1
  63. package/dist/kernel.js +24 -1
  64. package/dist/kernel.js.map +1 -1
  65. package/dist/killSwitch.d.ts +117 -0
  66. package/dist/killSwitch.d.ts.map +1 -0
  67. package/dist/killSwitch.js +161 -0
  68. package/dist/killSwitch.js.map +1 -0
  69. package/dist/lib/atomicWrite.d.ts +23 -0
  70. package/dist/lib/atomicWrite.d.ts.map +1 -1
  71. package/dist/lib/atomicWrite.js +84 -0
  72. package/dist/lib/atomicWrite.js.map +1 -1
  73. package/dist/lib/recordIdAllocator.d.ts +21 -5
  74. package/dist/lib/recordIdAllocator.d.ts.map +1 -1
  75. package/dist/lib/recordIdAllocator.js +25 -7
  76. package/dist/lib/recordIdAllocator.js.map +1 -1
  77. package/dist/orgId.d.ts +23 -0
  78. package/dist/orgId.d.ts.map +1 -0
  79. package/dist/orgId.js +73 -0
  80. package/dist/orgId.js.map +1 -0
  81. package/dist/pairCli.d.ts +2 -0
  82. package/dist/pairCli.d.ts.map +1 -0
  83. package/dist/pairCli.js +97 -0
  84. package/dist/pairCli.js.map +1 -0
  85. package/dist/peerBroker.d.ts +40 -0
  86. package/dist/peerBroker.d.ts.map +1 -0
  87. package/dist/peerBroker.js +70 -0
  88. package/dist/peerBroker.js.map +1 -0
  89. package/dist/peerBrokerSupervisor.d.ts +21 -0
  90. package/dist/peerBrokerSupervisor.d.ts.map +1 -0
  91. package/dist/peerBrokerSupervisor.js +131 -0
  92. package/dist/peerBrokerSupervisor.js.map +1 -0
  93. package/dist/projectPaths.d.ts.map +1 -1
  94. package/dist/projectPaths.js +1 -19
  95. package/dist/projectPaths.js.map +1 -1
  96. package/dist/projectRegistry.d.ts +22 -0
  97. package/dist/projectRegistry.d.ts.map +1 -1
  98. package/dist/projectRegistry.js +58 -2
  99. package/dist/projectRegistry.js.map +1 -1
  100. package/dist/pty/boundary.d.ts +39 -0
  101. package/dist/pty/boundary.d.ts.map +1 -0
  102. package/dist/pty/boundary.js +100 -0
  103. package/dist/pty/boundary.js.map +1 -0
  104. package/dist/pty/errors.d.ts +6 -0
  105. package/dist/pty/errors.d.ts.map +1 -0
  106. package/dist/pty/errors.js +12 -0
  107. package/dist/pty/errors.js.map +1 -0
  108. package/dist/pty/fleet.d.ts +159 -0
  109. package/dist/pty/fleet.d.ts.map +1 -0
  110. package/dist/pty/fleet.js +543 -0
  111. package/dist/pty/fleet.js.map +1 -0
  112. package/dist/pty/launchProfile.d.ts +45 -0
  113. package/dist/pty/launchProfile.d.ts.map +1 -0
  114. package/dist/pty/launchProfile.js +149 -0
  115. package/dist/pty/launchProfile.js.map +1 -0
  116. package/dist/pty/openTerminal.d.ts +48 -0
  117. package/dist/pty/openTerminal.d.ts.map +1 -0
  118. package/dist/pty/openTerminal.js +185 -0
  119. package/dist/pty/openTerminal.js.map +1 -0
  120. package/dist/pty/pairDialog.d.ts +16 -0
  121. package/dist/pty/pairDialog.d.ts.map +1 -0
  122. package/dist/pty/pairDialog.js +121 -0
  123. package/dist/pty/pairDialog.js.map +1 -0
  124. package/dist/pty/pairing.d.ts +86 -0
  125. package/dist/pty/pairing.d.ts.map +1 -0
  126. package/dist/pty/pairing.js +351 -0
  127. package/dist/pty/pairing.js.map +1 -0
  128. package/dist/pty/routes.d.ts +49 -0
  129. package/dist/pty/routes.d.ts.map +1 -0
  130. package/dist/pty/routes.js +624 -0
  131. package/dist/pty/routes.js.map +1 -0
  132. package/dist/pty/sessions.d.ts +167 -0
  133. package/dist/pty/sessions.d.ts.map +1 -0
  134. package/dist/pty/sessions.js +513 -0
  135. package/dist/pty/sessions.js.map +1 -0
  136. package/dist/pty/tmux.d.ts +109 -0
  137. package/dist/pty/tmux.d.ts.map +1 -0
  138. package/dist/pty/tmux.js +271 -0
  139. package/dist/pty/tmux.js.map +1 -0
  140. package/dist/roster.d.ts +37 -0
  141. package/dist/roster.d.ts.map +1 -0
  142. package/dist/roster.js +126 -0
  143. package/dist/roster.js.map +1 -0
  144. package/dist/runtime/daemonMeta.d.ts +53 -0
  145. package/dist/runtime/daemonMeta.d.ts.map +1 -0
  146. package/dist/runtime/daemonMeta.js +121 -0
  147. package/dist/runtime/daemonMeta.js.map +1 -0
  148. package/dist/runtime/ensureRuntime.d.ts.map +1 -1
  149. package/dist/runtime/ensureRuntime.js +5 -10
  150. package/dist/runtime/ensureRuntime.js.map +1 -1
  151. package/dist/sentinelIssues.d.ts +16 -0
  152. package/dist/sentinelIssues.d.ts.map +1 -1
  153. package/dist/sentinelIssues.js +42 -1
  154. package/dist/sentinelIssues.js.map +1 -1
  155. package/dist/server.js +66 -5
  156. package/dist/server.js.map +1 -1
  157. package/dist/toolConfig.d.ts.map +1 -1
  158. package/dist/toolConfig.js +12 -0
  159. package/dist/toolConfig.js.map +1 -1
  160. package/dist/tools/agentic/agentQueue.d.ts.map +1 -1
  161. package/dist/tools/agentic/agentQueue.js +4 -0
  162. package/dist/tools/agentic/agentQueue.js.map +1 -1
  163. package/dist/tools/architect/index.d.ts +1 -0
  164. package/dist/tools/architect/index.d.ts.map +1 -1
  165. package/dist/tools/architect/index.js +45 -1
  166. package/dist/tools/architect/index.js.map +1 -1
  167. package/dist/tools/architect.d.ts.map +1 -1
  168. package/dist/tools/architect.js +59 -8
  169. package/dist/tools/architect.js.map +1 -1
  170. package/dist/tools/artifacts.d.ts +5 -0
  171. package/dist/tools/artifacts.d.ts.map +1 -0
  172. package/dist/tools/artifacts.js +89 -0
  173. package/dist/tools/artifacts.js.map +1 -0
  174. package/dist/tools/asks.d.ts +6 -0
  175. package/dist/tools/asks.d.ts.map +1 -0
  176. package/dist/tools/asks.js +104 -0
  177. package/dist/tools/asks.js.map +1 -0
  178. package/dist/tools/coordinator/index.d.ts +1 -0
  179. package/dist/tools/coordinator/index.d.ts.map +1 -1
  180. package/dist/tools/coordinator/index.js +42 -2
  181. package/dist/tools/coordinator/index.js.map +1 -1
  182. package/dist/tools/data-inspector.d.ts +1 -1
  183. package/dist/tools/data-inspector.d.ts.map +1 -1
  184. package/dist/tools/data-inspector.js +1 -1
  185. package/dist/tools/data-inspector.js.map +1 -1
  186. package/dist/tools/dojo/index.d.ts.map +1 -1
  187. package/dist/tools/dojo/index.js +52 -3
  188. package/dist/tools/dojo/index.js.map +1 -1
  189. package/dist/tools/dojo.d.ts +62 -1
  190. package/dist/tools/dojo.d.ts.map +1 -1
  191. package/dist/tools/dojo.js +84 -1
  192. package/dist/tools/dojo.js.map +1 -1
  193. package/dist/tools/guardian.d.ts +7 -0
  194. package/dist/tools/guardian.d.ts.map +1 -1
  195. package/dist/tools/guardian.js +146 -9
  196. package/dist/tools/guardian.js.map +1 -1
  197. package/dist/tools/index.d.ts.map +1 -1
  198. package/dist/tools/index.js +6 -0
  199. package/dist/tools/index.js.map +1 -1
  200. package/dist/tools/killswitch/index.d.ts +13 -0
  201. package/dist/tools/killswitch/index.d.ts.map +1 -0
  202. package/dist/tools/killswitch/index.js +138 -0
  203. package/dist/tools/killswitch/index.js.map +1 -0
  204. package/dist/tools/oracle/index.d.ts.map +1 -1
  205. package/dist/tools/oracle/index.js +8 -0
  206. package/dist/tools/oracle/index.js.map +1 -1
  207. package/dist/tools/oracle.d.ts +7 -0
  208. package/dist/tools/oracle.d.ts.map +1 -1
  209. package/dist/tools/oracle.js +22 -6
  210. package/dist/tools/oracle.js.map +1 -1
  211. package/dist/tools/provenance.d.ts +11 -2
  212. package/dist/tools/provenance.d.ts.map +1 -1
  213. package/dist/tools/provenance.js +26 -24
  214. package/dist/tools/provenance.js.map +1 -1
  215. package/dist/tools/registry/index.d.ts.map +1 -1
  216. package/dist/tools/registry/index.js +24 -0
  217. package/dist/tools/registry/index.js.map +1 -1
  218. package/dist/tools/sentinel/index.d.ts.map +1 -1
  219. package/dist/tools/sentinel/index.js +54 -2
  220. package/dist/tools/sentinel/index.js.map +1 -1
  221. package/dist/tools/sentinel.d.ts +50 -0
  222. package/dist/tools/sentinel.d.ts.map +1 -1
  223. package/dist/tools/sentinel.js +72 -7
  224. package/dist/tools/sentinel.js.map +1 -1
  225. package/dist/tools/shared/runTracker.d.ts +2 -0
  226. package/dist/tools/shared/runTracker.d.ts.map +1 -1
  227. package/dist/tools/shared/runTracker.js +38 -0
  228. package/dist/tools/shared/runTracker.js.map +1 -1
  229. package/dist/tools/swarm.d.ts +3 -1
  230. package/dist/tools/swarm.d.ts.map +1 -1
  231. package/dist/tools/swarm.js +6 -12
  232. package/dist/tools/swarm.js.map +1 -1
  233. package/dist/tools/voice/index.d.ts +1 -0
  234. package/dist/tools/voice/index.d.ts.map +1 -1
  235. package/dist/tools/voice/index.js +33 -2
  236. package/dist/tools/voice/index.js.map +1 -1
  237. package/dist/tools/voice.d.ts +48 -1
  238. package/dist/tools/voice.d.ts.map +1 -1
  239. package/dist/tools/voice.js +63 -3
  240. package/dist/tools/voice.js.map +1 -1
  241. package/dist/transports/bridge.d.ts.map +1 -1
  242. package/dist/transports/bridge.js +2 -1
  243. package/dist/transports/bridge.js.map +1 -1
  244. package/dist/transports/mcp.d.ts.map +1 -1
  245. package/dist/transports/mcp.js +2 -1
  246. package/dist/transports/mcp.js.map +1 -1
  247. package/dist/version.d.ts +5 -0
  248. package/dist/version.d.ts.map +1 -0
  249. package/dist/version.js +23 -0
  250. package/dist/version.js.map +1 -0
  251. package/package.json +13 -2
  252. package/templates/AGENT.md +3 -0
  253. package/templates/CLAUDE-decibel.md +11 -0
@@ -0,0 +1,513 @@
1
+ // ============================================================================
2
+ // Hosted sessions — list, spawn, control (ADR-0012 / decibel-hq EPIC-0015)
3
+ // ============================================================================
4
+ // Three kinds of row, distinguished by `origin`:
5
+ //
6
+ // spawned a tmux session the daemon started (name dcb-<project>-<6hex>).
7
+ // Owned: attach, interrupt, kill, resume.
8
+ // adopted a tmux session the user started, hosting a claude-peers session.
9
+ // Attach and interrupt only. The daemon never kills what it did not
10
+ // start.
11
+ // peer a live claude-peers session in a plain terminal tty. A daemon
12
+ // cannot attach to another terminal's pty, so attachable:false.
13
+ //
14
+ // Spawned sessions live on the daemon's own tmux server (`-L decibel`, see
15
+ // tmux.ts); Ben reaches one directly with `tmux -L decibel attach -t <key>`
16
+ // (default C-b prefix there: the owned server does not read ~/.tmux.conf).
17
+ // They run `claude --session-id <uuid>` with a uuid the daemon
18
+ // generates, so the resume id is known from birth rather than scraped from
19
+ // ~/.claude afterwards. remain-on-exit keeps the session after claude exits,
20
+ // which is what lets an exited session be listed and resumed in place.
21
+ //
22
+ // Spawn takes a project_id, never a cwd. The cwd comes from the registry, and
23
+ // only from an exact id/alias hit: resolveProjectPaths' later strategies fall
24
+ // back to the daemon's own cwd, and "start claude wherever the daemon happens
25
+ // to be" is not a resolution.
26
+ // ============================================================================
27
+ import { randomBytes, randomUUID } from 'crypto';
28
+ import { existsSync, chmodSync, statSync } from 'fs';
29
+ import { homedir } from 'os';
30
+ import { dirname, join } from 'path';
31
+ import { createRequire } from 'module';
32
+ import { listProjects } from '../projectRegistry.js';
33
+ import { fetchPeers, getHumanPeerId } from '../peerBroker.js';
34
+ import { log } from '../config.js';
35
+ import { SessionError } from './errors.js';
36
+ import { SPAWNED_PREFIX, tmux, listSessions, findSession, paneTtyMap, serverFor, sessionTarget, paneTarget, cleanLabel, resolveTmux, isNoServer, unsetDaemonEnvArgs, OWNED_SOCKET_DEFAULT, TITLE_FORMAT, } from './tmux.js';
37
+ import { loadLaunchProfile, profileArgs, tmuxOptionArgs } from './launchProfile.js';
38
+ let ptyModule;
39
+ export async function loadPty() {
40
+ if (ptyModule !== undefined)
41
+ return ptyModule;
42
+ try {
43
+ const mod = (await import('node-pty'));
44
+ ptyModule = (mod.default ?? mod);
45
+ ensureSpawnHelperExecutable();
46
+ }
47
+ catch (err) {
48
+ log(`Sessions: node-pty unavailable, attach disabled (${err instanceof Error ? err.message : String(err)})`);
49
+ ptyModule = null;
50
+ }
51
+ return ptyModule;
52
+ }
53
+ /**
54
+ * npm unpacks node-pty's prebuilt spawn-helper without the exec bit on some
55
+ * installs, and every spawn then fails with a bare "posix_spawnp failed".
56
+ */
57
+ function ensureSpawnHelperExecutable() {
58
+ if (process.platform === 'win32')
59
+ return;
60
+ try {
61
+ const req = createRequire(import.meta.url);
62
+ const root = dirname(req.resolve('node-pty/package.json'));
63
+ const candidates = [
64
+ join(root, 'prebuilds', `${process.platform}-${process.arch}`, 'spawn-helper'),
65
+ join(root, 'build', 'Release', 'spawn-helper'),
66
+ ];
67
+ for (const helper of candidates) {
68
+ if (existsSync(helper) && (statSync(helper).mode & 0o111) === 0) {
69
+ chmodSync(helper, 0o755);
70
+ log(`Sessions: made ${helper} executable`);
71
+ }
72
+ }
73
+ }
74
+ catch {
75
+ // Best effort; a real failure surfaces at spawn with its own message.
76
+ }
77
+ }
78
+ export { SessionError };
79
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
80
+ export function isUuid(v) {
81
+ return typeof v === 'string' && UUID_RE.test(v);
82
+ }
83
+ /** Exact id or alias in projects.json, available on this device. Nothing else. */
84
+ export function resolveRegisteredProject(projectId) {
85
+ const projects = listProjects();
86
+ const hit = projects.find((p) => p.id === projectId) ?? projects.find((p) => p.aliases?.includes(projectId));
87
+ if (!hit) {
88
+ const known = projects.map((p) => p.id).join(', ') || '(none)';
89
+ throw new SessionError(404, 'PROJECT_NOT_FOUND', `Project "${projectId}" is not in the registry. Hosted sessions only start in registered projects. ` +
90
+ `Registered: ${known}. To fix: registry_add (or project_init) the project first.`);
91
+ }
92
+ if (hit.available === false || !existsSync(hit.path)) {
93
+ throw new SessionError(409, 'PROJECT_UNAVAILABLE', `Project "${hit.id}" is registered but its path is not available on this device (${hit.path}).`);
94
+ }
95
+ return { id: hit.id, path: hit.path };
96
+ }
97
+ export function isWithin(child, parent) {
98
+ const p = parent.replace(/\/+$/, '');
99
+ return child === p || child.startsWith(p + '/');
100
+ }
101
+ /** Every registered project available on this device. */
102
+ export function availableProjects() {
103
+ return listProjects()
104
+ .filter((p) => p.available !== false)
105
+ .map((p) => ({ id: p.id, path: p.path }));
106
+ }
107
+ /**
108
+ * The registered project a path belongs to: the LONGEST registered path that
109
+ * contains it, so a checkout nested inside another project belongs to itself.
110
+ * Undefined when nothing contains it (a session in ~/Documents/GitHub itself,
111
+ * or in a repo that was never registered).
112
+ */
113
+ export function projectForPath(path, projects) {
114
+ let best;
115
+ for (const p of projects) {
116
+ if (isWithin(path, p.path) && (!best || p.path.length > best.path.length))
117
+ best = p;
118
+ }
119
+ return best;
120
+ }
121
+ /**
122
+ * The session roster. With a project id, that project's sessions only (and an
123
+ * unregistered id is an error, never a fallback). Without one, every session
124
+ * on the machine; `project_id` is present where the session resolves to a
125
+ * registered project and omitted where it does not.
126
+ */
127
+ export async function listHostedSessions(projectId, deps = {}) {
128
+ const scope = projectId ? resolveRegisteredProject(projectId) : undefined;
129
+ const projects = (deps.projects ?? availableProjects)();
130
+ const [sessions, userSessions, ttyMap, peers, ptyOk] = await Promise.all([
131
+ (deps.ownedSessions ?? (() => listSessions('owned')))(),
132
+ (deps.userSessions ?? (() => listSessions('user')))(),
133
+ (deps.ttyMap ?? paneTtyMap)(),
134
+ (deps.peers ?? fetchPeers)(),
135
+ (deps.ptyAvailable ?? (async () => (await loadPty()) !== null))(),
136
+ ]);
137
+ const rows = new Map();
138
+ for (const s of sessions) {
139
+ if (!s.name.startsWith(SPAWNED_PREFIX))
140
+ continue;
141
+ if (scope && s.project_id !== scope.id)
142
+ continue;
143
+ const row = {
144
+ session_key: s.name,
145
+ ...(s.project_id ? { project_id: s.project_id } : {}),
146
+ origin: 'spawned',
147
+ state: s.dead ? 'exited' : 'live',
148
+ attachable: !s.dead && ptyOk,
149
+ };
150
+ if (!s.dead) {
151
+ row.attach_handle = `tmux:${s.name}`;
152
+ row.attach_argv = attachArgv(s.name);
153
+ }
154
+ else if (s.claude_session_id)
155
+ row.attach_handle = `resume:${s.claude_session_id}`;
156
+ if (s.claude_session_id)
157
+ row.claude_session_id = s.claude_session_id;
158
+ if (s.cwd)
159
+ row.cwd = s.cwd;
160
+ if (s.branch)
161
+ row.branch = s.branch;
162
+ if (s.issue_label)
163
+ row.issue_label = s.issue_label;
164
+ if (s.created)
165
+ row.created = s.created;
166
+ rows.set(s.name, row);
167
+ }
168
+ const sessionByName = new Map(userSessions.map((s) => [s.name, s]));
169
+ const humanPeer = getHumanPeerId();
170
+ for (const peer of peers) {
171
+ // The HQ chat peer stands for the human, not a session (src/chat.ts).
172
+ if (humanPeer && peer.id === humanPeer)
173
+ continue;
174
+ const tmuxName = peer.tty ? ttyMap.get(peer.tty) : undefined;
175
+ const peerFields = { peer_id: peer.id };
176
+ if (peer.pid)
177
+ peerFields.pid = peer.pid;
178
+ if (peer.tty)
179
+ peerFields.tty = peer.tty;
180
+ if (peer.summary)
181
+ peerFields.summary = peer.summary;
182
+ // The claude running inside a spawned session: fold it into that row
183
+ // rather than listing the same session twice.
184
+ if (tmuxName && rows.has(tmuxName)) {
185
+ Object.assign(rows.get(tmuxName), peerFields);
186
+ continue;
187
+ }
188
+ // A spawned session filtered out by scope (another project).
189
+ if (tmuxName && tmuxName.startsWith(SPAWNED_PREFIX))
190
+ continue;
191
+ const root = peer.git_root || peer.cwd;
192
+ const project = root ? projectForPath(root, projects) : undefined;
193
+ if (scope && project?.id !== scope.id)
194
+ continue;
195
+ const projectField = project ? { project_id: project.id } : {};
196
+ if (tmuxName) {
197
+ const s = sessionByName.get(tmuxName);
198
+ rows.set(tmuxName, {
199
+ session_key: tmuxName,
200
+ ...projectField,
201
+ origin: 'adopted',
202
+ state: 'live',
203
+ attachable: ptyOk,
204
+ attach_handle: `tmux:${tmuxName}`,
205
+ ...(peer.cwd ? { cwd: peer.cwd } : {}),
206
+ ...(s?.created ? { created: s.created } : {}),
207
+ ...peerFields,
208
+ });
209
+ continue;
210
+ }
211
+ rows.set(peer.id, {
212
+ session_key: peer.id,
213
+ ...projectField,
214
+ origin: 'peer',
215
+ state: 'live',
216
+ attachable: false,
217
+ ...(peer.cwd ? { cwd: peer.cwd } : {}),
218
+ ...(peer.registered_at ? { created: peer.registered_at } : {}),
219
+ ...peerFields,
220
+ });
221
+ }
222
+ return [...rows.values()];
223
+ }
224
+ // ----------------------------------------------------------------------------
225
+ // Attach eligibility
226
+ // ----------------------------------------------------------------------------
227
+ /**
228
+ * What an attach token may be minted for: a live spawned session, or a tmux
229
+ * session that is hosting a claude-peers session. Never an arbitrary tmux
230
+ * session — the user's other tmux work is not HQ's to open.
231
+ */
232
+ export async function resolveAttachable(key) {
233
+ const s = await findSession(key);
234
+ if (!s)
235
+ throw new SessionError(404, 'SESSION_NOT_FOUND', `No tmux session "${key}".`);
236
+ if (s.dead)
237
+ throw new SessionError(409, 'SESSION_EXITED', `Session "${key}" has exited; resume it first.`);
238
+ if (key.startsWith(SPAWNED_PREFIX))
239
+ return s;
240
+ const [ttyMap, peers] = await Promise.all([paneTtyMap(), fetchPeers()]);
241
+ const hosted = peers.some((p) => p.tty && ttyMap.get(p.tty) === key);
242
+ if (!hosted) {
243
+ throw new SessionError(403, 'NOT_A_CLAUDE_SESSION', `tmux session "${key}" is not hosting a Claude session.`);
244
+ }
245
+ return s;
246
+ }
247
+ // ----------------------------------------------------------------------------
248
+ // Spawn / control
249
+ // ----------------------------------------------------------------------------
250
+ /**
251
+ * launchd gives the daemon no login PATH. Find claude by absolute path, then
252
+ * run it under a login shell so it gets the user's environment.
253
+ */
254
+ export function resolveClaude() {
255
+ const home = homedir();
256
+ const candidates = [
257
+ process.env.DECIBEL_CLAUDE_BIN,
258
+ join(home, '.npm-global', 'bin', 'claude'),
259
+ join(home, '.claude', 'local', 'claude'),
260
+ join(home, '.local', 'bin', 'claude'),
261
+ '/opt/homebrew/bin/claude',
262
+ '/usr/local/bin/claude',
263
+ ].filter((p) => !!p);
264
+ return candidates.find((p) => existsSync(p)) ?? 'claude';
265
+ }
266
+ /**
267
+ * argv for the pane: `$SHELL -lc 'exec "$0" "$@"' <claude> <args...>`. The
268
+ * claude path and arguments travel as positional parameters, so nothing is
269
+ * interpolated into the shell script.
270
+ */
271
+ export function claudeCommand(claudeArgs) {
272
+ const shell = process.env.SHELL && existsSync(process.env.SHELL) ? process.env.SHELL : '/bin/zsh';
273
+ return [shell, '-lc', 'exec "$0" "$@"', resolveClaude(), ...claudeArgs];
274
+ }
275
+ export function sessionNameFor(projectId) {
276
+ const slug = projectId.toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 32) || 'project';
277
+ return `${SPAWNED_PREFIX}${slug}-${randomBytes(3).toString('hex')}`;
278
+ }
279
+ /**
280
+ * The argv for attaching to an owned session from a terminal (ADR-0018). The
281
+ * absolute tmux path, because launchd's PATH and a login shell's can resolve
282
+ * different binaries, and a client/server version mismatch refuses to attach.
283
+ * `=key` is an exact-match target. Not secret.
284
+ */
285
+ export function attachArgv(key) {
286
+ return [resolveTmux(), '-u', '-L', process.env.DECIBEL_TMUX_SOCKET || OWNED_SOCKET_DEFAULT, 'attach-session', '-t', sessionTarget(key)];
287
+ }
288
+ /**
289
+ * Apply launch.json's tmux options to the owned server, if it is running.
290
+ * Spawns apply them too; this covers a daemon restart, where the tmux server
291
+ * and its sessions outlive the daemon. A missing server or a bad launch.json
292
+ * is logged, never thrown: the daemon must still boot.
293
+ */
294
+ export async function applyTmuxProfile() {
295
+ let profile;
296
+ try {
297
+ profile = loadLaunchProfile();
298
+ }
299
+ catch (err) {
300
+ log(`Sessions: tmux options not applied: ${err instanceof Error ? err.message : String(err)}`);
301
+ return;
302
+ }
303
+ const unset = unsetDaemonEnvArgs();
304
+ const r = await tmux('owned', [...tmuxOptionArgs(profile.tmux), ...(unset.length ? [';', ...unset] : [])]);
305
+ if (!r.ok && !isNoServer(r.stderr))
306
+ log(`Sessions: tmux options not applied: ${r.stderr.trim()}`);
307
+ }
308
+ /** The largest single scroll step a client may ask for. */
309
+ const MAX_SCROLL_LINES = 10_000;
310
+ /**
311
+ * Scroll a session's pane through tmux copy-mode, for a client with no
312
+ * scrollback of its own (HQ's xterm.js pane: tmux draws on the alternate
313
+ * screen, and the owned server runs with mouse off). Never a keystroke into
314
+ * the session: copy-mode is tmux's own view.
315
+ *
316
+ * {lines: n<0} enter copy-mode -e and scroll up |n| lines
317
+ * {lines: n>0} scroll down; -e leaves copy-mode at the bottom by itself
318
+ * {cancel: true} leave copy-mode
319
+ *
320
+ * Down and cancel do nothing when the pane is not in copy-mode. HQ's line
321
+ * counter can overcount past the top of history, and it sends cancel before
322
+ * the first keystroke after any scroll, so both arrive when nothing is
323
+ * scrolled. tmux answers "not in a mode" then, which is the no-op.
324
+ */
325
+ export async function scrollPane(key, frame) {
326
+ const server = serverFor(key);
327
+ const pane = paneTarget(key);
328
+ let args;
329
+ if (frame.cancel === true) {
330
+ args = ['send-keys', '-X', '-t', pane, 'cancel'];
331
+ }
332
+ else {
333
+ const n = Math.trunc(Number(frame.lines));
334
+ if (!Number.isFinite(n) || n === 0)
335
+ return;
336
+ const count = String(Math.min(Math.abs(n), MAX_SCROLL_LINES));
337
+ args = n < 0
338
+ ? ['copy-mode', '-e', '-t', pane, ';', 'send-keys', '-X', '-N', count, '-t', pane, 'scroll-up']
339
+ : ['send-keys', '-X', '-N', count, '-t', pane, 'scroll-down'];
340
+ }
341
+ const r = await tmux(server, args);
342
+ if (!r.ok && !/not in a mode/.test(r.stderr)) {
343
+ log(`Sessions: scroll on ${key} failed: ${r.stderr.trim()}`);
344
+ }
345
+ }
346
+ export async function spawnSession(input, internal = {}) {
347
+ if (typeof input.project_id !== 'string' || !input.project_id) {
348
+ throw new SessionError(400, 'BAD_REQUEST', 'project_id is required.');
349
+ }
350
+ if (input.resume !== undefined && !isUuid(input.resume)) {
351
+ throw new SessionError(400, 'BAD_REQUEST', 'resume must be a Claude session uuid.');
352
+ }
353
+ // Before touching tmux: a broken launch.json refuses every spawn rather
354
+ // than starting claude without the flags Ben chose.
355
+ const profile = loadLaunchProfile();
356
+ const project = resolveRegisteredProject(input.project_id);
357
+ let cwd = project.path;
358
+ if (internal.cwd !== undefined) {
359
+ if (!isWithin(internal.cwd, project.path) || !existsSync(internal.cwd)) {
360
+ throw new SessionError(409, 'CWD_UNAVAILABLE', `"${internal.cwd}" is not an existing directory inside project "${project.id}".`);
361
+ }
362
+ cwd = internal.cwd;
363
+ }
364
+ const name = sessionNameFor(project.id);
365
+ const uuid = input.resume ?? randomUUID();
366
+ const claudeArgs = [...(input.resume ? ['--resume', uuid] : ['--session-id', uuid]), ...profileArgs(profile)];
367
+ const options = [
368
+ ['@dcb_project', project.id],
369
+ ['@dcb_uuid', uuid],
370
+ ['@dcb_cwd', cwd],
371
+ ['@dcb_branch', cleanLabel(input.branch)],
372
+ ['@dcb_issue', cleanLabel(input.issue_label)],
373
+ ];
374
+ // Start on a placeholder, set the options and remain-on-exit, THEN respawn the
375
+ // pane into claude. Starting claude directly races remain-on-exit: a claude
376
+ // that exits in milliseconds (bad flag, env refusal) takes the session with
377
+ // it before the option lands, and there is nothing left to report or resume.
378
+ // One tmux invocation; a lone ';' argument separates the commands.
379
+ // The profile's tmux options (history-limit, mouse) go first, as global
380
+ // options: a pane's history-limit is fixed when new-session creates it, and
381
+ // the owned server never reads ~/.tmux.conf (ISS-0205).
382
+ const unset = unsetDaemonEnvArgs();
383
+ const args = [...tmuxOptionArgs(profile.tmux), ';', ...(unset.length ? [...unset, ';'] : []),
384
+ 'new-session', '-d', '-s', name, '-c', cwd, '-x', '120', '-y', '32', 'sleep', '86400'];
385
+ for (const [opt, val] of options) {
386
+ if (val !== undefined)
387
+ args.push(';', 'set-option', '-t', paneTarget(name), opt, val);
388
+ }
389
+ args.push(';', 'set-option', '-w', '-t', paneTarget(name), 'remain-on-exit', 'on');
390
+ // HQ's 120x32 pane and a Ghostty tab can be attached at once; without this
391
+ // tmux shrinks the window to the smallest client. The client that typed
392
+ // last sets the size instead.
393
+ args.push(';', 'set-option', '-w', '-t', paneTarget(name), 'window-size', 'latest');
394
+ // tmux's status bar means nothing inside HQ's pane.
395
+ args.push(';', 'set-option', '-t', paneTarget(name), 'status', 'off');
396
+ // Claude Code asks for focus events and warns inside the pane without them.
397
+ // Server-scoped; setting it on every spawn keeps a restarted server right.
398
+ args.push(';', 'set-option', '-s', 'focus-events', 'on');
399
+ // Clients get the repo as their terminal title (a Ghostty tab, HQ's pane).
400
+ // Global, so sessions spawned before a daemon restart keep it too.
401
+ args.push(';', 'set-option', '-g', 'set-titles', 'on', ';', 'set-option', '-g', 'set-titles-string', TITLE_FORMAT);
402
+ args.push(';', 'respawn-pane', '-k', '-t', paneTarget(name), '-c', cwd, ...claudeCommand(claudeArgs));
403
+ const r = await tmux('owned', args, 10_000);
404
+ if (!r.ok) {
405
+ throw new SessionError(500, 'SPAWN_FAILED', `tmux could not start the session: ${r.stderr.trim() || 'unknown error'}`);
406
+ }
407
+ log(`Sessions: spawned ${name} in ${project.id}${input.resume ? ' (resume)' : ''}`);
408
+ const result = { session_key: name, claude_session_id: uuid, attach_argv: attachArgv(name) };
409
+ const attention = await confirmIfNeeded(name, profile);
410
+ if (attention)
411
+ result.needs_attention = attention;
412
+ return result;
413
+ }
414
+ export const DIALOG_TIMEOUT_MS = 15_000;
415
+ const DIALOG_POLL_MS = 250;
416
+ /** Exact match for the dev-channels warning, cursor on "1.". */
417
+ export function isDevChannelsDialog(screen) {
418
+ return (screen.includes('Loading development channels') &&
419
+ screen.includes('I am using this for local development') &&
420
+ /^[\s│|]*❯\s*1\./m.test(screen));
421
+ }
422
+ /** Null when confirmed; the attention code and message otherwise. */
423
+ export async function confirmDevChannels(key, deps = {}) {
424
+ const capture = deps.capture ?? (async (k) => {
425
+ const r = await tmux('owned', ['capture-pane', '-p', '-t', paneTarget(k)]);
426
+ return r.ok ? r.stdout : null;
427
+ });
428
+ const isDead = deps.isDead ?? (async (k) => {
429
+ const r = await tmux('owned', ['display-message', '-p', '-t', paneTarget(k), '#{pane_dead}']);
430
+ return r.ok && r.stdout.trim() === '1';
431
+ });
432
+ const sendEnter = deps.sendEnter ?? (async (k) => (await tmux('owned', ['send-keys', '-t', paneTarget(k), 'Enter'])).ok);
433
+ const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
434
+ const now = deps.now ?? Date.now;
435
+ const deadline = now() + (deps.timeoutMs ?? DIALOG_TIMEOUT_MS);
436
+ let last = '';
437
+ while (now() < deadline) {
438
+ const screen = await capture(key);
439
+ if (screen !== null) {
440
+ last = screen;
441
+ if (isDevChannelsDialog(screen)) {
442
+ if (!(await sendEnter(key))) {
443
+ return { code: 'DIALOG_UNRECOGNISED', error: 'The dev-channels dialog showed, but the Enter could not be sent.' };
444
+ }
445
+ log(`Sessions: confirmed the dev-channels dialog in ${key}`);
446
+ return null;
447
+ }
448
+ }
449
+ if (await isDead(key)) {
450
+ return { code: 'CLAUDE_EXITED', error: 'claude exited before the dev-channels dialog appeared. Attach to read why.' };
451
+ }
452
+ await sleep(DIALOG_POLL_MS);
453
+ }
454
+ return last.trim()
455
+ ? { code: 'DIALOG_UNRECOGNISED', error: 'The screen did not match the dev-channels dialog, so no key was pressed. Attach and answer it.' }
456
+ : { code: 'DIALOG_TIMEOUT', error: 'Nothing appeared within 15 s. Attach to check the session.' };
457
+ }
458
+ /** Run D4 only when the profile loads dev channels. */
459
+ async function confirmIfNeeded(key, profile) {
460
+ if (!profile.dev_channels.length)
461
+ return null;
462
+ const attention = await confirmDevChannels(key);
463
+ if (attention)
464
+ log(`Sessions: ${key} needs attention (${attention.code})`);
465
+ return attention;
466
+ }
467
+ export async function controlSession(key, action) {
468
+ if (action !== 'interrupt' && action !== 'kill' && action !== 'resume') {
469
+ throw new SessionError(400, 'BAD_REQUEST', 'action must be one of interrupt, kill, resume.');
470
+ }
471
+ const s = await findSession(key);
472
+ if (!s)
473
+ throw new SessionError(404, 'SESSION_NOT_FOUND', `No tmux session "${key}".`);
474
+ const owned = serverFor(key) === 'owned';
475
+ if (action === 'interrupt') {
476
+ if (!owned)
477
+ await resolveAttachable(key); // adopted: must be hosting claude
478
+ if (s.dead)
479
+ throw new SessionError(409, 'SESSION_EXITED', `Session "${key}" has exited.`);
480
+ const r = await tmux(serverFor(key), ['send-keys', '-t', paneTarget(key), 'C-c']);
481
+ if (!r.ok)
482
+ throw new SessionError(500, 'CONTROL_FAILED', r.stderr.trim());
483
+ return { ok: true, action };
484
+ }
485
+ if (!owned) {
486
+ throw new SessionError(403, 'NOT_OWNED', `"${key}" was not started by the daemon; only interrupt is allowed.`);
487
+ }
488
+ if (action === 'kill') {
489
+ const r = await tmux('owned', ['kill-session', '-t', sessionTarget(key)]);
490
+ if (!r.ok)
491
+ throw new SessionError(500, 'CONTROL_FAILED', r.stderr.trim());
492
+ log(`Sessions: killed ${key}`);
493
+ return { ok: true, action };
494
+ }
495
+ // resume
496
+ if (!s.dead)
497
+ throw new SessionError(409, 'SESSION_LIVE', `Session "${key}" is still running.`);
498
+ if (!isUuid(s.claude_session_id)) {
499
+ throw new SessionError(409, 'NO_RESUME_ID', `Session "${key}" has no Claude session id to resume.`);
500
+ }
501
+ const profile = loadLaunchProfile();
502
+ const cwd = s.cwd && existsSync(s.cwd) ? s.cwd : s.project_id ? resolveRegisteredProject(s.project_id).path : homedir();
503
+ const r = await tmux('owned', [
504
+ 'respawn-pane', '-k', '-t', paneTarget(key), '-c', cwd,
505
+ ...claudeCommand(['--resume', s.claude_session_id, ...profileArgs(profile)]),
506
+ ]);
507
+ if (!r.ok)
508
+ throw new SessionError(500, 'CONTROL_FAILED', r.stderr.trim());
509
+ log(`Sessions: resumed ${key}`);
510
+ const attention = await confirmIfNeeded(key, profile);
511
+ return attention ? { ok: true, action, needs_attention: attention } : { ok: true, action };
512
+ }
513
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/pty/sessions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ /** Prefix for sessions the daemon spawned. Anything else is adopted, never owned. */
2
+ export declare const SPAWNED_PREFIX = "dcb-";
3
+ export declare function resolveTmux(): string;
4
+ /** Test seam: forget the cached binary path. */
5
+ export declare function _resetTmuxPath(p?: string | null): void;
6
+ /**
7
+ * Two tmux servers are involved, and they are never mixed:
8
+ *
9
+ * owned `-L decibel` — the daemon's own server. Every spawned session lives
10
+ * here. The daemon starts it, so it starts with the daemon's scrubbed
11
+ * environment (see cleanEnv), and HQ-spawned sessions never appear in,
12
+ * or collide with, the user's own tmux.
13
+ * user the default socket — the user's tmux. Only ever read, attached to
14
+ * and interrupted (adopt); never spawned into or killed.
15
+ *
16
+ * DECIBEL_TMUX_SOCKET / DECIBEL_TMUX_USER_SOCKET rename them, which is how the
17
+ * tests run against private servers instead of real ones.
18
+ */
19
+ export type TmuxServer = 'owned' | 'user';
20
+ export declare const OWNED_SOCKET_DEFAULT = "decibel";
21
+ export declare function socketArgs(server: TmuxServer): string[];
22
+ /** Which server a session key lives on: the prefix is the ownership mark. */
23
+ /**
24
+ * Terminal title for clients of the owned server (set-titles): the repo, plus
25
+ * the issue label, or else a branch other than main/master. Built from the
26
+ * @dcb_* options the daemon sets at spawn, which cleanLabel has already
27
+ * stripped of control characters. tmux sends it as OSC 2 to every attached
28
+ * client, so a Ghostty tab is titled without any title text in its command
29
+ * (ISS-0210).
30
+ */
31
+ export declare const TITLE_FORMAT: string;
32
+ export declare function serverFor(name: string): TmuxServer;
33
+ export declare function isDaemonOnlyVar(name: string): boolean;
34
+ /** Names in `base` that cleanEnv drops for being the daemon's. Names only, never values. */
35
+ export declare function daemonOnlyNames(base?: NodeJS.ProcessEnv): string[];
36
+ export declare function cleanEnv(base?: NodeJS.ProcessEnv): Record<string, string>;
37
+ /**
38
+ * tmux keeps the environment its server started with as the GLOBAL
39
+ * environment and copies it into every new session. A server started before
40
+ * ISS-0225 already holds the daemon's variables, so cleaning the client env
41
+ * is not enough: unset them there too. Applied before every spawn and at
42
+ * daemon start.
43
+ */
44
+ export declare function unsetDaemonEnvArgs(base?: NodeJS.ProcessEnv): string[];
45
+ export interface TmuxResult {
46
+ ok: boolean;
47
+ stdout: string;
48
+ stderr: string;
49
+ }
50
+ export declare function tmux(server: TmuxServer, args: string[], timeoutMs?: number): Promise<TmuxResult>;
51
+ /**
52
+ * Exact-match session target, for commands that take a SESSION target:
53
+ * has-session, kill-session, attach-session.
54
+ */
55
+ export declare function sessionTarget(name: string): string;
56
+ /**
57
+ * Exact-match target for everything that parses -t as a pane or window:
58
+ * set-option, show-options, send-keys, respawn-pane. tmux 3.5a rejects a bare
59
+ * `=name` there with "no such session"; the trailing colon is required
60
+ * (verified 2026-09-25, and hit independently by decibel-hq's mock).
61
+ */
62
+ export declare function paneTarget(name: string): string;
63
+ /**
64
+ * Field separator for -F output. PRINTABLE on purpose: with no LANG/LC_* in
65
+ * the environment — which is exactly the launchd daemon's environment — tmux
66
+ * rewrites a TAB in format output as '_', every field collapsed into the
67
+ * session name, and no spawned session ever matched its project. The tests
68
+ * passed because the shell running them had a locale. Pinned by a test that
69
+ * lists with the locale removed.
70
+ */
71
+ export declare const FIELD_SEP = "|:dcb:|";
72
+ /**
73
+ * Newlines and the field separator would corrupt list output, and a label has
74
+ * no business being long. Applied to every value written into a tmux option.
75
+ */
76
+ export declare function cleanLabel(v: string | undefined, max?: number): string | undefined;
77
+ export interface TmuxSession {
78
+ name: string;
79
+ project_id?: string;
80
+ branch?: string;
81
+ issue_label?: string;
82
+ claude_session_id?: string;
83
+ created?: string;
84
+ cwd?: string;
85
+ /** The active pane's process has exited (remain-on-exit keeps the session). */
86
+ dead: boolean;
87
+ }
88
+ /**
89
+ * "No server" is a normal answer; "could not ask" is not. A missing socket
90
+ * means no server, so no sessions. Any other failure — a server that exists
91
+ * but did not answer — must NOT read as an empty list, because HQ would render
92
+ * a live session as gone. It gets one retry and then throws. (A spawned session
93
+ * once went unlisted for minutes while its server was plainly up; the old check
94
+ * swallowed every "error connecting to" as "no server". Cause unverified.)
95
+ */
96
+ export declare function isNoServer(stderr: string): boolean;
97
+ /** Every session on one server. Empty when that server is not running; throws when it cannot be read. */
98
+ export declare function listSessions(server: TmuxServer): Promise<TmuxSession[]>;
99
+ export declare function parseSessions(stdout: string): TmuxSession[];
100
+ /**
101
+ * pane tty (without /dev/) → session name, across BOTH servers, for matching
102
+ * claude-peers ttys. A user-server session carrying the dcb- prefix is skipped:
103
+ * the prefix means "owned", and honouring it on the wrong server would route
104
+ * attach and kill to a session the daemon does not own.
105
+ */
106
+ export declare function paneTtyMap(): Promise<Map<string, string>>;
107
+ /** Find a session by key on the server its key says it lives on. */
108
+ export declare function findSession(name: string): Promise<TmuxSession | undefined>;
109
+ //# sourceMappingURL=tmux.d.ts.map