@decibelsystems/tools 2.1.2 → 2.1.4

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 (151) hide show
  1. package/README.md +144 -63
  2. package/dist/agentCommands.d.ts +11 -0
  3. package/dist/agentCommands.d.ts.map +1 -0
  4. package/dist/agentCommands.js +267 -0
  5. package/dist/agentCommands.js.map +1 -0
  6. package/dist/agentInbox.d.ts +37 -0
  7. package/dist/agentInbox.d.ts.map +1 -0
  8. package/dist/agentInbox.js +128 -0
  9. package/dist/agentInbox.js.map +1 -0
  10. package/dist/agentPresence.d.ts +36 -0
  11. package/dist/agentPresence.d.ts.map +1 -0
  12. package/dist/agentPresence.js +227 -0
  13. package/dist/agentPresence.js.map +1 -0
  14. package/dist/agenticJobs.d.ts +49 -0
  15. package/dist/agenticJobs.d.ts.map +1 -0
  16. package/dist/agenticJobs.js +190 -0
  17. package/dist/agenticJobs.js.map +1 -0
  18. package/dist/architectAdrs.d.ts +7 -0
  19. package/dist/architectAdrs.d.ts.map +1 -1
  20. package/dist/architectAdrs.js +91 -28
  21. package/dist/architectAdrs.js.map +1 -1
  22. package/dist/daemon.d.ts +6 -0
  23. package/dist/daemon.d.ts.map +1 -1
  24. package/dist/daemon.js +14 -0
  25. package/dist/daemon.js.map +1 -1
  26. package/dist/facades/definitions.d.ts.map +1 -1
  27. package/dist/facades/definitions.js +40 -6
  28. package/dist/facades/definitions.js.map +1 -1
  29. package/dist/hq/defineAgent.d.ts +11 -0
  30. package/dist/hq/defineAgent.d.ts.map +1 -0
  31. package/dist/hq/defineAgent.js +185 -0
  32. package/dist/hq/defineAgent.js.map +1 -0
  33. package/dist/hq/index.d.ts +3 -0
  34. package/dist/hq/index.d.ts.map +1 -0
  35. package/dist/hq/index.js +28 -0
  36. package/dist/hq/index.js.map +1 -0
  37. package/dist/hq/types.d.ts +59 -0
  38. package/dist/hq/types.d.ts.map +1 -0
  39. package/dist/hq/types.js +12 -0
  40. package/dist/hq/types.js.map +1 -0
  41. package/dist/httpServer.d.ts +1 -1
  42. package/dist/httpServer.d.ts.map +1 -1
  43. package/dist/httpServer.js +327 -19
  44. package/dist/httpServer.js.map +1 -1
  45. package/dist/kernel.d.ts +3 -1
  46. package/dist/kernel.d.ts.map +1 -1
  47. package/dist/kernel.js +40 -9
  48. package/dist/kernel.js.map +1 -1
  49. package/dist/projectRegistry.d.ts +27 -0
  50. package/dist/projectRegistry.d.ts.map +1 -1
  51. package/dist/projectRegistry.js +191 -5
  52. package/dist/projectRegistry.js.map +1 -1
  53. package/dist/sentinelIssues.d.ts +5 -0
  54. package/dist/sentinelIssues.d.ts.map +1 -1
  55. package/dist/sentinelIssues.js +1 -1
  56. package/dist/sentinelIssues.js.map +1 -1
  57. package/dist/server.js +12 -0
  58. package/dist/server.js.map +1 -1
  59. package/dist/store/fsStore.d.ts +6 -0
  60. package/dist/store/fsStore.d.ts.map +1 -0
  61. package/dist/store/fsStore.js +77 -0
  62. package/dist/store/fsStore.js.map +1 -0
  63. package/dist/store/index.d.ts +8 -0
  64. package/dist/store/index.d.ts.map +1 -0
  65. package/dist/store/index.js +23 -0
  66. package/dist/store/index.js.map +1 -0
  67. package/dist/store/markdown.d.ts +21 -0
  68. package/dist/store/markdown.d.ts.map +1 -0
  69. package/dist/store/markdown.js +175 -0
  70. package/dist/store/markdown.js.map +1 -0
  71. package/dist/store/supabaseStore.d.ts +6 -0
  72. package/dist/store/supabaseStore.d.ts.map +1 -0
  73. package/dist/store/supabaseStore.js +152 -0
  74. package/dist/store/supabaseStore.js.map +1 -0
  75. package/dist/store/types.d.ts +49 -0
  76. package/dist/store/types.d.ts.map +1 -0
  77. package/dist/store/types.js +11 -0
  78. package/dist/store/types.js.map +1 -0
  79. package/dist/supabaseRetry.d.ts +21 -0
  80. package/dist/supabaseRetry.d.ts.map +1 -0
  81. package/dist/supabaseRetry.js +72 -0
  82. package/dist/supabaseRetry.js.map +1 -0
  83. package/dist/toolConfig.d.ts +49 -0
  84. package/dist/toolConfig.d.ts.map +1 -0
  85. package/dist/toolConfig.js +309 -0
  86. package/dist/toolConfig.js.map +1 -0
  87. package/dist/tools/agentic/agentQueue.d.ts.map +1 -1
  88. package/dist/tools/agentic/agentQueue.js +7 -5
  89. package/dist/tools/agentic/agentQueue.js.map +1 -1
  90. package/dist/tools/agentic/index.d.ts +3 -0
  91. package/dist/tools/agentic/index.d.ts.map +1 -1
  92. package/dist/tools/agentic/index.js +158 -1
  93. package/dist/tools/agentic/index.js.map +1 -1
  94. package/dist/tools/codereview/index.d.ts +4 -0
  95. package/dist/tools/codereview/index.d.ts.map +1 -0
  96. package/dist/tools/codereview/index.js +93 -0
  97. package/dist/tools/codereview/index.js.map +1 -0
  98. package/dist/tools/codereview.d.ts +56 -0
  99. package/dist/tools/codereview.d.ts.map +1 -0
  100. package/dist/tools/codereview.js +304 -0
  101. package/dist/tools/codereview.js.map +1 -0
  102. package/dist/tools/concepts.d.ts +38 -0
  103. package/dist/tools/concepts.d.ts.map +1 -0
  104. package/dist/tools/concepts.js +283 -0
  105. package/dist/tools/concepts.js.map +1 -0
  106. package/dist/tools/corpus/index.d.ts +3 -0
  107. package/dist/tools/corpus/index.d.ts.map +1 -1
  108. package/dist/tools/corpus/index.js +183 -1
  109. package/dist/tools/corpus/index.js.map +1 -1
  110. package/dist/tools/corpus.d.ts +52 -0
  111. package/dist/tools/corpus.d.ts.map +1 -1
  112. package/dist/tools/corpus.js +207 -2
  113. package/dist/tools/corpus.js.map +1 -1
  114. package/dist/tools/dojo.d.ts.map +1 -1
  115. package/dist/tools/dojo.js +11 -1
  116. package/dist/tools/dojo.js.map +1 -1
  117. package/dist/tools/git-sentinel.d.ts.map +1 -1
  118. package/dist/tools/git-sentinel.js +24 -8
  119. package/dist/tools/git-sentinel.js.map +1 -1
  120. package/dist/tools/index.d.ts.map +1 -1
  121. package/dist/tools/index.js +6 -0
  122. package/dist/tools/index.js.map +1 -1
  123. package/dist/tools/oracle.d.ts +2 -0
  124. package/dist/tools/oracle.d.ts.map +1 -1
  125. package/dist/tools/oracle.js +146 -17
  126. package/dist/tools/oracle.js.map +1 -1
  127. package/dist/tools/peers.d.ts +15 -0
  128. package/dist/tools/peers.d.ts.map +1 -0
  129. package/dist/tools/peers.js +70 -0
  130. package/dist/tools/peers.js.map +1 -0
  131. package/dist/tools/registry/index.d.ts +4 -0
  132. package/dist/tools/registry/index.d.ts.map +1 -1
  133. package/dist/tools/registry/index.js +274 -1
  134. package/dist/tools/registry/index.js.map +1 -1
  135. package/dist/tools/roadmap.d.ts +4 -0
  136. package/dist/tools/roadmap.d.ts.map +1 -1
  137. package/dist/tools/roadmap.js.map +1 -1
  138. package/dist/tools/sentinel.d.ts +15 -1
  139. package/dist/tools/sentinel.d.ts.map +1 -1
  140. package/dist/tools/sentinel.js +198 -34
  141. package/dist/tools/sentinel.js.map +1 -1
  142. package/dist/tools/voice/index.d.ts.map +1 -1
  143. package/dist/tools/voice/index.js +2 -3
  144. package/dist/tools/voice/index.js.map +1 -1
  145. package/dist/tools/voice.d.ts +1 -1
  146. package/dist/tools/voice.js +2 -2
  147. package/dist/tools/voice.js.map +1 -1
  148. package/dist/tools/workflow.js +5 -5
  149. package/dist/tools/workflow.js.map +1 -1
  150. package/dist/transports/http.js +1 -1
  151. package/package.json +4 -2
