reflectt-node 0.1.15 → 0.1.16

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 (214) hide show
  1. package/README.md +54 -0
  2. package/defaults/TEAM-ROLES.starter.yaml +87 -0
  3. package/defaults/lane-templates/ops.json +32 -0
  4. package/defaults/lane-templates/workflow.json +32 -0
  5. package/defaults/reviewer-routing.yaml +34 -0
  6. package/dist/activationEvents.d.ts +7 -1
  7. package/dist/activationEvents.d.ts.map +1 -1
  8. package/dist/activationEvents.js +29 -3
  9. package/dist/activationEvents.js.map +1 -1
  10. package/dist/activity-stream-normalizer.d.ts +37 -0
  11. package/dist/activity-stream-normalizer.d.ts.map +1 -0
  12. package/dist/activity-stream-normalizer.js +101 -0
  13. package/dist/activity-stream-normalizer.js.map +1 -0
  14. package/dist/agent-exec-guardrail.d.ts +9 -0
  15. package/dist/agent-exec-guardrail.d.ts.map +1 -0
  16. package/dist/agent-exec-guardrail.js +24 -0
  17. package/dist/agent-exec-guardrail.js.map +1 -0
  18. package/dist/agent-exec-guardrail.test.d.ts +2 -0
  19. package/dist/agent-exec-guardrail.test.d.ts.map +1 -0
  20. package/dist/agent-exec-guardrail.test.js +55 -0
  21. package/dist/agent-exec-guardrail.test.js.map +1 -0
  22. package/dist/agent-interface.d.ts +137 -0
  23. package/dist/agent-interface.d.ts.map +1 -0
  24. package/dist/agent-interface.js +463 -0
  25. package/dist/agent-interface.js.map +1 -0
  26. package/dist/agent-notifications.d.ts +51 -0
  27. package/dist/agent-notifications.d.ts.map +1 -0
  28. package/dist/agent-notifications.js +104 -0
  29. package/dist/agent-notifications.js.map +1 -0
  30. package/dist/agent-runs.d.ts +29 -2
  31. package/dist/agent-runs.d.ts.map +1 -1
  32. package/dist/agent-runs.js +118 -8
  33. package/dist/agent-runs.js.map +1 -1
  34. package/dist/artifact-mirror.d.ts.map +1 -1
  35. package/dist/artifact-mirror.js +4 -1
  36. package/dist/artifact-mirror.js.map +1 -1
  37. package/dist/assignment.d.ts.map +1 -1
  38. package/dist/assignment.js +54 -2
  39. package/dist/assignment.js.map +1 -1
  40. package/dist/boardHealthWorker.d.ts.map +1 -1
  41. package/dist/boardHealthWorker.js +15 -1
  42. package/dist/boardHealthWorker.js.map +1 -1
  43. package/dist/canvas-auto-state.d.ts +58 -0
  44. package/dist/canvas-auto-state.d.ts.map +1 -0
  45. package/dist/canvas-auto-state.js +89 -0
  46. package/dist/canvas-auto-state.js.map +1 -0
  47. package/dist/canvas-routes.d.ts +36 -0
  48. package/dist/canvas-routes.d.ts.map +1 -0
  49. package/dist/canvas-routes.js +47 -0
  50. package/dist/canvas-routes.js.map +1 -0
  51. package/dist/capability-readiness.d.ts +28 -0
  52. package/dist/capability-readiness.d.ts.map +1 -0
  53. package/dist/capability-readiness.js +162 -0
  54. package/dist/capability-readiness.js.map +1 -0
  55. package/dist/channels.d.ts.map +1 -1
  56. package/dist/channels.js +1 -0
  57. package/dist/channels.js.map +1 -1
  58. package/dist/cli.js +179 -4
  59. package/dist/cli.js.map +1 -1
  60. package/dist/cloud.d.ts +5 -0
  61. package/dist/cloud.d.ts.map +1 -1
  62. package/dist/cloud.js +485 -18
  63. package/dist/cloud.js.map +1 -1
  64. package/dist/comms-routing-policy.d.ts +31 -0
  65. package/dist/comms-routing-policy.d.ts.map +1 -0
  66. package/dist/comms-routing-policy.js +128 -0
  67. package/dist/comms-routing-policy.js.map +1 -0
  68. package/dist/config.d.ts.map +1 -1
  69. package/dist/config.js +1 -0
  70. package/dist/config.js.map +1 -1
  71. package/dist/continuity-loop.d.ts.map +1 -1
  72. package/dist/continuity-loop.js +26 -0
  73. package/dist/continuity-loop.js.map +1 -1
  74. package/dist/cost-enforcement.d.ts.map +1 -1
  75. package/dist/cost-enforcement.js +22 -0
  76. package/dist/cost-enforcement.js.map +1 -1
  77. package/dist/db.d.ts.map +1 -1
  78. package/dist/db.js +56 -5
  79. package/dist/db.js.map +1 -1
  80. package/dist/doctor.js +2 -2
  81. package/dist/events.d.ts +4 -2
  82. package/dist/events.d.ts.map +1 -1
  83. package/dist/events.js +22 -1
  84. package/dist/events.js.map +1 -1
  85. package/dist/executionSweeper.d.ts.map +1 -1
  86. package/dist/executionSweeper.js +155 -0
  87. package/dist/executionSweeper.js.map +1 -1
  88. package/dist/health.d.ts +21 -1
  89. package/dist/health.d.ts.map +1 -1
  90. package/dist/health.js +164 -19
  91. package/dist/health.js.map +1 -1
  92. package/dist/inbox.d.ts +4 -0
  93. package/dist/inbox.d.ts.map +1 -1
  94. package/dist/inbox.js +38 -1
  95. package/dist/inbox.js.map +1 -1
  96. package/dist/index.js +90 -14
  97. package/dist/index.js.map +1 -1
  98. package/dist/insight-auto-tagger.d.ts +58 -0
  99. package/dist/insight-auto-tagger.d.ts.map +1 -0
  100. package/dist/insight-auto-tagger.js +331 -0
  101. package/dist/insight-auto-tagger.js.map +1 -0
  102. package/dist/insight-task-bridge.d.ts +9 -0
  103. package/dist/insight-task-bridge.d.ts.map +1 -1
  104. package/dist/insight-task-bridge.js +43 -7
  105. package/dist/insight-task-bridge.js.map +1 -1
  106. package/dist/insights.d.ts +6 -0
  107. package/dist/insights.d.ts.map +1 -1
  108. package/dist/insights.js +13 -0
  109. package/dist/insights.js.map +1 -1
  110. package/dist/lane-config.d.ts.map +1 -1
  111. package/dist/lane-config.js +1 -0
  112. package/dist/lane-config.js.map +1 -1
  113. package/dist/lane-template-successor.d.ts +13 -0
  114. package/dist/lane-template-successor.d.ts.map +1 -0
  115. package/dist/lane-template-successor.js +132 -0
  116. package/dist/lane-template-successor.js.map +1 -0
  117. package/dist/local-whisper.d.ts +21 -0
  118. package/dist/local-whisper.d.ts.map +1 -0
  119. package/dist/local-whisper.js +137 -0
  120. package/dist/local-whisper.js.map +1 -0
  121. package/dist/macos-accessibility.d.ts +50 -0
  122. package/dist/macos-accessibility.d.ts.map +1 -0
  123. package/dist/macos-accessibility.js +185 -0
  124. package/dist/macos-accessibility.js.map +1 -0
  125. package/dist/manage.d.ts.map +1 -1
  126. package/dist/manage.js +47 -1
  127. package/dist/manage.js.map +1 -1
  128. package/dist/mcp.d.ts.map +1 -1
  129. package/dist/mcp.js +123 -0
  130. package/dist/mcp.js.map +1 -1
  131. package/dist/notification-worker.d.ts +66 -0
  132. package/dist/notification-worker.d.ts.map +1 -0
  133. package/dist/notification-worker.js +232 -0
  134. package/dist/notification-worker.js.map +1 -0
  135. package/dist/openclaw-usage-sync.d.ts +28 -0
  136. package/dist/openclaw-usage-sync.d.ts.map +1 -0
  137. package/dist/openclaw-usage-sync.js +161 -0
  138. package/dist/openclaw-usage-sync.js.map +1 -0
  139. package/dist/policy.js +1 -1
  140. package/dist/policy.js.map +1 -1
  141. package/dist/pr-link-reconciler.d.ts +61 -0
  142. package/dist/pr-link-reconciler.d.ts.map +1 -0
  143. package/dist/pr-link-reconciler.js +127 -0
  144. package/dist/pr-link-reconciler.js.map +1 -0
  145. package/dist/preflight.js +2 -2
  146. package/dist/presence-narrator.d.ts +52 -0
  147. package/dist/presence-narrator.d.ts.map +1 -0
  148. package/dist/presence-narrator.js +193 -0
  149. package/dist/presence-narrator.js.map +1 -0
  150. package/dist/presence.d.ts +2 -0
  151. package/dist/presence.d.ts.map +1 -1
  152. package/dist/presence.js +23 -3
  153. package/dist/presence.js.map +1 -1
  154. package/dist/product-observation-source.d.ts +52 -0
  155. package/dist/product-observation-source.d.ts.map +1 -0
  156. package/dist/product-observation-source.js +326 -0
  157. package/dist/product-observation-source.js.map +1 -0
  158. package/dist/reflection-automation.d.ts +25 -0
  159. package/dist/reflection-automation.d.ts.map +1 -1
  160. package/dist/reflection-automation.js +163 -42
  161. package/dist/reflection-automation.js.map +1 -1
  162. package/dist/review-autoclose.d.ts +62 -0
  163. package/dist/review-autoclose.d.ts.map +1 -0
  164. package/dist/review-autoclose.js +75 -0
  165. package/dist/review-autoclose.js.map +1 -0
  166. package/dist/sentry-webhook.d.ts +69 -0
  167. package/dist/sentry-webhook.d.ts.map +1 -0
  168. package/dist/sentry-webhook.js +88 -0
  169. package/dist/sentry-webhook.js.map +1 -0
  170. package/dist/sentry.d.ts +29 -0
  171. package/dist/sentry.d.ts.map +1 -0
  172. package/dist/sentry.js +86 -0
  173. package/dist/sentry.js.map +1 -0
  174. package/dist/server.d.ts.map +1 -1
  175. package/dist/server.js +5125 -230
  176. package/dist/server.js.map +1 -1
  177. package/dist/stale-candidate-reconciler.d.ts +69 -0
  178. package/dist/stale-candidate-reconciler.d.ts.map +1 -0
  179. package/dist/stale-candidate-reconciler.js +236 -0
  180. package/dist/stale-candidate-reconciler.js.map +1 -0
  181. package/dist/system-loop-state.d.ts +1 -1
  182. package/dist/system-loop-state.d.ts.map +1 -1
  183. package/dist/system-loop-state.js +1 -0
  184. package/dist/system-loop-state.js.map +1 -1
  185. package/dist/taskPrecheck.d.ts.map +1 -1
  186. package/dist/taskPrecheck.js +47 -2
  187. package/dist/taskPrecheck.js.map +1 -1
  188. package/dist/tasks.d.ts.map +1 -1
  189. package/dist/tasks.js +130 -0
  190. package/dist/tasks.js.map +1 -1
  191. package/dist/trust-events.d.ts +61 -0
  192. package/dist/trust-events.d.ts.map +1 -0
  193. package/dist/trust-events.js +148 -0
  194. package/dist/trust-events.js.map +1 -0
  195. package/dist/types.d.ts +1 -0
  196. package/dist/types.d.ts.map +1 -1
  197. package/dist/usage-tracking.d.ts +6 -0
  198. package/dist/usage-tracking.d.ts.map +1 -1
  199. package/dist/usage-tracking.js +14 -0
  200. package/dist/usage-tracking.js.map +1 -1
  201. package/dist/voice-sessions.d.ts +51 -0
  202. package/dist/voice-sessions.d.ts.map +1 -0
  203. package/dist/voice-sessions.js +143 -0
  204. package/dist/voice-sessions.js.map +1 -0
  205. package/dist/workflow-templates.d.ts.map +1 -1
  206. package/dist/workflow-templates.js +16 -1
  207. package/dist/workflow-templates.js.map +1 -1
  208. package/dist/working-contract.d.ts +22 -1
  209. package/dist/working-contract.d.ts.map +1 -1
  210. package/dist/working-contract.js +31 -2
  211. package/dist/working-contract.js.map +1 -1
  212. package/package.json +4 -4
  213. package/public/dashboard.js +12 -4
  214. package/public/docs.md +98 -10
