@agent-beam/beam 0.2.38 → 0.2.39

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 (109) hide show
  1. package/README.md +7 -13
  2. package/dist/access-risk.d.ts +10 -0
  3. package/dist/access-risk.js +43 -0
  4. package/dist/access-risk.js.map +1 -0
  5. package/dist/action-context.d.ts +30 -0
  6. package/dist/action-context.js +203 -0
  7. package/dist/action-context.js.map +1 -0
  8. package/dist/activity.d.ts +22 -0
  9. package/dist/activity.js +132 -0
  10. package/dist/activity.js.map +1 -0
  11. package/dist/agent-hook-install.d.ts +8 -0
  12. package/dist/agent-hook-install.js +278 -0
  13. package/dist/agent-hook-install.js.map +1 -0
  14. package/dist/agent-installers.js +16 -3
  15. package/dist/agent-installers.js.map +1 -1
  16. package/dist/agent-plugins.d.ts +3 -0
  17. package/dist/agent-plugins.js +66 -0
  18. package/dist/agent-plugins.js.map +1 -0
  19. package/dist/agents.d.ts +1 -0
  20. package/dist/agents.js +30 -184
  21. package/dist/agents.js.map +1 -1
  22. package/dist/behavioral-anomaly.d.ts +16 -0
  23. package/dist/behavioral-anomaly.js +66 -0
  24. package/dist/behavioral-anomaly.js.map +1 -0
  25. package/dist/cli.js +13 -8
  26. package/dist/cli.js.map +1 -1
  27. package/dist/client.d.ts +1 -1
  28. package/dist/client.js +177 -78
  29. package/dist/client.js.map +1 -1
  30. package/dist/core.d.ts +50 -2
  31. package/dist/core.js +73 -24
  32. package/dist/core.js.map +1 -1
  33. package/dist/credential-egress.d.ts +17 -0
  34. package/dist/credential-egress.js +325 -0
  35. package/dist/credential-egress.js.map +1 -0
  36. package/dist/custom-rules.js +2 -1
  37. package/dist/custom-rules.js.map +1 -1
  38. package/dist/data-detectors.js +60 -4
  39. package/dist/data-detectors.js.map +1 -1
  40. package/dist/destructive-risk.d.ts +1 -0
  41. package/dist/destructive-risk.js +70 -0
  42. package/dist/destructive-risk.js.map +1 -0
  43. package/dist/edit-stats.js +5 -5
  44. package/dist/edit-stats.js.map +1 -1
  45. package/dist/entity-redaction.d.ts +11 -0
  46. package/dist/entity-redaction.js +88 -0
  47. package/dist/entity-redaction.js.map +1 -0
  48. package/dist/file-lock.d.ts +1 -0
  49. package/dist/file-lock.js +54 -0
  50. package/dist/file-lock.js.map +1 -0
  51. package/dist/forward.d.ts +13 -6
  52. package/dist/forward.js +120 -42
  53. package/dist/forward.js.map +1 -1
  54. package/dist/hook-adapters.d.ts +2 -2
  55. package/dist/hook-adapters.js +59 -60
  56. package/dist/hook-adapters.js.map +1 -1
  57. package/dist/hook-catalog.d.ts +12 -0
  58. package/dist/hook-catalog.js +469 -0
  59. package/dist/hook-catalog.js.map +1 -0
  60. package/dist/hook-config.d.ts +8 -0
  61. package/dist/hook-config.js +80 -0
  62. package/dist/hook-config.js.map +1 -0
  63. package/dist/hook-spool.d.ts +3 -0
  64. package/dist/hook-spool.js +56 -0
  65. package/dist/hook-spool.js.map +1 -0
  66. package/dist/install.d.ts +5 -5
  67. package/dist/install.js +48 -199
  68. package/dist/install.js.map +1 -1
  69. package/dist/inventory.js +1 -1
  70. package/dist/inventory.js.map +1 -1
  71. package/dist/jev.js +2 -1
  72. package/dist/jev.js.map +1 -1
  73. package/dist/judge.js +2 -1
  74. package/dist/judge.js.map +1 -1
  75. package/dist/mitigation-pattern.d.ts +1 -0
  76. package/dist/mitigation-pattern.js +140 -0
  77. package/dist/mitigation-pattern.js.map +1 -0
  78. package/dist/package-risk-rules.js +2 -2
  79. package/dist/package-risk-rules.js.map +1 -1
  80. package/dist/policy-paths.d.ts +20 -0
  81. package/dist/policy-paths.js +145 -0
  82. package/dist/policy-paths.js.map +1 -0
  83. package/dist/policy.d.ts +5 -0
  84. package/dist/policy.js +78 -10
  85. package/dist/policy.js.map +1 -1
  86. package/dist/rule-catalog.d.ts +3 -1
  87. package/dist/rule-catalog.js +124 -24
  88. package/dist/rule-catalog.js.map +1 -1
  89. package/dist/rule-matches.d.ts +13 -0
  90. package/dist/rule-matches.js +42 -0
  91. package/dist/rule-matches.js.map +1 -0
  92. package/dist/serve.js +55 -5
  93. package/dist/serve.js.map +1 -1
  94. package/dist/server.js +7 -6
  95. package/dist/server.js.map +1 -1
  96. package/dist/setup.js +5 -3
  97. package/dist/setup.js.map +1 -1
  98. package/dist/skill-risk-rules.js +2 -2
  99. package/dist/skill-risk-rules.js.map +1 -1
  100. package/dist/skill-setup.js +4 -5
  101. package/dist/skill-setup.js.map +1 -1
  102. package/dist/skill-sync.d.ts +5 -1
  103. package/dist/skill-sync.js +115 -14
  104. package/dist/skill-sync.js.map +1 -1
  105. package/dist/store.js +6 -9
  106. package/dist/store.js.map +1 -1
  107. package/dist/studio.js +2 -2
  108. package/package.json +5 -2
  109. package/stress-report.json +46 -46
