reflectt-node 0.1.8 → 0.1.12

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 (167) hide show
  1. package/defaults/TEAM-ROLES.yaml +317 -5
  2. package/dist/agent-config.d.ts +51 -0
  3. package/dist/agent-config.d.ts.map +1 -0
  4. package/dist/agent-config.js +129 -0
  5. package/dist/agent-config.js.map +1 -0
  6. package/dist/agent-config.test.d.ts +2 -0
  7. package/dist/agent-config.test.d.ts.map +1 -0
  8. package/dist/agent-config.test.js +91 -0
  9. package/dist/agent-config.test.js.map +1 -0
  10. package/dist/agent-memories.d.ts +58 -0
  11. package/dist/agent-memories.d.ts.map +1 -0
  12. package/dist/agent-memories.js +168 -0
  13. package/dist/agent-memories.js.map +1 -0
  14. package/dist/agent-memories.test.d.ts +2 -0
  15. package/dist/agent-memories.test.d.ts.map +1 -0
  16. package/dist/agent-memories.test.js +327 -0
  17. package/dist/agent-memories.test.js.map +1 -0
  18. package/dist/agent-messaging.d.ts +50 -0
  19. package/dist/agent-messaging.d.ts.map +1 -0
  20. package/dist/agent-messaging.js +103 -0
  21. package/dist/agent-messaging.js.map +1 -0
  22. package/dist/agent-messaging.test.d.ts +2 -0
  23. package/dist/agent-messaging.test.d.ts.map +1 -0
  24. package/dist/agent-messaging.test.js +105 -0
  25. package/dist/agent-messaging.test.js.map +1 -0
  26. package/dist/agent-runs.d.ts +158 -0
  27. package/dist/agent-runs.d.ts.map +1 -0
  28. package/dist/agent-runs.js +514 -0
  29. package/dist/agent-runs.js.map +1 -0
  30. package/dist/agent-runs.test.d.ts +2 -0
  31. package/dist/agent-runs.test.d.ts.map +1 -0
  32. package/dist/agent-runs.test.js +386 -0
  33. package/dist/agent-runs.test.js.map +1 -0
  34. package/dist/approval-queue.test.d.ts +2 -0
  35. package/dist/approval-queue.test.d.ts.map +1 -0
  36. package/dist/approval-queue.test.js +118 -0
  37. package/dist/approval-queue.test.js.map +1 -0
  38. package/dist/artifact-store.d.ts +55 -0
  39. package/dist/artifact-store.d.ts.map +1 -0
  40. package/dist/artifact-store.js +128 -0
  41. package/dist/artifact-store.js.map +1 -0
  42. package/dist/artifact-store.test.d.ts +2 -0
  43. package/dist/artifact-store.test.d.ts.map +1 -0
  44. package/dist/artifact-store.test.js +119 -0
  45. package/dist/artifact-store.test.js.map +1 -0
  46. package/dist/boardHealthWorker.d.ts +28 -0
  47. package/dist/boardHealthWorker.d.ts.map +1 -1
  48. package/dist/boardHealthWorker.js +33 -1
  49. package/dist/boardHealthWorker.js.map +1 -1
  50. package/dist/canvas-input.test.d.ts +2 -0
  51. package/dist/canvas-input.test.d.ts.map +1 -0
  52. package/dist/canvas-input.test.js +96 -0
  53. package/dist/canvas-input.test.js.map +1 -0
  54. package/dist/canvas-render.test.d.ts +2 -0
  55. package/dist/canvas-render.test.d.ts.map +1 -0
  56. package/dist/canvas-render.test.js +95 -0
  57. package/dist/canvas-render.test.js.map +1 -0
  58. package/dist/capabilities/browser.d.ts +75 -0
  59. package/dist/capabilities/browser.d.ts.map +1 -0
  60. package/dist/capabilities/browser.js +172 -0
  61. package/dist/capabilities/browser.js.map +1 -0
  62. package/dist/channels.d.ts +1 -1
  63. package/dist/cli.js +4 -2
  64. package/dist/cli.js.map +1 -1
  65. package/dist/cloud.d.ts +2 -0
  66. package/dist/cloud.d.ts.map +1 -1
  67. package/dist/cloud.js +135 -3
  68. package/dist/cloud.js.map +1 -1
  69. package/dist/cost-enforcement.d.ts +38 -0
  70. package/dist/cost-enforcement.d.ts.map +1 -0
  71. package/dist/cost-enforcement.js +84 -0
  72. package/dist/cost-enforcement.js.map +1 -0
  73. package/dist/dashboard.d.ts.map +1 -1
  74. package/dist/dashboard.js +8 -0
  75. package/dist/dashboard.js.map +1 -1
  76. package/dist/db.d.ts.map +1 -1
  77. package/dist/db.js +131 -0
  78. package/dist/db.js.map +1 -1
  79. package/dist/e2e-loop-proof.test.d.ts +2 -0
  80. package/dist/e2e-loop-proof.test.d.ts.map +1 -0
  81. package/dist/e2e-loop-proof.test.js +104 -0
  82. package/dist/e2e-loop-proof.test.js.map +1 -0
  83. package/dist/email-sms-send.test.d.ts +2 -0
  84. package/dist/email-sms-send.test.d.ts.map +1 -0
  85. package/dist/email-sms-send.test.js +96 -0
  86. package/dist/email-sms-send.test.js.map +1 -0
  87. package/dist/events.d.ts +1 -1
  88. package/dist/events.d.ts.map +1 -1
  89. package/dist/events.js +2 -0
  90. package/dist/events.js.map +1 -1
  91. package/dist/fingerprint.d.ts.map +1 -1
  92. package/dist/fingerprint.js +5 -10
  93. package/dist/fingerprint.js.map +1 -1
  94. package/dist/github-webhook-chat.d.ts +75 -0
  95. package/dist/github-webhook-chat.d.ts.map +1 -0
  96. package/dist/github-webhook-chat.js +108 -0
  97. package/dist/github-webhook-chat.js.map +1 -0
  98. package/dist/handoff-state.test.d.ts +2 -0
  99. package/dist/handoff-state.test.d.ts.map +1 -0
  100. package/dist/handoff-state.test.js +102 -0
  101. package/dist/handoff-state.test.js.map +1 -0
  102. package/dist/health.d.ts +9 -0
  103. package/dist/health.d.ts.map +1 -1
  104. package/dist/health.js +18 -0
  105. package/dist/health.js.map +1 -1
  106. package/dist/host-error-correlation.d.ts +65 -0
  107. package/dist/host-error-correlation.d.ts.map +1 -0
  108. package/dist/host-error-correlation.js +123 -0
  109. package/dist/host-error-correlation.js.map +1 -0
  110. package/dist/index.js +39 -10
  111. package/dist/index.js.map +1 -1
  112. package/dist/notificationDedupeGuard.d.ts +4 -0
  113. package/dist/notificationDedupeGuard.d.ts.map +1 -1
  114. package/dist/notificationDedupeGuard.js +8 -4
  115. package/dist/notificationDedupeGuard.js.map +1 -1
  116. package/dist/presence.d.ts +37 -5
  117. package/dist/presence.d.ts.map +1 -1
  118. package/dist/presence.js +127 -16
  119. package/dist/presence.js.map +1 -1
  120. package/dist/review-sla.d.ts +9 -0
  121. package/dist/review-sla.d.ts.map +1 -0
  122. package/dist/review-sla.js +51 -0
  123. package/dist/review-sla.js.map +1 -0
  124. package/dist/routing-enforcement.test.d.ts +2 -0
  125. package/dist/routing-enforcement.test.d.ts.map +1 -0
  126. package/dist/routing-enforcement.test.js +86 -0
  127. package/dist/routing-enforcement.test.js.map +1 -0
  128. package/dist/run-retention.test.d.ts +2 -0
  129. package/dist/run-retention.test.d.ts.map +1 -0
  130. package/dist/run-retention.test.js +57 -0
  131. package/dist/run-retention.test.js.map +1 -0
  132. package/dist/run-stream.test.d.ts +2 -0
  133. package/dist/run-stream.test.d.ts.map +1 -0
  134. package/dist/run-stream.test.js +70 -0
  135. package/dist/run-stream.test.js.map +1 -0
  136. package/dist/server.d.ts.map +1 -1
  137. package/dist/server.js +1301 -75
  138. package/dist/server.js.map +1 -1
  139. package/dist/tasks.d.ts.map +1 -1
  140. package/dist/tasks.js +45 -0
  141. package/dist/tasks.js.map +1 -1
  142. package/dist/todoHoardingGuard.d.ts +17 -0
  143. package/dist/todoHoardingGuard.d.ts.map +1 -1
  144. package/dist/todoHoardingGuard.js +25 -2
  145. package/dist/todoHoardingGuard.js.map +1 -1
  146. package/dist/webhook-storage.d.ts +50 -0
  147. package/dist/webhook-storage.d.ts.map +1 -0
  148. package/dist/webhook-storage.js +102 -0
  149. package/dist/webhook-storage.js.map +1 -0
  150. package/dist/webhook-storage.test.d.ts +2 -0
  151. package/dist/webhook-storage.test.d.ts.map +1 -0
  152. package/dist/webhook-storage.test.js +86 -0
  153. package/dist/webhook-storage.test.js.map +1 -0
  154. package/dist/workflow-templates.d.ts +44 -0
  155. package/dist/workflow-templates.d.ts.map +1 -0
  156. package/dist/workflow-templates.js +154 -0
  157. package/dist/workflow-templates.js.map +1 -0
  158. package/dist/workflow-templates.test.d.ts +2 -0
  159. package/dist/workflow-templates.test.d.ts.map +1 -0
  160. package/dist/workflow-templates.test.js +76 -0
  161. package/dist/workflow-templates.test.js.map +1 -0
  162. package/package.json +3 -1
  163. package/public/dashboard.js +76 -1
  164. package/public/design-tokens-platform.md +118 -0
  165. package/public/design-tokens.css +195 -0
  166. package/public/docs.md +131 -2
  167. package/public/presence-loop-demo.html +473 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-storage.test.d.ts","sourceRoot":"","sources":["../src/webhook-storage.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,86 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { describe, it, beforeEach } from 'node:test';