@@ -1644,7 +1644,7 @@ async function loadDoctorPage() {
1644
1644
  const rows = [
1645
1645
  ['Status', data.status ?? '—'],
1646
1646
  ['Version', data.version ?? '—'],
1647
- ['Uptime', data.uptime != null ? `${Math.floor(data.uptime / 60)}m ${data.uptime % 60}s` : '—'],
1647
+ ['Uptime', data.uptime_seconds != null ? `${Math.floor(data.uptime_seconds / 60)}m ${data.uptime_seconds % 60}s` : '—'],
1648
1648
  ['PID', data.pid ?? data.runtime?.pid ?? '—'],
1649
1649
  ['Node', data.nodeVersion ?? data.runtime?.nodeVersion ?? '—'],
1650
1650
  ['Port', data.port ?? data.runtime?.port ?? '—'],
@@ -1840,10 +1840,16 @@ function renderReviewQueue() {
1840
1840
  const meta = t.metadata || {};
1841
1841
  const reviewState = typeof meta.review_state === 'string' ? meta.review_state : '';
1842
1842
  const reviewerDecision = meta.reviewer_decision;
1843
+ const prMerged = !!(meta.pr_merged || (meta.pr_integrity && meta.pr_integrity.pr_merged));
1844
+
1845
+ // If reviewer approved AND PR is merged, this task is effectively done —
1846
+ // don't page anyone. It just needs the status transition to 'done'.
1847
+ // This prevents false-positive "Author action needed" alerts on completed work.
1848
+ const isEffectivelyDone = prMerged && reviewerDecision && reviewerDecision.decision === 'approved';
1843
1849
 
1844
1850
  // If reviewer has acted (needs_author or reviewer_decision recorded), the ball is with the assignee.
1845
1851
  // We still track an SLA timer, but it should page the assignee (author), not the reviewer.
1846
- const waitOn = (reviewState === 'needs_author' || reviewerDecision != null) ? 'author' : 'reviewer';
1852
+ const waitOn = isEffectivelyDone ? 'done' : (reviewState === 'needs_author' || reviewerDecision != null) ? 'author' : 'reviewer';
1847
1853
 
1848
1854
  const rawEntered = waitOn === 'author'
1849
1855
  ? (reviewerDecision && reviewerDecision.decidedAt) || meta.review_last_activity_at || meta.entered_validating_at || t.updatedAt || t.createdAt
@@ -1853,7 +1859,7 @@ function renderReviewQueue() {
1853
1859
  const timeInReview = Math.min(Math.max(0, now - enteredAt), MAX_REVIEW_MS);
1854
1860
  const slaState = getReviewSlaState(timeInReview);
1855
1861
 
1856
- return { ...t, timeInReview, slaState, enteredAt, waitOn, reviewState, hasReviewerDecision: reviewerDecision != null };
1862
+ return { ...t, timeInReview, slaState, enteredAt, waitOn, reviewState, hasReviewerDecision: reviewerDecision != null, prMerged };
1857
1863
  })
1858
1864
  .sort((a, b) => {
1859
1865
  // Breaches first, then by time descending
@@ -1865,6 +1871,8 @@ function renderReviewQueue() {
1865
1871
 
1866
1872
  const reviewerQueue = validating.filter(t => t.waitOn === 'reviewer');
1867
1873
  const authorQueue = validating.filter(t => t.waitOn === 'author');
1874
+ // Tasks where PR is merged + reviewer approved — effectively done, no alerts needed
1875
+ const doneQueue = validating.filter(t => t.waitOn === 'done');
1868
1876
 
1869
1877
  if (validating.length === 0) {
1870
1878
  panel.style.display = '';
@@ -2458,7 +2466,7 @@ async function refresh() {
2458
2466
  if (refreshCount === 1 || forceFull) await refreshAgentRegistry();
2459
2467
  await loadTasks(forceFull);
2460
2468
  renderReviewQueue();
2461
- await Promise.all([loadPresence(), loadChat(forceFull), loadActivity(forceFull), loadResearch(), loadSharedArtifacts(), loadHealth(), loadReleaseStatus(forceFull), loadBuildInfo(), loadRuntimeTruthCard(), loadApprovalQueue(), loadAgentApprovals(), loadFeedback(), loadPauseStatus(), loadIntensityControl(), loadPolls()]);
2469
+ await Promise.all([loadPresence(), loadChat(forceFull), loadActivity(forceFull), loadResearch(), loadSharedArtifacts(), loadHealth(), loadReleaseStatus(forceFull), loadBuildInfo(), loadRuntimeTruthCard(), loadApprovalQueue(), loadAgentApprovals(), loadFeedback(), checkPauseBanner(), loadIntensityControl(), loadPolls()]);
2462
2470
  await renderPromotionSSOT();
2463
2471
  }
2464
2472
 
package/public/docs.md CHANGED
@@ -25,6 +25,7 @@ Base URL: `http://localhost:4445`
25
25
  - [Weekly Ship Log Template](../docs/WEEKLY_SHIP_LOG_TEMPLATE.md) — compact weekly status template.
26
26
  - [Incident Writeup Template](../docs/INCIDENT_WRITEUP_TEMPLATE.md) — timeline/root-cause/fix/prevention structure.
27
27
  - [Contributor Onboarding Script](../docs/CONTRIBUTOR_ONBOARDING_SCRIPT.md) — first-day workflow from clone to validated task.
28
+ - [Dev Workflow](../docs/DEV_WORKFLOW.md) — production vs. development: npm install for prod, feature branches for dev.
28
29
 
29
30
  ---
30
31
 
@@ -106,6 +107,7 @@ Remote hosts (multi-host installs) phone-home via a lightweight heartbeat so the
106
107
  | GET | `/health/chat` | Chat subsystem health: message counts, drop counters per agent (total + rolling 1h + reasons). Returns `{ totalMessages, rooms, subscribers, drops }`. |
107
108
  | GET | `/health/chat` | Chat subsystem health: message counts, drop counters per agent (total + rolling 1h + reasons). Returns `{ totalMessages, rooms, subscribers, drops }`. |
108
109
  | GET | `/health/errors` | Request error metrics: total errors, total requests, error rate, and last 20 errors for debugging. Returns `{ total_errors, total_requests, error_rate, recent[], timestamp }`. |
110
+ | GET | `/health/version` | Version summary for ops tooling + cloud dashboard. Returns `{ version, commit, uptime_ms, host_id, node_env }`. |
109
111
  | GET | `/health/keepalive` | Self-keepalive status for CF/serverless: warm boot detection, ping state, cold start count, environment info. |
110
112
  | GET | `/health/ping` | Ultra-lightweight keepalive — no DB access. Returns `{ status, uptime_seconds, ts }`. Use for cron triggers, load balancers, uptime monitors. |
111
113
  | GET | `/health/watchdog` | Richer keepalive with cold_start flag, task/chat stats, boot_info, and remediation hints. For monitoring dashboards. See `docs/KEEPALIVE.md`. |
@@ -134,6 +136,7 @@ Remote hosts (multi-host installs) phone-home via a lightweight heartbeat so the
134
136
  | GET | `/health/idle-nudge/debug` | Idle-nudge watchdog debug state |
135
137
  | GET | `/admin/task-comment-rejects` | Reject ledger for phantom task-comment IDs. Query: `limit` (max 200), `reason` (task_not_found\|invalid_task_refs), `author`, `since` (timestamp). Each row includes provenance (integration, sender_id, original_message_id). |
136
138
  | POST | `/health/idle-nudge/tick` | Trigger idle-nudge evaluation |
139
+ | POST | `/health/validating-nudge/tick` | Trigger validating-stall nudge evaluation |
137
140
  | POST | `/health/cadence-watchdog/tick` | Trigger cadence watchdog |
138
141
  | POST | `/health/mention-rescue/tick` | Trigger mention-rescue fallback |
139
142
  | POST | `/health/working-contract/tick` | Evaluate working-contract enforcement: auto-requeue stale doing tasks (90m warning → 15m grace → auto todo) and fire alerts. |
@@ -191,11 +194,13 @@ If your deployment needs quiet-hours behavior today, enforce it in scheduler/gat
191
194
  | GET | `/tasks/:id/pr-review` | PR review quality panel data. Returns diff scope, CI checks, done criteria alignment. Requires PR URL in task metadata (`pr_url`, `qa_bundle.pr_link`, or in `artifacts`). |
192
195
  | POST | `/tasks/:id/cancel` | Cancel a task. Body: `{ "reason": "why", "author": "agent" }`. Reason required. Sets status=cancelled + metadata.cancel_reason/cancelled_by/cancelled_at. Cannot cancel done tasks. |
193
196
  | POST | `/tasks/:id/outcome` | Capture 48h checkpoint verdict for completed tasks. Body: `verdict` (`PASS`\|`NO-CHANGE`\|`REGRESSION`), optional `author`, `notes` |
197
+ | POST | `/tasks/:id/block-external` | Mark a task as externally blocked (waiting on a human dependency). Suppresses idle detection, suggest-close, and auto-requeue. Body: `{ "reason": "Apple Developer credentials — human action required" }` (required). Returns `{ success, task, message }`. |
198
+ | POST | `/tasks/:id/unblock-external` | Remove the externally-blocked flag. Task becomes eligible for idle detection and auto-requeue again. Returns `{ success, task, message }`. |
194
199
  | POST | `/tasks/:id/review-bundle` | Auto-build reviewer packet by resolving PR URL + CI + artifact evidence from task metadata. Returns normalized `verdict` (`pass`/`fail`) and reasons. Optional body: `author`, `strict` (default `true`, requires CI=`success`). |
195
200
  | POST | `/tasks/:id/review` | Reviewer decision endpoint. Body: `{ "reviewer": "agent", "decision": "approve|reject", "comment": "..." }`. Only the assigned reviewer may submit. Approve auto-transitions validating→done. |
196
201
  | GET | `/reviews/pending` | Pending reviews for a reviewer. Query: `reviewer` (required), `compact` (optional). Returns tasks in validating awaiting review (excludes already-approved). Each item: id, title, assignee, priority, age_minutes, review_state, pr_url, artifact_path. Sorted oldest-first. |
197
- | POST | `/tasks` | Create task. Required: `title`, `createdBy`, `assignee`, `reviewer`, `done_criteria` (string[]), `eta`. Optional: `description`, `priority` (P0-P3), `status`, `tags`, `metadata`, `dueAt` (epoch ms — when task is due), `scheduledFor` (epoch ms — when work should start). **Reflection-origin invariant:** `metadata.source_reflection` or `metadata.source_insight` required (or `metadata.reflection_exempt=true` with `reflection_exempt_reason`). Status contract: `validating` also requires `metadata.artifact_path` under `process/`. |
198
- | PATCH | `/tasks/:id` | Update task (partial). Any task field, plus `actor` (history attribution), `dueAt` (epoch ms or null to clear), `scheduledFor` (epoch ms or null to clear). Status contract: `doing` requires reviewer + `metadata.eta`; `validating` requires `metadata.artifact_path` under `process/` (workspace-agnostic). |
202
+ | POST | `/tasks` | Create task. Required: `title`, `createdBy`, `assignee`, `reviewer`, `done_criteria` (string[]). Optional: `eta` (defaults to ~2h for P0/P1, ~4h for P2/P3 on claim), `description`, `priority` (P0-P3), `status`, `tags`, `metadata`, `dueAt` (epoch ms — when task is due), `scheduledFor` (epoch ms — when work should start). **Reflection-origin invariant:** `metadata.source_reflection` or `metadata.source_insight` required (or `metadata.reflection_exempt=true` with `reflection_exempt_reason`). Status contract: `validating` also requires `metadata.artifact_path` under `process/`. **Dedup (two tiers, same-assignee):** Tier 1 — exact title within 60s (reconnect collapse, returns 200+existing). Tier 2 — fuzzy ≥80% Jaccard within 24h (continuity-loop prevention, returns 409 `TASK_DUPLICATE` with `duplicateOf` + `similarity`). Set `metadata.skip_dedup=true` to bypass. |
203
+ | PATCH | `/tasks/:id` | Update task (partial). Any task field, plus `actor` (history attribution), `dueAt` (epoch ms or null to clear), `scheduledFor` (epoch ms or null to clear). Status contract: `doing` if `metadata.eta` absent, auto-defaults to ~2h (P0/P1) or ~4h (P2/P3); `validating` requires `metadata.artifact_path` under `process/` (workspace-agnostic). |
199
204
  | DELETE | `/tasks/:id` | Delete task |
200
205
  | GET | `/tasks/next` | Pull-based assignment. Query: `agent`, `compact`, `claim=1` (auto-transitions todo→doing on pull) |
201
206
  | GET | `/tasks/active` | Get active (doing) task for agent. Query: `agent`, `compact`. Returns null if no doing tasks. |
@@ -206,6 +211,7 @@ If your deployment needs quiet-hours behavior today, enforce it in scheduler/gat
206
211
  | GET | `/manage/config` | Remote management: config introspection with secrets redacted. Auth required. |
207
212
  | GET | `/manage/logs` | Remote management: bounded log tail. Query: `level`, `since`, `limit`, `format=text`. Auth required. |
208
213
  | POST | `/manage/restart` | Remote management: graceful restart (Docker/systemd/CLI). Auth required. |
214
+ | GET | `/manage/restart-context` | Read last restart context snapshot written on graceful restart. Auth required. Returns 404 if no snapshot exists. |
209
215
  | GET | `/manage/disk` | Remote management: data directory sizes. Auth required. |
210
216
  | GET | `/browser/config` | Browser capability configuration (max sessions, rate limits, viewport). |
211
217
  | POST | `/browser/sessions` | Create a new isolated browser session. Body: `{ agent, url?, headless?, viewport? }`. Returns session object. |
@@ -218,6 +224,7 @@ If your deployment needs quiet-hours behavior today, enforce it in scheduler/gat
218
224
  | POST | `/browser/sessions/:id/navigate` | Navigate to a URL. Body: `{ url }`. |
219
225
  | GET | `/browser/sessions/:id/screenshot` | Take a screenshot of the current page. Returns `{ base64, mimeType }`. |
220
226
  | GET | `/capabilities` | Agent-facing endpoint discovery. Lists all endpoints grouped by purpose, compact support flags, and usage recommendations. |
227
+ | GET | `/capabilities/readiness` | Per-capability readiness status for Browser/Email/SMS/Calendar. Returns `overall` + per-capability `status` (ready\|degraded\|not_ready\|unknown), `dependencies[]`, `last_error`, and `hint`. |
221
228
  | GET | `/version` | Current version + latest available from GitHub releases. Includes `update_available` boolean. Caches GitHub check for 15 minutes. |
222
229
  | GET | `/me/:agent` | Agent "My Now" cockpit payload: assigned tasks, pending reviews, blockers, failing-check signals, since-last-seen changelog, and next action. Supports `compact`. |
223
230
  | GET | `/tasks/intake-schema` | Task intake schema discovery — returns required/optional fields and per-type templates |
@@ -225,8 +232,12 @@ If your deployment needs quiet-hours behavior today, enforce it in scheduler/gat
225
232
  | GET | `/tasks/search` | Keyword search across task `title` + `description` (case-insensitive). Query: `q`, optional `limit`, `include_test=1|true` (include test-harness tasks; default excluded). |
226
233
  | GET | `/tasks/analytics` | Task completion analytics and velocity |
227
234
  | GET | `/tasks/instrumentation/lifecycle` | Reviewer/done-criteria gates + status-contract violations (`doing` missing ETA, `validating` missing artifact path) |
235
+ | POST | `/tasks/bulk-close` | Close up to 100 tasks in one call. Body: `{ "ids": ["task-..."], "reason": "superseded" }`. Closes validating tasks with reviewer_approved=true or reason=duplicate/superseded. Returns `{ closed[], skipped[], errors[], summary }`. |
228
236
  | POST | `/tasks/batch-create` | Batch create up to 20 tasks. Body: `{ "tasks": [...], "createdBy": "agent", "deduplicate": true, "dryRun": false }`. Each task follows the same schema as `POST /tasks`. Returns per-task results (created/duplicate/error) with summary counts. Deduplication checks exact title match + fuzzy word overlap (Jaccard >0.6) against active tasks. |
229
237
  | GET | `/tasks/heartbeat-status` | All doing tasks with stale comment activity (>30m). Returns `{ threshold, doingTaskCount, staleCount, staleTasks[] }`. Use for monitoring status heartbeat discipline compliance. |
238
+ | GET | `/tasks/slow-blocked` | Detect doing tasks that are slow vs explicitly blocked. Slow = doing >4h with no activity (not explicitly blocked, different handling path). Query: `slowThresholdHours` (default 4). Returns `{ slow[], blocked[], summary, slowCount, blockedCount }`. Host-enforced — no escalation needed. |
239
+ | (sweeper) | post-merge reviewer SLA | When a validating task's PR is confirmed merged: **2h** → sweeper posts `[reviewer-nudge]` comment + channel notification (once). **24h** → sweeper auto-closes task as done with `[auto-close]` comment (`closer=sweeper`). Neither fires the normal SLA escalation alert — done work doesn't queue in validating forever. |
240
+ | GET | `/tasks/validating-health` | Validating-lane health: per-task breakdown separating reviewer inactivity vs evidence mismatch. Returns `{ summary: { total, ok, reviewer_stale, evidence_missing, both }, tasks[] }`. Each task includes `failure_mode: "reviewer_stale"\|"evidence_missing"\|"both"\|"ok"`, `reviewer_active_recently`, `has_pr_link`, `pr_merged`, `age_ms`. Query: `reviewer_stale_threshold_ms` (default 7200000/2h), `include_test=1`. |
230
241
  | GET | `/tasks/board-health` | Board-level health metrics for backlog replenishment. Returns per-agent breakdown (doing, validating, todo, active counts), `needsWork`/`lowWatermark` flags, and `replenishNeeded` trigger (fires when 2+ agents idle or <3 backlog tasks). Query: `include_test=1` to include test-harness tasks. |
231
242
  | GET | `/agents` | Agent list — alias for /agents/roles. Returns all agents with roles, WIP status, affinity tags. |
232
243
  | GET | `/agents/roles` | Agent role registry with live WIP status. Returns all agents with `name`, `displayName`, `role`, `affinityTags`, `protectedDomains`, `wipCap`, `wipCount`, `overCap`. |
@@ -391,7 +402,7 @@ Preflight checks reconcile live task state (status, assignee, reviewer, recent c
391
402
 
392
403
  | Method | Path | Description |
393
404
  |--------|------|-------------|
394
- | GET | `/inbox/:agent` | Get inbox. Query: `limit`, `since` (epoch ms), `channel`, `compact` (strips id/reactions/replyCount) |
405
+ | GET | `/inbox/:agent` | Get inbox. Returns merged chat @mentions + task comments addressed to agent. Each item includes `from`, `content`, `timestamp`; task comment items also include `task_id`, `comment_id`, `type: 'task_comment'`. Query: `limit`, `since` (epoch ms), `channel`, `compact` (strips id/reactions/replyCount), `mark_read=true` (auto-acks chat messages) |
395
406
  | POST | `/inbox/:agent/ack` | Acknowledge messages. Body: `{ "upTo": epochMs }` |
396
407
  | POST | `/inbox/:agent/subscribe` | Replace channel subscriptions. Body: `{ "channels": ["reviews", "blockers"] }` |
397
408
  | GET | `/inbox/:agent/subscriptions` | List subscriptions |
@@ -403,6 +414,8 @@ Preflight checks reconcile live task state (status, assignee, reviewer, recent c
403
414
  | Method | Path | Description |
404
415
  |--------|------|-------------|
405
416
  | GET | `/pulse` | Team pulse snapshot: board counts + per-agent doing tasks + pending reviews + focus + deploy info + alert-preflight mode. Use `?compact=true` for <2000 char version |
417
+ | POST | `/pr-link-reconciler/sweep` | Manually trigger PR-link reconcile sweep. Finds validating tasks with merged PRs and stamps `canonical_pr` + `canonical_commit`. Returns `{ swept, stamped, skipped, errors, results[], durationMs }`. |
418
+ | GET | `/pr-link-reconciler/preview` | Dry-run: list validating tasks that would be updated by next sweep (PR URL present, no canonical refs yet). Returns `{ candidates[], total }`. |
406
419
  | POST | `/scope-overlap` | Scan for task scope overlap after PR merge. Body: `{ "prNumber": 707, "prTitle": "...", "prBranch": "kai/task-...", "mergedTaskId?": "...", "repo?": "owner/repo", "mergeCommit?": "abc123", "notify?": true }`. Idempotency key includes repo+prNumber+mergedTaskId+mergeCommit. Failed notifications allow retry (no-drop). |
407
420
  | GET | `/focus` | Current team focus directive (included in heartbeat) |
408
421
  | POST | `/focus` | Set team focus. Body: `{ "directive": "...", "setBy": "kai", "expiresAt?": 1234, "tags?": ["shipping"] }` |
@@ -412,6 +425,24 @@ Preflight checks reconcile live task state (status, assignee, reviewer, recent c
412
425
  | POST | `/presence/:agent` | Update presence. Body: `{ "status": "working|idle|blocked|reviewing|offline" }` |
413
426
  | GET | `/presence/:agent/focus` | Get agent focus state (active, level, expiry) |
414
427
  | POST | `/presence/:agent/focus` | Toggle focus mode. Body: `{ "active": true, "level": "soft|deep", "durationMin": 60, "reason": "shipping PR" }`. Soft: suppresses system nudges, allows direct mentions. Deep: suppresses everything except blocker/review pings. |
428
+ | GET | `/presence-loop` | Presence loop demo page — serves an HTML page that polls `/presence` to show live agent status changes. |
429
+
430
+ ## Agent Notifications
431
+
432
+ | Method | Path | Description |
433
+ |--------|------|-------------|
434
+ | POST | `/agent-notifications` | Create a notification. Body: `{ "target_agent": "link", "title": "Review PR", "source_agent": "kai", "type": "review|task|mention|alert|info|system", "body": "...", "priority": "low|medium|high|critical", "task_id": "...", "metadata": {}, "expires_at": 0 }`. Returns 201 + notification object. |
435
+ | POST | `/agent-notifications/:id/ack` | Acknowledge a notification. Body: `{ "decision": "seen|accept|defer|dismiss" }`. Returns updated notification. |
436
+ | GET | `/agent-notifications` | List notifications for an agent. Query: `agent` (required), `status` (default `pending`), `limit` (default 50). Returns `{ notifications, total }`. |
437
+ | GET | `/agent-notifications/worker/stats` | Notification delivery worker stats: delivered/skipped/failed/expired counts, tick count, running status. |
438
+ | POST | `/agent-notifications/worker/tick` | Manually trigger a delivery tick (useful for testing). Returns delivery results for the batch. |
439
+
440
+ ## Agent Presence (structured)
441
+
442
+ | Method | Path | Description |
443
+ |--------|------|-------------|
444
+ | POST | `/agent-presence` | Upsert agent presence + log to history. Body: `{ "agent": "link", "status": "working|idle|blocked|reviewing|offline", "task": "...", "focus_level": "soft|deep", "metadata": {} }`. Returns current presence. |
445
+ | GET | `/agent-presence` | Read current agent presence. Query: `agent` (required). Returns `{ presence }`. |
415
446
 
416
447
  ## Memory
417
448
 
@@ -491,6 +522,7 @@ Preflight checks reconcile live task state (status, assignee, reviewer, recent c
491
522
  |--------|------|-------------|
492
523
  | GET | `/agents/activity` | All agents activity summary |
493
524
  | GET | `/agents/:agent/activity` | Single agent activity |
525
+ | GET | `/agents/:agent/timeline` | Unified activity feed: runs + task state changes + trust events. Each event: `{ type, timestamp, summary, taskId?, runId?, meta? }`. Query: `limit` (default 50, max 200), `since` (epoch ms). Returns reverse-chronological order. |
494
526
  | GET | `/analytics/foragents` | forAgents.dev analytics |
495
527
  | GET | `/metrics` | Operational metrics snapshot (tasks/chat/presence/activity rates + uptime) |
496
528
  | GET | `/metrics/daily` | Daily funnel metrics by channel. Query: `timezone` (IANA tz, default `America/Vancouver`) |
@@ -524,7 +556,7 @@ Preflight checks reconcile live task state (status, assignee, reviewer, recent c
524
556
  | GET | `/provisioning/webhooks` | List configured webhook routes for this host. |
525
557
  | POST | `/provisioning/webhooks` | Add a webhook route. Body: `{ provider, path?, events?, active? }`. |
526
558
  | DELETE | `/provisioning/webhooks/:id` | Remove a webhook route by ID. |
527
- | POST | `/webhooks/incoming/:provider` | Receive incoming webhooks from external providers (GitHub, Stripe, etc.). Auto-routes through delivery engine to configured targets. Returns 202 Accepted. |
559
+ | POST | `/webhooks/incoming/:provider` | Receive incoming webhooks from external providers (GitHub, Sentry, Stripe, etc.). Auto-routes through delivery engine to configured targets. Provider `sentry` posts formatted error alerts to #ops channel (supports HMAC-SHA256 signature verification via `SENTRY_CLIENT_SECRET` env var). Returns 202 Accepted. |
528
560
  | POST | `/webhooks/deliver` | Enqueue a webhook for durable delivery. Body: `{ provider, eventType, payload, targetUrl, idempotencyKey?, metadata? }`. Returns event with idempotency key. |
529
561
  | GET | `/webhooks/events` | List webhook events. Query: `status`, `provider`, `limit`, `offset`. |
530
562
  | GET | `/webhooks/events/:id` | Get a webhook event by ID. |
@@ -588,6 +620,10 @@ Multi-host management: remote hosts register via heartbeat and are tracked by st
588
620
  | GET | `/widget/feedback.js` | Embeddable feedback widget (Shadow DOM, self-contained). Embed: `<script src="/widget/feedback.js" data-token="..." data-theme="auto">`. |
589
621
  | GET | `/routing/log` | Recent routing decisions. Query: `?limit=50&since=timestamp&category=watchdog-alert&severity=warning`. |
590
622
  | POST | `/routing/resolve` | Dry-run route resolution. Body: `{ from, content, severity?, category?, taskId?, mentions? }`. Returns where message would go. |
623
+ | POST | `/routing/simulate` | Comms routing policy simulator. Body: `{ policy: CommsRoutingPolicy, scenarios: RoutingScenario[] }` (max 100 scenarios). Returns `{ success, count, results: CommsRouteResult[] }`. Each result includes `owner`, `assignee`, `fallback`, `escalate`, `reasonCode`, `rationale`. |
624
+ | POST | `/voice/input` | Create a voice session and begin processing. Body: `{ agentId: string, transcript: string }`. Returns `{ sessionId }`. Connect to `GET /voice/session/:id/events` immediately to receive state events. |
625
+ | POST | `/voice/audio` | Accept an audio blob, transcribe via STT (local whisper.cpp → OpenAI Whisper fallback), pipe to voice pipeline. Multipart form: `agentId` (string field) + `audio` (file: webm/wav/mp3/ogg/m4a, max 25MB). Returns `{ sessionId }`. No API key required if openai-whisper is installed locally. |
626
+ | GET | `/voice/session/:id/events` | SSE stream of voice pipeline events for a session. Events: `transcript.final`, `agent.thinking`, `agent.done`, `tts.ready`, `error`, `session.end`. Each event is `data: { type, timestamp, text?, url?, stage?, message? }`. Replays past events on connect. |
591
627
  | POST | `/routing/overrides` | Create a routing override. Body: `CreateOverrideInput` with target, target_type, override config, TTL. Returns created override. |
592
628
  | GET | `/routing/overrides` | List routing overrides. Query: `?target=agent&target_type=agent|role&status=active|expired&limit=N`. |
593
629
  | GET | `/routing/overrides/:id` | Get a specific routing override by ID. |
@@ -618,6 +654,11 @@ Multi-host management: remote hosts register via heartbeat and are tracked by st
618
654
  | GET | `/activity` | Activity timeline: unified event feed with server-side grouping. Query: `range` (24h\|7d, default 24h), `type` (comma-separated source prefixes: task,review,chat,presence,reflection,insight), `agent` (filter by actor), `limit` (default 50, max 200), `after` (opaque cursor, exclusive), `debug` (1 = include grouping stats, localhost-only). Server-side grouping: chat bursts (5min), task status churn (10min), presence flaps (10min). Returns `{ events[], total, range{from,to,from_ms,to_ms,tz}, partial?{missing[],reason}, generated_at, generated_at_ms, next_cursor, debug?{grouping{rawCount,groupedCount,droppedCount,dropReasons}} }`. |
619
655
  | GET | `/activity/sources` | List allowed activity source names: tasks, reviews, chat, presence, reflections, insights. Used for `partial.missing` enum and `type` filter values. |
620
656
  | GET | `/insights` | List insights. Supports `compact=true` (slim: id/title/score/priority/status/task_id/independent_count). Query: `status` (candidate\|promoted\|pending_triage\|task_created\|cooldown\|closed), `priority` (P0-P3), `workflow_stage`, `failure_family`, `impacted_unit`, `limit`, `offset`. Sorted by score desc. |
657
+ | GET | `/insights/auto-tag/rules` | Return current keyword rule set for failure_family auto-tagging. Returns `{ rules, default_count }`. |
658
+ | PUT | `/insights/auto-tag/rules` | Replace rule set at runtime. Body: `{ rules: AutoTagRule[] }` where each rule has `{ family, patterns: string[] }` (regex patterns, case-insensitive). Returns `{ success, count }`. |
659
+ | DELETE | `/insights/auto-tag/rules` | Reset rule set to built-in defaults. |
660
+ | POST | `/insights/auto-tag/backfill` | Reclassify all uncategorized insights using current rules. Query: `dry_run=true` to preview without writing. Returns `{ scanned, reclassified, unchanged, results[] }`. |
661
+ | POST | `/insights/:id/auto-tag` | Re-run auto-tag on a single insight. Returns `{ changed, old_family, new_family }`. |
621
662
  | GET | `/insights/bridge/stats` | Insight→Task bridge stats: auto-created count, triaged count, duplicates skipped, errors. |
622
663
  | GET | `/insights/bridge/config` | Current bridge config including ownership guardrail settings. |
623
664
  | PATCH | `/insights/bridge/config` | Update bridge config. Body: partial config object (e.g. `{ ownershipGuardrail: { enabled: false } }`). |
@@ -631,13 +672,15 @@ Multi-host management: remote hosts register via heartbeat and are tracked by st
631
672
  | POST | `/insights/:id/cooldown` | Localhost-only. Set insight status to `cooldown` (default 14d window). Body: `{ actor, reason, notes?, cooldown_until?, cooldown_reason? }`. Optional auth via `REFLECTT_INSIGHT_MUTATION_TOKEN`. Audit logged. |
632
673
  | POST | `/insights/:id/close` | Localhost-only. Set insight status to `closed`. Body: `{ actor, reason, notes? }`. Optional auth via `REFLECTT_INSIGHT_MUTATION_TOKEN`. Audit logged. |
633
674
  | GET | `/insights/stats` | Aggregate stats: by status, priority, failure family. |
675
+ | POST | `/insights/stale-candidates/reconcile` | Run stale candidate reconcile sweep. Body: `{ dry_run?: boolean (default true), insight_ids?: string[], actor?: string }`. Closes candidate insights where recovery evidence exists and guardrails pass. Returns `{ swept, eligible, closed, blocked, errors, dryRun, candidates[], durationMs }`. |
676
+ | GET | `/insights/stale-candidates/preview` | Dry-run reconcile sweep (GET for convenience). Shows which candidate insights would be closed. |
634
677
  | POST | `/insights/tick-cooldowns` | Advance cooldown state machine: promoted past deadline → cooldown, expired cooldown → archived. |
635
678
  | POST | `/insights/:id/promote` | Promote insight to board task. Body: `{ contract: { owner, reviewer, eta, acceptance_check, artifact_proof_requirement, next_checkpoint_eta }, promoted_by }`. Optional: `title`, `description`, `priority`, `team_id`. Returns task_id + audit entry. |
636
679
  | GET | `/insights/:id/audit` | Promotion audit trail for an insight. |
637
680
  | GET | `/insights/promotions` | List all promotion audit entries. Query: `limit`. |
638
681
  | GET | `/insights/recurring/candidates` | List recurring task candidates from insights with persistent patterns. Auto-suggests owner/lane per failure family. Template-first (no auto task spam). |
639
682
  | GET | `/insights/top` | Top pain clusters by frequency within a time window. Query: `window` (e.g. `7d`, `24h`, `2w`; default `7d`), `limit` (1-50, default 10). Returns `{ clusters: [{ cluster_key, count, avg_score, last_seen_at, linked_task_ids }], window, since, limit }`. |
640
- | GET | `/loop/summary` | Supports `compact=true` (strips evidence_refs, slim linked_task). Top signals from the reflection→insight→task loop. Returns insights ranked by score, each with linked task details and evidence status. Query: `limit` (1-100, default 20), `min_score` (minimum score threshold, default 0), `exclude_addressed=1` (skip insights in cooldown/closed status or whose linked task is done/validating). Response: `{ success, entries[], total, filters }`. Each entry: `insight_id`, `title`, `score`, `priority`, `status`, `workflow_stage`, `failure_family`, `impacted_unit`, `independent_count`, `authors[]`, `evidence_count`, `evidence_refs[]`, `linked_task { id, title, status, assignee }`, `addressed`, `created_at`, `updated_at`. |
683
+ | GET | `/loop/summary` | Supports `compact=true` (strips evidence_refs, slim linked_task). Top signals from the reflection→insight→task loop. Returns insights ranked by score, each with linked task details and evidence status. Query: `limit` (1-100, default 20), `min_score` (minimum score threshold, default 0), `exclude_addressed=1` (skip insights in cooldown/closed status or whose linked task is done/validating). Response: `{ success, entries[], total, filters }`. Each entry: `insight_id`, `title`, `score`, `priority`, `status`, `workflow_stage`, `failure_family`, `impacted_unit`, `independent_count`, `authors[]`, `evidence_count`, `evidence_refs[]`, `linked_task { id, title, status, assignee }`, `addressed`, `created_at`, `updated_at`, `host_id` (owning host), `host_api_url` (base URL for cross-host resolution — set via `REFLECTT_HOST_API_URL` env var, falls back to `http://localhost:{PORT}`). |
641
684
 
642
685
  ### Example: `/insights/top`
643
686
 
@@ -870,6 +913,8 @@ Set via `reflectionNudge` in policy config:
870
913
  |--------|----------|-------------|
871
914
  | POST | `/usage/report` | Record model usage event. Body: `{ agent, model, provider?, input_tokens, output_tokens, estimated_cost_usd?, category?, task_id?, team_id? }`. Auto-estimates cost if not provided. |
872
915
  | POST | `/usage/report/batch` | Record batch of usage events. Body: `{ events: [...] }`. |
916
+ | POST | `/usage/ingest` | Accept external usage records from OpenClaw sessions. Auth: `REFLECTT_HOST_HEARTBEAT_TOKEN` (Bearer / x-heartbeat-token). Single: `{ agent, model, input_tokens, output_tokens, cost_usd?, session_id?, timestamp? }`. Batch: `{ events: [...] }`. Returns `{ success, event }` or `{ success, count }`. |
917
+ | POST | `/usage/sync/openclaw` | On-demand trigger for OpenClaw session sync. Reads `~/.openclaw/agents/*/sessions/sessions.json` and ingests new sessions. Auth: `REFLECTT_HOST_HEARTBEAT_TOKEN`. Returns `{ success, agentsScanned, sessionsFound, sessionsIngested, sessionsSkipped, errors }`. |
873
918
  | GET | `/usage/summary` | Aggregated usage totals. Query: `since`, `until`, `agent`, `team_id`. |
874
919
  | GET | `/usage/by-agent` | Per-agent cost breakdown. Query: `since`, `until`. |
875
920
  | GET | `/usage/by-model` | Per-model cost breakdown. Query: `since`, `until`. |
@@ -1014,7 +1059,8 @@ Full calendar event system with iCal-compatible fields, attendees, RSVP, recurre
1014
1059
 
1015
1060
  | Method | Path | Description |
1016
1061
  |--------|------|-------------|
1017
- | POST | `/calendar/events` | Create event. Body: `{ summary, dtstart, dtend, organizer, description?, timezone?, rrule?, attendees?, location?, categories?, reminders?, status? }` |
1062
+ | GET | `/calendar/upcoming` | Next N days of events (agent execution surface). Query: `days` (default 7). Returns `{ events: [{ id, title, start, end, attendees, calendar, description, location, provider }] }` sorted chronologically. |
1063
+ | POST | `/calendar/events` | Create event. Spec format: `{ title, start, duration_minutes?, attendees?, calendar?, description? }` — returns 422 for past dates, 409 for duplicates. Legacy: `{ summary, dtstart, dtend, organizer, ... }`. |
1018
1064
  | GET | `/calendar/events` | List events. Query: `organizer`, `attendee`, `status`, `from`, `to` (epoch ms), `categories` (comma-separated), `limit`. |
1019
1065
  | GET | `/calendar/events/:id` | Get single event. |
1020
1066
  | PATCH | `/calendar/events/:id` | Update event fields. |
@@ -1088,21 +1134,58 @@ Auth-gated endpoints for managing a reflectt-node instance remotely. Provide `RE
1088
1134
  | POST | `/agents/:agentId/runs` | Create a new agent run. Body: `{ objective, teamId?, taskId?, parentRunId? }` |
1089
1135
  | GET | `/agents/:agentId/runs` | List runs. Query: `?status=&teamId=&limit=` |
1090
1136
  | GET | `/agents/:agentId/runs/current` | Get active (non-terminal) run. Query: `?teamId=` |
1137
+ | GET | `/agents/:agentId/runs/current/pending-reviews` | List unresolved `review_requested` events for this reviewer agent — tasks in validating awaiting their decision. Query: `?teamId=` |
1091
1138
  | PATCH | `/agents/:agentId/runs/:runId` | Update run. Body: `{ status?, contextSnapshot?, artifacts? }` |
1092
- | POST | `/agents/:agentId/events` | Append an event (immutable). Body: `{ eventType, runId?, payload? }` |
1139
+ | POST | `/agents/:agentId/events` | Append an event (immutable). Body: `{ eventType, runId?, payload? }`. Routing enforced: actionable event types require `action_required` (review\|unblock\|approve\|fyi) and `urgency` (blocking\|normal\|low). |
1140
+ | POST | `/runs/:runId/events` | Append an event to a run by runId (resolves agentId automatically). Same routing enforcement as `/agents/:agentId/events`. Body: `{ eventType, payload? }`. |
1093
1141
  | GET | `/agents/:agentId/events` | List events. Query: `?runId=&type=&since=&limit=` |
1094
1142
  | GET | `/approvals/pending` | List pending approvals (review_requested events needing action). Query: `?agentId=&limit=` |
1095
1143
  | POST | `/approvals/:eventId/decide` | Submit approval decision. Body: `{ decision: "approve"|"reject", reviewer (required), comment? }`. Auto-unblocks run on approve. |
1144
+ | POST | `/run-approvals/:eventId/decide` | iOS lock screen action button endpoint. Body: `{ decision: "approve"|"reject", actor (required), reason? }`. Same effect as `/approvals/:eventId/decide` — emits canvas_input SSE on success. |
1096
1145
  | GET | `/agents/:agentId/runs/:runId/stream` | SSE stream for a specific run. Sends snapshot (run + recent events), then real-time events as they occur. Heartbeat every 15s. |
1146
+ | GET | `/runs/:runId/stream` | SSE stream for a run by ID (no agentId required). Cloud Presence surface subscribes here for live run activity. Sends snapshot then real-time events. Heartbeat every 15s. |
1097
1147
  | GET | `/agents/:agentId/stream` | SSE stream for all events for an agent. Sends snapshot (active run + recent events), then real-time events. Heartbeat every 15s. |
1098
1148
  | GET | `/workflows` | List available workflow templates. |
1099
1149
  | GET | `/workflows/:id` | Get template details (name, description, steps). |
1100
1150
  | POST | `/workflows/:id/run` | Execute a workflow. Body: `{ agentId?, teamId?, objective?, taskId?, reviewer?, prUrl?, title?, urgency?, nextOwner?, summary? }`. Returns step-by-step results with timing. Currently available: `pr-review` (6 steps: create → work → review → approve → handoff → complete). |
1151
+ | POST | `/workflows/pr-review-demo` | Canonical regression workflow. Happy path: create task (if missing) → run pr-review template → return run + recent events. Body: `{ agentId?, reviewer?, teamId?, taskId?, summary? }`. |
1101
1152
  | POST | `/canvas/input` | Human→agent control seam for Presence Layer. Body: `{ action: "decision"\|"interrupt"\|"pause"\|"resume"\|"mute"\|"unmute", actor (required), targetRunId?, decisionId?, choice?: "approve"\|"deny"\|"defer", comment? }`. Emits canvas_input SSE event. |
1102
1153
  | GET | `/canvas/input/schema` | Discovery: lists valid actions and field descriptions for canvas input. |
1103
- | POST | `/canvas/state` | Agent emits Presence Layer state transition. Body: `{ state: "floor"\|"listening"\|"thinking"\|"rendering"\|"ambient"\|"decision"\|"urgent"\|"handoff", sensors: null\|"mic"\|"camera"\|"mic+camera", agentId (required), payload?: { text?, media?, decision?: { question, decisionId, expiresAt?, autoAction? }, agents?: [{ name, state, task? }], summary?: { headline, items?, cost?, duration? } } }`. Emits canvas_render SSE event. |
1154
+ | POST | `/canvas/state` | Agent emits Presence Layer state transition. Body: `{ state, sensors, agentId, payload?: { text?, media?, content?: { type: "text"\|"markdown"\|"code"\|"image", lang? (syntax hint), progress?: [{label, state: "pending"\|"active"\|"done"\|"failed"}] }, decision?, agents?, summary? } }`. `content.type` enables deterministic rendering (no heuristics). Emits canvas_render SSE event. |
1104
1155
  | GET | `/canvas/state` | Current Presence Layer state for agents. Params: `agentId?` (single agent) or all agents. |
1105
1156
  | GET | `/canvas/states` | Discovery: valid states, sensors, and payload schema. |
1157
+ | POST | `/agents/:agentId/canvas` | Agent emits AgentPresence-compatible state transition. Body: `{ state, activeTask?, recency?, attention?, sensors?, payload?, progress?, urgency?, ambientCue?, content?: { type: "text"\|"markdown"\|"code"\|"image", lang?, progress?: [{label, state}] } }`. Emits canvas_render SSE event. Triggers immediate cloud sync. |
1158
+ | GET | `/agents/:agentId/canvas` | Current AgentPresence for one agent. Returns: `{ name, identityColor, state, activeTask?, recency, attention? }`. |
1159
+ | GET | `/canvas/presence` | All agents as AgentPresence[]. Returns: `{ agents: AgentPresence[], count }`. |
1160
+ | POST | `/canvas/pulse` | Agent pushes urgency + optional burst without a full canvas state update. Lighter than `POST /canvas/state`. Body: `{ agentId, urgency?: 0–1, burst?: boolean, label? }`. Fires `canvas_burst` event if `burst=true`. Returns `{ success, agentId, urgency, burst }`. |
1161
+ | POST | `/canvas/spark` | Fire an explicit agent-to-agent arc event. Body: `{ from, to, kind: "thought"\|"handoff"\|"collab"\|"decision"\|"sync", intensity?: 0–1, label? }`. Emits `canvas_spark` SSE event on the pulse stream. |
1162
+ | POST | `/canvas/express` | **Reality Mixer** — agent fires a multi-channel expression. Body: `{ agentId, channels: { voice?, visual?, typography?, sound?, haptic?, narrative? } }`. All channels optional. Emits `canvas_expression` SSE event on the pulse stream (same connection as burst/spark/milestone). Returns `{ success, id }`. |
1163
+ | POST | `/canvas/takeover` | **Agent takeover** — agent claims the full screen. Orbs fade to ambient, agent content becomes the canvas. Body: `{ agentId, content: { html?, markdown?, code?, image?, svg?, video?, threejs? }, title?, duration? (ms, max 120s, default 30s), transition?: 'fade'\|'slide'\|'instant' }`. Emits `canvas_takeover` SSE event with `action: 'claim'`. Auto-releases after duration. Returns `{ success, id, expiresAt }`. |
1164
+ | POST | `/canvas/takeover/release` | **Release takeover** — agent gives back the screen. Body: `{ agentId, transition?: 'fade'\|'slide'\|'instant' }`. Only the owning agent can release. Emits `canvas_takeover` with `action: 'release'`. |
1165
+ | GET | `/canvas/takeover` | **Takeover state** — returns current takeover status: `{ active, agentId?, id?, title?, startedAt?, expiresAt?, remainingMs? }`. |
1166
+ | GET | `/canvas/render/stream` | **Reality Mixer SSE stream** — subscribe to receive real-time medium commands from agents. New subscribers get last 20 commands for catch-up (event type `replay`). Live commands arrive as `data` events. Shape: `{ id, ts, agentId, cmd: { type, ...fields } }`. |
1167
+ | GET | `/canvas/activity-stream` | **Canvas activity SSE** — replays last 20 canvas events on connect (event: `backfill`, includes `_staggerMs` hint for animated replay at 50ms intervals), then streams live events (event: `activity`). Event types: canvas_message, canvas_render, canvas_expression, canvas_burst. `backfill_done` event signals replay complete. Canvas feels alive from frame 1. |
1168
+ | GET | `/canvas/attention` | **Single highest-priority attention item** for the canvas viewer. Query: `?viewer=human` (default). Returns `{ item: { source, priority, title, detail?, taskId?, prUrl?, agentId?, actionLabel, actionType, notificationId? } \| null, pendingCount: number }`. Priority order: critical/high notifications → validating tasks needing review → blocked tasks → remaining notifications. |
1169
+ | GET | `/canvas/pulse` | SSE stream emitting a heartbeat tick every 2s. Also emits real-time named events: `canvas_burst` (dramatic state transitions), `canvas_spark` (agent arcs), `canvas_milestone` (task_complete/pr_merged — the room exhaling), `canvas_message` (query response cards), `canvas_push` (agent proactive emissions). Connect once, animate forever. Tick shape: `{ t, agents: [{ id, state, urgency, activeSpeaker, color, age }], team: { rhythm, tension, ambientPulse, dominantColor } }`. |
1170
+ | POST | `/canvas/push` | **Proactive canvas** — agent self-initiates a canvas event without a human query. Types: `utterance` (text floats from orb, max 60 chars, default TTL 4000ms), `work_released` (release pulse when work ships, intensity 0–1), `handoff` (arc between agents when work moves, requires `toAgentId`), `canvas_response` (agent responds to a canvas query with a structured card — requires `card` object with `type` field, optional `query` string; also emits `canvas_message` for living-canvas rendering), `rich` (arbitrary visual content — agents paint the canvas with markdown, code, images, SVG, or HTML; requires `content` object with any of: `markdown`, `code`, `language`, `image` (URL), `svg`, `html`, `title`; optional `position: {x,y}`, `layer: foreground\|midground\|background`, `size: {w,h}`, `ttl` up to 120s default 30s). All types emit `canvas_push` on the pulse SSE stream. Body: `{ type, agentId, text?, ttl?, intensity?, toAgentId?, taskTitle?, card?, query?, content?, position?, layer?, size? }`. |
1171
+ | POST | `/canvas/artifact` | **Proof artifact stream** — emit a proof card that drifts through the canvas. Types: `commit`, `pr`, `test`, `run`, `approval`. Also fires automatically on task completion (approval type) and PR merge (pr type) via `/canvas/victory`. Payload: `{ type, agentId, title (max 80 chars), url?, taskId? }`. Emits `canvas_artifact` event on pulse SSE. |
1172
+ | POST | `/canvas/victory` | **The Victory** — whole team acknowledges a PR merge. Fires `canvas_expression { _victory: true }` (gold flash + celebration + resolve sound) then a `_victoryWave` per active agent staggered 350ms apart. Body: `{ prUrl, agentId, prTitle?, prNumber?, intensity? }`. Returns: `{ success, prNumber, intensity, wave: [{ agentId, delay }] }`. |
1173
+ | GET | `/canvas/flow-score` | **Team flow metric** — real-time 0–1 composite score. Factors: active agents (30%), state distribution (35%), expression velocity last 5m (25%), time of day (10%). Labels: surge/flow/grinding/quiet/idle. <50ms. Returns: `{ score, label, factors, activeAgents, expressionsLast5m }`. |
1174
+ | POST | `/canvas/briefing` | **The Briefing** — triggers a staggered team introduction on canvas mount. Fires one `canvas_expression` per active agent, 700ms apart. Each includes identity color, current task, state, and an LLM-generated one-line voice (template fallback). Idempotent: 30s cooldown per requesterId. Body: `{ requesterId? }`. Returns: `{ success, agents: [{ agentId, queued }], totalMs }`. |
1175
+ | POST | `/canvas/query` | **Human asks the canvas a question; agent responds with a typed visual card.** Body: `{ query: string, agentId?: string }`. Returns `{ success, card: { type, data, agentId, agentColor } }` and emits `canvas_message` event on the pulse SSE stream. Card types: `tasks` (team status), `info` (LLM prose answer), `revenue` (MRR/ARR), `onboarding` (setup steps). No polling needed — response appears in real-time via pulse stream. |
1176
+ | POST | `/canvas/gaze` | **Presence noticing presence** — fire after user holds cursor/gaze on an agent orb for ≥3 seconds. Agent generates a one-line response in their voice + fires `canvas_expression { _gaze: true }`. Body: `{ agentId, watcherId?, durationMs? }`. Returns `{ success, agentId, line, expressionId }`. When an LLM is configured: generates contextually; template fallback always available. |
1177
+ | GET | `/canvas/session/mode` | Inferred presence mode for the current session. Mode is derived from time of day + active canvas states + team rhythm — never manually selected. Returns: `{ mode: 'ambient'\|'conversational'\|'operational'\|'immersive', reason, narrative (one-line live caption), context }`. |
1178
+ | GET | `/canvas/session/snapshot` | Cross-device continuity: resumable session snapshot for the active agent. Params: `agentId?` (defaults to most-recently-updated non-floor agent). Returns: `{ snapshot: { agent_id, canvas_state, active_task?, active_decision?, content_snapshot?, handoff: { summary, stream_in_progress, sensor_consent_transferred } } \| null, generated_at }`. |
1179
+ | GET | `/canvas/team/mood` | Collective team mood — derived from all active agent states. Returns: `{ mood: { teamRhythm: 'quiet'\|'flow'\|'grinding'\|'tense'\|'surge', dominantState, tension: 0.0–1.0, ambientPulse: 'slow'\|'normal'\|'fast', dominantColor: hex, activeAgents: string[], counts } }`. Living canvas uses this to shift background atmosphere. |
1180
+ | POST | `/agent-interface/runs` | Create an agent action run. Body: `{ kind: "github_issue_create"\|"macos_ui_action", repo?, title?, body?, dryRun?, intent? }`. Returns `{ runId, status }`. Run lifecycle: `queued→running→awaiting_approval→completed\|failed\|rejected`. |
1181
+ | GET | `/agent-interface/runs` | List runs. Params: `status?` (e.g. `awaiting_approval`). Used by presence canvas to surface pending decisions. |
1182
+ | GET | `/agent-interface/runs/:runId` | Get run state + full log. |
1183
+ | GET | `/agent-interface/runs/:runId/replay` | Immutable audit + replay packet (`agent-interface-replay-v1`): intent, step timeline, approval decisions, outcome, rollback hints. |
1184
+ | GET | `/agent-interface/runs/:runId/events` | SSE stream of run events: `state_changed`, `step_started`, `step_succeeded`, `step_failed`, `approval_requested`, `approval_resolved`, `run_end`. |
1185
+ | POST | `/agent-interface/runs/:runId/approve` | Human approves the pending irreversible action (run must be in `awaiting_approval`). |
1186
+ | POST | `/agent-interface/runs/:runId/reject` | Human rejects the pending action. |
1187
+ | POST | `/agent-interface/kill-switch` | Engage or reset the macOS accessibility kill-switch. Body: `{ engage?: boolean }` (default true). Returns `{ killSwitch: boolean }`. |
1188
+ | GET | `/agent-interface/kill-switch` | Check current kill-switch state. Returns `{ killSwitch: boolean }`. |
1106
1189
  | GET | `/agents/:agentId/config` | Get agent config (model preference, cost caps, settings). |
1107
1190
  | PUT | `/agents/:agentId/config` | Upsert agent config. Body: `{ model?, fallbackModel?, costCapDaily?, costCapMonthly?, maxTokensPerCall?, teamId?, settings? }`. |
1108
1191
  | DELETE | `/agents/:agentId/config` | Remove agent config. |
@@ -1110,6 +1193,9 @@ Auth-gated endpoints for managing a reflectt-node instance remotely. Provide `RE
1110
1193
  | GET | `/agents/:agentId/cost-check` | Runtime cost enforcement check. Params: `dailySpend?`, `monthlySpend?`. Returns: allowed, action (allow\|warn\|downgrade\|deny), remaining budgets, model/fallback. |
1111
1194
  | POST | `/events/routing/validate` | Validate routing semantics for an event payload. Body: `{ eventType, payload }`. Returns: valid, errors[], warnings[]. Actionable events (review_requested, approval_requested, escalation, handoff) require: action_required, urgency (low\|normal\|high\|critical), owner. |
1112
1195
  | GET | `/agents/:name/identity` | Host-native agent identity resolution. Resolves by name, alias, or display name without requiring OpenClaw gateway. Returns: agentId, displayName, role, aliases, capabilities, model, costCap. Merges YAML roles + agent_config table. |
1196
+ | POST | `/agents/:name/identity/avatar` | Agent sets their own visual identity. Body: `{ type: 'svg'\|'image'\|'emoji', content: string, animated?: boolean, displayName?: string, bio?: string }`. Stored in agent_config settings. Emits canvas_expression with identity channel. |
1197
+ | GET | `/agents/:name/identity/avatar` | Read a single agent's visual identity (avatar SVG/image/emoji, bio, displayName). |
1198
+ | GET | `/agents/avatars` | All agent avatars — for canvas to render custom agent visuals instead of default circles. Returns: `{ avatars: { [agentId]: { type, content, animated, displayName?, bio? } } }`. |
1113
1199
  | POST | `/agents/:agentId/messages/send` | Send message to another agent. Body: `{ to (required), content (required), channel?, metadata? }`. Emits message_posted SSE event. |
1114
1200
  | GET | `/agents/:agentId/messages` | Inbox — list messages for an agent. Params: `channel?`, `unread?` (true), `since?`, `limit?`. Returns messages + unreadCount. |
1115
1201
  | GET | `/agents/:agentId/messages/sent` | Sent messages. Params: `limit?`. |
@@ -1128,10 +1214,12 @@ Auth-gated endpoints for managing a reflectt-node instance remotely. Provide `RE
1128
1214
  | GET | `/webhooks/payloads/:payloadId` | Get single payload with full body + headers. |
1129
1215
  | POST | `/webhooks/payloads/:payloadId/process` | Mark payload as processed. |
1130
1216
  | POST | `/webhooks/purge` | Delete old processed payloads. Body: `{ maxAgeDays? }` (default 30). |
1131
- | POST | `/agents/:agent/waiting` | Set agent to waiting state (blocked on human). Body: `{ reason (required), waitingFor?, taskId?, expiresAt? }`. Shows in heartbeat response. |
1132
- | DELETE | `/agents/:agent/waiting` | Clear waiting state agent is unblocked. |
1217
+ | GET | `/trust-events` | List trust-collapse signals. Params: `agentId?`, `eventType?` (false_assertion\|stale_status_claim\|self_review_violation\|missing_acceptance_criteria_block\|escalation_bypass), `since?` (epoch ms), `limit?`. |
1218
+ | POST | `/agents/:agent/waiting` | Set agent to waiting state (blocked on human). Body: `{ reason (required), waitingFor?, taskId?, expiresAt? }`. Heartbeat emits `agent.status="waiting"` + `waitingFor` + `waitingTaskId`. Canvas maps to `state="needs-attention"` (amber pulse). |
1219
+ | DELETE | `/agents/:agent/waiting` | Clear waiting state — agent is unblocked. Canvas state returns to normal. |
1133
1220
  | GET | `/approval-queue` | Unified approval queue — everything needing human decision. Params: `agentId?`, `category?` (review\|agent_action), `includeExpired?` (true), `limit?`. Returns: items[], count, hasExpired. Each item: id, category, title, description, urgency, owner, expiresAt, autoAction, isExpired. |
1134
1221
  | POST | `/approval-queue/:approvalId/decide` | Resolve an approval. Body: `{ decision: "approve"\|"reject"\|"defer", actor (required), comment? }`. Emits canvas_input SSE event. |
1222
+ | GET | `/email/inbound/:emailId` | Retrieve a raw inbound email payload by its stored ID. Returns the webhook_payloads record (source, eventType, body, headers, processed, createdAt). 404 if not found or not an email-source payload. |
1135
1223
  | POST | `/email/send` | Send email via cloud relay. Body: `{ from, to, subject, html/text (required), replyTo?, cc?, bcc?, agentId?, teamId? }`. Requires cloud connection. |
1136
1224
  | POST | `/sms/send` | Send SMS via cloud relay. Body: `{ to, body (required), from?, agentId?, teamId? }`. Requires cloud connection. |
1137
1225