@@ -0,0 +1,88 @@
1
+ // Session-consistent entity redaction: the same real secret value redacted twice in one session
2
+ // becomes the same placeholder both times (e.g. [SECRET_001]), and a *different* value of the
3
+ // same kind gets its own number ([SECRET_002]) -- unlike data-detectors.ts's redactSensitive(),
4
+ // which always emits the same static "[SECRET_REDACTED]" regardless of which value matched, so
5
+ // two different leaked secrets are indistinguishable in what the agent/reviewer sees. Only a
6
+ // truncated SHA-256 hash of each matched value is ever written to disk; the real value is hashed
7
+ // in memory and discarded immediately, never persisted or logged.
8
+ //
9
+ // Node-only (uses node:crypto and node:fs) -- kept out of data-detectors.ts on purpose, which
10
+ // stays pure/stateless so it can keep backing the edge-runtime SDK path (see its callers:
11
+ // monitor-rules.ts only imports its CustomDetector *type*, never calls its functions, at
12
+ // runtime). This module is only ever called from the CLI hook path in client.ts.
13
+ import { createHash } from "node:crypto";
14
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
15
+ import { dirname, join } from "node:path";
16
+ import { getDataDirectory } from "./config.js";
17
+ import { detectSensitive } from "./data-detectors.js";
18
+ import { withFileLock } from "./file-lock.js";
19
+ function entityMapPath(home = getDataDirectory()) {
20
+ return join(home, "entity-map.json");
21
+ }
22
+ async function readMap(home) {
23
+ try {
24
+ return JSON.parse(await readFile(entityMapPath(home), "utf8"));
25
+ }
26
+ catch {
27
+ return {};
28
+ }
29
+ }
30
+ async function writeMap(map, home) {
31
+ const path = entityMapPath(home);
32
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
33
+ await writeFile(path, `${JSON.stringify(map, null, 2)}\n`, { mode: 0o600 });
34
+ }
35
+ // A session untouched this long is pruned so the file doesn't grow forever. Only hashes and
36
+ // counters live here, never a real value, but there's no reason to keep them past the session's
37
+ // useful life either.
38
+ const SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1000;
39
+ function hashValue(value) {
40
+ return createHash("sha256").update(value).digest("hex").slice(0, 16);
41
+ }
42
+ /**
43
+ * Same redaction `detectSensitive()`/`redactSensitive()` already do, except each distinct real
44
+ * value gets a stable, numbered placeholder ([SECRET_001], [API_KEY_002], ...) that's consistent
45
+ * for that exact value within `sessionId`, and distinct per different value of the same kind.
46
+ * `home` is the beam data directory (injectable for tests); defaults to the real one.
47
+ */
48
+ export async function redactWithSessionEntities(text, sessionId, custom = [], home = getDataDirectory()) {
49
+ const findings = detectSensitive(text, custom);
50
+ if (findings.length === 0)
51
+ return { text, findings: [] };
52
+ // Locked end-to-end so concurrent tool calls in the same turn -- each spawning its own
53
+ // `beam hook` process -- can't both read the same pre-update counter and independently assign
54
+ // the same next id to two different values (see file-lock.ts / behavioral-anomaly.ts, which
55
+ // has the identical read-modify-write race).
56
+ return withFileLock(entityMapPath(home), async () => {
57
+ const map = await readMap(home);
58
+ const now = new Date().toISOString();
59
+ const cutoff = Date.now() - SESSION_TTL_MS;
60
+ for (const [id, session] of Object.entries(map))
61
+ if (Date.parse(session.updatedAt) < cutoff)
62
+ delete map[id];
63
+ const session = map[sessionId] ?? { updatedAt: now, counters: {}, entries: {} };
64
+ session.updatedAt = now;
65
+ // detectSensitive() returns findings sorted start-descending (last match first) specifically
66
+ // so a splice-based replace loop can apply them right-to-left without later offsets shifting
67
+ // -- this loop relies on that same ordering.
68
+ let result = text;
69
+ const entityFindings = [];
70
+ for (const finding of findings) {
71
+ const value = text.slice(finding.start, finding.end);
72
+ const hash = hashValue(value);
73
+ let id = session.entries[hash];
74
+ if (!id) {
75
+ session.counters[finding.kind] = (session.counters[finding.kind] ?? 0) + 1;
76
+ id = `${finding.kind}_${String(session.counters[finding.kind]).padStart(3, "0")}`;
77
+ session.entries[hash] = id;
78
+ }
79
+ const replacement = `[${id}]`;
80
+ result = result.slice(0, finding.start) + replacement + result.slice(finding.end);
81
+ entityFindings.push({ ...finding, replacement });
82
+ }
83
+ map[sessionId] = session;
84
+ await writeMap(map, home).catch(() => { });
85
+ return { text: result, findings: entityFindings };
86
+ });
87
+ }
88
+ //# sourceMappingURL=entity-redaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-redaction.js","sourceRoot":"","sources":["../src/entity-redaction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function withFileLock<T>(path: string, fn: () => Promise<T>): Promise<T>;
@@ -0,0 +1,54 @@
1
+ // A minimal, zero-dependency exclusive lock for a read-modify-write cycle against one JSON state
2
+ // file, used by behavioral-anomaly.ts and entity-redaction.ts. Without it, two `beam hook`
3
+ // processes racing (the common case: an agent issuing several tool calls in parallel in one turn,
4
+ // each spawning its own short-lived hook process) both read the same pre-update state, both
5
+ // compute their own "+1", and whichever writes last wins -- the other's update is silently lost.
6
+ // For the anomaly detector this is a real bypass: 5 genuinely concurrent high-risk actions can
7
+ // collapse to 1 persisted event, since each process independently sees itself as the first.
8
+ //
9
+ // Mechanism: `open(lockPath, "wx")` atomically fails with EEXIST if the lock file already exists
10
+ // (POSIX O_EXCL semantics, no race window), so creating it IS the acquisition. A short backoff
11
+ // retry loop waits for a concurrent holder to release it (by deleting the file). Fails open on a
12
+ // timeout or any error -- a hook must never hang or crash the agent's turn waiting on a lock; a
13
+ // worst-case missed increment is far better than a stuck tool call.
14
+ import { mkdir, open, rm, stat, unlink } from "node:fs/promises";
15
+ import { dirname } from "node:path";
16
+ const MAX_WAIT_MS = 2000;
17
+ const RETRY_DELAY_MS = 20;
18
+ // A lock older than this is assumed to belong to a process that crashed or was killed mid-update
19
+ // (hook processes are short-lived; nothing legitimate holds this lock for seconds) and is removed
20
+ // so a stuck lock file doesn't permanently block every future update.
21
+ const STALE_LOCK_MS = 5000;
22
+ export async function withFileLock(path, fn) {
23
+ const lockPath = `${path}.lock`;
24
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 }).catch(() => { });
25
+ const deadline = Date.now() + MAX_WAIT_MS;
26
+ let acquired = false;
27
+ while (Date.now() < deadline) {
28
+ try {
29
+ const handle = await open(lockPath, "wx");
30
+ await handle.close();
31
+ acquired = true;
32
+ break;
33
+ }
34
+ catch (e) {
35
+ if (e.code !== "EEXIST")
36
+ break; // unexpected error: proceed unlocked rather than hang
37
+ try {
38
+ const info = await stat(lockPath);
39
+ if (Date.now() - info.mtimeMs > STALE_LOCK_MS)
40
+ await rm(lockPath, { force: true });
41
+ }
42
+ catch { /* lock vanished between the failed open and this check -- fine, loop will retry the open */ }
43
+ await new Promise(resolve => setTimeout(resolve, RETRY_DELAY_MS + Math.random() * RETRY_DELAY_MS));
44
+ }
45
+ }
46
+ try {
47
+ return await fn();
48
+ }
49
+ finally {
50
+ if (acquired)
51
+ await unlink(lockPath).catch(() => { });
52
+ }
53
+ }
54
+ //# sourceMappingURL=file-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-lock.js","sourceRoot":"","sources":["../src/file-lock.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,2FAA2F;AAC3F,kGAAkG;AAClG,4FAA4F;AAC5F,iGAAiG;AACjG,+FAA+F;AAC/F,4FAA4F;AAC5F,EAAE;AACF,iGAAiG;AACjG,+FAA+F;AAC/F,iGAAiG;AACjG,gGAAgG;AAChG,oEAAoE;AACpE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,WAAW,GAAG,IAAI,CAAC;AACzB,MAAM,cAAc,GAAG,EAAE,CAAC;AAC1B,iGAAiG;AACjG,kGAAkG;AAClG,sEAAsE;AACtE,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B,MAAM,CAAC,KAAK,UAAU,YAAY,CAAI,IAAY,EAAE,EAAoB;IACtE,MAAM,QAAQ,GAAG,GAAG,IAAI,OAAO,CAAC;IAChC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAE7E,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,CAAC;IAC1C,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC1C,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACrB,QAAQ,GAAG,IAAI,CAAC;YAChB,MAAM;QACR,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAK,CAA2B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,CAAC,sDAAsD;YACjH,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAClC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,GAAG,aAAa;oBAAE,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACrF,CAAC;YAAC,MAAM,CAAC,CAAC,4FAA4F,CAAC,CAAC;YACxG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC;QACrG,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,EAAE,CAAC;IACpB,CAAC;YAAS,CAAC;QACT,IAAI,QAAQ;YAAE,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACvD,CAAC;AACH,CAAC"}
package/dist/forward.d.ts CHANGED
@@ -1,22 +1,29 @@
1
+ import { type Identity } from "./enroll.js";
1
2
  import type { Event, Scan } from "./core.js";
