unfairly 0.8.0 → 0.8.2

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 (83) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +15 -2
  3. package/dist/auth/browser-flow.d.ts +9 -0
  4. package/dist/auth/browser-flow.d.ts.map +1 -1
  5. package/dist/auth/browser-flow.js +11 -4
  6. package/dist/auth/browser-flow.js.map +1 -1
  7. package/dist/cmd/invite.js +2 -2
  8. package/dist/cmd/invite.js.map +1 -1
  9. package/dist/cmd/pace.d.ts +80 -0
  10. package/dist/cmd/pace.d.ts.map +1 -0
  11. package/dist/cmd/pace.js +393 -0
  12. package/dist/cmd/pace.js.map +1 -0
  13. package/dist/cmd/pull.d.ts.map +1 -1
  14. package/dist/cmd/pull.js +23 -1
  15. package/dist/cmd/pull.js.map +1 -1
  16. package/dist/cmd/setup.d.ts +2 -0
  17. package/dist/cmd/setup.d.ts.map +1 -1
  18. package/dist/cmd/setup.js +20 -6
  19. package/dist/cmd/setup.js.map +1 -1
  20. package/dist/index.js +96 -2
  21. package/dist/index.js.map +1 -1
  22. package/dist/lib/types/index.d.ts +6 -6
  23. package/dist/pace/device.d.ts +101 -0
  24. package/dist/pace/device.d.ts.map +1 -0
  25. package/dist/pace/device.js +148 -0
  26. package/dist/pace/device.js.map +1 -0
  27. package/dist/pace/identity.d.ts +10 -0
  28. package/dist/pace/identity.d.ts.map +1 -0
  29. package/dist/pace/identity.js +17 -0
  30. package/dist/pace/identity.js.map +1 -0
  31. package/dist/pace/install.d.ts +28 -0
  32. package/dist/pace/install.d.ts.map +1 -0
  33. package/dist/pace/install.js +127 -0
  34. package/dist/pace/install.js.map +1 -0
  35. package/dist/pace/local-evidence.d.ts +75 -0
  36. package/dist/pace/local-evidence.d.ts.map +1 -0
  37. package/dist/pace/local-evidence.js +228 -0
  38. package/dist/pace/local-evidence.js.map +1 -0
  39. package/dist/pace/local-sessions.d.ts +48 -0
  40. package/dist/pace/local-sessions.d.ts.map +1 -0
  41. package/dist/pace/local-sessions.js +254 -0
  42. package/dist/pace/local-sessions.js.map +1 -0
  43. package/dist/pace/managed-config.d.ts +21 -0
  44. package/dist/pace/managed-config.d.ts.map +1 -0
  45. package/dist/pace/managed-config.js +102 -0
  46. package/dist/pace/managed-config.js.map +1 -0
  47. package/dist/pace/normalize.d.ts +10 -0
  48. package/dist/pace/normalize.d.ts.map +1 -0
  49. package/dist/pace/normalize.js +293 -0
  50. package/dist/pace/normalize.js.map +1 -0
  51. package/dist/pace/repository.d.ts +72 -0
  52. package/dist/pace/repository.d.ts.map +1 -0
  53. package/dist/pace/repository.js +120 -0
  54. package/dist/pace/repository.js.map +1 -0
  55. package/dist/pace/schema.d.ts +1325 -0
  56. package/dist/pace/schema.d.ts.map +1 -0
  57. package/dist/pace/schema.js +134 -0
  58. package/dist/pace/schema.js.map +1 -0
  59. package/dist/pace/session-summary.d.ts +179 -0
  60. package/dist/pace/session-summary.d.ts.map +1 -0
  61. package/dist/pace/session-summary.js +140 -0
  62. package/dist/pace/session-summary.js.map +1 -0
  63. package/dist/pace/spool.d.ts +6 -0
  64. package/dist/pace/spool.d.ts.map +1 -0
  65. package/dist/pace/spool.js +87 -0
  66. package/dist/pace/spool.js.map +1 -0
  67. package/package.json +3 -1
  68. package/plugin/.claude-plugin/plugin.json +9 -0
  69. package/plugin/.codex-plugin/plugin.json +21 -0
  70. package/plugin/.cursor-plugin/plugin.json +16 -0
  71. package/plugin/.mcp.json +8 -0
  72. package/plugin/README.md +19 -0
  73. package/plugin/deploy/README.md +30 -0
  74. package/plugin/gemini/GEMINI.md +3 -0
  75. package/plugin/gemini/gemini-extension.json +12 -0
  76. package/plugin/gemini/hooks/hooks.json +52 -0
  77. package/plugin/hooks/cursor-hooks.json +25 -0
  78. package/plugin/hooks/hooks.json +56 -0
  79. package/plugin/mcp.json +10 -0
  80. package/plugin/plugin.json +32 -0
  81. package/plugin/scripts/capture.cjs +46 -0
  82. package/plugin/skills/find-company-playbook/SKILL.md +12 -0
  83. package/plugin/skills/publish-learning/SKILL.md +8 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.2 — 2026-09-24
