@decibelsystems/tools 2.1.2 → 2.1.3

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 (150) hide show
  1. package/dist/agentCommands.d.ts +11 -0
  2. package/dist/agentCommands.d.ts.map +1 -0
  3. package/dist/agentCommands.js +267 -0
  4. package/dist/agentCommands.js.map +1 -0
  5. package/dist/agentInbox.d.ts +37 -0
  6. package/dist/agentInbox.d.ts.map +1 -0
  7. package/dist/agentInbox.js +128 -0
  8. package/dist/agentInbox.js.map +1 -0
  9. package/dist/agentPresence.d.ts +36 -0
  10. package/dist/agentPresence.d.ts.map +1 -0
  11. package/dist/agentPresence.js +227 -0
  12. package/dist/agentPresence.js.map +1 -0
  13. package/dist/agenticJobs.d.ts +49 -0
  14. package/dist/agenticJobs.d.ts.map +1 -0
  15. package/dist/agenticJobs.js +190 -0
  16. package/dist/agenticJobs.js.map +1 -0
  17. package/dist/architectAdrs.d.ts +7 -0
  18. package/dist/architectAdrs.d.ts.map +1 -1
  19. package/dist/architectAdrs.js +91 -28
  20. package/dist/architectAdrs.js.map +1 -1
  21. package/dist/daemon.d.ts +6 -0
  22. package/dist/daemon.d.ts.map +1 -1
  23. package/dist/daemon.js +14 -0
  24. package/dist/daemon.js.map +1 -1
  25. package/dist/facades/definitions.d.ts.map +1 -1
  26. package/dist/facades/definitions.js +40 -6
  27. package/dist/facades/definitions.js.map +1 -1
  28. package/dist/hq/defineAgent.d.ts +11 -0
  29. package/dist/hq/defineAgent.d.ts.map +1 -0
  30. package/dist/hq/defineAgent.js +185 -0
  31. package/dist/hq/defineAgent.js.map +1 -0
  32. package/dist/hq/index.d.ts +3 -0
  33. package/dist/hq/index.d.ts.map +1 -0
  34. package/dist/hq/index.js +28 -0
  35. package/dist/hq/index.js.map +1 -0
  36. package/dist/hq/types.d.ts +59 -0
  37. package/dist/hq/types.d.ts.map +1 -0
  38. package/dist/hq/types.js +12 -0
  39. package/dist/hq/types.js.map +1 -0
  40. package/dist/httpServer.d.ts +1 -1
  41. package/dist/httpServer.d.ts.map +1 -1
  42. package/dist/httpServer.js +327 -19
  43. package/dist/httpServer.js.map +1 -1
  44. package/dist/kernel.d.ts +3 -1
  45. package/dist/kernel.d.ts.map +1 -1
  46. package/dist/kernel.js +40 -9
  47. package/dist/kernel.js.map +1 -1
  48. package/dist/projectRegistry.d.ts +27 -0
  49. package/dist/projectRegistry.d.ts.map +1 -1
  50. package/dist/projectRegistry.js +191 -5
  51. package/dist/projectRegistry.js.map +1 -1
  52. package/dist/sentinelIssues.d.ts +5 -0
  53. package/dist/sentinelIssues.d.ts.map +1 -1
  54. package/dist/sentinelIssues.js +1 -1
  55. package/dist/sentinelIssues.js.map +1 -1
  56. package/dist/server.js +12 -0
  57. package/dist/server.js.map +1 -1
  58. package/dist/store/fsStore.d.ts +6 -0
  59. package/dist/store/fsStore.d.ts.map +1 -0
  60. package/dist/store/fsStore.js +77 -0
  61. package/dist/store/fsStore.js.map +1 -0
  62. package/dist/store/index.d.ts +8 -0
  63. package/dist/store/index.d.ts.map +1 -0
  64. package/dist/store/index.js +23 -0
  65. package/dist/store/index.js.map +1 -0
  66. package/dist/store/markdown.d.ts +21 -0
  67. package/dist/store/markdown.d.ts.map +1 -0
  68. package/dist/store/markdown.js +175 -0
  69. package/dist/store/markdown.js.map +1 -0
  70. package/dist/store/supabaseStore.d.ts +6 -0
  71. package/dist/store/supabaseStore.d.ts.map +1 -0
  72. package/dist/store/supabaseStore.js +152 -0
  73. package/dist/store/supabaseStore.js.map +1 -0
  74. package/dist/store/types.d.ts +49 -0
  75. package/dist/store/types.d.ts.map +1 -0
  76. package/dist/store/types.js +11 -0
  77. package/dist/store/types.js.map +1 -0
  78. package/dist/supabaseRetry.d.ts +21 -0
  79. package/dist/supabaseRetry.d.ts.map +1 -0
  80. package/dist/supabaseRetry.js +72 -0
  81. package/dist/supabaseRetry.js.map +1 -0
  82. package/dist/toolConfig.d.ts +49 -0
  83. package/dist/toolConfig.d.ts.map +1 -0
  84. package/dist/toolConfig.js +309 -0
  85. package/dist/toolConfig.js.map +1 -0
  86. package/dist/tools/agentic/agentQueue.d.ts.map +1 -1
  87. package/dist/tools/agentic/agentQueue.js +7 -5
  88. package/dist/tools/agentic/agentQueue.js.map +1 -1
  89. package/dist/tools/agentic/index.d.ts +3 -0
  90. package/dist/tools/agentic/index.d.ts.map +1 -1
  91. package/dist/tools/agentic/index.js +158 -1
  92. package/dist/tools/agentic/index.js.map +1 -1
  93. package/dist/tools/codereview/index.d.ts +4 -0
  94. package/dist/tools/codereview/index.d.ts.map +1 -0
  95. package/dist/tools/codereview/index.js +93 -0
  96. package/dist/tools/codereview/index.js.map +1 -0
  97. package/dist/tools/codereview.d.ts +56 -0
  98. package/dist/tools/codereview.d.ts.map +1 -0
  99. package/dist/tools/codereview.js +304 -0
  100. package/dist/tools/codereview.js.map +1 -0
  101. package/dist/tools/concepts.d.ts +38 -0
  102. package/dist/tools/concepts.d.ts.map +1 -0
  103. package/dist/tools/concepts.js +283 -0
  104. package/dist/tools/concepts.js.map +1 -0
  105. package/dist/tools/corpus/index.d.ts +3 -0
  106. package/dist/tools/corpus/index.d.ts.map +1 -1
  107. package/dist/tools/corpus/index.js +183 -1
  108. package/dist/tools/corpus/index.js.map +1 -1
  109. package/dist/tools/corpus.d.ts +52 -0
  110. package/dist/tools/corpus.d.ts.map +1 -1
  111. package/dist/tools/corpus.js +207 -2
  112. package/dist/tools/corpus.js.map +1 -1
  113. package/dist/tools/dojo.d.ts.map +1 -1
  114. package/dist/tools/dojo.js +11 -1
  115. package/dist/tools/dojo.js.map +1 -1
  116. package/dist/tools/git-sentinel.d.ts.map +1 -1
  117. package/dist/tools/git-sentinel.js +24 -8
  118. package/dist/tools/git-sentinel.js.map +1 -1
  119. package/dist/tools/index.d.ts.map +1 -1
  120. package/dist/tools/index.js +6 -0
  121. package/dist/tools/index.js.map +1 -1
  122. package/dist/tools/oracle.d.ts +2 -0
  123. package/dist/tools/oracle.d.ts.map +1 -1
  124. package/dist/tools/oracle.js +146 -17
  125. package/dist/tools/oracle.js.map +1 -1
  126. package/dist/tools/peers.d.ts +15 -0
  127. package/dist/tools/peers.d.ts.map +1 -0
  128. package/dist/tools/peers.js +70 -0
  129. package/dist/tools/peers.js.map +1 -0
  130. package/dist/tools/registry/index.d.ts +4 -0
  131. package/dist/tools/registry/index.d.ts.map +1 -1
  132. package/dist/tools/registry/index.js +274 -1
  133. package/dist/tools/registry/index.js.map +1 -1
  134. package/dist/tools/roadmap.d.ts +4 -0
  135. package/dist/tools/roadmap.d.ts.map +1 -1
  136. package/dist/tools/roadmap.js.map +1 -1
  137. package/dist/tools/sentinel.d.ts +15 -1
  138. package/dist/tools/sentinel.d.ts.map +1 -1
  139. package/dist/tools/sentinel.js +198 -34
  140. package/dist/tools/sentinel.js.map +1 -1
  141. package/dist/tools/voice/index.d.ts.map +1 -1
  142. package/dist/tools/voice/index.js +2 -3
  143. package/dist/tools/voice/index.js.map +1 -1
  144. package/dist/tools/voice.d.ts +1 -1
  145. package/dist/tools/voice.js +2 -2
  146. package/dist/tools/voice.js.map +1 -1
  147. package/dist/tools/workflow.js +5 -5
  148. package/dist/tools/workflow.js.map +1 -1
  149. package/dist/transports/http.js +1 -1
  150. package/package.json +4 -2
