reflectt-node 0.1.15 → 0.1.17

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 (216) 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 +486 -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.d.ts.map +1 -1
  146. package/dist/preflight.js +9 -2
  147. package/dist/preflight.js.map +1 -1
  148. package/dist/presence-narrator.d.ts +52 -0
  149. package/dist/presence-narrator.d.ts.map +1 -0
  150. package/dist/presence-narrator.js +193 -0
  151. package/dist/presence-narrator.js.map +1 -0
  152. package/dist/presence.d.ts +2 -0
  153. package/dist/presence.d.ts.map +1 -1
  154. package/dist/presence.js +23 -3
  155. package/dist/presence.js.map +1 -1
  156. package/dist/product-observation-source.d.ts +52 -0
  157. package/dist/product-observation-source.d.ts.map +1 -0
  158. package/dist/product-observation-source.js +326 -0
  159. package/dist/product-observation-source.js.map +1 -0
  160. package/dist/reflection-automation.d.ts +25 -0
  161. package/dist/reflection-automation.d.ts.map +1 -1
  162. package/dist/reflection-automation.js +163 -42
  163. package/dist/reflection-automation.js.map +1 -1
  164. package/dist/review-autoclose.d.ts +62 -0
  165. package/dist/review-autoclose.d.ts.map +1 -0
  166. package/dist/review-autoclose.js +75 -0
  167. package/dist/review-autoclose.js.map +1 -0
  168. package/dist/sentry-webhook.d.ts +69 -0
  169. package/dist/sentry-webhook.d.ts.map +1 -0
  170. package/dist/sentry-webhook.js +88 -0
  171. package/dist/sentry-webhook.js.map +1 -0
  172. package/dist/sentry.d.ts +29 -0
  173. package/dist/sentry.d.ts.map +1 -0
  174. package/dist/sentry.js +86 -0
  175. package/dist/sentry.js.map +1 -0
  176. package/dist/server.d.ts.map +1 -1
  177. package/dist/server.js +5146 -223
  178. package/dist/server.js.map +1 -1
  179. package/dist/stale-candidate-reconciler.d.ts +69 -0
  180. package/dist/stale-candidate-reconciler.d.ts.map +1 -0
  181. package/dist/stale-candidate-reconciler.js +236 -0
  182. package/dist/stale-candidate-reconciler.js.map +1 -0
  183. package/dist/system-loop-state.d.ts +1 -1
  184. package/dist/system-loop-state.d.ts.map +1 -1
  185. package/dist/system-loop-state.js +1 -0
  186. package/dist/system-loop-state.js.map +1 -1
  187. package/dist/taskPrecheck.d.ts.map +1 -1
  188. package/dist/taskPrecheck.js +47 -2
  189. package/dist/taskPrecheck.js.map +1 -1
  190. package/dist/tasks.d.ts.map +1 -1
  191. package/dist/tasks.js +130 -0
  192. package/dist/tasks.js.map +1 -1
  193. package/dist/trust-events.d.ts +61 -0
  194. package/dist/trust-events.d.ts.map +1 -0
  195. package/dist/trust-events.js +148 -0
  196. package/dist/trust-events.js.map +1 -0
  197. package/dist/types.d.ts +1 -0
  198. package/dist/types.d.ts.map +1 -1
  199. package/dist/usage-tracking.d.ts +6 -0
  200. package/dist/usage-tracking.d.ts.map +1 -1
  201. package/dist/usage-tracking.js +14 -0
  202. package/dist/usage-tracking.js.map +1 -1
  203. package/dist/voice-sessions.d.ts +51 -0
  204. package/dist/voice-sessions.d.ts.map +1 -0
  205. package/dist/voice-sessions.js +143 -0
  206. package/dist/voice-sessions.js.map +1 -0
  207. package/dist/workflow-templates.d.ts.map +1 -1
  208. package/dist/workflow-templates.js +16 -1
  209. package/dist/workflow-templates.js.map +1 -1
  210. package/dist/working-contract.d.ts +22 -1
  211. package/dist/working-contract.d.ts.map +1 -1
  212. package/dist/working-contract.js +31 -2
  213. package/dist/working-contract.js.map +1 -1
  214. package/package.json +4 -4
  215. package/public/dashboard.js +12 -4
  216. package/public/docs.md +98 -10
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-exec-guardrail.test.d.ts","sourceRoot":"","sources":["../src/agent-exec-guardrail.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,55 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { describe, it } from 'node:test';
3
+ import assert from 'node:assert/strict';
4
+ import { checkActionAllowed, requiresApprovalGate } from './agent-exec-guardrail.js';
5
+ describe('checkActionAllowed', () => {
6
+ it('allows github_issue_create with github.com domain', () => {
7
+ const result = checkActionAllowed('github_issue_create', 'https://github.com/owner/repo');
8
+ assert.equal(result.allowed, true);
9
+ assert.equal(result.reason, undefined);
10
+ });
11
+ it('allows github_issue_create with no target', () => {
12
+ const result = checkActionAllowed('github_issue_create');
13
+ assert.equal(result.allowed, true);
14
+ });
15
+ it('denies unknown action with reason', () => {
16
+ // @ts-expect-error testing unknown kind
17
+ const result = checkActionAllowed('unknown_action');
18
+ assert.equal(result.allowed, false);
19
+ assert.ok(result.reason?.includes('unknown_action'));
20
+ assert.ok(result.reason?.includes('approved action list'));
21
+ });
22
+ it('denies out-of-scope domain with reason', () => {
23
+ const result = checkActionAllowed('github_issue_create', 'https://evil.com/owner/repo');
24
+ assert.equal(result.allowed, false);
25
+ assert.ok(result.reason?.includes('evil.com'));
26
+ assert.ok(result.reason?.includes('approved domain list'));
27
+ });
28
+ it('allows subdomain of github.com', () => {
29
+ const result = checkActionAllowed('github_issue_create', 'https://api.github.com/repos/owner/repo/issues');
30
+ assert.equal(result.allowed, true);
31
+ });
32
+ it('does not match partial domain (githubx.com)', () => {
33
+ const result = checkActionAllowed('github_issue_create', 'https://githubx.com/owner/repo');
34
+ assert.equal(result.allowed, false);
35
+ assert.ok(result.reason?.includes('githubx.com'));
36
+ });
37
+ it('ignores non-URL target strings (no hostname to check)', () => {
38
+ // If target is not a URL, URL parsing returns null and no domain check is done
39
+ const result = checkActionAllowed('github_issue_create', 'owner/repo');
40
+ assert.equal(result.allowed, true);
41
+ });
42
+ });
43
+ describe('requiresApprovalGate', () => {
44
+ it('returns true for github_issue_create', () => {
45
+ assert.equal(requiresApprovalGate('github_issue_create'), true);
46
+ });
47
+ it('returns true for all v1 actions (always requires human approval)', () => {
48
+ // All v1 actions are irreversible — gate is always on
49
+ const kinds = ['github_issue_create'];
50
+ for (const kind of kinds) {
51
+ assert.equal(requiresApprovalGate(kind), true);
52
+ }
53
+ });
54
+ });
55
+ //# sourceMappingURL=agent-exec-guardrail.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-exec-guardrail.test.js","sourceRoot":"","sources":["../src/agent-exec-guardrail.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Agent Interface — browser-driven software actions on behalf of the human.
3
+ *
4
+ * v1 scope: GitHub issue creation with full approval gate + run log.
5
+ *
6
+ * Run lifecycle: queued → running → awaiting_approval → completed | failed
7
+ *
8
+ * Spec: process/agent-interface-mvp-execution-v1.md
9
+ * Task: task-1773257734617-6fvzfl52z
10
+ */
11
+ export type RunKind = 'github_issue_create' | 'macos_ui_action';
12
+ export type RunStatus = 'queued' | 'running' | 'awaiting_approval' | 'completed' | 'failed' | 'rejected' | 'aborted';
13
+ /**
14
+ * Approval timeout in ms.
15
+ * In production: 10 minutes (never hardcoded — reads from env).
16
+ * In CI/tests: set APPROVAL_TIMEOUT_MS_TEST to a smaller value (e.g. 200).
17
+ * The test env var ONLY applies when running tests — never override in production code.
18
+ */
19
+ export declare function getApprovalTimeoutMs(): number;
20
+ export interface RunEvent {
21
+ type: 'state_changed' | 'step_started' | 'step_succeeded' | 'step_failed' | 'approval_requested' | 'approval_resolved';
22
+ timestamp: number;
23
+ payload: Record<string, unknown>;
24
+ }
25
+ export interface AgentInterfaceRun {
26
+ id: string;
27
+ kind: RunKind;
28
+ status: RunStatus;
29
+ createdAt: number;
30
+ updatedAt: number;
31
+ input: Record<string, unknown>;
32
+ log: RunEvent[];
33
+ result?: {
34
+ outcome: 'completed' | 'failed' | 'rejected';
35
+ issueUrl?: string;
36
+ errorMessage?: string;
37
+ recoveryHint?: string;
38
+ };
39
+ }
40
+ export declare function createRun(kind: RunKind, input: Record<string, unknown>): AgentInterfaceRun;
41
+ export declare function getRun(runId: string): AgentInterfaceRun | null;
42
+ export declare function subscribeRun(runId: string, cb: (event: RunEvent) => void): () => void;
43
+ export interface GithubIssueInput {
44
+ repo: string;
45
+ title: string;
46
+ body: string;
47
+ dryRun?: boolean;
48
+ }
49
+ export interface PendingApproval {
50
+ runId: string;
51
+ resolve: (approved: boolean) => void;
52
+ }
53
+ /**
54
+ * Execute a github_issue_create run.
55
+ * Flow: validate → step:draft → request_approval → (approved) → step:submit → complete
56
+ * or → (rejected) → rejected
57
+ */
58
+ export declare function executeGithubIssueCreate(runId: string, input: GithubIssueInput): Promise<void>;
59
+ /**
60
+ * Approve a pending run (human confirms irreversible action).
61
+ * Returns false if no pending approval found.
62
+ */
63
+ export declare function approveRun(runId: string): boolean;
64
+ /**
65
+ * Reject a pending run (human declines the action).
66
+ */
67
+ export declare function rejectRun(runId: string): boolean;
68
+ /**
69
+ * Abort a run — emergency kill for kill-switch or out-of-band termination.
70
+ * Works on any active state: awaiting_approval (rejects pending gate),
71
+ * running (forces abort), or queued (cancels before execution).
72
+ *
73
+ * @param actor identity invoking the abort (for audit log)
74
+ */
75
+ export declare function abortRun(runId: string, actor?: string): boolean;
76
+ /**
77
+ * List runs currently awaiting human approval — surfaced in /approval-queue
78
+ * so the presence canvas decision card can show them.
79
+ */
80
+ export declare function listPendingRuns(): AgentInterfaceRun[];
81
+ /**
82
+ * List all runs with optional status filter.
83
+ */
84
+ export declare function listRuns(status?: string): AgentInterfaceRun[];
85
+ export declare function _clearRunsForTest(): void;
86
+ /**
87
+ * Structured audit packet for a completed run.
88
+ * Includes intent, step timeline, approval decisions, outcome, and rollback hints.
89
+ *
90
+ * task-1773486840057-e92leqnr1
91
+ */
92
+ export interface ReplayPacket {
93
+ schema: 'agent-interface-replay-v1';
94
+ runId: string;
95
+ kind: string;
96
+ generatedAt: number;
97
+ run: {
98
+ id: string;
99
+ kind: string;
100
+ status: RunStatus;
101
+ createdAt: number;
102
+ completedAt: number | null;
103
+ durationMs: number | null;
104
+ };
105
+ intent: Record<string, unknown>;
106
+ stepTimeline: Array<{
107
+ type: string;
108
+ timestamp: number;
109
+ offsetMs: number;
110
+ payload: Record<string, unknown>;
111
+ }>;
112
+ approvals: Array<{
113
+ requestedAt: number;
114
+ resolvedAt: number | null;
115
+ approved: boolean | null;
116
+ timeoutMs: number;
117
+ }>;
118
+ outcome: {
119
+ status: RunStatus;
120
+ result: Record<string, unknown> | null;
121
+ errorMessage: string | null;
122
+ };
123
+ rollbackHints: string[];
124
+ }
125
+ /**
126
+ * Build an immutable audit/replay packet for the given run.
127
+ * Safe to call at any run lifecycle stage.
128
+ */
129
+ export declare function buildReplayPacket(runId: string): ReplayPacket | null;
130
+ /**
131
+ * Execute a macOS UI action run.
132
+ * Handles the awaiting_approval gate for irreversible intents.
133
+ *
134
+ * task-1773486840001-u0shj14v3 / task-1773486840036-8x0o76rmp
135
+ */
136
+ export declare function executeMacOSUIAction(runId: string, intent: Record<string, unknown>): Promise<void>;
137
+ //# sourceMappingURL=agent-interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-interface.d.ts","sourceRoot":"","sources":["../src/agent-interface.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,463 @@
1
+ /**
2
+ * Agent Interface — browser-driven software actions on behalf of the human.
3
+ *
4
+ * v1 scope: GitHub issue creation with full approval gate + run log.
5
+ *
6
+ * Run lifecycle: queued → running → awaiting_approval → completed | failed
7
+ *
8
+ * Spec: process/agent-interface-mvp-execution-v1.md
9
+ * Task: task-1773257734617-6fvzfl52z
10
+ */
11
+ import { checkActionAllowed } from "./agent-exec-guardrail.js";
12
+ import { executeIntent as macOSExecuteIntent, requiresApproval as macOSRequiresApproval, validateIntent as macOSValidateIntent } from "./macos-accessibility.js";
13
+ /**
14
+ * Approval timeout in ms.
15
+ * In production: 10 minutes (never hardcoded — reads from env).
16
+ * In CI/tests: set APPROVAL_TIMEOUT_MS_TEST to a smaller value (e.g. 200).
17
+ * The test env var ONLY applies when running tests — never override in production code.
18
+ */
19
+ export function getApprovalTimeoutMs() {
20
+ const testOverride = process.env.APPROVAL_TIMEOUT_MS_TEST;
21
+ if (testOverride)
22
+ return Number(testOverride);
23
+ return 10 * 60 * 1000; // 10 minutes (production default)
24
+ }
25
+ // ── In-memory run store ───────────────────────────────────────────────────────
26
+ const runs = new Map();
27
+ const subscribers = new Map();
28
+ function genRunId() {
29
+ return `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
30
+ }
31
+ export function createRun(kind, input) {
32
+ const run = {
33
+ id: genRunId(),
34
+ kind,
35
+ status: 'queued',
36
+ createdAt: Date.now(),
37
+ updatedAt: Date.now(),
38
+ input,
39
+ log: [],
40
+ };
41
+ runs.set(run.id, run);
42
+ // Guardrail check: deny non-approved actions or out-of-scope domains immediately
43
+ const target = typeof input.repo === 'string' ? input.repo : undefined;
44
+ const guard = checkActionAllowed(kind, target);
45
+ if (!guard.allowed) {
46
+ run.status = 'failed';
47
+ run.result = { outcome: 'failed', errorMessage: guard.reason, recoveryHint: 'Only approved actions and domains are permitted.' };
48
+ run.log.push({
49
+ type: 'step_failed',
50
+ timestamp: Date.now(),
51
+ payload: { step: 'guardrail', reason: guard.reason },
52
+ });
53
+ run.updatedAt = Date.now();
54
+ }
55
+ return run;
56
+ }
57
+ export function getRun(runId) {
58
+ return runs.get(runId) ?? null;
59
+ }
60
+ export function subscribeRun(runId, cb) {
61
+ if (!subscribers.has(runId))
62
+ subscribers.set(runId, new Set());
63
+ subscribers.get(runId).add(cb);
64
+ return () => subscribers.get(runId)?.delete(cb);
65
+ }
66
+ function emit(runId, event) {
67
+ const run = runs.get(runId);
68
+ if (run) {
69
+ run.log.push(event);
70
+ run.updatedAt = Date.now();
71
+ }
72
+ for (const cb of subscribers.get(runId) ?? []) {
73
+ try {
74
+ cb(event);
75
+ }
76
+ catch { /* non-fatal */ }
77
+ }
78
+ }
79
+ function transition(runId, status, payload = {}) {
80
+ const run = runs.get(runId);
81
+ if (!run)
82
+ return;
83
+ const prev = run.status;
84
+ run.status = status;
85
+ run.updatedAt = Date.now();
86
+ emit(runId, { type: 'state_changed', timestamp: Date.now(), payload: { from: prev, to: status, ...payload } });
87
+ }
88
+ const pendingApprovals = new Map();
89
+ /**
90
+ * Execute a github_issue_create run.
91
+ * Flow: validate → step:draft → request_approval → (approved) → step:submit → complete
92
+ * or → (rejected) → rejected
93
+ */
94
+ export async function executeGithubIssueCreate(runId, input) {
95
+ const run = runs.get(runId);
96
+ if (!run)
97
+ return;
98
+ transition(runId, 'running');
99
+ // Step 1: Draft the issue content
100
+ emit(runId, {
101
+ type: 'step_started',
102
+ timestamp: Date.now(),
103
+ payload: { step: 'draft', repo: input.repo, title: input.title },
104
+ });
105
+ if (!input.repo || !input.title || !input.body) {
106
+ emit(runId, {
107
+ type: 'step_failed',
108
+ timestamp: Date.now(),
109
+ payload: { step: 'draft', error: 'repo, title, and body are required' },
110
+ });
111
+ run.result = { outcome: 'failed', errorMessage: 'Missing required fields', recoveryHint: 'Provide repo (owner/repo), title, and body.' };
112
+ transition(runId, 'failed');
113
+ return;
114
+ }
115
+ emit(runId, {
116
+ type: 'step_succeeded',
117
+ timestamp: Date.now(),
118
+ payload: { step: 'draft', title: input.title, bodyLen: input.body.length },
119
+ });
120
+ // Step 2: Request human approval before irreversible submit
121
+ transition(runId, 'awaiting_approval');
122
+ emit(runId, {
123
+ type: 'approval_requested',
124
+ timestamp: Date.now(),
125
+ payload: {
126
+ message: `Create GitHub issue in ${input.repo}: "${input.title}"`,
127
+ repo: input.repo,
128
+ title: input.title,
129
+ bodyPreview: input.body.slice(0, 200),
130
+ dryRun: input.dryRun ?? false,
131
+ },
132
+ });
133
+ // Wait for approval decision (max 10 minutes)
134
+ const approved = await new Promise((resolve) => {
135
+ pendingApprovals.set(runId, { runId, resolve });
136
+ setTimeout(() => {
137
+ if (pendingApprovals.has(runId)) {
138
+ pendingApprovals.delete(runId);
139
+ resolve(false); // auto-reject on timeout
140
+ }
141
+ }, getApprovalTimeoutMs());
142
+ });
143
+ emit(runId, {
144
+ type: 'approval_resolved',
145
+ timestamp: Date.now(),
146
+ payload: { approved, runId },
147
+ });
148
+ if (!approved) {
149
+ run.result = { outcome: 'rejected', recoveryHint: 'Re-submit when ready to approve.' };
150
+ transition(runId, 'rejected');
151
+ return;
152
+ }
153
+ // Step 3: Submit to GitHub
154
+ transition(runId, 'running');
155
+ emit(runId, {
156
+ type: 'step_started',
157
+ timestamp: Date.now(),
158
+ payload: { step: 'submit', dryRun: input.dryRun ?? false },
159
+ });
160
+ if (input.dryRun) {
161
+ // Dry run — simulate success, don't actually call GitHub API
162
+ emit(runId, {
163
+ type: 'step_succeeded',
164
+ timestamp: Date.now(),
165
+ payload: { step: 'submit', dryRun: true, issueUrl: `https://github.com/${input.repo}/issues/[dry-run]` },
166
+ });
167
+ run.result = { outcome: 'completed', issueUrl: `https://github.com/${input.repo}/issues/[dry-run]` };
168
+ transition(runId, 'completed');
169
+ return;
170
+ }
171
+ // Real GitHub API call
172
+ const token = process.env.GITHUB_TOKEN;
173
+ if (!token) {
174
+ emit(runId, {
175
+ type: 'step_failed',
176
+ timestamp: Date.now(),
177
+ payload: { step: 'submit', error: 'GITHUB_TOKEN not configured' },
178
+ });
179
+ run.result = { outcome: 'failed', errorMessage: 'GITHUB_TOKEN not set', recoveryHint: 'Set GITHUB_TOKEN env var with repo write scope.' };
180
+ transition(runId, 'failed');
181
+ return;
182
+ }
183
+ try {
184
+ const [owner, repo] = input.repo.split('/');
185
+ const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/issues`, {
186
+ method: 'POST',
187
+ headers: {
188
+ Authorization: `Bearer ${token}`,
189
+ Accept: 'application/vnd.github+json',
190
+ 'Content-Type': 'application/json',
191
+ 'X-GitHub-Api-Version': '2022-11-28',
192
+ },
193
+ body: JSON.stringify({ title: input.title, body: input.body }),
194
+ signal: AbortSignal.timeout(15000),
195
+ });
196
+ if (!res.ok) {
197
+ const errBody = await res.text();
198
+ emit(runId, {
199
+ type: 'step_failed',
200
+ timestamp: Date.now(),
201
+ payload: { step: 'submit', status: res.status, error: errBody.slice(0, 200) },
202
+ });
203
+ run.result = {
204
+ outcome: 'failed',
205
+ errorMessage: `GitHub API error ${res.status}`,
206
+ recoveryHint: 'Check GITHUB_TOKEN permissions (needs repo write scope).',
207
+ };
208
+ transition(runId, 'failed');
209
+ return;
210
+ }
211
+ const issue = await res.json();
212
+ emit(runId, {
213
+ type: 'step_succeeded',
214
+ timestamp: Date.now(),
215
+ payload: { step: 'submit', issueUrl: issue.html_url, issueNumber: issue.number },
216
+ });
217
+ run.result = { outcome: 'completed', issueUrl: issue.html_url };
218
+ transition(runId, 'completed');
219
+ }
220
+ catch (err) {
221
+ const message = err instanceof Error ? err.message : 'Unknown error';
222
+ emit(runId, {
223
+ type: 'step_failed',
224
+ timestamp: Date.now(),
225
+ payload: { step: 'submit', error: message },
226
+ });
227
+ run.result = { outcome: 'failed', errorMessage: message, recoveryHint: 'Check network connectivity and retry.' };
228
+ transition(runId, 'failed');
229
+ }
230
+ }
231
+ /**
232
+ * Approve a pending run (human confirms irreversible action).
233
+ * Returns false if no pending approval found.
234
+ */
235
+ export function approveRun(runId) {
236
+ const pending = pendingApprovals.get(runId);
237
+ if (!pending)
238
+ return false;
239
+ pendingApprovals.delete(runId);
240
+ pending.resolve(true);
241
+ return true;
242
+ }
243
+ /**
244
+ * Reject a pending run (human declines the action).
245
+ */
246
+ export function rejectRun(runId) {
247
+ const pending = pendingApprovals.get(runId);
248
+ if (!pending)
249
+ return false;
250
+ pendingApprovals.delete(runId);
251
+ pending.resolve(false);
252
+ return true;
253
+ }
254
+ /**
255
+ * Abort a run — emergency kill for kill-switch or out-of-band termination.
256
+ * Works on any active state: awaiting_approval (rejects pending gate),
257
+ * running (forces abort), or queued (cancels before execution).
258
+ *
259
+ * @param actor identity invoking the abort (for audit log)
260
+ */
261
+ export function abortRun(runId, actor = 'kill-switch') {
262
+ const run = runs.get(runId);
263
+ if (!run)
264
+ return false;
265
+ if (['completed', 'failed', 'rejected', 'aborted', 'cancelled'].includes(run.status))
266
+ return false;
267
+ // If waiting for approval — resolve the gate with rejection first
268
+ const pending = pendingApprovals.get(runId);
269
+ if (pending) {
270
+ pendingApprovals.delete(runId);
271
+ pending.resolve(false);
272
+ }
273
+ run.result = { outcome: 'aborted', actor, abortedAt: Date.now() };
274
+ transition(runId, 'aborted', { actor, abortedAt: Date.now() });
275
+ emit(runId, { type: 'step_failed', timestamp: Date.now(), payload: { step: 'execute', error: `aborted by ${actor}` } });
276
+ return true;
277
+ }
278
+ /**
279
+ * List runs currently awaiting human approval — surfaced in /approval-queue
280
+ * so the presence canvas decision card can show them.
281
+ */
282
+ export function listPendingRuns() {
283
+ const result = [];
284
+ for (const run of runs.values()) {
285
+ if (run.status === 'awaiting_approval')
286
+ result.push(run);
287
+ }
288
+ return result;
289
+ }
290
+ /**
291
+ * List all runs with optional status filter.
292
+ */
293
+ export function listRuns(status) {
294
+ const result = [];
295
+ for (const run of runs.values()) {
296
+ if (!status || run.status === status)
297
+ result.push(run);
298
+ }
299
+ return result.sort((a, b) => b.createdAt - a.createdAt);
300
+ }
301
+ export function _clearRunsForTest() {
302
+ runs.clear();
303
+ subscribers.clear();
304
+ pendingApprovals.clear();
305
+ }
306
+ /**
307
+ * Build an immutable audit/replay packet for the given run.
308
+ * Safe to call at any run lifecycle stage.
309
+ */
310
+ export function buildReplayPacket(runId) {
311
+ const run = runs.get(runId);
312
+ if (!run)
313
+ return null;
314
+ const startMs = run.createdAt;
315
+ // Find approval events
316
+ const approvals = [];
317
+ let pendingApproval = null;
318
+ for (const event of run.log) {
319
+ if (event.type === 'approval_requested') {
320
+ pendingApproval = { requestedAt: event.timestamp, resolvedAt: null, approved: null, timeoutMs: getApprovalTimeoutMs() };
321
+ }
322
+ else if (event.type === 'approval_resolved' && pendingApproval) {
323
+ pendingApproval.resolvedAt = event.timestamp;
324
+ pendingApproval.approved = event.payload.approved ?? null;
325
+ approvals.push(pendingApproval);
326
+ pendingApproval = null;
327
+ }
328
+ }
329
+ if (pendingApproval)
330
+ approvals.push(pendingApproval);
331
+ // Determine completion time
332
+ const terminalEvent = [...run.log].reverse().find(e => e.type === 'state_changed' && ['completed', 'failed', 'rejected'].includes(e.payload.to));
333
+ const completedAt = terminalEvent?.timestamp ?? null;
334
+ // Rollback hints based on intent action
335
+ const intent = run.input.intent ?? run.input;
336
+ const rollbackHints = buildRollbackHints(run.kind, intent);
337
+ return {
338
+ schema: 'agent-interface-replay-v1',
339
+ runId,
340
+ kind: run.kind,
341
+ generatedAt: Date.now(),
342
+ run: {
343
+ id: run.id,
344
+ kind: run.kind,
345
+ status: run.status,
346
+ createdAt: run.createdAt,
347
+ completedAt,
348
+ durationMs: completedAt ? completedAt - startMs : null,
349
+ },
350
+ intent,
351
+ stepTimeline: run.log.map(event => ({
352
+ type: event.type,
353
+ timestamp: event.timestamp,
354
+ offsetMs: event.timestamp - startMs,
355
+ payload: event.payload,
356
+ })),
357
+ approvals,
358
+ outcome: {
359
+ status: run.status,
360
+ result: run.result ? { ...run.result } : null,
361
+ errorMessage: run.result?.errorMessage ?? null,
362
+ },
363
+ rollbackHints,
364
+ };
365
+ }
366
+ function buildRollbackHints(kind, intent) {
367
+ if (kind === 'github_issue_create') {
368
+ return ['Close the created GitHub issue via the issue URL in the run result.'];
369
+ }
370
+ if (kind === 'macos_ui_action') {
371
+ const action = String(intent?.action ?? '');
372
+ switch (action) {
373
+ case 'create_reminder':
374
+ return ['Open Reminders app → find and delete the created reminder manually.'];
375
+ case 'draft_email':
376
+ return ['Open Mail app → Drafts → delete the draft before sending.'];
377
+ case 'summarize_note':
378
+ return ['Read-only — no rollback needed.'];
379
+ case 'open_app':
380
+ case 'focus_window':
381
+ return ['Reversible — close the application if needed.'];
382
+ case 'type_text':
383
+ return ['Undo via Cmd+Z in the focused application.'];
384
+ default:
385
+ return ['Review the target application manually for any unintended changes.'];
386
+ }
387
+ }
388
+ return ['Review output carefully; consult the run log for step-by-step details.'];
389
+ }
390
+ /**
391
+ * Execute a macOS UI action run.
392
+ * Handles the awaiting_approval gate for irreversible intents.
393
+ *
394
+ * task-1773486840001-u0shj14v3 / task-1773486840036-8x0o76rmp
395
+ */
396
+ export async function executeMacOSUIAction(runId, intent) {
397
+ // Validate
398
+ const validation = macOSValidateIntent(intent);
399
+ if (!validation.ok) {
400
+ emit(runId, { type: 'step_failed', timestamp: Date.now(), payload: { step: 'validate', error: validation.reason } });
401
+ transition(runId, 'failed');
402
+ return;
403
+ }
404
+ transition(runId, 'running');
405
+ emit(runId, { type: 'step_started', timestamp: Date.now(), payload: { step: 'validate', action: intent.action } });
406
+ emit(runId, { type: 'step_succeeded', timestamp: Date.now(), payload: { step: 'validate', action: intent.action } });
407
+ // Irreversible: request human approval before executing
408
+ if (macOSRequiresApproval(intent)) {
409
+ transition(runId, 'awaiting_approval');
410
+ emit(runId, { type: 'approval_requested', timestamp: Date.now(), payload: {
411
+ message: `macOS action "${intent.action}" requires human approval (pilot safety gate)`,
412
+ action: intent.action,
413
+ app: intent.app,
414
+ preview: (intent.text ?? '').slice(0, 200),
415
+ } });
416
+ // Wait for approve/reject (10m timeout → auto-reject, matching github_issue_create pattern)
417
+ const approved = await new Promise((resolve) => {
418
+ pendingApprovals.set(runId, { runId, resolve });
419
+ setTimeout(() => {
420
+ if (pendingApprovals.has(runId)) {
421
+ pendingApprovals.delete(runId);
422
+ resolve(false);
423
+ }
424
+ }, getApprovalTimeoutMs());
425
+ });
426
+ emit(runId, { type: 'approval_resolved', timestamp: Date.now(), payload: { approved, runId } });
427
+ if (!approved) {
428
+ const run = runs.get(runId);
429
+ if (run)
430
+ run.result = { outcome: 'rejected', recoveryHint: 'Re-submit and approve when ready.' };
431
+ transition(runId, 'rejected');
432
+ return;
433
+ }
434
+ transition(runId, 'running');
435
+ }
436
+ // Execute
437
+ emit(runId, { type: 'step_started', timestamp: Date.now(), payload: { step: 'execute', action: intent.action } });
438
+ const result = await macOSExecuteIntent(intent);
439
+ // Check if aborted while executing — kill-switch may have fired during the await
440
+ const runAfterExec = runs.get(runId);
441
+ if (!runAfterExec || runAfterExec.status === 'aborted')
442
+ return;
443
+ if (result.ok) {
444
+ emit(runId, { type: 'step_succeeded', timestamp: Date.now(), payload: {
445
+ step: 'execute',
446
+ action: intent.action,
447
+ output: result.output?.slice(0, 200),
448
+ stepCount: result.steps.length,
449
+ } });
450
+ const run = runs.get(runId);
451
+ if (run)
452
+ run.result = { outcome: 'completed', output: result.output, steps: result.steps };
453
+ transition(runId, 'completed');
454
+ }
455
+ else {
456
+ emit(runId, { type: 'step_failed', timestamp: Date.now(), payload: { step: 'execute', error: result.error } });
457
+ const run = runs.get(runId);
458
+ if (run)
459
+ run.result = { outcome: 'failed', errorMessage: result.error };
460
+ transition(runId, 'failed');
461
+ }
462
+ }
463
+ //# sourceMappingURL=agent-interface.js.map