3
+ import assert from 'node:assert/strict';
4
+ class InMemoryWebhookStore {
5
+ payloads = [];
6
+ counter = 0;
7
+ ingest(source, eventType, body, agentId) {
8
+ const p = { id: `whk-${++this.counter}`, source, eventType, agentId: agentId ?? null, body, processed: false };
9
+ this.payloads.push(p);
10
+ return p;
11
+ }
12
+ get(id) { return this.payloads.find(p => p.id === id) ?? null; }
13
+ list(opts) {
14
+ return this.payloads.filter(p => {
15
+ if (opts?.source && p.source !== opts.source)
16
+ return false;
17
+ if (opts?.agentId && p.agentId !== opts.agentId)
18
+ return false;
19
+ if (opts?.unprocessedOnly && p.processed)
20
+ return false;
21
+ return true;
22
+ });
23
+ }
24
+ markProcessed(id) {
25
+ const p = this.get(id);
26
+ if (!p || p.processed)
27
+ return false;
28
+ p.processed = true;
29
+ return true;
30
+ }
31
+ unprocessedCount(opts) {
32
+ return this.list({ source: opts?.source, unprocessedOnly: true }).length;
33
+ }
34
+ clear() { this.payloads = []; this.counter = 0; }
35
+ }
36
+ describe('webhook storage', () => {
37
+ let store;
38
+ beforeEach(() => { store = new InMemoryWebhookStore(); });
39
+ it('ingests a payload', () => {
40
+ const p = store.ingest('resend', 'email.received', { from: 'user@example.com', subject: 'Test' });
41
+ assert.ok(p.id.startsWith('whk-'));
42
+ assert.equal(p.source, 'resend');
43
+ assert.equal(p.eventType, 'email.received');
44
+ assert.equal(p.processed, false);
45
+ });
46
+ it('retrieves by id', () => {
47
+ const p = store.ingest('resend', 'email.received', { subject: 'Hello' });
48
+ assert.deepEqual(store.get(p.id)?.body, { subject: 'Hello' });
49
+ });
50
+ it('returns null for missing id', () => {
51
+ assert.equal(store.get('nonexistent'), null);
52
+ });
53
+ it('lists by source', () => {
54
+ store.ingest('resend', 'email.received', { a: 1 });
55
+ store.ingest('twilio', 'sms.received', { b: 2 });
56
+ store.ingest('resend', 'email.bounced', { c: 3 });
57
+ assert.equal(store.list({ source: 'resend' }).length, 2);
58
+ assert.equal(store.list({ source: 'twilio' }).length, 1);
59
+ });
60
+ it('lists by agent', () => {
61
+ store.ingest('resend', 'email.received', {}, 'link');
62
+ store.ingest('resend', 'email.received', {}, 'kai');
63
+ assert.equal(store.list({ agentId: 'link' }).length, 1);
64
+ });
65
+ it('marks as processed', () => {
66
+ const p = store.ingest('resend', 'email.received', {});
67
+ assert.equal(store.markProcessed(p.id), true);
68
+ assert.equal(store.get(p.id)?.processed, true);
69
+ // Double-process returns false
70
+ assert.equal(store.markProcessed(p.id), false);
71
+ });
72
+ it('filters unprocessed only', () => {
73
+ const p1 = store.ingest('resend', 'email.received', {});
74
+ store.ingest('resend', 'email.received', {});
75
+ store.markProcessed(p1.id);
76
+ assert.equal(store.list({ unprocessedOnly: true }).length, 1);
77
+ });
78
+ it('tracks unprocessed count', () => {
79
+ store.ingest('resend', 'email.received', {});
80
+ store.ingest('resend', 'email.received', {});
81
+ store.ingest('twilio', 'sms.received', {});
82
+ assert.equal(store.unprocessedCount(), 3);
83
+ assert.equal(store.unprocessedCount({ source: 'resend' }), 2);
84
+ });
85
+ });
86
+ //# sourceMappingURL=webhook-storage.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-storage.test.js","sourceRoot":"","sources":["../src/webhook-storage.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ export interface WorkflowStep {
2
+ name: string;
3
+ description: string;
4
+ action: (ctx: WorkflowContext) => Promise<StepResult> | StepResult;
5
+ }
6
+ export interface WorkflowContext {
7
+ runId: string;
8
+ agentId: string;
9
+ teamId: string;
10
+ params: Record<string, unknown>;
11
+ }
12
+ export interface StepResult {
13
+ success: boolean;
14
+ data?: Record<string, unknown>;
15
+ error?: string;
16
+ }
17
+ export interface WorkflowTemplate {
18
+ id: string;
19
+ name: string;
20
+ description: string;
21
+ steps: WorkflowStep[];
22
+ }
23
+ export interface WorkflowResult {
24
+ success: boolean;
25
+ runId: string;
26
+ steps: Array<{
27
+ name: string;
28
+ success: boolean;
29
+ data?: Record<string, unknown>;
30
+ error?: string;
31
+ durationMs: number;
32
+ }>;
33
+ totalDurationMs: number;
34
+ }
35
+ export declare const prReviewWorkflow: WorkflowTemplate;
36
+ export declare function runWorkflow(template: WorkflowTemplate, agentId: string, teamId: string, params?: Record<string, unknown>): Promise<WorkflowResult>;
37
+ export declare function getWorkflowTemplate(id: string): WorkflowTemplate | undefined;
38
+ export declare function listWorkflowTemplates(): Array<{
39
+ id: string;
40
+ name: string;
41
+ description: string;
42
+ stepCount: number;
43
+ }>;
44
+ //# sourceMappingURL=workflow-templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-templates.d.ts","sourceRoot":"","sources":["../src/workflow-templates.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,UAAU,CAAC,GAAG,UAAU,CAAA;CACnE;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,YAAY,EAAE,CAAA;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACpH,eAAe,EAAE,MAAM,CAAA;CACxB;AAID,eAAO,MAAM,gBAAgB,EAAE,gBAoG9B,CAAA;AAID,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,OAAO,CAAC,cAAc,CAAC,CA+BzB;AAQD,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAE5E;AAED,wBAAgB,qBAAqB,IAAI,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAOnH"}
@@ -0,0 +1,154 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Canonical workflow templates — reusable, runnable, regression-testable
3
+ // Packages the proven PR review → approve → handoff → completion loop
4
+ import { createAgentRun, updateAgentRun, appendAgentEvent } from './agent-runs.js';
5
+ // ── PR Review Workflow ──────────────────────────────────────────────────
6
+ export const prReviewWorkflow = {
7
+ id: 'pr-review',
8
+ name: 'PR Review → Approve → Handoff → Complete',
9
+ description: 'The canonical agent workflow: create run, attach task, request review, approve, handoff, complete.',
10
+ steps: [
11
+ {
12
+ name: 'create_run',
13
+ description: 'Create an agent run with objective',
14
+ action: (ctx) => {
15
+ const run = createAgentRun(ctx.agentId, ctx.teamId, ctx.params.objective ?? 'PR review workflow', {
16
+ taskId: ctx.params.taskId,
17
+ });
18
+ ctx.runId = run.id;
19
+ return { success: true, data: { runId: run.id } };
20
+ },
21
+ },
22
+ {
23
+ name: 'start_work',
24
+ description: 'Move run to working status',
25
+ action: (ctx) => {
26
+ updateAgentRun(ctx.runId, { status: 'working' });
27
+ appendAgentEvent({
28
+ agentId: ctx.agentId,
29
+ runId: ctx.runId,
30
+ eventType: 'work_started',
31
+ payload: { message: 'Agent began working on objective' },
32
+ });
33
+ return { success: true };
34
+ },
35
+ },
36
+ {
37
+ name: 'request_review',
38
+ description: 'Submit work for review',
39
+ action: (ctx) => {
40
+ updateAgentRun(ctx.runId, { status: 'waiting_review' });
41
+ const event = appendAgentEvent({
42
+ agentId: ctx.agentId,
43
+ runId: ctx.runId,
44
+ eventType: 'review_requested',
45
+ payload: {
46
+ action_required: 'Review and approve the submitted work',
47
+ urgency: ctx.params.urgency ?? 'normal',
48
+ owner: ctx.params.reviewer ?? 'kai',
49
+ pr_url: ctx.params.prUrl,
50
+ title: ctx.params.title ?? 'Review requested',
51
+ },
52
+ });
53
+ return { success: true, data: { eventId: event.id } };
54
+ },
55
+ },
56
+ {
57
+ name: 'approve',
58
+ description: 'Approve the review (simulates reviewer action)',
59
+ action: (ctx) => {
60
+ updateAgentRun(ctx.runId, { status: 'working' });
61
+ appendAgentEvent({
62
+ agentId: ctx.agentId,
63
+ runId: ctx.runId,
64
+ eventType: 'review_approved',
65
+ payload: {
66
+ reviewer: ctx.params.reviewer ?? 'kai',
67
+ comment: 'LGTM',
68
+ },
69
+ });
70
+ return { success: true };
71
+ },
72
+ },
73
+ {
74
+ name: 'handoff',
75
+ description: 'Hand off to next owner or complete',
76
+ action: (ctx) => {
77
+ appendAgentEvent({
78
+ agentId: ctx.agentId,
79
+ runId: ctx.runId,
80
+ eventType: 'handoff',
81
+ payload: {
82
+ action_required: 'Deploy or merge the approved work',
83
+ urgency: 'normal',
84
+ owner: ctx.params.nextOwner ?? ctx.agentId,
85
+ summary: ctx.params.summary ?? 'Work reviewed and approved',
86
+ },
87
+ });
88
+ return { success: true };
89
+ },
90
+ },
91
+ {
92
+ name: 'complete',
93
+ description: 'Mark run as completed',
94
+ action: (ctx) => {
95
+ updateAgentRun(ctx.runId, { status: 'completed', completedAt: Date.now() });
96
+ appendAgentEvent({
97
+ agentId: ctx.agentId,
98
+ runId: ctx.runId,
99
+ eventType: 'run_completed',
100
+ payload: { message: 'Workflow completed successfully' },
101
+ });
102
+ return { success: true };
103
+ },
104
+ },
105
+ ],
106
+ };
107
+ // ── Workflow runner ──────────────────────────────────────────────────────
108
+ export async function runWorkflow(template, agentId, teamId, params = {}) {
109
+ const start = Date.now();
110
+ const ctx = { runId: '', agentId, teamId, params };
111
+ const stepResults = [];
112
+ for (const step of template.steps) {
113
+ const stepStart = Date.now();
114
+ try {
115
+ const result = await step.action(ctx);
116
+ stepResults.push({
117
+ name: step.name,
118
+ success: result.success,
119
+ data: result.data,
120
+ error: result.error,
121
+ durationMs: Date.now() - stepStart,
122
+ });
123
+ if (!result.success) {
124
+ return { success: false, runId: ctx.runId, steps: stepResults, totalDurationMs: Date.now() - start };
125
+ }
126
+ }
127
+ catch (err) {
128
+ stepResults.push({
129
+ name: step.name,
130
+ success: false,
131
+ error: err.message,
132
+ durationMs: Date.now() - stepStart,
133
+ });
134
+ return { success: false, runId: ctx.runId, steps: stepResults, totalDurationMs: Date.now() - start };
135
+ }
136
+ }
137
+ return { success: true, runId: ctx.runId, steps: stepResults, totalDurationMs: Date.now() - start };
138
+ }
139
+ // ── Template registry ───────────────────────────────────────────────────
140
+ const TEMPLATES = new Map([
141
+ ['pr-review', prReviewWorkflow],
142
+ ]);
143
+ export function getWorkflowTemplate(id) {
144
+ return TEMPLATES.get(id);
145
+ }
146
+ export function listWorkflowTemplates() {
147
+ return [...TEMPLATES.values()].map(t => ({
148
+ id: t.id,
149
+ name: t.name,
150
+ description: t.description,
151
+ stepCount: t.steps.length,
152
+ }));
153
+ }
154
+ //# sourceMappingURL=workflow-templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-templates.js","sourceRoot":"","sources":["../src/workflow-templates.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=workflow-templates.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-templates.test.d.ts","sourceRoot":"","sources":["../src/workflow-templates.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,76 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { describe, it } from 'node:test';
3
+ import assert from 'node:assert/strict';
4
+ async function runSteps(steps) {
5
+ const completed = [];
6
+ for (const step of steps) {
7
+ const result = step.action();
8
+ if (!result.success)
9
+ return { success: false, completed, failed: step.name };
10
+ completed.push(step.name);
11
+ }
12
+ return { success: true, completed };
13
+ }
14
+ describe('workflow templates', () => {
15
+ it('runs all steps in order on success', async () => {
16
+ const steps = [
17
+ { name: 'create', action: () => ({ success: true }) },
18
+ { name: 'work', action: () => ({ success: true }) },
19
+ { name: 'review', action: () => ({ success: true }) },
20
+ { name: 'approve', action: () => ({ success: true }) },
21
+ { name: 'handoff', action: () => ({ success: true }) },
22
+ { name: 'complete', action: () => ({ success: true }) },
23
+ ];
24
+ const result = await runSteps(steps);
25
+ assert.equal(result.success, true);
26
+ assert.equal(result.completed.length, 6);
27
+ assert.deepEqual(result.completed, ['create', 'work', 'review', 'approve', 'handoff', 'complete']);
28
+ });
29
+ it('stops on first failure', async () => {
30
+ const steps = [
31
+ { name: 'create', action: () => ({ success: true }) },
32
+ { name: 'work', action: () => ({ success: true }) },
33
+ { name: 'review', action: () => ({ success: false, error: 'Reviewer rejected' }) },
34
+ { name: 'approve', action: () => ({ success: true }) },
35
+ ];
36
+ const result = await runSteps(steps);
37
+ assert.equal(result.success, false);
38
+ assert.equal(result.failed, 'review');
39
+ assert.equal(result.completed.length, 2);
40
+ });
41
+ it('handles empty workflow', async () => {
42
+ const result = await runSteps([]);
43
+ assert.equal(result.success, true);
44
+ assert.equal(result.completed.length, 0);
45
+ });
46
+ it('pr-review template has 6 steps', () => {
47
+ const prReviewSteps = ['create_run', 'start_work', 'request_review', 'approve', 'handoff', 'complete'];
48
+ assert.equal(prReviewSteps.length, 6);
49
+ });
50
+ it('step order matches the canonical flow', () => {
51
+ const expected = ['create_run', 'start_work', 'request_review', 'approve', 'handoff', 'complete'];
52
+ // This is the order that was proven in the demo run
53
+ assert.equal(expected[0], 'create_run');
54
+ assert.equal(expected[1], 'start_work');
55
+ assert.equal(expected[2], 'request_review');
56
+ assert.equal(expected[3], 'approve');
57
+ assert.equal(expected[4], 'handoff');
58
+ assert.equal(expected[5], 'complete');
59
+ });
60
+ it('failure at create prevents all subsequent steps', async () => {
61
+ const steps = [
62
+ { name: 'create', action: () => ({ success: false, error: 'DB error' }) },
63
+ { name: 'work', action: () => ({ success: true }) },
64
+ ];
65
+ const result = await runSteps(steps);
66
+ assert.equal(result.success, false);
67
+ assert.equal(result.completed.length, 0);
68
+ assert.equal(result.failed, 'create');
69
+ });
70
+ it('single step workflow succeeds', async () => {
71
+ const result = await runSteps([{ name: 'only', action: () => ({ success: true }) }]);
72
+ assert.equal(result.success, true);
73
+ assert.equal(result.completed.length, 1);
74
+ });
75
+ });
76
+ //# sourceMappingURL=workflow-templates.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-templates.test.js","sourceRoot":"","sources":["../src/workflow-templates.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reflectt-node",
3
- "version": "0.1.8",
3
+ "version": "0.1.12",
4
4
  "description": "Coordinate your AI agent team. Shared tasks, memory, reflections, and presence. Self-host for free.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -44,6 +44,7 @@