2
3
  import type { McpInventory } from "./mcp-config.js";
3
4
  import type { SkillReport } from "./skill-sync.js";
4
- export declare function forwardEvents(events: Event | Event[]): Promise<void>;
5
+ export declare function forwardEvents(events: Event | Event[], expected?: Pick<Identity, "deviceId" | "orgId" | "apiBase">): Promise<boolean>;
5
6
  export interface ForwardMcpInventoryResult {
6
7
  ok: boolean;
7
8
  reason?: string;
8
9
  }
9
10
  export declare function forwardMcpInventory(servers: McpInventory[]): Promise<ForwardMcpInventoryResult>;
10
- export declare function forwardSkillInventory(report: SkillReport): Promise<ForwardMcpInventoryResult>;
11
+ export declare function forwardSkillInventory(report: SkillReport, existingSyncId?: string): Promise<ForwardMcpInventoryResult>;
11
12
  export declare const FORWARD_BATCH_SIZE = 6;
12
13
  export declare const FORWARD_FLUSH_MS = 10000;
14
+ export declare const SKILL_INVENTORY_BATCH_SIZE = 20;
13
15
  export declare class ForwardQueue {
14
- private pending;
15
16
  private flushTimer;
16
- push(events: Event[]): void;
17
+ private writing;
18
+ private flushing;
19
+ private stopped;
20
+ private readonly directory;
21
+ constructor(directory?: string);
22
+ push(events: Event[]): Promise<void>;
17
23
  private scheduleFlush;
18
- /** Force out whatever is pending right now (e.g. on collector shutdown). */
19
- flush(): void;
24
+ flush(): Promise<void>;
25
+ close(): Promise<void>;
26
+ private deliver;
20
27
  }
