agenthub-multiagent-mcp 1.51.0 → 1.53.0

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 (78) hide show
  1. package/README.md +192 -192
  2. package/dist/captureHookInstall.d.ts +127 -0
  3. package/dist/captureHookInstall.d.ts.map +1 -0
  4. package/dist/captureHookInstall.js +256 -0
  5. package/dist/captureHookInstall.js.map +1 -0
  6. package/dist/captureHookInstall.test.d.ts +10 -0
  7. package/dist/captureHookInstall.test.d.ts.map +1 -0
  8. package/dist/captureHookInstall.test.js +325 -0
  9. package/dist/captureHookInstall.test.js.map +1 -0
  10. package/dist/channel.js +99 -3
  11. package/dist/channel.js.map +1 -1
  12. package/dist/client.d.ts +80 -1
  13. package/dist/client.d.ts.map +1 -1
  14. package/dist/client.js +197 -4
  15. package/dist/client.js.map +1 -1
  16. package/dist/client.unit.test.js +50 -0
  17. package/dist/client.unit.test.js.map +1 -1
  18. package/dist/daemon.js +217 -3
  19. package/dist/daemon.js.map +1 -1
  20. package/dist/hooks/periodicReminder.js +9 -9
  21. package/dist/index.js +25 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/session-capture.sh +410 -0
  24. package/dist/sessionCapture.d.ts +163 -0
  25. package/dist/sessionCapture.d.ts.map +1 -0
  26. package/dist/sessionCapture.js +279 -0
  27. package/dist/sessionCapture.js.map +1 -0
  28. package/dist/sessionCapture.test.d.ts +9 -0
  29. package/dist/sessionCapture.test.d.ts.map +1 -0
  30. package/dist/sessionCapture.test.js +366 -0
  31. package/dist/sessionCapture.test.js.map +1 -0
  32. package/dist/sessionCaptureScript.test.d.ts +16 -0
  33. package/dist/sessionCaptureScript.test.d.ts.map +1 -0
  34. package/dist/sessionCaptureScript.test.js +92 -0
  35. package/dist/sessionCaptureScript.test.js.map +1 -0
  36. package/dist/setup.js +56 -56
  37. package/dist/steering.test.d.ts +2 -0
  38. package/dist/steering.test.d.ts.map +1 -0
  39. package/dist/steering.test.js +223 -0
  40. package/dist/steering.test.js.map +1 -0
  41. package/dist/streamingRuntimeLib.d.ts +391 -0
  42. package/dist/streamingRuntimeLib.d.ts.map +1 -0
  43. package/dist/streamingRuntimeLib.js +628 -0
  44. package/dist/streamingRuntimeLib.js.map +1 -0
  45. package/dist/streamingRuntimeLib.test.d.ts +2 -0
  46. package/dist/streamingRuntimeLib.test.d.ts.map +1 -0
  47. package/dist/streamingRuntimeLib.test.js +233 -0
  48. package/dist/streamingRuntimeLib.test.js.map +1 -0
  49. package/dist/tools/index.d.ts.map +1 -1
  50. package/dist/tools/index.js +149 -13
  51. package/dist/tools/index.js.map +1 -1
  52. package/dist/tools/openspec-viz/diagram-generator.js +54 -54
  53. package/dist/tools/openspec-viz/html-generator.js +503 -503
  54. package/dist/tools/sessionControl.d.ts +53 -0
  55. package/dist/tools/sessionControl.d.ts.map +1 -0
  56. package/dist/tools/sessionControl.js +265 -0
  57. package/dist/tools/sessionControl.js.map +1 -0
  58. package/dist/tools/sessionControl.test.d.ts +14 -0
  59. package/dist/tools/sessionControl.test.d.ts.map +1 -0
  60. package/dist/tools/sessionControl.test.js +298 -0
  61. package/dist/tools/sessionControl.test.js.map +1 -0
  62. package/dist/tools/tools.test.js +5 -2
  63. package/dist/tools/tools.test.js.map +1 -1
  64. package/dist/tools/zohoAttachments.test.d.ts +10 -0
  65. package/dist/tools/zohoAttachments.test.d.ts.map +1 -0
  66. package/dist/tools/zohoAttachments.test.js +204 -0
  67. package/dist/tools/zohoAttachments.test.js.map +1 -0
  68. package/dist/worker.js +10 -10
  69. package/native/agenthub-memvid/agenthub-memvid.linux-x64-gnu.node +0 -0
  70. package/native/agenthub-memvid/index.d.ts +58 -58
  71. package/native/agenthub-memvid/index.js +319 -319
  72. package/package.json +77 -77
  73. package/skills/catalog.json +76 -76
  74. package/skills/commands/close-session.md +144 -144
  75. package/skills/manifest.json +50 -50
  76. package/skills/skills/code-brain-hook/SKILL.md +61 -61
  77. package/skills/skills/file-bug/SKILL.md +75 -75
  78. package/skills/skills/multiagent-brainstorm/SKILL.md +142 -142
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Consent-gated install of the session-capture hook for **human interactive**
3
+ * Claude Code sessions (add-multiplayer-sessions §3.2/§3.3).
4
+ *
5
+ * The daemon path (§1.5) never comes through here: it passes an ephemeral
6
+ * `--settings` file to the process it spawns, so no user-owned file is ever
7
+ * touched. An interactive session has no such seam — the person launches
8
+ * `claude` themselves — so the hook has to be registered in their own
9
+ * `~/.claude/settings.json`.
10
+ *
11
+ * That makes consent the whole design, not a feature of it:
12
+ *
13
+ * - Nothing is written until the **server** confirms that *this user*
14
+ * consented. The MCP does not decide; it asks and obeys.
15
+ * - What gets written is the hook registration plus a grant file. The hook is
16
+ * inert without the grant, and the grant is only ever produced by a
17
+ * confirmed consent, so a stale settings entry cannot capture anything.
18
+ * - When consent is absent or withdrawn, this module **removes** both. Running
19
+ * it is how a revocation reaches the machine, not merely how a grant does.
20
+ * - Every failure — network, parse, permission — leaves capture off. There is
21
+ * no path where "we could not tell" results in recording.
22
+ *
23
+ * `mergeCaptureHook` (sessionCapture.ts) does the settings merge, so the
24
+ * non-destructive behaviour proven there is the behaviour here: other
25
+ * PostToolUse entries, other hook events, and unrelated settings keys survive.
26
+ */
27
+ import * as fs from "fs";
28
+ import * as path from "path";
29
+ import { CAPTURE_HOOK_MARKER, captureHookCommand, mergeCaptureHook, resolveCaptureHookScript, } from "./sessionCapture.js";
30
+ /** Default gap between in-session recording notices, in seconds. */
31
+ export const DEFAULT_NOTICE_INTERVAL_SEC = 900;
32
+ /** Timeout on the consent lookup. Startup must not hang on it. */
33
+ export const CONSENT_TIMEOUT_MS = 4000;
34
+ /**
35
+ * Remove the capture hook from a settings object, preserving everything else.
36
+ *
37
+ * The inverse of `mergeCaptureHook`, and the reason revocation is effective on
38
+ * the *next* MCP start rather than only server-side. A PostToolUse entry is
39
+ * dropped only if every command in it is ours; an entry a user has added other
40
+ * commands to keeps those, so this can never eat a hook we did not install.
41
+ */
42
+ export function removeCaptureHook(base) {
43
+ const merged = { ...base };
44
+ const hooksIn = (base.hooks ?? {});
45
+ if (!Array.isArray(hooksIn.PostToolUse))
46
+ return merged;
47
+ const hooks = { ...hooksIn };
48
+ const kept = [];
49
+ for (const entry of hooksIn.PostToolUse) {
50
+ const commands = entry?.hooks ?? [];
51
+ const survivors = commands.filter((h) => !(typeof h?.command === "string" && h.command.includes(CAPTURE_HOOK_MARKER)));
52
+ if (survivors.length > 0)
53
+ kept.push({ ...entry, hooks: survivors });
54
+ }
55
+ hooks.PostToolUse = kept;
56
+ merged.hooks = hooks;
57
+ return merged;
58
+ }
59
+ /**
60
+ * Strip the client's `/api` suffix. `AgentHubClient` appends it for its own
61
+ * calls, but the hook builds `<root>/api/...` itself, and a doubled `/api/api`
62
+ * 404s with an HTML body — a failure that shows up as "capture silently never
63
+ * worked" rather than as an error.
64
+ */
65
+ export function captureApiRoot(clientBaseUrl) {
66
+ return clientBaseUrl.replace(/\/+$/, "").replace(/\/api$/, "");
67
+ }
68
+ /**
69
+ * Bring the local machine into line with a consent decision, and report what
70
+ * changed.
71
+ *
72
+ * Consented → write the grant, register the hook.
73
+ * Not → delete the grant and its per-session state, deregister the hook.
74
+ *
75
+ * Both directions are idempotent and safe to run on every MCP start.
76
+ */
77
+ export function applyCaptureConsent(consented, opts, deps = {}) {
78
+ const exists = deps.exists ?? fs.existsSync;
79
+ const readFile = deps.readFile ?? ((p) => fs.readFileSync(p, "utf8"));
80
+ const writeFile = deps.writeFile ?? ((p, d) => fs.writeFileSync(p, d));
81
+ const mkdir = deps.mkdir ?? ((p) => fs.mkdirSync(p, { recursive: true }));
82
+ const removeFile = deps.removeFile ?? fs.unlinkSync;
83
+ const removeDir = deps.removeDir ?? ((p) => fs.rmSync(p, { recursive: true, force: true }));
84
+ const chmod = deps.chmod ?? fs.chmodSync;
85
+ const now = deps.now ?? (() => new Date());
86
+ // Read the user's settings first. A settings file we cannot parse is left
87
+ // strictly alone — rewriting it would destroy configuration we do not
88
+ // understand, and the cost of not installing is only that capture stays off.
89
+ let settings = {};
90
+ if (exists(opts.settingsPath)) {
91
+ try {
92
+ settings = JSON.parse(readFile(opts.settingsPath));
93
+ }
94
+ catch {
95
+ return "skipped: ~/.claude/settings.json is not valid JSON";
96
+ }
97
+ }
98
+ if (!consented) {
99
+ // Revocation reaching the machine. Remove the credential first: if the
100
+ // process dies between the two writes, the leftover is a hook with no
101
+ // grant, which captures nothing.
102
+ let removed = false;
103
+ for (const target of [opts.grantPath]) {
104
+ if (exists(target)) {
105
+ try {
106
+ removeFile(target);
107
+ removed = true;
108
+ }
109
+ catch {
110
+ /* best effort — the server refuses the events regardless */
111
+ }
112
+ }
113
+ }
114
+ try {
115
+ removeDir(captureStateDir(opts.grantPath));
116
+ }
117
+ catch {
118
+ /* never existed */
119
+ }
120
+ const stripped = removeCaptureHook(settings);
121
+ if (JSON.stringify(stripped) !== JSON.stringify(settings)) {
122
+ try {
123
+ mkdir(path.dirname(opts.settingsPath));
124
+ writeFile(opts.settingsPath, JSON.stringify(stripped, null, 2));
125
+ removed = true;
126
+ }
127
+ catch (e) {
128
+ return `not consented; could not deregister hook: ${errText(e)}`;
129
+ }
130
+ }
131
+ return removed
132
+ ? "not consented — capture hook and grant removed"
133
+ : "not consented — capture is off (nothing to remove)";
134
+ }
135
+ if (!opts.hookScript) {
136
+ return "consented, but session-capture.sh was not found — capture not installed";
137
+ }
138
+ if (!opts.connectToken) {
139
+ // Without a credential the hook cannot bootstrap, and a grant file with an
140
+ // empty token would look installed while doing nothing.
141
+ return "consented, but no connect token is available yet — capture not installed";
142
+ }
143
+ const grant = {
144
+ api_url: opts.apiRoot,
145
+ user_id: opts.userId,
146
+ agent_id: opts.agentId,
147
+ connect_token: opts.connectToken,
148
+ consented_at: now().toISOString(),
149
+ notice_interval_sec: opts.noticeIntervalSec ?? DEFAULT_NOTICE_INTERVAL_SEC,
150
+ };
151
+ try {
152
+ mkdir(path.dirname(opts.grantPath));
153
+ writeFile(opts.grantPath, JSON.stringify(grant, null, 2));
154
+ // The grant carries a credential, so it is owner-read/write only. A no-op
155
+ // on Windows; still correct to ask for on every platform that honours it.
156
+ try {
157
+ chmod(opts.grantPath, 0o600);
158
+ }
159
+ catch {
160
+ /* filesystem does not support it */
161
+ }
162
+ }
163
+ catch (e) {
164
+ return `consented, but the capture grant could not be written: ${errText(e)}`;
165
+ }
166
+ const command = captureHookCommand(opts.hookScript);
167
+ const alreadyRegistered = JSON.stringify(mergeCaptureHook(settings, command)) === JSON.stringify(settings);
168
+ if (alreadyRegistered)
169
+ return "consented — capture hook already installed, grant refreshed";
170
+ try {
171
+ mkdir(path.dirname(opts.settingsPath));
172
+ writeFile(opts.settingsPath, JSON.stringify(mergeCaptureHook(settings, command), null, 2));
173
+ }
174
+ catch (e) {
175
+ return `consented, but the hook could not be registered: ${errText(e)}`;
176
+ }
177
+ return "consented — capture hook installed";
178
+ }
179
+ /** Where the hook keeps its per-Claude-session bootstrap cache. */
180
+ export function captureStateDir(grantPath) {
181
+ return path.join(path.dirname(grantPath), "agenthub-capture-state");
182
+ }
183
+ /**
184
+ * Ask the server whether the calling identity's owner has consented.
185
+ *
186
+ * Returns `null` on any failure — unreachable server, non-2xx, unparseable
187
+ * body. `null` is not "unknown, carry on"; every caller treats it as "do not
188
+ * capture", so a server we cannot reach can never be the reason someone gets
189
+ * recorded.
190
+ */
191
+ export async function fetchCaptureConsent(clientBaseUrl, connectToken, fetchImpl, timeoutMs = CONSENT_TIMEOUT_MS) {
192
+ const doFetch = fetchImpl ?? globalThis.fetch;
193
+ if (!doFetch || !connectToken)
194
+ return null;
195
+ const controller = new AbortController();
196
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
197
+ try {
198
+ const res = await doFetch(`${clientBaseUrl.replace(/\/+$/, "")}/session-capture/consent`, {
199
+ method: "GET",
200
+ headers: { "X-Connect-Token": connectToken },
201
+ signal: controller.signal,
202
+ });
203
+ if (!res.ok)
204
+ return null;
205
+ const body = (await res.json());
206
+ return {
207
+ consented: body?.consent?.consented === true,
208
+ userId: typeof body?.consent?.user_id === "string" ? body.consent.user_id : "",
209
+ streamingEnabled: body?.streaming_enabled === true,
210
+ };
211
+ }
212
+ catch {
213
+ return null;
214
+ }
215
+ finally {
216
+ clearTimeout(timer);
217
+ }
218
+ }
219
+ /**
220
+ * Startup entry point: ask, then apply. Never throws — a capture concern must
221
+ * not be able to stop the MCP from starting.
222
+ */
223
+ export async function installCaptureHook(opts) {
224
+ try {
225
+ const settingsPath = path.join(opts.homeDir, ".claude", "settings.json");
226
+ const grantPath = path.join(opts.homeDir, ".claude", "agenthub-capture.json");
227
+ const status = await fetchCaptureConsent(opts.clientBaseUrl, opts.connectToken, opts.fetchImpl);
228
+ // Unreachable or unauthenticated ⇒ treat exactly like "not consented", so
229
+ // an offline start also tears down a grant left by a previous one.
230
+ const consented = status?.consented === true;
231
+ const applied = applyCaptureConsent(consented, {
232
+ settingsPath,
233
+ grantPath,
234
+ hookScript: resolveCaptureHookScript(opts.moduleDir, opts.deps?.exists ?? fs.existsSync),
235
+ apiRoot: captureApiRoot(opts.clientBaseUrl),
236
+ userId: status?.userId ?? "",
237
+ agentId: opts.agentId,
238
+ connectToken: opts.connectToken,
239
+ }, opts.deps);
240
+ if (consented && status && !status.streamingEnabled) {
241
+ // Worth saying out loud: the person agreed, and nothing will be recorded
242
+ // anyway. Silence here reads as "it is working".
243
+ return `${applied} (server-side session streaming is disabled, so nothing will be captured yet)`;
244
+ }
245
+ if (status === null)
246
+ return `${applied} (consent could not be confirmed with the server)`;
247
+ return applied;
248
+ }
249
+ catch (e) {
250
+ return `skipped: ${errText(e)}`;
251
+ }
252
+ }
253
+ function errText(e) {
254
+ return e instanceof Error ? e.message : String(e);
255
+ }
256
+ //# sourceMappingURL=captureHookInstall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captureHookInstall.js","sourceRoot":"","sources":["../src/captureHookInstall.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * add-multiplayer-sessions §3.2/§3.3/§3.4 — consent-gated install of the
3
+ * interactive capture hook.
4
+ *
5
+ * The load-bearing assertions here are the negative ones: with no consent, or
6
+ * with a consent lookup that failed, nothing is written to the user's machine
7
+ * that could cause their session to be recorded.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=captureHookInstall.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captureHookInstall.test.d.ts","sourceRoot":"","sources":["../src/captureHookInstall.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,325 @@
1
+ /**
2
+ * add-multiplayer-sessions §3.2/§3.3/§3.4 — consent-gated install of the
3
+ * interactive capture hook.
4
+ *
5
+ * The load-bearing assertions here are the negative ones: with no consent, or
6
+ * with a consent lookup that failed, nothing is written to the user's machine
7
+ * that could cause their session to be recorded.
8
+ */
9
+ import { describe, it, expect } from "vitest";
10
+ import * as path from "path";
11
+ import { applyCaptureConsent, captureApiRoot, captureStateDir, fetchCaptureConsent, installCaptureHook, removeCaptureHook, } from "./captureHookInstall.js";
12
+ import { CAPTURE_HOOK_MARKER } from "./sessionCapture.js";
13
+ /** In-memory filesystem so the tests never touch a real ~/.claude. */
14
+ function fakeFs(seed = {}) {
15
+ const files = new Map(Object.entries(seed));
16
+ const dirs = new Set();
17
+ const removedDirs = [];
18
+ const modes = new Map();
19
+ const deps = {
20
+ exists: (p) => files.has(p),
21
+ readFile: (p) => {
22
+ const v = files.get(p);
23
+ if (v === undefined)
24
+ throw new Error("ENOENT " + p);
25
+ return v;
26
+ },
27
+ writeFile: (p, d) => void files.set(p, d),
28
+ mkdir: (p) => void dirs.add(p),
29
+ removeFile: (p) => void files.delete(p),
30
+ removeDir: (p) => void removedDirs.push(p),
31
+ chmod: (p, m) => void modes.set(p, m),
32
+ now: () => new Date("2026-07-26T00:00:00.000Z"),
33
+ };
34
+ return { files, dirs, removedDirs, modes, deps };
35
+ }
36
+ const SETTINGS = "/home/alice/.claude/settings.json";
37
+ const GRANT = "/home/alice/.claude/agenthub-capture.json";
38
+ function opts(over = {}) {
39
+ return {
40
+ settingsPath: SETTINGS,
41
+ grantPath: GRANT,
42
+ hookScript: "/opt/mcp/dist/session-capture.sh",
43
+ apiRoot: "https://agenthub.example.com",
44
+ userId: "user-alice",
45
+ agentId: "agent-alice",
46
+ connectToken: "rt-alice",
47
+ ...over,
48
+ };
49
+ }
50
+ describe("captureApiRoot", () => {
51
+ it("strips the client's /api suffix so the hook does not build /api/api", () => {
52
+ expect(captureApiRoot("https://agenthub.example.com/api")).toBe("https://agenthub.example.com");
53
+ expect(captureApiRoot("https://agenthub.example.com/api/")).toBe("https://agenthub.example.com");
54
+ });
55
+ it("leaves a root that has no /api alone", () => {
56
+ expect(captureApiRoot("https://agenthub.example.com")).toBe("https://agenthub.example.com");
57
+ });
58
+ });
59
+ describe("applyCaptureConsent — consent absent", () => {
60
+ it("writes nothing at all for a user who has not consented", () => {
61
+ const fs = fakeFs();
62
+ const status = applyCaptureConsent(false, opts(), fs.deps);
63
+ expect(fs.files.has(GRANT)).toBe(false);
64
+ expect(fs.files.has(SETTINGS)).toBe(false);
65
+ expect(status).toMatch(/not consented/);
66
+ });
67
+ it("removes a grant and deregisters the hook when consent is withdrawn", () => {
68
+ const existing = {
69
+ hooks: {
70
+ PostToolUse: [
71
+ { matcher: "", hooks: [{ type: "command", command: `bash "/opt/mcp/${CAPTURE_HOOK_MARKER}"` }] },
72
+ ],
73
+ },
74
+ };
75
+ const fs = fakeFs({
76
+ [SETTINGS]: JSON.stringify(existing),
77
+ [GRANT]: JSON.stringify({ connect_token: "rt-alice" }),
78
+ });
79
+ applyCaptureConsent(false, opts(), fs.deps);
80
+ expect(fs.files.has(GRANT)).toBe(false);
81
+ const after = JSON.parse(fs.files.get(SETTINGS));
82
+ expect(after.hooks.PostToolUse).toEqual([]);
83
+ // The per-session bootstrap cache goes too, so a stale session id cannot
84
+ // be reused if consent is granted again later.
85
+ expect(fs.removedDirs).toContain(captureStateDir(GRANT));
86
+ });
87
+ it("leaves the user's other hooks untouched while removing ours", () => {
88
+ const existing = {
89
+ model: "opus",
90
+ hooks: {
91
+ SessionStart: [{ hooks: [{ type: "command", command: "node inboxHookCli.js" }] }],
92
+ PostToolUse: [
93
+ { matcher: "", hooks: [{ type: "command", command: "node activityHookCli.js" }] },
94
+ { matcher: "", hooks: [{ type: "command", command: `bash "/opt/${CAPTURE_HOOK_MARKER}"` }] },
95
+ ],
96
+ },
97
+ };
98
+ const fs = fakeFs({ [SETTINGS]: JSON.stringify(existing) });
99
+ applyCaptureConsent(false, opts(), fs.deps);
100
+ const after = JSON.parse(fs.files.get(SETTINGS));
101
+ expect(after.model).toBe("opus");
102
+ expect(after.hooks.SessionStart).toHaveLength(1);
103
+ expect(after.hooks.PostToolUse).toHaveLength(1);
104
+ expect(after.hooks.PostToolUse[0].hooks[0].command).toContain("activityHookCli");
105
+ });
106
+ });
107
+ describe("applyCaptureConsent — consent given", () => {
108
+ it("writes the grant and registers the hook", () => {
109
+ const fs = fakeFs();
110
+ const status = applyCaptureConsent(true, opts(), fs.deps);
111
+ const grant = JSON.parse(fs.files.get(GRANT));
112
+ expect(grant.api_url).toBe("https://agenthub.example.com");
113
+ expect(grant.connect_token).toBe("rt-alice");
114
+ expect(grant.user_id).toBe("user-alice");
115
+ expect(grant.notice_interval_sec).toBeGreaterThan(0);
116
+ const settings = JSON.parse(fs.files.get(SETTINGS));
117
+ expect(settings.hooks.PostToolUse[0].hooks[0].command).toContain(CAPTURE_HOOK_MARKER);
118
+ expect(status).toMatch(/consented/);
119
+ });
120
+ it("writes the grant owner-only — it carries a credential", () => {
121
+ const fs = fakeFs();
122
+ applyCaptureConsent(true, opts(), fs.deps);
123
+ expect(fs.modes.get(GRANT)).toBe(0o600);
124
+ });
125
+ // The required test: the merge must not be a rewrite.
126
+ it("preserves an existing user hook configuration", () => {
127
+ const existing = {
128
+ permissions: { allow: ["Bash(npm run test)"] },
129
+ hooks: {
130
+ SessionStart: [{ hooks: [{ type: "command", command: "node inboxHookCli.js" }] }],
131
+ PostToolUse: [{ matcher: "Edit", hooks: [{ type: "command", command: "node activityHookCli.js" }] }],
132
+ Stop: [{ hooks: [{ type: "command", command: "say done" }] }],
133
+ },
134
+ };
135
+ const fs = fakeFs({ [SETTINGS]: JSON.stringify(existing) });
136
+ applyCaptureConsent(true, opts(), fs.deps);
137
+ const after = JSON.parse(fs.files.get(SETTINGS));
138
+ expect(after.permissions).toEqual(existing.permissions);
139
+ expect(after.hooks.SessionStart).toEqual(existing.hooks.SessionStart);
140
+ expect(after.hooks.Stop).toEqual(existing.hooks.Stop);
141
+ // The pre-existing PostToolUse entry survives verbatim, alongside ours.
142
+ expect(after.hooks.PostToolUse).toHaveLength(2);
143
+ expect(after.hooks.PostToolUse[0]).toEqual(existing.hooks.PostToolUse[0]);
144
+ expect(after.hooks.PostToolUse[1].hooks[0].command).toContain(CAPTURE_HOOK_MARKER);
145
+ });
146
+ it("is idempotent — a second run does not duplicate the hook entry", () => {
147
+ const fs = fakeFs();
148
+ applyCaptureConsent(true, opts(), fs.deps);
149
+ applyCaptureConsent(true, opts(), fs.deps);
150
+ const after = JSON.parse(fs.files.get(SETTINGS));
151
+ expect(after.hooks.PostToolUse).toHaveLength(1);
152
+ });
153
+ it("refuses to install without a credential rather than writing an inert grant", () => {
154
+ const fs = fakeFs();
155
+ const status = applyCaptureConsent(true, opts({ connectToken: "" }), fs.deps);
156
+ expect(fs.files.has(GRANT)).toBe(false);
157
+ expect(fs.files.has(SETTINGS)).toBe(false);
158
+ expect(status).toMatch(/no connect token/);
159
+ });
160
+ it("refuses to install when the hook script cannot be found", () => {
161
+ const fs = fakeFs();
162
+ const status = applyCaptureConsent(true, opts({ hookScript: null }), fs.deps);
163
+ expect(fs.files.has(GRANT)).toBe(false);
164
+ expect(status).toMatch(/not found/);
165
+ });
166
+ it("never rewrites a settings file it cannot parse", () => {
167
+ const fs = fakeFs({ [SETTINGS]: "{ this is not json" });
168
+ const status = applyCaptureConsent(true, opts(), fs.deps);
169
+ expect(fs.files.get(SETTINGS)).toBe("{ this is not json");
170
+ expect(fs.files.has(GRANT)).toBe(false);
171
+ expect(status).toMatch(/not valid JSON/);
172
+ });
173
+ });
174
+ describe("removeCaptureHook", () => {
175
+ it("keeps an entry that also carries a hook we did not install", () => {
176
+ const base = {
177
+ hooks: {
178
+ PostToolUse: [
179
+ {
180
+ matcher: "",
181
+ hooks: [
182
+ { type: "command", command: "node activityHookCli.js" },
183
+ { type: "command", command: `bash "${CAPTURE_HOOK_MARKER}"` },
184
+ ],
185
+ },
186
+ ],
187
+ },
188
+ };
189
+ const after = removeCaptureHook(base);
190
+ expect(after.hooks.PostToolUse).toHaveLength(1);
191
+ expect(after.hooks.PostToolUse[0].hooks).toHaveLength(1);
192
+ });
193
+ it("does not mutate the input", () => {
194
+ const base = {
195
+ hooks: { PostToolUse: [{ hooks: [{ type: "command", command: CAPTURE_HOOK_MARKER }] }] },
196
+ };
197
+ const snapshot = JSON.stringify(base);
198
+ removeCaptureHook(base);
199
+ expect(JSON.stringify(base)).toBe(snapshot);
200
+ });
201
+ it("is a no-op on settings that never had the hook", () => {
202
+ const base = { hooks: { SessionStart: [{ hooks: [{ type: "command", command: "x" }] }] } };
203
+ expect(removeCaptureHook(base)).toEqual(base);
204
+ });
205
+ });
206
+ describe("fetchCaptureConsent", () => {
207
+ const okResponse = (consented, streaming = true) => async () => ({
208
+ ok: true,
209
+ status: 200,
210
+ json: async () => ({
211
+ consent: { consented, user_id: "user-alice" },
212
+ streaming_enabled: streaming,
213
+ }),
214
+ });
215
+ it("reports consent when the server grants it", async () => {
216
+ const got = await fetchCaptureConsent("https://x/api", "rt", okResponse(true));
217
+ expect(got).toEqual({ consented: true, userId: "user-alice", streamingEnabled: true });
218
+ });
219
+ it("reports the absence of consent as such, not as an error", async () => {
220
+ const got = await fetchCaptureConsent("https://x/api", "rt", okResponse(false));
221
+ expect(got?.consented).toBe(false);
222
+ });
223
+ it("returns null (⇒ do not capture) when the server refuses", async () => {
224
+ const f = async () => ({ ok: false, status: 401, json: async () => ({}) });
225
+ expect(await fetchCaptureConsent("https://x/api", "rt", f)).toBeNull();
226
+ });
227
+ it("returns null (⇒ do not capture) when the server is unreachable", async () => {
228
+ const f = async () => {
229
+ throw new Error("ECONNREFUSED");
230
+ };
231
+ expect(await fetchCaptureConsent("https://x/api", "rt", f)).toBeNull();
232
+ });
233
+ it("does not even ask without a credential", async () => {
234
+ let called = false;
235
+ const f = async () => {
236
+ called = true;
237
+ return { ok: true, status: 200, json: async () => ({}) };
238
+ };
239
+ expect(await fetchCaptureConsent("https://x/api", "", f)).toBeNull();
240
+ expect(called).toBe(false);
241
+ });
242
+ });
243
+ describe("installCaptureHook — end to end", () => {
244
+ const base = {
245
+ clientBaseUrl: "https://agenthub.example.com/api",
246
+ connectToken: "rt-alice",
247
+ agentId: "agent-alice",
248
+ homeDir: "/home/alice",
249
+ moduleDir: "/opt/mcp/dist",
250
+ };
251
+ // installCaptureHook builds these with path.join, so the tests must too —
252
+ // otherwise they only pass on POSIX.
253
+ const grantPath = path.join(base.homeDir, ".claude", "agenthub-capture.json");
254
+ const settingsPath = path.join(base.homeDir, ".claude", "settings.json");
255
+ function withScript(seed = {}) {
256
+ const fs = fakeFs(seed);
257
+ // resolveCaptureHookScript probes the filesystem through the same `exists`.
258
+ fs.files.set(path.join(base.moduleDir, "session-capture.sh"), "#!/bin/bash");
259
+ return fs;
260
+ }
261
+ it("§3.4 — a server that says 'not consented' leaves the machine clean", async () => {
262
+ const fs = withScript();
263
+ const f = async () => ({
264
+ ok: true,
265
+ status: 200,
266
+ json: async () => ({ consent: { consented: false, user_id: "user-alice" }, streaming_enabled: true }),
267
+ });
268
+ await installCaptureHook({ ...base, fetchImpl: f, deps: fs.deps });
269
+ expect(fs.files.has(grantPath)).toBe(false);
270
+ expect(fs.files.has(settingsPath)).toBe(false);
271
+ });
272
+ it("fails closed — an unreachable server installs nothing", async () => {
273
+ const fs = withScript();
274
+ const f = async () => {
275
+ throw new Error("network down");
276
+ };
277
+ const status = await installCaptureHook({ ...base, fetchImpl: f, deps: fs.deps });
278
+ expect(fs.files.has(grantPath)).toBe(false);
279
+ expect(status).toMatch(/could not be confirmed/);
280
+ });
281
+ it("installs when the server confirms consent", async () => {
282
+ const fs = withScript();
283
+ const f = async () => ({
284
+ ok: true,
285
+ status: 200,
286
+ json: async () => ({ consent: { consented: true, user_id: "user-alice" }, streaming_enabled: true }),
287
+ });
288
+ await installCaptureHook({ ...base, fetchImpl: f, deps: fs.deps });
289
+ const grant = JSON.parse(fs.files.get(grantPath));
290
+ expect(grant.api_url).toBe("https://agenthub.example.com");
291
+ expect(grant.user_id).toBe("user-alice");
292
+ const settings = JSON.parse(fs.files.get(settingsPath));
293
+ expect(settings.hooks.PostToolUse[0].hooks[0].command).toContain(CAPTURE_HOOK_MARKER);
294
+ });
295
+ it("says so when consent is given but the server-side switch is off", async () => {
296
+ const fs = withScript();
297
+ const f = async () => ({
298
+ ok: true,
299
+ status: 200,
300
+ json: async () => ({ consent: { consented: true, user_id: "user-alice" }, streaming_enabled: false }),
301
+ });
302
+ const status = await installCaptureHook({ ...base, fetchImpl: f, deps: fs.deps });
303
+ expect(status).toMatch(/streaming is disabled/);
304
+ });
305
+ it("propagates a revocation: a previously installed hook and grant are removed", async () => {
306
+ const fs = withScript({
307
+ [settingsPath]: JSON.stringify({
308
+ hooks: {
309
+ PostToolUse: [{ matcher: "", hooks: [{ type: "command", command: `bash "${CAPTURE_HOOK_MARKER}"` }] }],
310
+ },
311
+ }),
312
+ [grantPath]: JSON.stringify({ connect_token: "rt-alice" }),
313
+ });
314
+ const f = async () => ({
315
+ ok: true,
316
+ status: 200,
317
+ json: async () => ({ consent: { consented: false, user_id: "user-alice" }, streaming_enabled: true }),
318
+ });
319
+ await installCaptureHook({ ...base, fetchImpl: f, deps: fs.deps });
320
+ expect(fs.files.has(grantPath)).toBe(false);
321
+ const settings = JSON.parse(fs.files.get(settingsPath));
322
+ expect(settings.hooks.PostToolUse).toEqual([]);
323
+ });
324
+ });
325
+ //# sourceMappingURL=captureHookInstall.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captureHookInstall.test.js","sourceRoot":"","sources":["../src/captureHookInstall.test.ts"],"names":[],"mappings":"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"}