4
+
5
+ - **Unfairly Intelligence collector** — `unfairly setup` installs a metadata-only collector for Claude Code, Codex, Cursor, and Gemini CLI. At session boundaries it summarizes each session from the client's own local transcript: model, token counts by type, turns, tool calls and errors, approval denials, subagent use, the approved repository, the SHAs of the session's own commits, and which published skills or company playbooks it used. Prompts, responses, code, commands, paths, branch names, and commit messages never leave the machine; skills outside the published set are counted, not named.
6
+ - **`unfairly setup --org <id>`** — scopes the collector to an organization, for people in several orgs (also `UNFAIRLY_ORG`).
7
+ - **`unfairly intelligence backfill --days 90`** — uploads summaries of sessions already on the machine.
8
+ - **Approved repositories** — the collector pulls the org's approved-repository and skill catalog from the server; remotes are fingerprinted canonically (`github.com/owner/repo`) so every remote form matches.
9
+ - **Non-blocking hooks** — capture hands off to a detached process so AI clients never wait on summarizing or uploading.
10
+
11
+ ## 0.8.1 — 2026-05-16
12
+
13
+ - **Fix(pull resilience)** — `unfairly pull` and `unfairly init` no longer abort the entire sync when one vault fails. Each vault is now wrapped in its own try/catch; failures are collected, surfaced at the end with the slug + first error line, and the process exits with code 1 so CI can detect partial failures. Previously a single 404 (e.g. from a duplicate-slug ambiguity) on `hq` would crash the loop and silently skip every vault alphabetically after it. The server fix that triggered this (deterministic dedupe by slug in `/api/sync/{status,pull,push}`) shipped alongside.
14
+
3
15
  ## 0.8.0 — 2026-05-15
4
16
 
5
17
  - **`unfairly connect <urlOrId>`** — wire a shared vault from a share link in one command, no browser OAuth, no signup. POSTs the link to the new `/api/share-links/<id>/mint-token` endpoint (Phase B), receives a ≤24h read-only token, and writes it to Claude Desktop, Claude Code, and Codex as scoped MCP entries keyed by `unfairly-<org>-<room>`. Accepts the full URL, `s/<uuid>` short form, or a bare UUID. `--as <email>` and `--label <text>` flow through to the audit log. Re-connecting the same share link is idempotent — strips the old entry and writes a fresh token at the same key. Hosts that aren't installed are skipped, not errored.
package/README.md CHANGED
@@ -13,6 +13,8 @@ That's it. One command — auto-detects state and:
13
13
 
14
14
  - pops a browser for OAuth (skipped if you already have a token)
15
15
  - wires Claude Desktop, Claude Code, and Codex to the Unfairly MCP server
16
+ - installs the Unfairly Intelligence metadata collector in detected Codex, Claude Code, Cursor, and Gemini CLI clients
17
+ - enrolls one versioned user-space runtime under `~/.unfairly` (no global npm install or admin access)
16
18
  - writes `UNFAIRLY_MCP_TOKEN` into your shell rc
17
19
  - binds `.unfairly/` to your repo (pulls all accessible vaults)
18
20
  - stitches `@.unfairly/CLAUDE.md` into your repo's `CLAUDE.md`
@@ -21,7 +23,7 @@ Re-run any time inside the repo for a daily check-in card showing what your team
21
23
 
22
24
  ### Power-user commands
23
25
 
24
- `unfairly setup` (machine wiring only), `unfairly init` (workspace bind only), `unfairly pull`, `unfairly push`, `unfairly status`, `unfairly watch`, `unfairly mcp`, `unfairly login`. The bare command above delegates to these — they're available when you want fine-grained control.
26
+ `unfairly setup` (machine wiring), `unfairly intelligence install` (collector install/repair), `unfairly intelligence status`, `unfairly intelligence policy`, `unfairly intelligence pause`, `unfairly intelligence uninstall`, `unfairly init` (workspace bind only), `unfairly pull`, `unfairly push`, `unfairly status`, `unfairly watch`, `unfairly mcp`, `unfairly login`. The bare command above delegates to these — they're available when you want fine-grained control.
25
27
 
26
28
  ## Commands
27
29
 
@@ -33,6 +35,15 @@ Re-run any time inside the repo for a daily check-in card showing what your team
33
35
  - `unfairly watch` — opt-in fs watcher; debounced 5s auto-push on save
34
36
  - `unfairly mcp` — stdio MCP server exposing `pull_vault`, `push_vault`, `sync_status` tools to agent hosts (Claude Code, Codex, cowork)
35
37
 
38
+ The CLI keeps its browser-authorized credential in the OS keychain (or a
39
+ mode-`0600` fallback). Sync accepts a Supabase session JWT or a human MCP
40
+ credential with the `sync` audience. MCP sync credentials need `read` for
41
+ pull/status and `write` for push. Owner/admin invitation requests require a
42
+ separate `sync-invite-admin` audience with `invite:admin` scope; bound
43
+ organization and room scope are always enforced. Broad `desktop-control`,
44
+ short-lived Hermes child credentials, and agent-runtime credentials cannot be
45
+ used for sync or CLI invitation requests.
46
+
36
47
  ## Local store layout
37
48
 