@@ -0,0 +1,11 @@
1
+ export declare const ORG_ID: string;
2
+ /**
3
+ * Settle a command from an SDK ack (POST /agents/commands/ack). The SDK ran
4
+ * onCommand and reports done|failed (+ optional result/error). We write it back to
5
+ * Core via service_role, org-scoped. Returns false if Supabase is unconfigured.
6
+ * Only 'done'|'failed' are accepted from an ack (the SDK can't set other states).
7
+ */
8
+ export declare function settleCommandFromAck(id: string, status: 'done' | 'failed', result?: Record<string, unknown> | null, error?: string | null): Promise<boolean>;
9
+ /** Start the command dispatcher loop. Returns a stop function. No-ops if Supabase unconfigured. */
10
+ export declare function startCommandDispatcher(): () => void;
11
+ //# sourceMappingURL=agentCommands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentCommands.d.ts","sourceRoot":"","sources":["../src/agentCommands.ts"],"names":[],"mappings":"AAyCA,eAAO,MAAM,MAAM,QAAuE,CAAC;AAoO3F;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,GAAG,QAAQ,EACzB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACvC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACpB,OAAO,CAAC,OAAO,CAAC,CASlB;AAED,mGAAmG;AACnG,wBAAgB,sBAAsB,IAAI,MAAM,IAAI,CAcnD"}
@@ -0,0 +1,267 @@
1
+ // ============================================================================
2
+ // Agent-command dispatcher — HQ → agent control surface (Plan D, v1)
3
+ // ============================================================================
4
+ // The inverse of agentPresence: HQ (org admin) WRITES commands into
5
+ // hq.agent_commands under RLS; this daemon picks up pending commands targeted at
6
+ // sessions IT hosts and dispatches them, writing status/result back via
7
+ // SERVICE_ROLE. v1 kinds (Ben-scoped, allowlisted): 'message' + 'request_status'.
8
+ // No shell, no lifecycle. Contract co-designed with decibel-hq (hq.agent_commands
9
+ // migration + cancel RPC + expire sweep). EPIC-0033.
10
+ //
11
+ // SAFETY: a 'message' is delivered to the target peer as untrusted-channel DATA
12
+ // (via the claude-peers broker) — it is NOT an auto-executed instruction. The
13
+ // daemon only transports it.
14
+ //
15
+ // ⚠️ LOAD-BEARING CROSS-ORG INVARIANT (per HQ /security-review):
16
+ // target_session_key / target_host are FREE TEXT on hq.agent_commands — HQ does
17
+ // NOT bind them to a same-org live session (keys churn). The ENTIRE cross-org
18
+ // targeting defense therefore rests on THIS query: it MUST filter on org_id
19
+ // FIRST (the org this daemon is authorized to serve), with target_host ==
20
+ // own hostname + target_session_key ∈ live registry as defense-in-depth.
21
+ // An org-A admin can write a row naming an org-B peer's guessable session_key,
22
+ // but that row carries org_id=A; as long as org-B's daemon only ever dispatches
23
+ // rows scoped to org B's org_id, the A-row never reaches B. DO NOT refactor this
24
+ // to key purely on (host, session_key) ignoring org_id — that opens cross-org
25
+ // targeting on a shared host.
26
+ //
27
+ // Other boundaries:
28
+ // - host-match: only dispatch commands whose target_host == this host.
29
+ // - expiry: skip expires_at < now; also call the DB sweep each cycle.
30
+ // - poll every 5s (robust; realtime is HQ's status view, not our pickup path).
31
+ // ============================================================================
32
+ import { createClient } from '@supabase/supabase-js';
33
+ import { log } from './config.js';
34
+ import { withRetryResult } from './supabaseRetry.js';
35
+ import { enqueueCommand } from './agentInbox.js';
36
+ import { resolveHost } from './agentPresence.js';
37
+ const BROKER_URL = `http://127.0.0.1:${process.env.CLAUDE_PEERS_PORT ?? '7899'}`;
38
+ // Exported so the HTTP inbox endpoints (httpServer.ts) org-scope drainCommands to
39
+ // the same org this daemon serves — keeps the org-id-first invariant on the read path.
40
+ export const ORG_ID = process.env.DECIBEL_ORG_ID || '1cb79e24-e06f-46c9-8a22-5ee025ffb0f4';
41
+ const POLL_MS = 5_000;
42
+ const ALLOWED_KINDS = new Set(['message', 'request_status']);
43
+ // Ben-scoped staged rollout: the FIRST run dispatches request_status ONLY so he can
44
+ // watch a read-only round-trip before any message reaches a live agent. 'message'
45
+ // dispatch stays held (rows left pending → they expire) until DECIBEL_ENABLE_AGENT_MESSAGES=1.
46
+ const MESSAGES_ENABLED = process.env.DECIBEL_ENABLE_AGENT_MESSAGES === '1';
47
+ function serviceClient() {
48
+ const url = process.env.SUPABASE_URL;
49
+ const key = process.env.SUPABASE_SERVICE_KEY;
50
+ if (!url || !key)
51
+ return null;
52
+ return createClient(url, key, {
53
+ auth: { persistSession: false, autoRefreshToken: false },
54
+ db: { schema: 'hq' },
55
+ });
56
+ }
57
+ async function brokerListPeers() {
58
+ try {
59
+ const res = await fetch(`${BROKER_URL}/list-peers`, {
60
+ method: 'POST',
61
+ headers: { 'Content-Type': 'application/json' },
62
+ body: JSON.stringify({ requester_id: 'decibel-daemon', scope: 'machine' }),
63
+ signal: AbortSignal.timeout(2000),
64
+ });
65
+ if (!res.ok)
66
+ return [];
67
+ return (await res.json());
68
+ }
69
+ catch {
70
+ return [];
71
+ }
72
+ }
73
+ /** Send a peer message via the claude-peers broker. Returns ok + optional error. */
74
+ async function brokerSend(to, message) {
75
+ try {
76
+ const res = await fetch(`${BROKER_URL}/send-message`, {
77
+ method: 'POST',
78
+ headers: { 'Content-Type': 'application/json' },
79
+ // Broker contract: {from_id, to_id, text} (claude-peers broker.ts:280,
80
+ // shared/types.ts:59 — confirmed line-by-line by the claude-peers owner).
81
+ // from_id="decibel-daemon": a plainly NON-privileged relay label. Deliberately
82
+ // NOT "system" — a "system" sender reads to the receiving agent as a trusted/
83
+ // privileged directive, the OPPOSITE of the required posture (an HQ command is
84
+ // untrusted-channel DATA, not an auto-executed instruction). Human provenance
85
+ // rides in `text` ("HQ · <issuer>: ..."). FK is off in the broker so any
86
+ // from_id delivers; if FK is ever enabled, register decibel-daemon then.
87
+ body: JSON.stringify({ from_id: 'decibel-daemon', to_id: to, text: message }),
88
+ signal: AbortSignal.timeout(3000),
89
+ });
90
+ const body = (await res.json().catch(() => ({})));
91
+ return { ok: body.ok === true, error: body.error ?? (res.ok ? undefined : `HTTP ${res.status}`) };
92
+ }
93
+ catch (e) {
94
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
95
+ }
96
+ }
97
+ const TERMINAL = new Set(['done', 'failed', 'cancelled', 'expired']);
98
+ async function settle(client, id, patch) {
99
+ // Stamp completed_at on terminal transitions so HQ can show completion timing.
100
+ if (typeof patch.status === 'string' && TERMINAL.has(patch.status) && !('completed_at' in patch)) {
101
+ patch = { ...patch, completed_at: new Date().toISOString() };
102
+ }
103
+ // org_id-scoped settle too — never update a row outside our authorized org.
104
+ const { error } = await client.from('agent_commands').update(patch).eq('id', id).eq('org_id', ORG_ID);
105
+ if (error)
106
+ log(`Commands: settle ${id} failed: ${error.message}`);
107
+ }
108
+ async function dispatchOne(client, cmd, peers, sdkSessions) {
109
+ // Defensive expiry + kind allowlist (DB also enforces, belt-and-suspenders).
110
+ if (cmd.expires_at && Date.parse(cmd.expires_at) < Date.now())
111
+ return;
112
+ if (!ALLOWED_KINDS.has(cmd.kind)) {
113
+ await settle(client, cmd.id, { status: 'failed', error: `unsupported kind '${cmd.kind}'` });
114
+ return;
115
+ }
116
+ // Staged rollout: hold 'message' until explicitly enabled. Leave the row pending
117
+ // (untouched) so it simply expires — do NOT settle, so it can dispatch later if
118
+ // messages are enabled before it expires.
119
+ if (cmd.kind === 'message' && !MESSAGES_ENABLED)
120
+ return;
121
+ const peer = peers.find((p) => p.id === cmd.target_session_key);
122
+ const isSdkSession = !peer && sdkSessions.has(cmd.target_session_key);
123
+ // Target not (yet) resolvable as either a broker peer OR a known SDK session.
124
+ // DON'T permanently fail it — a session that registers in the split-second after
125
+ // the command was issued would otherwise be lost on the first tick. Leave it
126
+ // 'pending' so the next tick re-checks; the DB expiry sweep terminates it (→
127
+ // 'expired') if the target never appears. (Race fix found via SDK e2e 2026-06-15.)
128
+ if (!peer && !isSdkSession) {
129
+ return; // leave pending; retried next tick, expires if truly absent
130
+ }
131
+ // SDK-runtime target (registered via /agents/register, NOT a claude-peers peer):
132
+ // it has no broker transport, so we don't resolve/deliver here. Enqueue it for the
133
+ // SDK to poll + ack (which settles the Core row). Mark 'delivered' so HQ sees
134
+ // pickup; the SDK ack flips it to done/failed. (request_status is treated the same
135
+ // way for SDK sessions — the SDK answers it via onCommand and acks with a result.)
136
+ if (isSdkSession) {
137
+ await settle(client, cmd.id, { status: 'delivered', delivered_at: new Date().toISOString() });
138
+ enqueueCommand(cmd.target_session_key, {
139
+ id: cmd.id,
140
+ org_id: cmd.org_id,
141
+ kind: cmd.kind,
142
+ payload: cmd.payload ?? {},
143
+ enqueued_at: Date.now(),
144
+ });
145
+ return;
146
+ }
147
+ // From here, peer is a live broker session. Mark delivered (picked up).
148
+ await settle(client, cmd.id, { status: 'delivered', delivered_at: new Date().toISOString() });
149
+ if (cmd.kind === 'request_status') {
150
+ await settle(client, cmd.id, {
151
+ status: 'done',
152
+ result: { summary: peer.summary || null, cwd: peer.cwd || null, last_seen: peer.last_seen, status: 'active' },
153
+ });
154
+ return;
155
+ }
156
+ // kind === 'message'
157
+ const text = cmd.payload?.text;
158
+ if (!text) {
159
+ await settle(client, cmd.id, { status: 'failed', error: 'message payload missing text' });
160
+ return;
161
+ }
162
+ const label = cmd.payload?.from_label ? `${cmd.payload.from_label}: ` : '';
163
+ const r = await brokerSend(cmd.target_session_key, `${label}${text}`);
164
+ if (r.ok) {
165
+ await settle(client, cmd.id, { status: 'done', result: { delivered: true } });
166
+ }
167
+ else {
168
+ await settle(client, cmd.id, { status: 'failed', error: r.error || 'broker send failed' });
169
+ }
170
+ }
171
+ async function tick(client) {
172
+ const host = resolveHost();
173
+ // DB-side sweep: flip pending->expired where past expiry (idempotent, service_role).
174
+ try {
175
+ await client.rpc('expire_stale_agent_commands');
176
+ }
177
+ catch { /* RPC optional/non-fatal */ }
178
+ // Pending commands for OUR org (primary cross-org filter — see invariant above),
179
+ // targeted at sessions THIS daemon hosts. withRetryResult smooths transient
180
+ // "fetch failed" blips (issue 2026-06-13) before giving up for this tick.
181
+ const { data, error } = await withRetryResult(() => client
182
+ .from('agent_commands')
183
+ .select('id,org_id,target_session_key,target_host,kind,payload,expires_at,status')
184
+ .eq('org_id', ORG_ID)
185
+ .eq('status', 'pending')
186
+ .eq('target_host', host), 'commands.poll');
187
+ if (error) {
188
+ log(`Commands: poll failed: ${error.message}`);
189
+ return;
190
+ }
191
+ const pending = (data ?? []);
192
+ if (pending.length === 0)
193
+ return;
194
+ // SDK runtimes hosting on this box (live, non-claude-code sessions): commands
195
+ // targeting these route to the in-daemon inbox for the SDK to poll+ack, instead
196
+ // of failing as "not found in live registry". org-id-first + host-match preserved.
197
+ const sdkSessions = await loadSdkSessions(client, host);
198
+ const peers = await brokerListPeers();
199
+ for (const cmd of pending) {
200
+ try {
201
+ await dispatchOne(client, cmd, peers, sdkSessions);
202
+ }
203
+ catch (e) {
204
+ log(`Commands: dispatch ${cmd.id} error: ${e instanceof Error ? e.message : String(e)}`);
205
+ await settle(client, cmd.id, { status: 'failed', error: 'dispatch exception' }).catch(() => { });
206
+ }
207
+ }
208
+ }
209
+ /**
210
+ * Live SDK-runtime session_keys this daemon hosts (org + host scoped, runtime not
211
+ * claude-code, status active). These receive commands via the inbox, not the broker.
212
+ */
213
+ async function loadSdkSessions(client, host) {
214
+ const { data, error } = await withRetryResult(() => client
215
+ .from('agent_sessions')
216
+ .select('session_key,runtime,status')
217
+ .eq('org_id', ORG_ID)
218
+ .eq('host', host)
219
+ .eq('status', 'active')
220
+ .neq('runtime', 'claude-code'), 'commands.sdkSessions');
221
+ const set = new Set();
222
+ if (error)
223
+ log(`Commands: loadSdkSessions query error: ${error.message}`);
224
+ if (!error && data) {
225
+ for (const r of data)
226
+ set.add(r.session_key);
227
+ }
228
+ return set;
229
+ }
230
+ /**
231
+ * Settle a command from an SDK ack (POST /agents/commands/ack). The SDK ran
232
+ * onCommand and reports done|failed (+ optional result/error). We write it back to
233
+ * Core via service_role, org-scoped. Returns false if Supabase is unconfigured.
234
+ * Only 'done'|'failed' are accepted from an ack (the SDK can't set other states).
235
+ */
236
+ export async function settleCommandFromAck(id, status, result, error) {
237
+ const client = serviceClient();
238
+ if (!client)
239
+ return false;
240
+ if (status !== 'done' && status !== 'failed')
241
+ return false;
242
+ const patch = { status };
243
+ if (result !== undefined && result !== null)
244
+ patch.result = result;
245
+ if (error !== undefined && error !== null)
246
+ patch.error = error;
247
+ await settle(client, id, patch);
248
+ return true;
249
+ }
250
+ /** Start the command dispatcher loop. Returns a stop function. No-ops if Supabase unconfigured. */
251
+ export function startCommandDispatcher() {
252
+ const client = serviceClient();
253
+ if (!client) {
254
+ log('Commands: SUPABASE_URL/SERVICE_KEY not set — agent-command dispatcher disabled.');
255
+ return () => { };
256
+ }
257
+ log(`Commands: dispatcher started (5s poll of hq.agent_commands, host ${resolveHost()}, org ${ORG_ID}).`);
258
+ const run = () => {
259
+ tick(client).catch((e) => log(`Commands: tick error: ${e instanceof Error ? e.message : String(e)}`));
260
+ };
261
+ run();
262
+ const iv = setInterval(run, POLL_MS);
263
+ if (typeof iv.unref === 'function')
264
+ iv.unref();
265
+ return () => clearInterval(iv);
266
+ }
267
+ //# sourceMappingURL=agentCommands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentCommands.js","sourceRoot":"","sources":["../src/agentCommands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ export interface InboxCommand {
2
+ id: string;
3
+ org_id: string;
4
+ kind: string;
5
+ payload: Record<string, unknown>;
6
+ enqueued_at: number;
7
+ }
8
+ /**
9
+ * Enqueue a command for an SDK session to poll. Idempotent on command id. Also
10
+ * records the id→session ownership (for ack authorization). cmd.org_id is the
11
+ * owning org; drainCommands only returns commands whose org matches the caller's.
12
+ */
13
+ export declare function enqueueCommand(sessionKey: string, cmd: InboxCommand): void;
14
+ /**
15
+ * Drain pending commands for a session (returns + removes them). Evicts stale
16
+ * first. Only returns commands belonging to `orgId` — a cross-org command (which
17
+ * should never be enqueued here given the dispatcher's org-first filter) is
18
+ * dropped, never delivered.
19
+ */
20
+ export declare function drainCommands(sessionKey: string, orgId: string): InboxCommand[];
21
+ /** Is this session_key one the inbox is tracking? (has pending commands) */
22
+ export declare function hasInbox(sessionKey: string): boolean;
23
+ /** Store (or replace) the capability token for a session. */
24
+ export declare function setSessionToken(sessionKey: string, token: string): void;
25
+ /** The capability token for a session, or undefined if none is registered. */
26
+ export declare function getSessionToken(sessionKey: string): string | undefined;
27
+ /** Forget a session's token (on 'ended' lifecycle). */
28
+ export declare function dropSessionToken(sessionKey: string): void;
29
+ /** The session_key a command id was enqueued for (for ack authorization). */
30
+ export declare function ackOwnerOf(id: string): string | undefined;
31
+ /** Forget an id→session ownership once the command has been acked/settled. */
32
+ export declare function clearAckOwner(id: string): void;
33
+ /** Total queued commands across all sessions (for /health + tests). */
34
+ export declare function inboxDepth(): number;
35
+ /** Test/shutdown helper — resets inboxes, tokens, and ack-owners. */
36
+ export declare function clearInbox(): void;
37
+ //# sourceMappingURL=agentInbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentInbox.d.ts","sourceRoot":"","sources":["../src/agentInbox.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;CACrB;AAyBD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,IAAI,CAQ1E;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CAK/E;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAGpD;AAMD,6DAA6D;AAC7D,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAEvE;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEtE;AAED,uDAAuD;AACvD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAEzD;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEzD;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE9C;AAeD,uEAAuE;AACvE,wBAAgB,UAAU,IAAI,MAAM,CAInC;AAED,qEAAqE;AACrE,wBAAgB,UAAU,IAAI,IAAI,CAIjC"}
@@ -0,0 +1,128 @@
1
+ // ============================================================================
2
+ // Agent command inbox — daemon-side delivery for SDK (non-broker) runtimes
3
+ // ============================================================================
4
+ // The HQ→agent command dispatcher (agentCommands.ts) delivers `message` to
5
+ // claude-peers sessions via the broker, and answers `request_status` from the
6
+ // broker's live registry. But SDK-registered runtimes (hermes/openclaw/codex/…
7
+ // from @decibelsystems/tools/hq) aren't in the broker — they have no transport.
8
+ //
9
+ // This module bridges that gap WITHOUT giving the SDK any Supabase creds (the
10
+ // "no service_role in untrusted hands" rule holds): the dispatcher ENQUEUES a
11
+ // command targeting a known SDK session here; the SDK polls GET /agents/commands
12
+ // + acks POST /agents/commands/ack (both localhost-only); the daemon settles the
13
+ // hq.agent_commands row in Core via service_role on ack.
14
+ //
15
+ // IDENTITY — capability token, NOT a bare label (security review 2026-06-15):
16
+ // session_key is a GUESSABLE label, so localhost-bind alone would let any
17
+ // co-resident local runtime drain/ack ANOTHER runtime's commands (the SDK's whole
18
+ // premise is several runtimes side-by-side). So the daemon mints a per-session
19
+ // SECRET token at /agents/register, returns it to that one SDK process, and
20
+ // requires it on poll + ack. session_key says "which inbox"; the token proves
21
+ // "I'm the process that registered it." The org-id-first cross-org invariant is
22
+ // preserved here too: InboxCommand carries org_id and drainCommands filters on it,
23
+ // so even a token-authenticated poll only ever receives its own org's commands.
24
+ // ============================================================================
25
+ // session_key → its pending commands. In-memory: the daemon is the single
26
+ // authority for sessions it hosts; a restart re-derives state from Core (pending
27
+ // rows get re-dispatched on the next tick).
28
+ const inboxes = new Map();
29
+ // session_key → its per-session capability token (the secret an SDK must present
30
+ // on poll/ack to prove it's the registrant). Minted by the HTTP register/heartbeat
31
+ // handler; verified there too (timing-safe). Wiped on a daemon restart — the SDK's
32
+ // next heartbeat re-mints, and it stores the fresh token from the response.
33
+ const sessionTokens = new Map();
34
+ // command id → the session_key it was enqueued for, with a timestamp for TTL
35
+ // eviction. The ack arrives AFTER drainCommands has already removed the command
36
+ // from the inbox, so the inbox itself can't tell us who owns an acked id — this
37
+ // map can. The ack handler maps id→session→token to authorize the settle.
38
+ const ackOwners = new Map();
39
+ // Drop inbox entries an SDK never polled, so a vanished runtime doesn't leak
40
+ // memory. Slightly longer than the command expiry (5min) so a slow poller still
41
+ // gets it; the dispatcher's expiry sweep settles the Core row regardless.
42
+ const INBOX_TTL_MS = 6 * 60_000;
43
+ const MAX_PER_SESSION = 100; // backstop against unbounded growth per session
44
+ /**
45
+ * Enqueue a command for an SDK session to poll. Idempotent on command id. Also
46
+ * records the id→session ownership (for ack authorization). cmd.org_id is the
47
+ * owning org; drainCommands only returns commands whose org matches the caller's.
48
+ */
49
+ export function enqueueCommand(sessionKey, cmd) {
50
+ const list = inboxes.get(sessionKey) ?? [];
51
+ if (list.some((c) => c.id === cmd.id))
52
+ return; // already queued (keep first owner)
53
+ list.push(cmd);
54
+ // Trim oldest if a session somehow accumulates a flood (shouldn't happen).
55
+ while (list.length > MAX_PER_SESSION)
56
+ list.shift();
57
+ inboxes.set(sessionKey, list);
58
+ ackOwners.set(cmd.id, { sessionKey, at: cmd.enqueued_at });
59
+ }
60
+ /**
61
+ * Drain pending commands for a session (returns + removes them). Evicts stale
62
+ * first. Only returns commands belonging to `orgId` — a cross-org command (which
63
+ * should never be enqueued here given the dispatcher's org-first filter) is
64
+ * dropped, never delivered.
65
+ */
66
+ export function drainCommands(sessionKey, orgId) {
67
+ evictStale();
68
+ const list = inboxes.get(sessionKey) ?? [];
69
+ inboxes.delete(sessionKey);
70
+ return list.filter((c) => c.org_id === orgId);
71
+ }
72
+ /** Is this session_key one the inbox is tracking? (has pending commands) */
73
+ export function hasInbox(sessionKey) {
74
+ const list = inboxes.get(sessionKey);
75
+ return !!list && list.length > 0;
76
+ }
77
+ // ---------------------------------------------------------------------------
78
+ // Per-session capability tokens (proof-of-possession for poll/ack)
79
+ // ---------------------------------------------------------------------------
80
+ /** Store (or replace) the capability token for a session. */
81
+ export function setSessionToken(sessionKey, token) {
82
+ sessionTokens.set(sessionKey, token);
83
+ }
84
+ /** The capability token for a session, or undefined if none is registered. */
85
+ export function getSessionToken(sessionKey) {
86
+ return sessionTokens.get(sessionKey);
87
+ }
88
+ /** Forget a session's token (on 'ended' lifecycle). */
89
+ export function dropSessionToken(sessionKey) {
90
+ sessionTokens.delete(sessionKey);
91
+ }
92
+ /** The session_key a command id was enqueued for (for ack authorization). */
93
+ export function ackOwnerOf(id) {
94
+ return ackOwners.get(id)?.sessionKey;
95
+ }
96
+ /** Forget an id→session ownership once the command has been acked/settled. */
97
+ export function clearAckOwner(id) {
98
+ ackOwners.delete(id);
99
+ }
100
+ /** Evict commands + ack-owners older than the TTL (the dispatcher/DB sweep handles Core-side). */
101
+ function evictStale() {
102
+ const cutoff = Date.now() - INBOX_TTL_MS;
103
+ for (const [key, list] of inboxes) {
104
+ const kept = list.filter((c) => c.enqueued_at >= cutoff);
105
+ if (kept.length === 0)
106
+ inboxes.delete(key);
107
+ else if (kept.length !== list.length)
108
+ inboxes.set(key, kept);
109
+ }
110
+ for (const [id, owner] of ackOwners) {
111
+ if (owner.at < cutoff)
112
+ ackOwners.delete(id);
113
+ }
114
+ }
115
+ /** Total queued commands across all sessions (for /health + tests). */
116
+ export function inboxDepth() {
117
+ let n = 0;
118
+ for (const list of inboxes.values())
119
+ n += list.length;
120
+ return n;
121
+ }
122
+ /** Test/shutdown helper — resets inboxes, tokens, and ack-owners. */
123
+ export function clearInbox() {
124
+ inboxes.clear();
125
+ sessionTokens.clear();
126
+ ackOwners.clear();
127
+ }
128
+ //# sourceMappingURL=agentInbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentInbox.js","sourceRoot":"","sources":["../src/agentInbox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The daemon's host identity for presence + command targeting. Defaults to
3
+ * os.hostname() but is overridable via DECIBEL_PRESENCE_HOST — needed where the
4
+ * OS hostname isn't a stable/unique daemon identity (containers with shared
5
+ * hostnames, or two daemons on one machine, e.g. a rolling restart or a test
6
+ * harness). target_host on hq.agent_commands matches this, so it must be the same
7
+ * value the presence writer stamps. Shared by the dispatcher (agentCommands.ts).
8
+ */
9
+ export declare function resolveHost(): string;
10
+ export interface LocalAgentRegistration {
11
+ session_key: string;
12
+ runtime: string;
13
+ agent?: string | null;
14
+ cwd?: string | null;
15
+ summary?: string | null;
16
+ meta?: Record<string, unknown>;
17
+ status?: 'active' | 'ended';
18
+ }
19
+ /** Strip control chars/null bytes and cap length. Empty-after-clean → null. */
20
+ export declare function sanitizeText(s: string | null | undefined, maxLen: number): string | null;
21
+ /**
22
+ * Flatten meta to display-safe primitives: strings cleaned + capped, numbers/
23
+ * booleans/null kept, nested objects/arrays dropped (HQ typeof-guards numeric
24
+ * meta and renders strings as text). If the result still exceeds META_MAX_BYTES,
25
+ * drop it entirely — the caller re-adds the trusted last_action_at.
26
+ */
27
+ export declare function sanitizeMeta(meta: Record<string, unknown> | undefined): Record<string, unknown>;
28
+ /**
29
+ * Upsert a local-runtime session into hq.agent_sessions (register or heartbeat).
30
+ * Returns true on success, false if Supabase is unconfigured or the write failed.
31
+ * Reuses the presence writer's org config + cwd→project resolution.
32
+ */
33
+ export declare function writeLocalAgentSession(reg: LocalAgentRegistration): Promise<boolean>;
34
+ /** Start the presence writer loop. Returns a stop function. No-ops if Supabase unconfigured. */
35
+ export declare function startPresenceWriter(): () => void;
36
+ //# sourceMappingURL=agentPresence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentPresence.d.ts","sourceRoot":"","sources":["../src/agentPresence.ts"],"names":[],"mappings":"AAsBA;;;;;;;GAOG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAwHD,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAG/B,MAAM,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;CAC7B;AAmBD,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIxF;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAU/F;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,sBAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CAuC1F;AAED,gGAAgG;AAChG,wBAAgB,mBAAmB,IAAI,MAAM,IAAI,CAchD"}