21
28
  export declare function forwardScan(scan: Scan): Promise<void>;
22
29
  export declare function forwardReview(id: string, reviewed: boolean): Promise<void>;
package/dist/forward.js CHANGED
@@ -1,4 +1,5 @@
1
- import { mkdir, rename, writeFile } from "node:fs/promises";
1
+ import { mkdir, readdir, readFile, rename, rm, writeFile } from "node:fs/promises";
2
+ import { randomUUID } from "node:crypto";
2
3
  import { dirname, join } from "node:path";
3
4
  import { getApiUrl, getDataDirectory } from "./config.js";
4
5
  import { lastGoodPolicyPath } from "./policy.js";
@@ -7,18 +8,19 @@ import { versionHeaders } from "./version.js";
7
8
  function policyPath() {
8
9
  return join(getDataDirectory(), "policy.json");
9
10
  }
10
- // Fire-and-forget: forwarding to the workspace must never block or fail the agent. Like every
11
- // other identity-authenticated call in this file, this must hit the *enrolled* device's own
11
+ // Like other identity-authenticated calls, this must hit the enrolled device's own
12
12
  // identity.apiBase -- not a separately configured URL -- otherwise a device enrolled against a
13
13
  // non-default workspace API (self-hosted, staging, a local dev dashboard) silently forwards
14
14
  // events to the wrong place while `beam whoami`/`beam account` and policy sync (which do use
15
15
  // identity.apiBase) keep reporting the device as enrolled and healthy.