@@ -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"}
@@ -0,0 +1,227 @@
1
+ // ============================================================================
2
+ // Agent-presence writer — heartbeats live claude-peers sessions into Core
3
+ // ============================================================================
4
+ // Daemon-side telemetry writer for the Plan D agent-presence domain. On a 30s
5
+ // loop it:
6
+ // 1. reads live peers from the claude-peers broker (POST /list-peers),
7
+ // 2. upserts each into hq.agent_sessions via SERVICE_ROLE (deliberate scoped
8
+ // exception — presence is telemetry, peers carry no user JWT, v1 single-org),
9
+ // 3. stale-sweeps active rows that stopped heartbeating → idle (>90s) / ended (>5min).
10
+ // Never throws into the daemon; no-ops if Supabase isn't configured or the broker
11
+ // is down. Contract co-designed with decibel-hq (migration 20260524000005). EPIC-0033.
12
+ // ============================================================================
13
+ import os from 'os';
14
+ import path from 'path';
15
+ import { createClient } from '@supabase/supabase-js';
16
+ import { log } from './config.js';
17
+ import { withRetryResult } from './supabaseRetry.js';
18
+ const BROKER_URL = `http://127.0.0.1:${process.env.CLAUDE_PEERS_PORT ?? '7899'}`;
19
+ const ORG_ID = process.env.DECIBEL_ORG_ID || '1cb79e24-e06f-46c9-8a22-5ee025ffb0f4';
20
+ /**
21
+ * The daemon's host identity for presence + command targeting. Defaults to
22
+ * os.hostname() but is overridable via DECIBEL_PRESENCE_HOST — needed where the
23
+ * OS hostname isn't a stable/unique daemon identity (containers with shared
24
+ * hostnames, or two daemons on one machine, e.g. a rolling restart or a test
25
+ * harness). target_host on hq.agent_commands matches this, so it must be the same
26
+ * value the presence writer stamps. Shared by the dispatcher (agentCommands.ts).
27
+ */
28
+ export function resolveHost() {
29
+ return process.env.DECIBEL_PRESENCE_HOST || os.hostname();
30
+ }
31
+ const HEARTBEAT_MS = 30_000;
32
+ const IDLE_AFTER_MS = 90_000;
33
+ const ENDED_AFTER_MS = 5 * 60_000;
34
+ function serviceClient() {
35
+ const url = process.env.SUPABASE_URL;
36
+ const key = process.env.SUPABASE_SERVICE_KEY;
37
+ if (!url || !key)
38
+ return null;
39
+ return createClient(url, key, {
40
+ auth: { persistSession: false, autoRefreshToken: false },
41
+ db: { schema: 'hq' },
42
+ });
43
+ }
44
+ async function fetchPeers() {
45
+ try {
46
+ const res = await fetch(`${BROKER_URL}/list-peers`, {
47
+ method: 'POST',
48
+ headers: { 'Content-Type': 'application/json' },
49
+ body: JSON.stringify({ requester_id: 'decibel-presence', scope: 'machine' }),
50
+ signal: AbortSignal.timeout(2000),
51
+ });
52
+ if (!res.ok)
53
+ return [];
54
+ return (await res.json());
55
+ }
56
+ catch {
57
+ return [];
58
+ }
59
+ }
60
+ // project label (repo dir basename) → hq.projects.id within the org. Cached;
61
+ // refreshed on a miss so newly-onboarded projects resolve.
62
+ let projectMap = null;
63
+ async function loadProjects(client) {
64
+ const { data, error } = await client.from('projects').select('id,key').eq('org_id', ORG_ID);
65
+ const m = new Map();
66
+ if (!error && data)
67
+ for (const r of data)
68
+ m.set(r.key, r.id);
69
+ return m;
70
+ }
71
+ async function resolveProjectId(client, cwd) {
72
+ const key = path.basename(cwd || '');
73
+ if (!key)
74
+ return null;
75
+ if (!projectMap)
76
+ projectMap = await loadProjects(client);
77
+ if (!projectMap.has(key))
78
+ projectMap = await loadProjects(client); // refresh once on miss
79
+ return projectMap.get(key) ?? null;
80
+ }
81
+ async function tick(client) {
82
+ const host = resolveHost();
83
+ const nowMs = Date.now();
84
+ const nowIso = new Date(nowMs).toISOString();
85
+ const peers = await fetchPeers();
86
+ for (const p of peers) {
87
+ if (!p.id)
88
+ continue;
89
+ const project_id = await resolveProjectId(client, p.cwd);
90
+ const row = {
91
+ org_id: ORG_ID,
92
+ host,
93
+ session_key: p.id,
94
+ agent: null,
95
+ // These are Claude Code sessions read from the claude-peers broker; tag the
96
+ // runtime so HQ's /agents board renders the runtime badge + filter (P1/P5).
97
+ runtime: 'claude-code',
98
+ summary: p.summary || null,
99
+ cwd: p.cwd || null,
100
+ project_id,
101
+ status: 'active',
102
+ started_at: p.registered_at || nowIso,
103
+ last_seen_at: nowIso,
104
+ ended_at: null,
105
+ // last_action_at = the broker's per-peer last_seen → HQ's ActivityLine renders
106
+ // a tool-call pulse from it. Richer self-report fields (tokens, current_file)
107
+ // are a follow-on (agent-side self-report; not daemon-observable).
108
+ meta: { last_action_at: p.last_seen || nowIso },
109
+ };
110
+ const { error } = await withRetryResult(() => client.from('agent_sessions').upsert(row, { onConflict: 'org_id,host,session_key' }), `presence.upsert ${p.id}`);
111
+ if (error)
112
+ log(`Presence: upsert ${p.id} failed: ${error.message}`);
113
+ }
114
+ // Stale-sweep: sessions that stopped heartbeating (broker dropped them, or daemon
115
+ // missed beats). idle first, then ended (ended uses status<>'ended' so it also
116
+ // catches idle rows past the longer cutoff).
117
+ const idleCut = new Date(nowMs - IDLE_AFTER_MS).toISOString();
118
+ const endedCut = new Date(nowMs - ENDED_AFTER_MS).toISOString();
119
+ await client.from('agent_sessions').update({ status: 'idle' })
120
+ .eq('org_id', ORG_ID).eq('status', 'active').lt('last_seen_at', idleCut);
121
+ await client.from('agent_sessions').update({ status: 'ended', ended_at: nowIso })
122
+ .eq('org_id', ORG_ID).neq('status', 'ended').lt('last_seen_at', endedCut);
123
+ }
124
+ // ---------------------------------------------------------------------------
125
+ // Write-time presence-field belt (NON-encoding) — co-designed with decibel-hq.
126
+ // ---------------------------------------------------------------------------
127
+ // HQ's /agents board already escapes these at render (React JSX text children),
128
+ // so the daemon MUST NOT HTML-encode here — that would double-escape and corrupt
129
+ // legitimate data (a real summary like "fix <Button> when a < b"). The correct
130
+ // write-time defense-in-depth is non-encoding: cap length + strip control chars /
131
+ // null bytes (preserving \t\n\r) so the stored value stays faithful but bounded.
132
+ // Also closes the review's "no validation on meta/result blobs" finding.
133
+ const FIELD_CAPS = { agent: 200, summary: 500, cwd: 1024 };
134
+ const META_VALUE_CAP = 1000;
135
+ const META_MAX_BYTES = 4096;
136
+ // C0 controls (U+0000..U+001F) + DEL (U+007F), EXCEPT tab/newline/carriage-return.
137
+ // eslint-disable-next-line no-control-regex
138
+ const CONTROL_CHARS = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g;
139
+ /** Strip control chars/null bytes and cap length. Empty-after-clean → null. */
140
+ export function sanitizeText(s, maxLen) {
141
+ if (s == null)
142
+ return null;
143
+ const cleaned = String(s).replace(CONTROL_CHARS, '').slice(0, maxLen);
144
+ return cleaned.length ? cleaned : null;
145
+ }
146
+ /**
147
+ * Flatten meta to display-safe primitives: strings cleaned + capped, numbers/
148
+ * booleans/null kept, nested objects/arrays dropped (HQ typeof-guards numeric
149
+ * meta and renders strings as text). If the result still exceeds META_MAX_BYTES,
150
+ * drop it entirely — the caller re-adds the trusted last_action_at.
151
+ */
152
+ export function sanitizeMeta(meta) {
153
+ if (!meta || typeof meta !== 'object')
154
+ return {};
155
+ const out = {};
156
+ for (const [k, v] of Object.entries(meta)) {
157
+ if (typeof v === 'string')
158
+ out[k] = v.replace(CONTROL_CHARS, '').slice(0, META_VALUE_CAP);
159
+ else if (typeof v === 'number' || typeof v === 'boolean' || v === null)
160
+ out[k] = v;
161
+ // nested objects/arrays intentionally dropped
162
+ }
163
+ if (Buffer.byteLength(JSON.stringify(out)) > META_MAX_BYTES)
164
+ return {};
165
+ return out;
166
+ }
167
+ /**
168
+ * Upsert a local-runtime session into hq.agent_sessions (register or heartbeat).
169
+ * Returns true on success, false if Supabase is unconfigured or the write failed.
170
+ * Reuses the presence writer's org config + cwd→project resolution.
171
+ */
172
+ export async function writeLocalAgentSession(reg) {
173
+ const client = serviceClient();
174
+ if (!client)
175
+ return false;
176
+ if (!reg.session_key || !reg.runtime)
177
+ return false;
178
+ const host = resolveHost();
179
+ const nowIso = new Date().toISOString();
180
+ const project_id = await resolveProjectId(client, reg.cwd || '');
181
+ const ended = reg.status === 'ended';
182
+ const row = {
183
+ org_id: ORG_ID,
184
+ host,
185
+ session_key: reg.session_key,
186
+ runtime: reg.runtime,
187
+ // Non-encoding belt on caller-supplied display fields (store-raw / escape-at-
188
+ // render is HQ's job; we only cap length + strip control chars/null bytes).
189
+ agent: sanitizeText(reg.agent, FIELD_CAPS.agent),
190
+ summary: sanitizeText(reg.summary, FIELD_CAPS.summary),
191
+ cwd: sanitizeText(reg.cwd, FIELD_CAPS.cwd),
192
+ project_id,
193
+ status: ended ? 'ended' : 'active',
194
+ started_at: nowIso,
195
+ last_seen_at: nowIso,
196
+ ended_at: ended ? nowIso : null,
197
+ // Default last_action_at to now; a self-reporting SDK may override it via meta
198
+ // (intended richer signal — HQ reads last_seen_at, not this, for liveness).
199
+ meta: { last_action_at: nowIso, ...sanitizeMeta(reg.meta) },
200
+ };
201
+ // started_at only matters on first insert; onConflict updates the rest. We let it
202
+ // re-send started_at — harmless on heartbeat since HQ reads last_seen_at for liveness.
203
+ const { error } = await withRetryResult(() => client.from('agent_sessions').upsert(row, { onConflict: 'org_id,host,session_key' }), `presence.local-register ${reg.session_key}`);
204
+ if (error) {
205
+ log(`Presence: local register ${reg.session_key} (${reg.runtime}) failed: ${error.message}`);
206
+ return false;
207
+ }
208
+ return true;
209
+ }
210
+ /** Start the presence writer loop. Returns a stop function. No-ops if Supabase unconfigured. */
211
+ export function startPresenceWriter() {
212
+ const client = serviceClient();
213
+ if (!client) {
214
+ log('Presence: SUPABASE_URL/SERVICE_KEY not set — agent-presence writer disabled.');
215
+ return () => { };
216
+ }
217
+ log(`Presence: writer started (30s heartbeat → hq.agent_sessions, org ${ORG_ID}).`);
218
+ const run = () => {
219
+ tick(client).catch((e) => log(`Presence: tick error: ${e instanceof Error ? e.message : String(e)}`));
220
+ };
221
+ run();
222
+ const iv = setInterval(run, HEARTBEAT_MS);
223
+ if (typeof iv.unref === 'function')
224
+ iv.unref();
225
+ return () => clearInterval(iv);
226
+ }
227
+ //# sourceMappingURL=agentPresence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentPresence.js","sourceRoot":"","sources":["../src/agentPresence.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,0EAA0E;AAC1E,+EAA+E;AAC/E,8EAA8E;AAC9E,WAAW;AACX,yEAAyE;AACzE,+EAA+E;AAC/E,mFAAmF;AACnF,yFAAyF;AACzF,kFAAkF;AAClF,uFAAuF;AACvF,+EAA+E;AAE/E,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,UAAU,GAAG,oBAAoB,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,MAAM,EAAE,CAAC;AACjF,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,sCAAsC,CAAC;AAEpF;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC;AAC5D,CAAC;AACD,MAAM,YAAY,GAAG,MAAM,CAAC;AAC5B,MAAM,aAAa,GAAG,MAAM,CAAC;AAC7B,MAAM,cAAc,GAAG,CAAC,GAAG,MAAM,CAAC;AAYlC,SAAS,aAAa;IACpB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACrC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IAC7C,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAC9B,OAAO,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE;QAC5B,IAAI,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE;QACxD,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;KACrB,CAAC,CAAC;AACL,CAAC;AAGD,KAAK,UAAU,UAAU;IACvB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,UAAU,aAAa,EAAE;YAClD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,kBAAkB,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;YAC5E,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC;SAClC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,EAAE,CAAC;QACvB,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAiB,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,6EAA6E;AAC7E,2DAA2D;AAC3D,IAAI,UAAU,GAA+B,IAAI,CAAC;AAClD,KAAK,UAAU,YAAY,CAAC,MAAgB;IAC1C,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC5F,MAAM,CAAC,GAAG,IAAI,GAAG,EAAkB,CAAC;IACpC,IAAI,CAAC,KAAK,IAAI,IAAI;QAAE,KAAK,MAAM,CAAC,IAAI,IAA0C;YAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACnG,OAAO,CAAC,CAAC;AACX,CAAC;AACD,KAAK,UAAU,gBAAgB,CAAC,MAAgB,EAAE,GAAW;IAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;IACrC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,CAAC,UAAU;QAAE,UAAU,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IACzD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,UAAU,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,uBAAuB;IAC1F,OAAO,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;AACrC,CAAC;AAED,KAAK,UAAU,IAAI,CAAC,MAAgB;IAClC,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;IAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IAE7C,MAAM,KAAK,GAAG,MAAM,UAAU,EAAE,CAAC;IACjC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,CAAC,EAAE;YAAE,SAAS;QACpB,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;QACzD,MAAM,GAAG,GAAG;YACV,MAAM,EAAE,MAAM;YACd,IAAI;YACJ,WAAW,EAAE,CAAC,CAAC,EAAE;YACjB,KAAK,EAAE,IAAqB;YAC5B,4EAA4E;YAC5E,4EAA4E;YAC5E,OAAO,EAAE,aAAa;YACtB,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,IAAI;YAC1B,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,IAAI;YAClB,UAAU;YACV,MAAM,EAAE,QAAQ;YAChB,UAAU,EAAE,CAAC,CAAC,aAAa,IAAI,MAAM;YACrC,YAAY,EAAE,MAAM;YACpB,QAAQ,EAAE,IAAqB;YAC/B,+EAA+E;YAC/E,8EAA8E;YAC9E,mEAAmE;YACnE,IAAI,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC,SAAS,IAAI,MAAM,EAAE;SAChD,CAAC;QACF,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,eAAe,CACrC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,yBAAyB,EAAE,CAAC,EAC1F,mBAAmB,CAAC,CAAC,EAAE,EAAE,CAC1B,CAAC;QACF,IAAI,KAAK;YAAE,GAAG,CAAC,oBAAoB,CAAC,CAAC,EAAE,YAAY,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,kFAAkF;IAClF,+EAA+E;IAC/E,6CAA6C;IAC7C,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,KAAK,GAAG,aAAa,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9D,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,KAAK,GAAG,cAAc,CAAC,CAAC,WAAW,EAAE,CAAC;IAChE,MAAM,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;SAC3D,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IAC3E,MAAM,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;SAC9E,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;AAC9E,CAAC;AA6BD,8EAA8E;AAC9E,+EAA+E;AAC/E,8EAA8E;AAC9E,gFAAgF;AAChF,iFAAiF;AACjF,+EAA+E;AAC/E,kFAAkF;AAClF,iFAAiF;AACjF,yEAAyE;AAEzE,MAAM,UAAU,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAW,CAAC;AACpE,MAAM,cAAc,GAAG,IAAI,CAAC;AAC5B,MAAM,cAAc,GAAG,IAAI,CAAC;AAC5B,mFAAmF;AACnF,4CAA4C;AAC5C,MAAM,aAAa,GAAG,iDAAiD,CAAC;AAExE,+EAA+E;AAC/E,MAAM,UAAU,YAAY,CAAC,CAA4B,EAAE,MAAc;IACvE,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACtE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AACzC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,IAAyC;IACpE,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACjD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;aACrF,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACnF,8CAA8C;IAChD,CAAC;IACD,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,cAAc;QAAE,OAAO,EAAE,CAAC;IACvE,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,GAA2B;IACtE,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,GAAG,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAEnD,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACxC,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,KAAK,OAAO,CAAC;IACrC,MAAM,GAAG,GAAG;QACV,MAAM,EAAE,MAAM;QACd,IAAI;QACJ,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,8EAA8E;QAC9E,4EAA4E;QAC5E,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC;QAChD,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC;QACtD,GAAG,EAAE,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,GAAG,CAAC;QAC1C,UAAU;QACV,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ;QAClC,UAAU,EAAE,MAAM;QAClB,YAAY,EAAE,MAAM;QACpB,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;QAC/B,+EAA+E;QAC/E,4EAA4E;QAC5E,IAAI,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;KAC5D,CAAC;IACF,kFAAkF;IAClF,uFAAuF;IACvF,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,eAAe,CACrC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,yBAAyB,EAAE,CAAC,EAC1F,2BAA2B,GAAG,CAAC,WAAW,EAAE,CAC7C,CAAC;IACF,IAAI,KAAK,EAAE,CAAC;QACV,GAAG,CAAC,4BAA4B,GAAG,CAAC,WAAW,KAAK,GAAG,CAAC,OAAO,aAAa,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7F,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,mBAAmB;IACjC,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,GAAG,CAAC,8EAA8E,CAAC,CAAC;QACpF,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;IAClB,CAAC;IACD,GAAG,CAAC,oEAAoE,MAAM,IAAI,CAAC,CAAC;IACpF,MAAM,GAAG,GAAG,GAAG,EAAE;QACf,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,yBAAyB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACxG,CAAC,CAAC;IACF,GAAG,EAAE,CAAC;IACN,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAC1C,IAAI,OAAQ,EAA6B,CAAC,KAAK,KAAK,UAAU;QAAG,EAA4B,CAAC,KAAK,EAAE,CAAC;IACtG,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,49 @@
1
+ export type JobStatus = 'queued' | 'claimed' | 'running' | 'done' | 'cancelled' | 'failed';
2
+ export interface AgenticJob {
3
+ id: string;
4
+ project_id: string;
5
+ prompt: string;
6
+ status: JobStatus;
7
+ claimed_by?: string;
8
+ claimed_at?: string;
9
+ completed_at?: string;
10
+ result?: {
11
+ output?: string;
12
+ files_changed?: string[];
13
+ };
14
+ created_at: string;
15
+ created_by?: string;
16
+ [key: string]: unknown;
17
+ }
18
+ export interface EnqueueJobInput {
19
+ projectId: string;
20
+ prompt: string;
21
+ createdBy?: string;
22
+ }
23
+ export interface EnqueueJobOutput {
24
+ jobId: string;
25
+ queuePosition: number;
26
+ filePath: string;
27
+ }
28
+ export interface CancelJobInput {
29
+ projectId: string;
30
+ jobId: string;
31
+ }
32
+ /**
33
+ * List all jobs for a project. Returns oldest-first (creation order). Reads
34
+ * from both .decibel/ and decibel/ paths, deduplicating by id.
35
+ */
36
+ export declare function listJobs(projectId: string): Promise<AgenticJob[]>;
37
+ /**
38
+ * Enqueue a new dispatch job. Allocates the next JOB-NNNN id, writes the
39
+ * YAML file, and returns the id + queue position (number of active jobs
40
+ * ahead of this one, inclusive).
41
+ */
42
+ export declare function enqueueJob(input: EnqueueJobInput): Promise<EnqueueJobOutput>;
43
+ /**
44
+ * Cancel a queued / claimed job. No-op if the job is already terminal
45
+ * (done / cancelled / failed). Marks status: cancelled and stamps
46
+ * completed_at.
47
+ */
48
+ export declare function cancelJob(input: CancelJobInput): Promise<AgenticJob>;
49
+ //# sourceMappingURL=agenticJobs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agenticJobs.d.ts","sourceRoot":"","sources":["../src/agenticJobs.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,SAAS,GACjB,QAAQ,GACR,SAAS,GACT,SAAS,GACT,MAAM,GACN,WAAW,GACX,QAAQ,CAAC;AAEb,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;KAC1B,CAAC;IACF,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AA2FD;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAoBvE;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsClF;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CA6B1E"}