44
44
  "author": "Team Reflectt",
45
45
  "license": "Apache-2.0",
46
46
  "dependencies": {
47
+ "@browserbasehq/stagehand": "^3.1.0",
47
48
  "@fastify/cors": "^10.0.1",
48
49
  "@fastify/multipart": "^9.4.0",
49
50
  "@fastify/websocket": "^11.0.1",
@@ -87,6 +88,7 @@
87
88
  "README.md"
88
89
  ],
89
90
  "optionalDependencies": {
91
+ "@browserbasehq/stagehand": "^3.1.0",
90
92
  "@xenova/transformers": "^2.17.2"
91
93
  },
92
94
  "overrides": {
@@ -1457,6 +1457,7 @@ async function loadHealth() {
1457
1457
  const team = health.team || { blockers: [], overlaps: [], compliance: null, agents: [] };
1458
1458
  const agentsSummary = health.agentsSummary || { agents: [] };
1459
1459
  const idleNudgeDebug = health.idleNudgeDebug || null;
1460
+ const scope = team.scope || null;
1460
1461
 
1461
1462
  healthAgentMap = new Map((team.agents || []).map(a => [String(a.agent || '').toLowerCase(), a]));
1462
1463
  const workflow = health.workflow || { agents: [] };
@@ -1525,6 +1526,16 @@ async function loadHealth() {
1525
1526
  const body = document.getElementById('health-body');
1526
1527
  let html = '';
1527
1528
 
1529
+ if (scope) {
1530
+ const orgHealthLink = scope.orgHealthUrl
1531
+ ? ` <a href="${esc(scope.orgHealthUrl)}" target="_blank" rel="noopener">Open org-health</a>`
1532
+ : '';
1533
+ html += `<div class="blocker-item" style="border-left:4px solid var(--yellow);margin-bottom:12px">
1534
+ <div class="blocker-agent">${esc(scope.label || 'Host-local health')}</div>
1535
+ <div class="blocker-text">${esc(scope.message || '')}${orgHealthLink}</div>
1536
+ </div>`;
1537
+ }
1538
+
1528
1539
  // Agent Health Grid
1529
1540
  if (displayAgents.length > 0) {
1530
1541
  html += '<div class="health-section"><div class="health-section-title">Agent Status</div><div class="health-grid">';
@@ -2208,6 +2219,70 @@ async function batchApproveHighConfidence() {
2208
2219
  } catch (e) { console.error('Batch approve failed:', e); }
2209
2220
  }
2210
2221
 
2222
+ // ---- Agent Action Approval Queue (review_requested events from agent runs) ----
2223
+ let agentApprovalData = null;
2224
+
2225
+ async function loadAgentApprovals() {
2226
+ try {
2227
+ const res = await fetch(BASE + '/approval-queue?category=review');
2228
+ agentApprovalData = await res.json();
2229
+ renderAgentApprovals();
2230
+ } catch (e) {
2231
+ const body = document.getElementById('agent-approval-body');
2232
+ if (body) body.innerHTML = '<div class="empty">Failed to load agent approvals</div>';
2233
+ }
2234
+ }
2235
+
2236
+ function renderAgentApprovals() {
2237
+ const body = document.getElementById('agent-approval-body');
2238
+ const count = document.getElementById('agent-approval-count');
2239
+ if (!body) return;
2240
+
2241
+ const items = (agentApprovalData && agentApprovalData.items) ? agentApprovalData.items : [];
2242
+ if (count) count.textContent = items.length > 0 ? items.length + ' pending' : '';
2243
+
2244
+ if (items.length === 0) {
2245
+ body.innerHTML = '<div class="empty" style="text-align:center;padding:20px;color:var(--text-dim)">✓ No pending agent approvals.</div>';
2246
+ return;
2247
+ }
2248
+
2249
+ let html = '';
2250
+ items.forEach(function(item) {
2251
+ const urgencyColor = item.urgency === 'critical' ? '#ef4444' : item.urgency === 'high' ? '#f59e0b' : 'var(--text-dim)';
2252
+ const title = (item.title || item.event && item.event.payload && item.event.payload.action_required || 'Agent action pending').substring(0, 80);
2253
+ const desc = item.description || (item.event && item.event.payload && item.event.payload.description) || '';
2254
+ const agentId = item.agentId || '?';
2255
+ const runId = item.runId || '';
2256
+ html += '<div class="approval-card" style="border-left:3px solid ' + urgencyColor + '">';
2257
+ html += '<div class="approval-header">';
2258
+ html += '<span style="color:' + urgencyColor + '">⚡</span> ';
2259
+ html += '<span class="approval-title">' + esc(title) + '</span>';
2260
+ html += '<span class="assignee-tag" style="margin-left:8px">@' + esc(agentId) + '</span>';
2261
+ if (item.urgency) html += '<span style="font-size:10px;color:' + urgencyColor + ';margin-left:6px">' + esc(item.urgency) + '</span>';
2262
+ html += '</div>';
2263
+ if (desc) html += '<div class="approval-meta" style="font-size:12px;margin-top:4px">' + esc(desc.substring(0, 120)) + '</div>';
2264
+ if (runId) html += '<div class="approval-meta" style="font-size:10px;color:var(--text-dim)">Run: ' + esc(runId) + '</div>';
2265
+ html += '<div class="approval-actions">';
2266
+ html += '<button class="btn-reject" onclick="decideAgentApproval(\'' + esc(item.id) + '\',\'reject\')">✗ Reject</button>';
2267
+ html += '<button class="btn-approve" onclick="decideAgentApproval(\'' + esc(item.id) + '\',\'approve\')">✓ Approve</button>';
2268
+ html += '</div>';
2269
+ html += '</div>';
2270
+ });
2271
+
2272
+ body.innerHTML = html;
2273
+ }
2274
+
2275
+ async function decideAgentApproval(eventId, decision) {
2276
+ try {
2277
+ await fetch(BASE + '/approval-queue/' + encodeURIComponent(eventId) + '/decide', {
2278
+ method: 'POST',
2279
+ headers: { 'Content-Type': 'application/json' },
2280
+ body: JSON.stringify({ decision: decision, actor: 'dashboard' })
2281
+ });
2282
+ await loadAgentApprovals();
2283
+ } catch (e) { console.error('Agent approval decision failed:', e); }
2284
+ }
2285
+
2211
2286
  // ---- Routing Policy Editor ----
2212
2287
  function toggleRoutingPolicy() {
2213
2288
  routingPolicyVisible = !routingPolicyVisible;
@@ -2383,7 +2458,7 @@ async function refresh() {
2383
2458
  if (refreshCount === 1 || forceFull) await refreshAgentRegistry();
2384
2459
  await loadTasks(forceFull);
2385
2460
  renderReviewQueue();
2386
- await Promise.all([loadPresence(), loadChat(forceFull), loadActivity(forceFull), loadResearch(), loadSharedArtifacts(), loadHealth(), loadReleaseStatus(forceFull), loadBuildInfo(), loadRuntimeTruthCard(), loadApprovalQueue(), loadFeedback(), loadPauseStatus(), loadIntensityControl(), loadPolls()]);
2461
+ await Promise.all([loadPresence(), loadChat(forceFull), loadActivity(forceFull), loadResearch(), loadSharedArtifacts(), loadHealth(), loadReleaseStatus(forceFull), loadBuildInfo(), loadRuntimeTruthCard(), loadApprovalQueue(), loadAgentApprovals(), loadFeedback(), loadPauseStatus(), loadIntensityControl(), loadPolls()]);
2387
2462
  await renderPromotionSSOT();
2388
2463
  }
2389
2464
 
@@ -0,0 +1,118 @@
1
+ # Design Tokens — Platform Mapping
2
+
3
+ Source of truth: `/public/design-tokens.css`
4
+
5
+ ## iOS (Swift)
6
+
7
+ ```swift
8
+ // Colors.swift — auto-generate from design-tokens.css
9
+ import SwiftUI
10
+
11
+ extension Color {
12
+ static let brandPrimary = Color(hex: "#7C3AED")
13
+ static let brandPrimaryLight = Color(hex: "#A78BFA")
14
+ static let brandPrimaryDark = Color(hex: "#5B21B6")
15
+
16
+ // Canvas states
17
+ static let stateFloor = Color(hex: "#1F2937")
18
+ static let stateListening = Color(hex: "#7C3AED")
19
+ static let stateThinking = Color(hex: "#6366F1")
20
+ static let stateRendering = Color(hex: "#8B5CF6")
21
+ static let stateAmbient = Color(hex: "#374151")
22
+ static let stateDecision = Color(hex: "#F59E0B")
23
+ static let stateUrgent = Color(hex: "#EF4444")
24
+ static let stateHandoff = Color(hex: "#10B981")
25
+
26
+ // Trust
27
+ static let trustActive = Color(hex: "#F87171") // Red 400 — visible without alarming
28
+ }
29
+
30
+ // Dimensions.swift
31
+ enum Dimension {
32
+ static let tapTargetMin: CGFloat = 44
33
+ static let tapTargetUrgent: CGFloat = 52
34
+ static let orbSizeIdle: CGFloat = 64
35
+ static let orbSizeTranscript: CGFloat = 44
36
+ static let trustIndicatorSize: CGFloat = 10
37
+ static let overrideBarHeight: CGFloat = 52
38
+ static let presenceDotSize: CGFloat = 8
39
+ }
40
+
41
+ // Animation.swift
42
+ enum Timing {
43
+ static let fast: Double = 0.15
44
+ static let base: Double = 0.25
45
+ static let slow: Double = 0.35
46
+ static let canvas: Double = 0.5
47
+ static let orbGlow: Double = 1.8
48
+ }
49
+ ```
50
+
51
+ ## Android (Kotlin)
52
+
53
+ ```xml
54
+ <!-- colors.xml -->
55
+ <color name="brand_primary">#FF7C3AED</color>
56
+ <color name="brand_primary_light">#FFA78BFA</color>
57
+ <color name="brand_primary_dark">#FF5B21B6</color>
58
+
59
+ <color name="state_floor">#FF1F2937</color>
60
+ <color name="state_listening">#FF7C3AED</color>
61
+ <color name="state_thinking">#FF6366F1</color>
62
+ <color name="state_rendering">#FF8B5CF6</color>
63
+ <color name="state_ambient">#FF374151</color>
64
+ <color name="state_decision">#FFF59E0B</color>
65
+ <color name="state_urgent">#FFEF4444</color>
66
+ <color name="state_handoff">#FF10B981</color>
67
+
68
+ <color name="trust_active">#FFF87171</color> <!-- Red 400 -->
69
+
70
+ <!-- dimens.xml -->
71
+ <dimen name="tap_target_min">44dp</dimen>
72
+ <dimen name="tap_target_urgent">52dp</dimen>
73
+ <dimen name="orb_size_idle">64dp</dimen>
74
+ <dimen name="orb_size_transcript">44dp</dimen>
75
+ <dimen name="trust_indicator_size">10dp</dimen>
76
+ <dimen name="override_bar_height">52dp</dimen>
77
+ <dimen name="presence_dot_size">8dp</dimen>
78
+ ```
79
+
80
+ ```kotlin
81
+ // Timing.kt
82
+ object Timing {
83
+ const val FAST = 150L
84
+ const val BASE = 250L
85
+ const val SLOW = 350L
86
+ const val CANVAS = 500L
87
+ const val ORB_GLOW = 1800L
88
+ }
89
+ ```
90
+
91
+ ## Important Notes
92
+
93
+ **Canvas state colors are tint references, not solid fills.** The spec uses these as radial gradient tints. Platform implementations should apply them as gradient bases, not flat backgrounds. For v0 surfaces using solid fills, these values are acceptable approximations.
94
+
95
+ **Trust indicator uses red-400 (#F87171)** — deliberately lighter than error red. Present and visible without being alarming. Both-sensors escalates to red-500 (#EF4444).
96
+
97
+ ## Token Categories
98
+
99
+ | Category | Count | Notes |
100
+ |----------|-------|-------|
101
+ | Brand colors | 4 | Purple family |
102
+ | Semantic colors | 8 | Success/warning/error/info + backgrounds |
103
+ | Canvas states | 8 | Maps to state machine spec |
104
+ | Trust | 4 | Mic/camera indicators |
105
+ | Surface/bg | 6 | Light + dark mode |
106
+ | Text | 6 | Primary through inverse |
107
+ | Borders | 4 | Default/hover/focus/error |
108
+ | Typography | 14 | Font family, size, weight, line-height |
109
+ | Spacing | 12 | 0–64px scale |
110
+ | Radius | 6 | 4px–full |
111
+ | Shadows | 6 | sm through glow |
112
+ | Transitions | 9 | Duration + easing |
113
+ | Z-index | 8 | Layering scale |
114
+ | Interactive | 3 | Tap targets + focus ring |
115
+ | Canvas/orb | 5 | Orb dimensions + glow |
116
+ | Override bar | 3 | Height/bg/blur |
117
+ | Presence | 3 | Dot states |
118
+ | **Total** | **~105** | |