38
49
  ```
@@ -64,7 +75,9 @@ const client = createUnfairlyClient({ apiKey: process.env.UNFAIRLY_API_KEY });
64
75
  const me = await client.me();
65
76
 
66
77
  // Search a vault
67
- const hits = await client.vault("growth").search("burn rate", { matchCount: 8 });
78
+ const hits = await client
79
+ .vault("growth")
80
+ .search("burn rate", { matchCount: 8 });
68
81
 
69
82
  // Read a document
70
83
  const doc = await client.vault("growth").readDocument("operating-plan");
@@ -2,7 +2,16 @@ export declare function openBrowser(url: string): void;
2
2
  export type BrowserAuthOptions = {
3
3
  tokenName?: string;
4
4
  timeoutMs?: number;
5
+ /** Organization ID to scope the token to; the server honours it only for a member. Falls back to UNFAIRLY_ORG. */
6
+ org?: string;
5
7
  };
8
+ /** The /cli-auth page URL. An organization ID scopes the minted token to that org when the user is a member. */
9
+ export declare function cliAuthUrl(apiUrl: string, input: {
10
+ cb: string;
11
+ state: string;
12
+ tokenName: string;
13
+ org?: string;
14
+ }): URL;
6
15
  /**
7
16
  * OAuth handshake via loopback HTTP server. Used by `unfairly setup`,
8
17
  * `unfairly login`, and `unfairly init`.
@@ -1 +1 @@
1
- {"version":3,"file":"browser-flow.d.ts","sourceRoot":"","sources":["../../src/auth/browser-flow.ts"],"names":[],"mappings":"AAMA,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAc7C;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA+CxB"}
1
+ {"version":3,"file":"browser-flow.d.ts","sourceRoot":"","sources":["../../src/auth/browser-flow.ts"],"names":[],"mappings":"AAMA,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAc7C;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kHAAkH;IAClH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,gHAAgH;AAChH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,GAAG,CAOrH;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4CxB"}
@@ -16,6 +16,16 @@ export function openBrowser(url) {
16
16
  // best-effort
17
17
  }
18
18
  }
19
+ /** The /cli-auth page URL. An organization ID scopes the minted token to that org when the user is a member. */
20
+ export function cliAuthUrl(apiUrl, input) {
21
+ const url = new URL("/cli-auth", apiUrl);
22
+ url.searchParams.set("cb", input.cb);
23
+ url.searchParams.set("state", input.state);
24
+ url.searchParams.set("name", input.tokenName);
25
+ if (input.org && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(input.org))
26
+ url.searchParams.set("org", input.org);
27
+ return url;
28
+ }
19
29
  /**
20
30
  * OAuth handshake via loopback HTTP server. Used by `unfairly setup`,
21
31
  * `unfairly login`, and `unfairly init`.
@@ -62,10 +72,7 @@ export async function browserAuthFlow(apiUrl, opts = {}) {
62
72
  const addr = server.address();
63
73
  const port = typeof addr === "object" && addr ? addr.port : 0;
64
74
  const cb = `http://localhost:${port}/done`;
65
- const authUrl = new URL("/cli-auth", apiUrl);
66
- authUrl.searchParams.set("cb", cb);
67
- authUrl.searchParams.set("state", state);
68
- authUrl.searchParams.set("name", tokenName);
75
+ const authUrl = cliAuthUrl(apiUrl, { cb, state, tokenName, org: opts.org ?? process.env.UNFAIRLY_ORG });
69
76
  console.error(`\nOpening browser to authorize:\n ${authUrl.toString()}\n(if it doesn't open, click the link above)\n`);
70
77
  openBrowser(authUrl.toString());
71
78
  });
