@papi-ai/server 0.7.41 → 0.7.43

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.
@@ -82,7 +82,12 @@ Your output is **not done** if ANY of these is true (these are falsifiable — c
82
82
  - [ ] Gradient text or gradient fill used decoratively (the generic-AI look).
83
83
  - [ ] Your brand accent used as a glow / dark-mode-only sheen instead of solid, intentional colour.
84
84
  - [ ] An "earned" signal colour (insight/intelligence/premium) used as a plain decorative fill.
85
- - [ ] No single dominant above-the-fold hero, or two elements competing for dominance.
85
+ - [ ] Two elements competing for dominance (both fight for the eye). NOTE: a page with NO hero is
86
+ fine when nothing must lead — do not manufacture one to satisfy this line; only *competing*
87
+ dominance fails (see The core principle).
88
+ - [ ] Guidance, state, or a next action explained in a sentence of helper text where a visual cue
89
+ (position, size, colour, icon, a signpost, motion-on-change) would carry it — prose used as a
90
+ crutch for missing visual steering.
86
91
  - [ ] More than ~3 sections fully visible above the fold.
87
92
  - [ ] Identical-sized cards in a uniform row (flat hierarchy — needs a dominant 2×+ cell).
88
93
  - [ ] Monospace on labels, status, timestamps, or nav.
@@ -121,6 +126,16 @@ Return a tight report (this is data, not prose):
121
126
 
122
127
  ## The core principle
123
128
 
124
- **Composition > decoration.** A page with perfect tokens and flat hierarchy is worse than one with
125
- slightly-off tokens and a clear hero, reading order, and earned elements. One hero, clear reading
126
- order, everything earns its space.
129
+ **Composition > decoration, and show > tell.** A page with perfect tokens and flat hierarchy is
130
+ worse than one with slightly-off tokens and a clear reading order where every element earns its
131
+ space. Two rules generalist agents keep getting wrong:
132
+
133
+ - **Steer with visuals, not text.** When the user needs guidance — what to do, where to look, what
134
+ state something is in — reach for a VISUAL cue first: position, size, colour, an icon, a
135
+ connecting line, a spatial signpost, motion on change. A sentence of helper text is the last
136
+ resort, not the first. Over-explaining in prose is the default AI failure mode; a well-placed
137
+ arrow, a pulse, or a bolded number beats a paragraph. Visual steering and motion beat words.
138
+ - **A hero is conditional, not mandatory.** Give the page one dominant focal point WHEN it has a
139
+ single thing that must lead. Dense tool, list, board, and settings surfaces can carry a calm, even
140
+ hierarchy — do not manufacture a hero, an oversized stat, or a promoted banner where the content
141
+ has no natural lead. Only *competing* dominance is always wrong; the absence of a hero is not.
@@ -48,15 +48,17 @@ Plus a one-line page read: `kind / audience / the one thing they must see first`
48
48
  3. **Read the target page in full** — not just the component you're editing. You need full page
49
49
  context to judge compositional hierarchy.
50
50
  4. **List all sibling components** on the page. You'll weigh each one's visual weight against the others.
51
- 5. **Check both states:** populated and empty/zero-data. Both need a hero and hierarchy.
51
+ 5. **Check both states:** populated and empty/zero-data. Both need clear hierarchy (a hero only where one is warranted — see Check 1).
52
52
 
53
53
  ## Step 2: The Seven Checks
54
54
 
55
55
  For each, give ✅ Pass / ⚠️ Weak / ❌ Fail plus a one-line reason.
56
56
 
57
- ### Check 1: Hero Element
58
- > Every page has exactly ONE dominant visual element above the fold.
59
- Name it. If you can't, or two things compete, it's ❌. Squint test: blurred, only the hero shows.
57
+ ### Check 1: Hero Element (conditional — NOT every page needs one)
58
+ > Give the page one dominant element WHEN it has one thing that must lead. A hero is not mandatory.
59
+ Does this page have a single thing that must lead? If yes: name it; missing/weak = ⚠️/❌. If no (a
60
+ dense tool / list / board / settings surface): a calm, even hierarchy is ✅ — do NOT manufacture a
61
+ hero to pass this check. Two elements *competing* for dominance is ALWAYS ❌ (the real failure).
60
62
 
61
63
  ### Check 2: Reading Order
62
64
  > hero → primary action → supporting content → tertiary.
@@ -101,7 +103,11 @@ Run through every named anti-pattern and flag matches. Common universal ones:
101
103
  - Gradient text or gradient fill used decoratively.
102
104
  - Brand accent as a glow / dark-only sheen instead of solid intentional colour.
103
105
  - A "signal" colour used as a plain decorative fill.
104
- - No single dominant above-fold hero, or two competing for it.
106
+ - Two elements competing for dominance (both fight for the eye). NOT a fail: a page with no hero
107
+ when nothing must lead (see Check 1) — only *competing* dominance fails here.
108
+ - Guidance, state, or a next action carried by a sentence of helper text where a visual cue
109
+ (position, size, colour, an icon, a signpost, motion-on-change) would do — prose as a crutch for
110
+ missing visual steering. Prefer showing over telling.
105
111
  - Identical-sized cards in a uniform row (flat hierarchy).
106
112
  - Monospace on labels / status / timestamps / nav.
107
113
  - Header, label, and value at the same size+weight.
@@ -158,5 +164,10 @@ The pre-build critique catches composition; this catches what's only visible in
158
164
 
159
165
  ## The Core Principle
160
166
 
161
- **Composition > Decoration.** A page with perfect tokens and flat hierarchy is worse than one with
162
- slightly-off tokens and clear hierarchy. One hero, clear reading order, everything earns its space.
167
+ **Composition > Decoration, and show > tell.** A page with perfect tokens and flat hierarchy is
168
+ worse than one with slightly-off tokens and clear hierarchy. Two things generalist agents keep
169
+ getting wrong: (1) **steer with visuals, not text** — reach for position, size, colour, an icon, a
170
+ signpost, or motion-on-change before a sentence of helper text; over-explaining in prose is the
171
+ default AI failure mode. (2) **A hero is conditional** — give a page one dominant element only when
172
+ it has one thing that must lead; dense list / tool / settings surfaces carry a calm, even hierarchy,
173
+ so do not manufacture a hero. Clear reading order, everything earns its space, show before you tell.
@@ -73,7 +73,7 @@ __export(git_exports, {
73
73
  taskBranchName: () => taskBranchName,
74
74
  withBaseBranchSync: () => withBaseBranchSync
75
75
  });
76
- import { execFileSync } from "child_process";
76
+ import { execFileSync, spawnSync } from "child_process";
77
77
  function isGitAvailable() {
78
78
  try {
79
79
  execFileSync("git", ["--version"], { stdio: "ignore" });
@@ -317,21 +317,24 @@ function gitPull(cwd) {
317
317
  }
318
318
  }
319
319
  function gitPush(cwd, branch) {
320
- try {
321
- execFileSync("git", ["push", "-u", "origin", branch], {
322
- cwd,
323
- encoding: "utf-8",
324
- timeout: GIT_NETWORK_TIMEOUT_MS
325
- });
326
- return { success: true, message: `Pushed branch '${branch}' to origin.` };
327
- } catch (err) {
328
- const msg = err instanceof Error ? err.message : String(err);
329
- const isTimeout = msg.includes("ETIMEDOUT") || msg.includes("killed");
330
- return {
331
- success: false,
332
- message: isTimeout ? `Push timed out after 30s.` : `Push failed: ${msg}`
333
- };
320
+ const result = spawnSync("git", ["push", "-u", "origin", branch], {
321
+ cwd,
322
+ encoding: "utf-8",
323
+ timeout: GIT_NETWORK_TIMEOUT_MS
324
+ });
325
+ if (result.error) {
326
+ const msg = result.error.message;
327
+ const isTimeout = msg.includes("ETIMEDOUT") || result.signal === "SIGTERM" || msg.includes("killed");
328
+ return { success: false, pushed: false, message: isTimeout ? "Push timed out after 30s." : `Push failed: ${msg}` };
334
329
  }
330
+ const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
331
+ if (result.status !== 0) {
332
+ return { success: false, pushed: false, message: `Push failed: ${output || `git exited ${result.status}`}` };
333
+ }
334
+ if (/Everything up-to-date/i.test(output)) {
335
+ return { success: true, pushed: false, message: `'${branch}' already up to date on origin \u2014 nothing to push.` };
336
+ }
337
+ return { success: true, pushed: true, message: `Pushed '${branch}' to origin.` };
335
338
  }
336
339
  function isGhAvailable() {
337
340
  try {
@@ -1012,6 +1015,48 @@ var init_git = __esm({
1012
1015
  }
1013
1016
  });
1014
1017
 
1018
+ // src/lib/install-id.ts
1019
+ import { randomUUID as randomUUID7 } from "crypto";
1020
+ import { mkdirSync, readFileSync, writeFileSync, chmodSync } from "fs";
1021
+ import { homedir } from "os";
1022
+ import { join as join2 } from "path";
1023
+ var PAPI_HOME_DIR, INSTALL_ID_FILE;
1024
+ var init_install_id = __esm({
1025
+ "src/lib/install-id.ts"() {
1026
+ "use strict";
1027
+ PAPI_HOME_DIR = join2(homedir(), ".papi");
1028
+ INSTALL_ID_FILE = join2(PAPI_HOME_DIR, "install-id.json");
1029
+ }
1030
+ });
1031
+
1032
+ // src/lib/telemetry.ts
1033
+ function reportTelemetryEmitFailure(reason, ctx) {
1034
+ consecutiveTelemetryFailures += 1;
1035
+ console.error(
1036
+ `[telemetry] emit failed (${reason}) tool=${ctx.toolName} event=${ctx.eventType} project=${ctx.projectId}`
1037
+ );
1038
+ if (consecutiveTelemetryFailures >= TELEMETRY_BLACKOUT_THRESHOLD) {
1039
+ console.error(
1040
+ `[telemetry] BLACKOUT: ${consecutiveTelemetryFailures} consecutive telemetry emit failures \u2014 activation metrics are under-counting for this pod. Check the data-proxy /telemetry path (auth/RLS/network/schema).`
1041
+ );
1042
+ }
1043
+ }
1044
+ function noteTelemetryEmitSuccess() {
1045
+ consecutiveTelemetryFailures = 0;
1046
+ }
1047
+ var HOSTED_SUPABASE_URL, DEFAULT_TELEMETRY_ENDPOINT, MD_PINGS_SUPABASE_URL, consecutiveTelemetryFailures, TELEMETRY_BLACKOUT_THRESHOLD;
1048
+ var init_telemetry = __esm({
1049
+ "src/lib/telemetry.ts"() {
1050
+ "use strict";
1051
+ init_install_id();
1052
+ HOSTED_SUPABASE_URL = process.env["PAPI_HOSTED_SUPABASE_URL"] ?? "https://guewgygcpcmrcoppihzx.supabase.co";
1053
+ DEFAULT_TELEMETRY_ENDPOINT = `${HOSTED_SUPABASE_URL}/functions/v1/data-proxy`;
1054
+ MD_PINGS_SUPABASE_URL = process.env["PAPI_MD_PINGS_URL"] ?? HOSTED_SUPABASE_URL;
1055
+ consecutiveTelemetryFailures = 0;
1056
+ TELEMETRY_BLACKOUT_THRESHOLD = 3;
1057
+ }
1058
+ });
1059
+
1015
1060
  // src/proxy-adapter.ts
1016
1061
  var proxy_adapter_exports = {};
1017
1062
  __export(proxy_adapter_exports, {
@@ -1058,6 +1103,7 @@ var JSONB_PASSTHROUGH_KEYS, DISPLAY_ID_METHODS, ProxyPapiAdapter;
1058
1103
  var init_proxy_adapter = __esm({
1059
1104
  "src/proxy-adapter.ts"() {
1060
1105
  "use strict";
1106
+ init_telemetry();
1061
1107
  JSONB_PASSTHROUGH_KEYS = /* @__PURE__ */ new Set([
1062
1108
  "buildHandoff",
1063
1109
  "stateHistory",
@@ -1420,6 +1466,7 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
1420
1466
  * telemetry failure must never surface as a tool error.
1421
1467
  */
1422
1468
  emitTelemetry(event) {
1469
+ const ctx = { projectId: event.projectId, toolName: event.toolName, eventType: event.eventType };
1423
1470
  fetch(`${this.endpoint}/telemetry`, {
1424
1471
  method: "POST",
1425
1472
  headers: {
@@ -1433,7 +1480,11 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
1433
1480
  metadata: event.metadata ?? {}
1434
1481
  }),
1435
1482
  signal: AbortSignal.timeout(5e3)
1436
- }).catch(() => {
1483
+ }).then((res) => {
1484
+ if (res.ok) noteTelemetryEmitSuccess();
1485
+ else reportTelemetryEmitFailure(`HTTP ${res.status}`, ctx);
1486
+ }).catch((err) => {
1487
+ reportTelemetryEmitFailure(err instanceof Error ? err.name : "network error", ctx);
1437
1488
  });
1438
1489
  }
1439
1490
  readToolMetrics() {
@@ -1525,6 +1576,19 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
1525
1576
  getStrategyReviews(limit, includeFullAnalysis) {
1526
1577
  return this.invoke("getStrategyReviews", [limit, includeFullAnalysis]);
1527
1578
  }
1579
+ // --- Strategy Review Agenda (SUP-2026-026 — hosted parity) ---
1580
+ // Present on the pg/md adapters since C302 but never forwarded here, so
1581
+ // `strategy_agenda` failed for every hosted user. The proxy-parity guard missed
1582
+ // it because it only checked forwarded⊆allowed, never pg⊆forwarded (now fixed).
1583
+ addAgendaTopic(input) {
1584
+ return this.invoke("addAgendaTopic", [input]);
1585
+ }
1586
+ getPendingAgendaTopics() {
1587
+ return this.invoke("getPendingAgendaTopics");
1588
+ }
1589
+ markAgendaTopicsAddressed(ids, cycleNumber) {
1590
+ return this.invoke("markAgendaTopicsAddressed", [ids, cycleNumber]);
1591
+ }
1528
1592
  // --- Dogfood ---
1529
1593
  writeDogfoodEntries(entries) {
1530
1594
  return this.invoke("writeDogfoodEntries", [entries]);
@@ -1763,12 +1827,12 @@ import { pathToFileURL } from "url";
1763
1827
  import path2 from "path";
1764
1828
  import { execSync } from "child_process";
1765
1829
 
1766
- // ../adapter-md/dist/index.js
1830
+ // ../../../../../packages/adapter-md/dist/index.js
1767
1831
  import { readFile, writeFile, access } from "fs/promises";
1768
1832
  import { randomUUID as randomUUID6 } from "crypto";
1769
1833
  import { join } from "path";
1770
1834
 
1771
- // ../shared/dist/index.js
1835
+ // ../../../../../packages/shared/dist/index.js
1772
1836
  var VALID_TRANSITIONS = {
1773
1837
  "Backlog": ["In Cycle", "Ready", "In Progress", "Blocked", "Cancelled", "Deferred", "Done"],
1774
1838
  "In Cycle": ["In Progress", "Backlog", "Blocked", "Cancelled"],
@@ -1787,7 +1851,7 @@ function isLiveDecision(d) {
1787
1851
  return true;
1788
1852
  }
1789
1853
 
1790
- // ../adapter-md/dist/index.js
1854
+ // ../../../../../packages/adapter-md/dist/index.js
1791
1855
  import { randomUUID } from "crypto";
1792
1856
  import { randomUUID as randomUUID3 } from "crypto";
1793
1857
  import yaml from "js-yaml";
@@ -4064,8 +4128,8 @@ function detectUserId() {
4064
4128
  }
4065
4129
  return void 0;
4066
4130
  }
4067
- var HOSTED_SUPABASE_URL = process.env["PAPI_HOSTED_SUPABASE_URL"] ?? "https://guewgygcpcmrcoppihzx.supabase.co";
4068
- var HOSTED_PROXY_ENDPOINT = `${HOSTED_SUPABASE_URL}/functions/v1/data-proxy`;
4131
+ var HOSTED_SUPABASE_URL2 = process.env["PAPI_HOSTED_SUPABASE_URL"] ?? "https://guewgygcpcmrcoppihzx.supabase.co";
4132
+ var HOSTED_PROXY_ENDPOINT = `${HOSTED_SUPABASE_URL2}/functions/v1/data-proxy`;
4069
4133
  var PLACEHOLDER_PATTERNS = [
4070
4134
  "<YOUR_DATABASE_URL>",
4071
4135
  "your-database-url",