16
- export async function forwardEvents(events) {
16
+ export async function forwardEvents(events, expected) {
17
17
  const identity = await readIdentity();
18
18
  if (!identity)
19
- return;
19
+ return false;
20
+ if (expected && (identity.deviceId !== expected.deviceId || identity.orgId !== expected.orgId || identity.apiBase !== expected.apiBase))
21
+ return false;
20
22
  try {
21
- await fetch(`${identity.apiBase}/v1/ingest`, {
23
+ const response = await fetch(`${identity.apiBase}/v1/ingest`, {
22
24
  method: "POST",
23
25
  headers: {
24
26
  "Content-Type": "application/json",
@@ -27,9 +29,13 @@ export async function forwardEvents(events) {
27
29
  body: JSON.stringify(events),
28
30
  signal: AbortSignal.timeout(5000),
29
31
  });
32
+ if (!response.ok)
33
+ throw new Error(`HTTP ${response.status}`);
34
+ return true;
30
35
  }
31
- catch {
32
- /* offline / unreachable — the local collector still has the record */
36
+ catch (error) {
37
+ console.error(`Beam workspace event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
38
+ return false;
33
39
  }
34
40
  }
35
41
  // Unlike every other call in this file, this deliberately does NOT use identity.apiBase (the
@@ -65,19 +71,24 @@ export async function forwardMcpInventory(servers) {
65
71
  }
66
72
  }
67
73
  // Same host and auth as MCP inventory: the dedicated collector, keyed by the device secret.
68
- export async function forwardSkillInventory(report) {
74
+ export async function forwardSkillInventory(report, existingSyncId) {
69
75
  const identity = await readIdentity();
70
76
  if (!identity)
71
77
  return { ok: false, reason: "This device isn't connected to a Beam workspace." };
72
78
  const collectorBase = getApiUrl();
73
79
  try {
74
- const response = await fetch(`${collectorBase}/v1/skills/inventory`, {
75
- method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${identity.deviceSecret}` },
76
- body: JSON.stringify(report), signal: AbortSignal.timeout(15_000),
77
- });
78
- if (response.ok)
79
- return { ok: true };
80
- return { ok: false, reason: response.status === 404 ? "This workspace's collector doesn't support skill inventory yet." : `Collector responded with HTTP ${response.status}.` };
80
+ const batchCount = Math.max(1, Math.ceil(report.skills.length / SKILL_INVENTORY_BATCH_SIZE));
81
+ const syncId = existingSyncId ?? randomUUID();
82
+ for (let batchIndex = 0; batchIndex < batchCount; batchIndex++) {
83
+ const skills = report.skills.slice(batchIndex * SKILL_INVENTORY_BATCH_SIZE, (batchIndex + 1) * SKILL_INVENTORY_BATCH_SIZE);
84
+ const response = await fetch(`${collectorBase}/v1/skills/inventory`, {
85
+ method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${identity.deviceSecret}` },
86
+ body: JSON.stringify({ ...report, skills, syncId, batchIndex, batchCount, totalSkills: report.skills.length }), signal: AbortSignal.timeout(15_000),
87
+ });
88
+ if (!response.ok)
89
+ return { ok: false, reason: response.status === 404 ? "This workspace's collector doesn't support skill inventory yet." : `Collector rejected skill batch ${batchIndex + 1}/${batchCount} with HTTP ${response.status}.` };
90
+ }
91
+ return { ok: true };
81
92
  }
82
93
  catch (error) {
83
94
  return { ok: false, reason: `Could not reach ${collectorBase}: ${error instanceof Error ? error.message : String(error)}` };
@@ -85,49 +96,116 @@ export async function forwardSkillInventory(report) {
85
96
  }
86
97
  export const FORWARD_BATCH_SIZE = 6;
87
98
  export const FORWARD_FLUSH_MS = 10_000;
88
- // Batches events in memory (per collector process) and forwards them to the workspace in
89
- // groups of FORWARD_BATCH_SIZE, instead of one HTTP request per captured action. Lives on the
90
- // long-running collector (see serve.ts) since that's the only process with state that persists
91
- // across the many short-lived `beam hook` invocations. A leftover partial batch (below
92
- // FORWARD_BATCH_SIZE) is flushed after FORWARD_FLUSH_MS of inactivity so a quiet machine still
93
- // reaches the dashboard instead of waiting indefinitely for a 15th event.
99
+ export const SKILL_INVENTORY_BATCH_SIZE = 20;
100
+ // Persist before acknowledging local ingest. Delivery failures and process restarts leave
101
+ // events in this outbox; the remote (orgId, dedupeKey) constraint makes retries idempotent.
102
+ // Each envelope is bound to the enrolled device so re-enrollment cannot send old activity
103
+ // into a different workspace. Only already-normalized, redacted events enter this queue.
94
104
  export class ForwardQueue {
95
- pending = [];
96
105
  flushTimer;
97
- push(events) {
98
- if (!events.length)
99
- return;
100
- this.pending.push(...events);
101
- while (this.pending.length >= FORWARD_BATCH_SIZE) {
102
- const batch = this.pending.splice(0, FORWARD_BATCH_SIZE);
103
- void forwardEvents(batch);
104
- }
106
+ writing = Promise.resolve();
107
+ flushing;
108
+ stopped = false;
109
+ directory;
110
+ constructor(directory = getDataDirectory()) {
111
+ this.directory = join(directory, "event-outbox");
105
112
  this.scheduleFlush();
106
113
  }
114
+ push(events) {
115
+ const write = this.writing.then(async () => {
116
+ if (!events.length)
117
+ return;
118
+ const identity = await readIdentity();
119
+ if (!identity)
120
+ return;
121
+ await mkdir(this.directory, { recursive: true, mode: 0o700 });
122
+ for (let i = 0; i < events.length; i += FORWARD_BATCH_SIZE) {
123
+ const name = `${Date.now()}-${randomUUID()}`;
124
+ const temp = join(this.directory, `${name}.tmp`);
125
+ const envelope = { deviceId: identity.deviceId, orgId: identity.orgId, apiBase: identity.apiBase, events: events.slice(i, i + FORWARD_BATCH_SIZE) };
126
+ try {
127
+ await writeFile(temp, JSON.stringify(envelope), { mode: 0o600, flag: "wx" });
128
+ await rename(temp, join(this.directory, `${name}.json`));
129
+ }
130
+ finally {
131
+ await rm(temp, { force: true });
132
+ }
133
+ }
134
+ });
135
+ this.writing = write.catch(() => { });
136
+ return write.then(() => {
137
+ if (events.length >= FORWARD_BATCH_SIZE)
138
+ void this.flush();
139
+ });
140
+ }
107
141
  scheduleFlush() {
108
- if (this.flushTimer)
109
- clearTimeout(this.flushTimer);
110
- if (!this.pending.length)
142
+ if (this.stopped || this.flushTimer)
111
143
  return;
112
144
  this.flushTimer = setTimeout(() => {
113
145
  this.flushTimer = undefined;
114
- if (!this.pending.length)
115
- return;
116
- const batch = this.pending.splice(0, this.pending.length);
117
- void forwardEvents(batch);
146
+ void this.flush();
118
147
  }, FORWARD_FLUSH_MS);
119
148
  this.flushTimer.unref?.();
120
149
  }
121
- /** Force out whatever is pending right now (e.g. on collector shutdown). */
122
150
  flush() {
151
+ if (this.flushing)
152
+ return this.flushing;
123
153
  if (this.flushTimer) {
124
154
  clearTimeout(this.flushTimer);
125
155
  this.flushTimer = undefined;
126
156
  }
127
- if (!this.pending.length)
157
+ this.flushing = this.deliver().catch(error => {
158
+ console.error(`Beam event outbox: ${error instanceof Error ? error.message : String(error)}`);
159
+ }).finally(() => { this.flushing = undefined; this.scheduleFlush(); });
160
+ return this.flushing;
161
+ }
162
+ close() {
163
+ this.stopped = true;
164
+ return this.flush();
165
+ }
166
+ async deliver() {
167
+ await this.writing;
168
+ const identity = await readIdentity();
169
+ if (!identity)
128
170
  return;
129
- const batch = this.pending.splice(0, this.pending.length);
130
- void forwardEvents(batch);
171
+ let files;
172
+ try {
173
+ files = await readdir(this.directory);
174
+ }
175
+ catch (error) {
176
+ if (error.code === "ENOENT")
177
+ return;
178
+ throw error;
179
+ }
180
+ let attempted = 0;
181
+ let pending = [];
182
+ let paths = [];
183
+ const send = async () => {
184
+ if (!pending.length)
185
+ return true;
186
+ if (!await forwardEvents(pending, identity))
187
+ return false;
188
+ for (const path of paths)
189
+ await rm(path, { force: true });
190
+ pending = [];
191
+ paths = [];
192
+ return true;
193
+ };
194
+ for (const name of files.filter(n => /^\d+-[\da-f-]+\.json$/.test(n)).sort()) {
195
+ const path = join(this.directory, name);
196
+ const envelope = JSON.parse(await readFile(path, "utf8"));
197
+ if (envelope.deviceId !== identity.deviceId || envelope.orgId !== identity.orgId || envelope.apiBase !== identity.apiBase)
198
+ continue;
199
+ if (attempted++ >= 100)
200
+ break;
201
+ if (pending.length + envelope.events.length > FORWARD_BATCH_SIZE && !await send())
202
+ return;
203
+ pending.push(...envelope.events);
204
+ paths.push(path);
205
+ if (pending.length >= FORWARD_BATCH_SIZE && !await send())
206
+ return;
207
+ }
208
+ await send();
131
209
  }
132
210
  }
133
211
  // Fire-and-forget: forwarding a local skill/MCP scan must never block or fail the caller.
@@ -1 +1 @@
1
- {"version":3,"file":"forward.js","sourceRoot":"","sources":["../src/forward.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"forward.js","sourceRoot":"","sources":["../src/forward.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { type Obj } from "./agents.js";
2
- export declare function adaptHookPayload(sourceAgent: string, raw: Obj): Obj;
1
+ import type { Obj } from './agents.js';
2
+ export declare function adaptHookPayload(sourceAgent: string, raw: Obj, suppliedEvent?: string): Obj;
@@ -1,74 +1,73 @@
1
- import { findAgent } from "./agents.js";
2
- // Claude Code, Codex, and Cursor all send tool_name/tool_input/session_id/cwd/hook_event_name
3
- // (core.normalize already reads exactly these fields, case-insensitively for hook_event_name).
4
- function passthrough(raw) {
5
- // Cursor names its session `conversation_id`; without this its events are all "unassigned".
6
- return raw.session_id === undefined && typeof raw.conversation_id === "string" ? { ...raw, session_id: raw.conversation_id } : raw;
7
- }
8
- // GitHub Copilot CLI's preToolUse payload is camelCase and has no hook_event_name field —
9
- // it's implied by which hooks.json array the handler is registered under.
10
- // toolArgs arrives as a JSON *string* (docs.github.com Copilot hooks reference), so parse it --
11
- // left as a string, everything downstream reads tool_input as empty.
1
+ import { hookSpec, hookKind } from './hook-catalog.js';
2
+ import { commandOf } from './activity.js';
3
+ const obj = (v) => v && typeof v === 'object' && !Array.isArray(v) ? v : {};
12
4
  function parseArgs(value) {
13
- if (typeof value !== "string")
5
+ if (typeof value !== 'string')
14
6
  return value;
15
7
  try {
16
- const parsed = JSON.parse(value);
17
- return parsed && typeof parsed === "object" ? parsed : value;
8
+ return JSON.parse(value);
18
9
  }
19
10
  catch {
20
11
  return value;
21
12
  }
22
13
  }
23
- function copilotCamel(raw) {
24
- return {
25
- ...raw,
26
- session_id: raw.sessionId ?? raw.session_id,
27
- cwd: raw.cwd,
28
- tool_name: raw.toolName ?? raw.tool_name,
29
- tool_input: parseArgs(raw.toolArgs ?? raw.tool_input),
30
- // userPromptSubmitted carries `prompt` and no tool; treat it like Claude's UserPromptSubmit.
31
- hook_event_name: raw.hook_event_name ?? (typeof raw.prompt === "string" && !(raw.toolName ?? raw.tool_name) ? "UserPromptSubmit" : "PreToolUse"),
32
- };
33
- }
34
- // Best-effort fallback for agents whose stdin schema is not (yet) verified against vendor docs.
35
- function generic(raw) {
36
- return {
37
- ...raw,
38
- session_id: raw.session_id ?? raw.sessionId ?? raw.session,
39
- cwd: raw.cwd ?? raw.project_path ?? raw.workingDirectory ?? raw.working_directory,
40
- tool_name: raw.tool_name ?? raw.toolName ?? raw.tool,
41
- tool_input: raw.tool_input ?? raw.toolArgs ?? raw.toolInput ?? raw.input ?? raw.args,
42
- hook_event_name: raw.hook_event_name ?? raw.hookEventName ?? raw.event,
43
- };
14
+ function text(value) {
15
+ if (typeof value === 'string')
16
+ return value;
17
+ if (Array.isArray(value))
18
+ return value.map(p => text(obj(p).text ?? obj(p).content) ?? '').filter(Boolean).join('\n');
19
+ if (value && typeof value === 'object')
20
+ return text(obj(value).content ?? obj(value).text);
44
21
  }
45
- // Gemini's documented pre_tool_execution contract nests the call under tool_call.
46
- // Gemini CLI's own documented hook payload (geminicli.com/docs/hooks/reference) is already flat
47
- // -- session_id, cwd, hook_event_name, tool_name, tool_input, prompt -- the same shape as Claude
48
- // Code/Codex/Cursor. The tool_call.{name,args} nesting and environment_id field below are kept
49
- // only as a defensive fallback for the unrelated Gemini API "Managed Agents" sandbox hook format
50
- // (ai.google.dev/gemini-api/docs/agent-hooks), which beam's config previously (incorrectly)
51
- // targeted for the "Gemini CLI" agent entry -- harmless to keep matching, never the primary path.
52
- function gemini(raw) {
53
- const call = raw.tool_call && typeof raw.tool_call === "object" ? raw.tool_call : {};
22
+ export function adaptHookPayload(sourceAgent, raw, suppliedEvent) {
23
+ let fields = raw;
24
+ if (sourceAgent === 'windsurf')
25
+ fields = Object.keys(obj(raw.tool_info)).length ? obj(raw.tool_info) : raw;
26
+ if (sourceAgent === 'antigravity')
27
+ fields = Object.keys(obj(raw.toolCall)).length ? obj(raw.toolCall) : raw;
28
+ if (sourceAgent === 'cline') {
29
+ for (const name of ['preToolUse', 'postToolUse', 'userPromptSubmit', 'taskStart', 'taskResume', 'taskCancel', 'taskComplete', 'taskError', 'sessionShutdown'])
30
+ if (raw[name] && typeof raw[name] === 'object') {
31
+ fields = obj(raw[name]);
32
+ break;
33
+ }
34
+ }
35
+ const kindName = suppliedEvent ?? raw.hook_event_name ?? raw.hookEventName ?? raw.hookName ?? raw.event;
36
+ const event = String(kindName ?? (typeof raw.prompt === 'string' && !(raw.toolName ?? raw.tool_name) ? 'UserPromptSubmit' : hookSpec(sourceAgent)?.events.find(e => hookKind(e) === 'pre') ?? ''));
37
+ const kind = hookKind(event);
38
+ const nested = obj(raw.tool_call);
39
+ let tool = fields.tool_name ?? fields.toolName ?? fields.tool ?? fields.name ?? nested.name;
40
+ if (sourceAgent === 'windsurf' && !tool)
41
+ tool = /read_code/.test(event) ? 'Read' : /write_code/.test(event) ? 'Edit' : /run_command/.test(event) ? 'Bash' : /mcp/.test(event) ? fields.mcp_tool_name : undefined;
42
+ if (event === 'beforeReadFile')
43
+ tool = tool ?? 'Read';
44
+ const inputValue = fields.tool_input ?? fields.toolArgs ?? fields.toolInput ?? fields.tool_args ?? fields.mcp_tool_arguments ?? fields.parameters ?? fields.arguments ?? fields.input ?? fields.args ?? nested.args;
45
+ const parsed = parseArgs(inputValue);
46
+ const toolInput = parsed === undefined ? (['windsurf', 'antigravity', 'cline'].includes(sourceAgent) ? { ...fields } : {}) : parsed;
47
+ const args = obj(toolInput);
48
+ const workspaceRoots = raw.workspace_roots ?? raw.workspaceRoots ?? raw.workspacePaths;
49
+ const roots = Array.isArray(workspaceRoots) ? workspaceRoots : [];
50
+ const prompt = text(raw.prompt ?? raw.user_prompt ?? raw.userPrompt ?? obj(raw.extra).user_message ?? fields.prompt ?? fields.userMessage ?? fields.message ?? (kind === 'prompt' ? raw.message ?? raw.input : undefined));
51
+ const isVSCode = ['copilot-cli', 'vscode'].includes(sourceAgent) && ((raw.hookEventName !== undefined && /^[A-Z]/.test(String(raw.hookEventName))) || (raw.tool_name !== undefined && raw.toolName === undefined && /^[A-Z]/.test(String(raw.hook_event_name ?? ''))));
54
52
  return {
55
53
  ...raw,
56
- session_id: raw.session_id ?? raw.sessionId ?? raw.environment_id,
57
- tool_name: raw.tool_name ?? raw.toolName ?? call.name,
58
- tool_input: raw.tool_input ?? raw.toolArgs ?? call.args,
59
- hook_event_name: raw.hook_event_name ?? "BeforeTool",
54
+ source_agent: isVSCode ? 'vscode' : sourceAgent,
55
+ session_id: (sourceAgent === "codex" ? raw.agent_id : undefined) ?? raw.session_id ?? raw.sessionId ?? raw.sessionID ?? raw.conversation_id ?? raw.conversationId ?? raw.trajectory_id ?? raw.taskId ?? raw.execution_id ?? raw.session ?? raw.environment_id,
56
+ parent_session_id: raw.parent_session_id ?? raw.parentSessionId ?? (sourceAgent === "codex" && raw.agent_id ? raw.session_id : undefined),
57
+ cwd: sourceAgent === 'junie' ? raw.project_path ?? raw.cwd : raw.cwd ?? raw.project_path ?? raw.workingDirectory ?? raw.working_directory ?? raw.working_dir ?? fields.cwd ?? roots[0],
58
+ tool_name: tool,
59
+ tool_input: toolInput,
60
+ tool_use_id: fields.tool_use_id ?? fields.toolUseId ?? fields.tool_call_id ?? fields.toolCallId ?? fields.callID ?? raw.tool_use_id ?? raw.tool_call_id ?? raw.call_id,
61
+ tool_response: raw.tool_response ?? raw.tool_result ?? raw.toolResult ?? raw.tool_output ?? raw.toolOutput ?? raw.result ?? raw.output ?? raw.metadata ?? fields.result ?? fields.output,
62
+ hook_event_name: event,
63
+ timestamp: typeof raw.timestamp === 'number' && Number.isFinite(raw.timestamp) ? new Date(raw.timestamp < 100000000000 ? raw.timestamp * 1000 : raw.timestamp).toISOString() : raw.timestamp,
64
+ command: commandOf({ ...raw, command: raw.command ?? fields.command ?? fields.commandLine ?? fields.command_line, tool_name: tool, tool_input: toolInput }),
65
+ file_path: raw.file_path ?? fields.file_path ?? fields.filePath ?? fields.path ?? args.file_path ?? args.filePath ?? args.path,
66
+ ...(prompt !== undefined ? { prompt } : {}),
67
+ ...(kind === 'prompt' ? { event_type: 'prompt.submit', tool_name: tool ?? event } : {}),
68
+ ...(kind === 'permission' ? { event_type: /denied/i.test(event) ? 'permission.denied' : /result|response/i.test(event) ? 'permission.result' : 'permission.requested' } : {}),
69
+ ...(kind === 'session' ? { event_type: /stop|end|finalize|shutdown|complete|cancel|error|deleted/i.test(event) ? 'session.end' : 'session.start' } : {}),
70
+ ...(kind === 'assistant' ? { event_type: 'message.assistant', assistant_text: text(raw.assistant_text ?? raw.last_assistant_message ?? obj(raw.extra).assistant_message ?? raw.response ?? raw.text ?? raw.message) } : {}),
60
71
  };
61
72
  }
62
- // Hermes Agent (pre_tool_call) and OpenClaw (before_tool_call) send the Claude-style fields but
63
- // name the event in their own words; core.normalize only marks an action "proposed" for PreToolUse.
64
- function preToolAlias(raw) {
65
- const name = typeof raw.hook_event_name === "string" ? raw.hook_event_name.toLowerCase() : "";
66
- return name === "pre_tool_call" || name === "before_tool_call" ? { ...raw, hook_event_name: "PreToolUse" } : raw;
67
- }
68
- const adapters = { passthrough, "copilot-camel": copilotCamel, generic, gemini, "pre-tool-alias": preToolAlias };
69
- export function adaptHookPayload(sourceAgent, raw) {
70
- const agent = findAgent(sourceAgent);
71
- const adapter = adapters[agent?.adapter ?? "generic"];
72
- return adapter(raw);
73
- }
74
73
  //# sourceMappingURL=hook-adapters.js.map