@@ -1 +1 @@
1
- {"version":3,"file":"browser-flow.js","sourceRoot":"","sources":["../../src/auth/browser-flow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browser-flow.js","sourceRoot":"","sources":["../../src/auth/browser-flow.ts"],"names":[],"mappings":"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"}
@@ -12,8 +12,8 @@ export const inviteCommand = {
12
12
  .option("persona-surface", {
13
13
  type: "string",
14
14
  demandOption: true,
15
- choices: ["builder-terminal", "oracle-cowork", "player-web"],
16
- describe: "Which surface they'll primarily use",
15
+ choices: ["builder-terminal", "player-web"],
16
+ describe: "Onboarding role: builder-terminal = Champion, player-web = Player",
17
17
  })
18
18
  .option("persona-workflow", {
19
19
  type: "string",
@@ -1 +1 @@
1
- {"version":3,"file":"invite.js","sourceRoot":"","sources":["../../src/cmd/invite.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAUlD,MAAM,WAAW,GAAG,yBAAyB,CAAC;AAE9C,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,OAAO,EAAE,QAAQ;IACjB,QAAQ,EAAE,sDAAsD;IAChE,OAAO,EAAE,CAAC,KAAW,EAAE,EAAE,CACvB,KAAK;SACF,MAAM,CAAC,OAAO,EAAE;QACf,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,qCAAqC;KAChD,CAAC;SACD,MAAM,CAAC,iBAAiB,EAAE;QACzB,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;QAClB,OAAO,EAAE,CAAC,kBAAkB,EAAE,eAAe,EAAE,YAAY,CAAC;QAC5D,QAAQ,EAAE,qCAAqC;KAChD,CAAC;SACD,MAAM,CAAC,kBAAkB,EAAE;QAC1B,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE;YACP,UAAU;YACV,WAAW;YACX,qBAAqB;YACrB,eAAe;YACf,WAAW;YACX,YAAY;SACb;QACD,QAAQ,EAAE,wBAAwB;KACnC,CAAC;SACD,MAAM,CAAC,KAAK,EAAE;QACb,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,2DAA2D;KACtE,CAAC;SACD,MAAM,CAAC,KAAK,EAAE;QACb,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,WAAW;QACpB,QAAQ,EAAE,YAAY;KACvB,CAAC;IACN,OAAO,EAAE,KAAK,EAAE,IAAgB,EAAE,EAAE;QAClC,MAAM,KAAK,GAAG,MAAM,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CACX,2EAA2E,CAC5E,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,qBAAqB,EAAE;YACxD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,KAAK,EAAE;aACjC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,eAAe,EAAE,IAAI,CAAC,iBAAiB,CAAC;gBACxC,gBAAgB,EAAE,IAAI,CAAC,kBAAkB,CAAC;gBAC1C,QAAQ,EAAE,IAAI,CAAC,GAAG;aACnB,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC7B,OAAO,CAAC,KAAK,CAAC,4BAA4B,GAAG,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;YAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC;QACpD,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC;QACvD,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CACT,0EAA0E,CAC3E,CAAC;IACJ,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"invite.js","sourceRoot":"","sources":["../../src/cmd/invite.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAUlD,MAAM,WAAW,GAAG,yBAAyB,CAAC;AAE9C,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,OAAO,EAAE,QAAQ;IACjB,QAAQ,EAAE,sDAAsD;IAChE,OAAO,EAAE,CAAC,KAAW,EAAE,EAAE,CACvB,KAAK;SACF,MAAM,CAAC,OAAO,EAAE;QACf,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,qCAAqC;KAChD,CAAC;SACD,MAAM,CAAC,iBAAiB,EAAE;QACzB,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;QAClB,OAAO,EAAE,CAAC,kBAAkB,EAAE,YAAY,CAAC;QAC3C,QAAQ,EAAE,mEAAmE;KAC9E,CAAC;SACD,MAAM,CAAC,kBAAkB,EAAE;QAC1B,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE;YACP,UAAU;YACV,WAAW;YACX,qBAAqB;YACrB,eAAe;YACf,WAAW;YACX,YAAY;SACb;QACD,QAAQ,EAAE,wBAAwB;KACnC,CAAC;SACD,MAAM,CAAC,KAAK,EAAE;QACb,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,2DAA2D;KACtE,CAAC;SACD,MAAM,CAAC,KAAK,EAAE;QACb,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,WAAW;QACpB,QAAQ,EAAE,YAAY;KACvB,CAAC;IACN,OAAO,EAAE,KAAK,EAAE,IAAgB,EAAE,EAAE;QAClC,MAAM,KAAK,GAAG,MAAM,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CACX,2EAA2E,CAC5E,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,qBAAqB,EAAE;YACxD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,KAAK,EAAE;aACjC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,eAAe,EAAE,IAAI,CAAC,iBAAiB,CAAC;gBACxC,gBAAgB,EAAE,IAAI,CAAC,kBAAkB,CAAC;gBAC1C,QAAQ,EAAE,IAAI,CAAC,GAAG;aACnB,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC7B,OAAO,CAAC,KAAK,CAAC,4BAA4B,GAAG,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;YAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC;QACpD,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC;QACvD,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CACT,0EAA0E,CAC3E,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,80 @@
1
+ import { detectPaceClients } from "../pace/device.js";
2
+ export declare const RUNTIME_VERSION = "0.8.2";
3
+ export declare const INTELLIGENCE_POLICY: {
4
+ version: "metadata-v3";
5
+ mode: "metadata_only";
6
+ permitted_fields: string[];
7
+ prohibited_content: string[];
8
+ };
9
+ export declare function runPaceCapture(source: string, hookEvent: string): Promise<void>;
10
+ /** Summarizes local sessions changed since the last sweep and queues them for upload. */
11
+ export declare function runPaceSweep(options?: {
12
+ sinceMs?: number;
13
+ collectedVia?: "hook" | "backfill";
14
+ }): Promise<{
15
+ summarized: number;
16
+ queued: number;
17
+ }>;
18
+ /** Pulls the organization's approved repositories so sessions can carry repository IDs and commit SHAs. */
19
+ export declare function refreshRepositoryPolicy(options?: {
20
+ force?: boolean;
21
+ }): Promise<{
22
+ repositories: number;
23
+ } | null>;
24
+ /** Summarizes and uploads the last `days` of local sessions so Intelligence has history from day one. */
25
+ export declare function runPaceBackfill(days: number): Promise<{
26
+ summarized: number;
27
+ uploaded: number;
28
+ remaining: number;
29
+ approved_repositories: number | null;
30
+ }>;
31
+ export declare function runPaceFlush(): Promise<{
32
+ sent: number;
33
+ remaining: number;
34
+ sessions_sent: number;
35
+ }>;
36
+ export declare function runPaceHeartbeat(): Promise<{
37
+ installation_id: string;
38
+ clients: Awaited<ReturnType<typeof detectPaceClients>>;
39
+ }>;
40
+ export declare function runPaceInstall(): Promise<{
41
+ runtime_entrypoint: string;
42
+ marketplace_root: string;
43
+ activations: Array<{
44
+ client: string;
45
+ status: "activated" | "skipped";
46
+ detail: string;
47
+ }>;
48
+ verification: {
49
+ heartbeat: "ok";
50
+ live_events: "pending_first_session";
51
+ };
52
+ }>;
53
+ export declare function runPaceStatus(): Promise<void>;
54
+ export declare function runPaceConfig(input: {
55
+ outputDir: string;
56
+ command: string;
57
+ managedDir: string;
58
+ evidenceRoots?: string[];
59
+ includeFileNames?: boolean;
60
+ }): Promise<string[]>;
61
+ export declare function runPacePolicy(): typeof INTELLIGENCE_POLICY;
62
+ export declare function runPacePause(paused: boolean): Promise<{
63
+ paused: boolean;
64
+ }>;
65
+ export declare function runPaceUninstall(): Promise<{
66
+ removed_files: number;
67
+ retained_global_credentials: true;
68
+ }>;
69
+ export declare function runPaceEvidenceScan(policyPath?: string): Promise<{
70
+ observed: number;
71
+ baseline_roots: string[];
72
+ queued: number;
73
+ }>;
74
+ export declare function runPaceEvidenceWatch(policyPath?: string): Promise<void>;
75
+ export declare function runPacePlaybooks(query?: string): Promise<unknown[]>;
76
+ export declare function runPacePlaybookUse(playbookId: string, playbookVersion: string): Promise<{
77
+ workflow_id: string;
78
+ queued: number;
79
+ }>;
80
+ //# sourceMappingURL=pace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pace.d.ts","sourceRoot":"","sources":["../../src/cmd/pace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,393 @@
1
+ import { getToken } from "../sync/credentials.js";
2
+ import fs from "node:fs/promises";
3
+ import { randomUUID } from "node:crypto";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { PaceEventV3Schema } from "../pace/schema.js";
7
+ import { normalizeHookEvent } from "../pace/normalize.js";
8
+ import { appendPaceEvent, paceSpoolPath, readPaceEvents, removePaceEvents } from "../pace/spool.js";
9
+ import { currentDevicePlatform, detectPaceClients, paceDevicePath, readOrCreatePaceDevice, writePaceDevice, } from "../pace/device.js";
10
+ const DEFAULT_API = "https://app.unfairly.ai";
11
+ export const RUNTIME_VERSION = "0.8.2";
12
+ const PLUGIN_VERSION = "0.1.0";
13
+ export const INTELLIGENCE_POLICY = {
14
+ version: "metadata-v3",
15
+ mode: "metadata_only",
16
+ permitted_fields: [
17
+ "event_id", "installation_id", "session_id", "workflow_id", "occurred_at",
18
+ "source", "event_type", "evidence_level", "model_family", "tool_category",
19
+ "blocker_category", "approval_mode", "permission_mode", "duration_bucket",
20
+ "exit_code_bucket", "token_counts", "playbook_id", "playbook_version",
21
+ "repository_id", "commit_sha", "artifact_type", "outcome_state",
22
+ ],
23
+ prohibited_content: [
24
+ "prompts", "responses", "source code", "file contents", "screenshots",
25
+ "keystrokes", "clipboard", "raw commands", "arguments", "tool output",
26
+ "stdout", "stderr", "paths", "filenames", "repository names", "branches",
27
+ "URLs", "titles", "secrets", "arbitrary errors",
28
+ ],
29
+ };
30
+ async function readStdin() {
31
+ if (process.stdin.isTTY)
32
+ return {};
33
+ let raw = "";
34
+ for await (const chunk of process.stdin)
35
+ raw += String(chunk);
36
+ if (!raw.trim())
37
+ return {};
38
+ try {
39
+ return JSON.parse(raw);
40
+ }
41
+ catch {
42
+ return {};
43
+ }
44
+ }
45
+ export async function runPaceCapture(source, hookEvent) {
46
+ const device = await readOrCreatePaceDevice();
47
+ if (device.paused)
48
+ return;
49
+ const event = normalizeHookEvent({
50
+ source,
51
+ hookEvent,
52
+ payload: await readStdin(),
53
+ installationId: device.installation_id,
54
+ });
55
+ await appendPaceEvent(event);
56
+ const boundary = event.event_type === "session.started" || event.event_type === "session.ended" || event.event_type === "workflow.stopped";
57
+ if (boundary) {
58
+ // Session summaries (model, tokens, commits) come from the client's own
59
+ // transcript, which hook payloads do not carry. Summarize whatever changed
60
+ // since the last sweep; a failure here must never break the AI workflow.
61
+ await runPaceSweep().catch(() => undefined);
62
+ }
63
+ if (process.env.UNFAIRLY_PACE_NO_FLUSH !== "1" && boundary) {
64
+ // A telemetry outage must never break the employee's AI workflow. Keep the
65
+ // durable queue and retry on the next session boundary or explicit flush.
66
+ await runPaceFlush().catch(() => undefined);
67
+ }
68
+ }
69
+ const SWEEP_OVERLAP_MS = 15 * 60_000;
70
+ const FIRST_SWEEP_LOOKBACK_MS = 2 * 86_400_000;
71
+ /** Summarizes local sessions changed since the last sweep and queues them for upload. */
72
+ export async function runPaceSweep(options = {}) {
73
+ const { sweepLocalSessions } = await import("../pace/session-summary.js");
74
+ const device = await readOrCreatePaceDevice();
75
+ const startedAt = Date.now();
76
+ const last = device.last_session_sweep_at ? Date.parse(device.last_session_sweep_at) : startedAt - FIRST_SWEEP_LOOKBACK_MS;
77
+ const result = await sweepLocalSessions({
78
+ since: options.sinceMs ?? last - SWEEP_OVERLAP_MS,
79
+ installationId: device.installation_id,
80
+ collectedVia: options.collectedVia ?? "hook",
81
+ });
82
+ await writePaceDevice({ ...(await readOrCreatePaceDevice()), last_session_sweep_at: new Date(startedAt).toISOString() });
83
+ return result;
84
+ }
85
+ function apiBase() {
86
+ return (process.env.UNFAIRLY_API_URL ?? DEFAULT_API).replace(/\/$/, "");
87
+ }
88
+ /** Pulls the organization's approved repositories so sessions can carry repository IDs and commit SHAs. */
89
+ export async function refreshRepositoryPolicy(options = {}) {
90
+ const device = await readOrCreatePaceDevice();
91
+ const fresh = device.repository_policy_refreshed_at && Date.now() - Date.parse(device.repository_policy_refreshed_at) < 3_600_000;
92
+ if (fresh && !options.force)
93
+ return null;
94
+ const token = await getToken();
95
+ if (!token)
96
+ return null;
97
+ const response = await fetch(`${apiBase()}/api/ai-pace/repository-policy`, { headers: { Authorization: `Bearer ${token}` } });
98
+ if (!response.ok)
99
+ return null;
100
+ const { parseRepositoryPolicy, repositoryPolicyPath } = await import("../pace/repository.js");
101
+ const policy = parseRepositoryPolicy(await response.json());
102
+ const file = repositoryPolicyPath();
103
+ await fs.mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
104
+ await fs.writeFile(file, `${JSON.stringify(policy, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
105
+ await writePaceDevice({ ...(await readOrCreatePaceDevice()), repository_policy_refreshed_at: new Date().toISOString() });
106
+ return { repositories: policy.mappings.length };
107
+ }
108
+ async function uploadSessionSummaries(token) {
109
+ const { readSessionSpool, removeSessionSummaries } = await import("../pace/session-summary.js");
110
+ let sent = 0;
111
+ for (let round = 0; round < 50; round += 1) {
112
+ const pending = Object.values(await readSessionSpool()).slice(0, 100);
113
+ if (!pending.length)
114
+ break;
115
+ const response = await fetch(`${apiBase()}/api/ai-pace/sessions`, {
116
+ method: "POST",
117
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
118
+ body: JSON.stringify({ sessions: pending }),
119
+ });
120
+ if (!response.ok)
121
+ throw new Error(`Unfairly Intelligence session upload failed (${response.status})`);
122
+ await removeSessionSummaries(pending);
123
+ sent += pending.length;
124
+ }
125
+ return { sent, remaining: Object.keys(await readSessionSpool()).length };
126
+ }
127
+ /** Summarizes and uploads the last `days` of local sessions so Intelligence has history from day one. */
128
+ export async function runPaceBackfill(days) {
129
+ const policy = await refreshRepositoryPolicy({ force: true }).catch(() => null);
130
+ // Uploads are only accepted from a registered, active installation.
131
+ await runPaceHeartbeat();
132
+ const sweep = await runPaceSweep({ sinceMs: Date.now() - days * 86_400_000, collectedVia: "backfill" });
133
+ const token = await getToken();
134
+ if (!token)
135
+ throw new Error("Unfairly Intelligence is not enrolled. Run `unfairly setup` first.");
136
+ const upload = await uploadSessionSummaries(token);
137
+ return { summarized: sweep.summarized, uploaded: upload.sent, remaining: upload.remaining, approved_repositories: policy?.repositories ?? null };
138
+ }
139
+ export async function runPaceFlush() {
140
+ const token = await getToken();
141
+ if (!token)
142
+ throw new Error("Unfairly Intelligence is not enrolled. Run `unfairly setup` first.");
143
+ await refreshRepositoryPolicy().catch(() => null);
144
+ const sessions = await uploadSessionSummaries(token);
145
+ const events = await readPaceEvents();
146
+ if (!events.length)
147
+ return { sent: 0, remaining: 0, sessions_sent: sessions.sent };
148
+ const apiUrl = (process.env.UNFAIRLY_API_URL ?? DEFAULT_API).replace(/\/$/, "");
149
+ const device = await readOrCreatePaceDevice();
150
+ const attributedEvents = events.map((event) => ({
151
+ ...event,
152
+ installation_id: event.installation_id ?? device.installation_id,
153
+ }));
154
+ const response = await fetch(`${apiUrl}/api/ai-pace/events`, {
155
+ method: "POST",
156
+ headers: {
157
+ Authorization: `Bearer ${token}`,
158
+ "Content-Type": "application/json",
159
+ ...(device.attestation ? { "X-AI-Pace-Attestation": device.attestation } : {}),
160
+ },
161
+ body: JSON.stringify({ events: attributedEvents }),
162
+ });
163
+ if (!response.ok)
164
+ throw new Error(`Unfairly Intelligence upload failed (${response.status})`);
165
+ await removePaceEvents(new Set(events.map((event) => event.event_id)));
166
+ const remaining = (await readPaceEvents()).length;
167
+ await writePaceDevice({ ...(await readOrCreatePaceDevice()), last_upload_at: new Date().toISOString(), collection_policy_version: "metadata-v3" });
168
+ return { sent: events.length, remaining, sessions_sent: sessions.sent };
169
+ }
170
+ export async function runPaceHeartbeat() {
171
+ const token = await getToken();
172
+ if (!token)
173
+ throw new Error("Unfairly Intelligence is not enrolled. Run `unfairly setup` first.");
174
+ const device = await readOrCreatePaceDevice();
175
+ const clients = await detectPaceClients();
176
+ const apiUrl = (process.env.UNFAIRLY_API_URL ?? DEFAULT_API).replace(/\/$/, "");
177
+ const response = await fetch(`${apiUrl}/api/ai-pace/heartbeat`, {
178
+ method: "POST",
179
+ headers: {
180
+ Authorization: `Bearer ${token}`,
181
+ "Content-Type": "application/json",
182
+ },
183
+ body: JSON.stringify({
184
+ installation_id: device.installation_id,
185
+ agent_version: RUNTIME_VERSION,
186
+ runtime_version: RUNTIME_VERSION,
187
+ plugin_version: PLUGIN_VERSION,
188
+ collection_policy_version: "metadata-v3",
189
+ ...currentDevicePlatform(),
190
+ clients,
191
+ }),
192
+ });
193
+ if (!response.ok)
194
+ throw new Error(`Unfairly Intelligence heartbeat failed (${response.status})`);
195
+ const body = await response.json();
196
+ if (!body.attestation || !body.expires_at)
197
+ throw new Error("Unfairly Intelligence heartbeat returned no attestation");
198
+ await writePaceDevice({
199
+ ...device,
200
+ attestation: body.attestation,
201
+ attestation_expires_at: body.expires_at,
202
+ collection_policy_version: "metadata-v3",
203
+ });
204
+ return { installation_id: device.installation_id, clients };
205
+ }
206
+ export async function runPaceInstall() {
207
+ const clients = await detectPaceClients();
208
+ const { installIntelligence } = await import("../pace/install.js");
209
+ const installed = await installIntelligence({ version: RUNTIME_VERSION, clients });
210
+ const device = await readOrCreatePaceDevice();
211
+ await writePaceDevice({
212
+ ...device,
213
+ collection_policy_version: "metadata-v3",
214
+ intelligence_install: {
215
+ version: RUNTIME_VERSION,
216
+ runtime_entrypoint: installed.runtime_entrypoint,
217
+ marketplace_root: installed.marketplace_root,
218
+ owned_paths: installed.owned_paths,
219
+ activated_clients: installed.activations.filter((item) => item.status === "activated").map((item) => item.client),
220
+ installed_at: new Date().toISOString(),
221
+ },
222
+ });
223
+ await runPaceHeartbeat();
224
+ return {
225
+ runtime_entrypoint: installed.runtime_entrypoint,
226
+ marketplace_root: installed.marketplace_root,
227
+ activations: installed.activations,
228
+ verification: { heartbeat: "ok", live_events: "pending_first_session" },
229
+ };
230
+ }
231
+ export async function runPaceStatus() {
232
+ const queued = (await readPaceEvents()).length;
233
+ const device = await readOrCreatePaceDevice();
234
+ const clients = await detectPaceClients();
235
+ process.stdout.write(JSON.stringify({
236
+ enabled: !device.paused,
237
+ paused: Boolean(device.paused),
238
+ installation_id: device.installation_id,
239
+ attestation_expires_at: device.attestation_expires_at ?? null,
240
+ last_upload_at: device.last_upload_at ?? null,
241
+ runtime_version: RUNTIME_VERSION,
242
+ plugin_version: PLUGIN_VERSION,
243
+ collection_policy_version: device.collection_policy_version ?? INTELLIGENCE_POLICY.version,
244
+ queued,
245
+ spool: paceSpoolPath(),
246
+ clients,
247
+ }, null, 2) + "\n");
248
+ }
249
+ export async function runPaceConfig(input) {
250
+ const { writeManagedConfigs } = await import("../pace/managed-config.js");
251
+ const files = await writeManagedConfigs(input);
252
+ const device = await readOrCreatePaceDevice();
253
+ await writePaceDevice({ ...device, managed_files: files, collection_policy_version: "metadata-v3" });
254
+ return files;
255
+ }
256
+ export function runPacePolicy() {
257
+ return INTELLIGENCE_POLICY;
258
+ }
259
+ export async function runPacePause(paused) {
260
+ const device = await readOrCreatePaceDevice();
261
+ await writePaceDevice({ ...device, paused });
262
+ return { paused };
263
+ }
264
+ export async function runPaceUninstall() {
265
+ const device = await readOrCreatePaceDevice();
266
+ const activated = new Set(device.intelligence_install?.activated_clients ?? []);
267
+ const { spawnSync } = await import("node:child_process");
268
+ if (activated.has("codex")) {
269
+ spawnSync("codex", ["plugin", "remove", "ai-pace@unfairly-intelligence", "--json"], { encoding: "utf8", timeout: 30_000 });
270
+ spawnSync("codex", ["plugin", "marketplace", "remove", "unfairly-intelligence"], { encoding: "utf8", timeout: 30_000 });
271
+ }
272
+ if (activated.has("claude_code")) {
273
+ spawnSync("claude", ["plugin", "uninstall", "ai-pace@unfairly-intelligence", "--scope", "user"], { encoding: "utf8", timeout: 30_000 });
274
+ spawnSync("claude", ["plugin", "marketplace", "remove", "unfairly-intelligence", "--scope", "user"], { encoding: "utf8", timeout: 30_000 });
275
+ }
276
+ const permitted = new Set([
277
+ "codex-requirements.toml", "claude-settings.json", "gemini-settings.json",
278
+ "evidence-policy.json", "repository-policy.json",
279
+ ]);
280
+ let removed = 0;
281
+ for (const file of device.managed_files ?? []) {
282
+ const name = file.split(/[\\/]/).at(-1);
283
+ if (!name || !permitted.has(name))
284
+ continue;
285
+ await fs.unlink(file).then(() => { removed += 1; }).catch((error) => {
286
+ if (error.code !== "ENOENT")
287
+ throw error;
288
+ });
289
+ }
290
+ const safeOwnedRoots = [
291
+ path.join(os.homedir(), ".unfairly"),
292
+ path.join(os.homedir(), ".cursor", "plugins", "local", "ai-pace"),
293
+ path.join(os.homedir(), ".gemini", "extensions", "unfairly-intelligence"),
294
+ ];
295
+ for (const ownedPath of device.intelligence_install?.owned_paths ?? []) {
296
+ const resolved = path.resolve(ownedPath);
297
+ if (!safeOwnedRoots.some((root) => resolved === root || resolved.startsWith(`${root}${path.sep}`)))
298
+ continue;
299
+ await fs.rm(resolved, { recursive: true, force: true });
300
+ removed += 1;
301
+ }
302
+ await fs.unlink(paceSpoolPath()).catch((error) => {
303
+ if (error.code !== "ENOENT")
304
+ throw error;
305
+ });
306
+ await fs.unlink(paceDevicePath()).catch((error) => {
307
+ if (error.code !== "ENOENT" && error.code !== "EISDIR")
308
+ throw error;
309
+ });
310
+ return { removed_files: removed, retained_global_credentials: true };
311
+ }
312
+ export async function runPaceEvidenceScan(policyPath) {
313
+ const { scanLocalEvidence } = await import("../pace/local-evidence.js");
314
+ const device = await readOrCreatePaceDevice();
315
+ const result = await scanLocalEvidence({ policyPath, installationId: device.installation_id });
316
+ await Promise.all(result.events.map((event) => appendPaceEvent(event)));
317
+ return { observed: result.events.length, baseline_roots: result.baselineRoots, queued: (await readPaceEvents()).length };
318
+ }
319
+ export async function runPaceEvidenceWatch(policyPath) {
320
+ const { readLocalEvidencePolicy } = await import("../pace/local-evidence.js");
321
+ const policy = await readLocalEvidencePolicy(policyPath);
322
+ process.stdout.write(`Unfairly Intelligence is watching ${policy.roots.length} approved local evidence root(s).\n`);
323
+ const scan = async () => {
324
+ const result = await runPaceEvidenceScan(policyPath);
325
+ if (result.observed)
326
+ await runPaceFlush().catch(() => undefined);
327
+ };
328
+ await scan();
329
+ await new Promise((resolve) => {
330
+ const timer = setInterval(() => { void scan(); }, policy.scan_interval_seconds * 1000);
331
+ const stop = () => { clearInterval(timer); resolve(); };
332
+ process.once("SIGINT", stop);
333
+ process.once("SIGTERM", stop);
334
+ });
335
+ }
336
+ export async function runPacePlaybooks(query) {
337
+ const token = await getToken();
338
+ if (!token)
339
+ throw new Error("Unfairly Intelligence is not enrolled. Run `unfairly setup` first.");
340
+ const apiUrl = (process.env.UNFAIRLY_API_URL ?? DEFAULT_API).replace(/\/$/, "");
341
+ const params = query?.trim() ? `?q=${encodeURIComponent(query.trim())}` : "";
342
+ const response = await fetch(`${apiUrl}/api/ai-pace/playbooks${params}`, {
343
+ headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
344
+ });
345
+ if (!response.ok)
346
+ throw new Error(`Unfairly Intelligence playbooks failed (${response.status})`);
347
+ const body = await response.json();
348
+ return body.playbooks ?? [];
349
+ }
350
+ export async function runPacePlaybookUse(playbookId, playbookVersion) {
351
+ const token = await getToken();
352
+ if (!token)
353
+ throw new Error("Unfairly Intelligence is not enrolled. Run `unfairly setup` first.");
354
+ const device = await readOrCreatePaceDevice();
355
+ if (device.paused)
356
+ throw new Error("Unfairly Intelligence is paused. Run `unfairly intelligence resume` first.");
357
+ const workflowId = randomUUID();
358
+ const sourceRecordId = randomUUID();
359
+ const event = PaceEventV3Schema.parse({
360
+ schema_version: 3,
361
+ collection_policy_version: "metadata-v3",
362
+ event_id: randomUUID(),
363
+ installation_id: device.installation_id,
364
+ occurred_at: new Date().toISOString(),
365
+ source: "unknown",
366
+ event_type: "session.started",
367
+ evidence_level: "exact",
368
+ session_id: sourceRecordId,
369
+ workflow_id: workflowId,
370
+ playbook_id: playbookId,
371
+ playbook_version: playbookVersion,
372
+ attributes: { client_event: "session_start" },
373
+ });
374
+ await appendPaceEvent(event);
375
+ const apiUrl = (process.env.UNFAIRLY_API_URL ?? DEFAULT_API).replace(/\/$/, "");
376
+ const response = await fetch(`${apiUrl}/api/ai-pace/playbooks`, {
377
+ method: "POST",
378
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
379
+ body: JSON.stringify({
380
+ action: "record",
381
+ playbook_id: playbookId,
382
+ playbook_version: Number(playbookVersion),
383
+ event_type: "started",
384
+ source_record_id: sourceRecordId,
385
+ workflow_id: workflowId,
386
+ }),
387
+ });
388
+ // The event remains safely spooled when the marketplace endpoint is offline.
389
+ if (!response.ok && response.status < 500)
390
+ throw new Error(`Unfairly Intelligence playbook use failed (${response.status})`);
391
+ return { workflow_id: workflowId, queued: (await readPaceEvents()).length };
392
+ }
393
+ //# sourceMappingURL=pace.js.map